From 2299d7a02f6a974311ff84455cf74cec5cd7dd99 Mon Sep 17 00:00:00 2001 From: "Claude (noether)" Date: Wed, 20 May 2026 11:05:36 +0200 Subject: [PATCH] Revert "linux-pinetab2-danctnix-besser: pkgrel=6 (per-series reconstruction, kernel-agent#29 done proper)" This reverts commit 31da35a54971daa92e5f354d0bbc839e1c6f5a9e. --- ...s2600-besser-kernel-agent-cumulative.patch | 9551 ++++++----------- arch/linux-pinetab2-danctnix-besser/PKGBUILD | 4 +- .../manifest.lock | 170 +- 3 files changed, 3314 insertions(+), 6411 deletions(-) diff --git a/arch/linux-pinetab2-danctnix-besser/0001-bes2600-besser-kernel-agent-cumulative.patch b/arch/linux-pinetab2-danctnix-besser/0001-bes2600-besser-kernel-agent-cumulative.patch index c6818aff2..1b2df3e32 100644 --- a/arch/linux-pinetab2-danctnix-besser/0001-bes2600-besser-kernel-agent-cumulative.patch +++ b/arch/linux-pinetab2-danctnix-besser/0001-bes2600-besser-kernel-agent-cumulative.patch @@ -1,85 +1,306 @@ -From 4a1bbc7444c94be044fae4377ccd612a6cd28460 Mon Sep 17 00:00:00 2001 +From 4e176b8f930373bc02382c903e6d739ab2d5fd47 Mon Sep 17 00:00:00 2001 From: Markus Fritsche -Date: Wed, 22 Apr 2026 10:09:44 +0200 -Subject: [PATCH 01/29] bes2600: use request_firmware() for factory.txt read +Date: Fri, 8 May 2026 10:07:47 +0200 +Subject: [PATCH] bes2600: BESser cumulative patch series (16 commits squashed) -The BES2600 factory calibration file (bes2600_factory.txt) was being read -via filp_open() + kernel_read() from a hard-coded absolute path baked in -at compile time via the FACTORY_PATH Makefile macro -(default: /lib/firmware/bes2600_factory.txt). +This is a squashed cumulative diff of the marfrit/bes2600-dkms cleanups +branch (Mobian-flavor source) overlaid onto the v7.0-danctnix1 staging +tree, with danctnix-flavor adaptations applied: -This had several problems: + - timer_container_of() / timer_delete_sync() (vs Mobian's deprecated + from_timer / del_timer_sync) + - bes2600_config / bes2600_set_rts_threshold sigs include radio_idx + - bes2600_switch_bt orchestration removed (Mobian-only; not called + in danctnix tree) -1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5 - device-pine64-pinetab2) ships the file at - /lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The - driver logged '(NULL device *): read and check - /lib/firmware/bes2600_factory.txt error' on every boot on PineTab2 - running linux-pinetab2 6.19.10-danctnix1-1. +Patch series squashed: -2. Direct filesystem access via filp_open() / kernel_read() from a driver - is an anti-pattern that upstream rejects: drivers should use - request_firmware() to get binary data from userspace-managed firmware - directories. request_firmware() natively searches the firmware_class - path list (typically /lib/firmware + derivatives), associates the load - with a uevent, and respects the firmware-loading infrastructure. + 1. c5.1 bes2600/scan-defer-on-reject + 2. c5.1.1 widen scan-defer backoff to 30s and decay reject_count + 3. c5.2 recover wedged firmware via mmc_hw_reset on link break + 4. c6.1 gate PM indication completion on pending request + 5. c6.2 short-circuit wake handshake when chip is confirmed ACTIVE + 6. c7 self-detect when firmware does not honor PSM and skip + 7. c5.2.1 multi-function SDIO mmc_hw_reset rescan + 8. Patch A decrypt-storm fast-recover (ieee80211_connection_loss) + 9. Patch B connection-loss-storm bus_reset + 10. Patch F1 hw_scan SKB lifecycle UAF (cw1200 backport 86760e0d) + 11. Patch F2 init_common destroy_workqueue on error (cw1200 7ec8a926) + 12. Patch F3 atomic_add(1, x) -> atomic_inc(x) (cw1200 07f995ca) + 13. Patch C v3 drop sdio_rx_work relay, IRQ->bh-direct + 14. Patch G SPDX + ST-Ericsson attribution restoration + 15. Patch D ba_lock atomicization + drop spinlock + 16. Patch E ps_state_lock skip when pm_unsupported + 17. Patch C2 ieee80211_rx_irqsafe -> ieee80211_rx_ni + 18. Patch H bh.c hygiene cleanup (drop fossil blocks, dead stubs) -3. The (NULL device *) prefix in error messages indicated the absence of - proper device-context logging. While this patch does not yet thread - struct device through, the upstream path uses request_firmware() which - works with dev=NULL and is the building block for a follow-up patch - that adds per-chip device context. +Net: 48 files changed, ~1500 insertions, ~2000 deletions. -Repoint the FACTORY_PATH default to the firmware-class name -(bes2600/bes2600_factory.txt) - request_firmware() prepends -/lib/firmware/ from the configured search paths. The macro remains -overridable at build time for non-standard deployments. +Verified clean compile + +73% throughput vs Patch B baseline + race-free +under stress on the Mobian DKMS path. Danctnix-flavor build verification +deferred to PKGBUILD CI / Markus's test cycle. -Rewrite factory_section_read_file() to: - * Call request_firmware(&fw, path, NULL). - * Size-check fw->size against FACTORY_MAX_SIZE. - * memcpy the data into the caller's buffer. - * Always call release_firmware() on exit. - -The file write path (factory_section_write_file + kernel_write) is left -unchanged in this patch; it is the subject of a follow-up patch that -removes kernel_write and moves any remaining userspace-visible factory -configuration to a standard kernel-userspace boundary (debugfs or -nl80211 testmode). - -No caller signature changes. No Makefile flag drops. Bisectable. - -Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2 -6.19.10-danctnix1-1, deployed via /lib/modules//extra/. Verified -post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt -error' is gone; request_firmware reads the file successfully (a separate -factory_parse() bug, previously masked by the read failure, is now -exposed and tracked separately). +For per-patch history see git.reauktion.de/marfrit/bes2600-dkms cleanups +branch. Signed-off-by: Markus Fritsche --- - bes2600/Makefile | 2 +- - bes2600/bes2600_factory.c | 33 ++++++++++++++------------------- - 2 files changed, 15 insertions(+), 20 deletions(-) + drivers/staging/bes2600/ap.c | 25 +- + drivers/staging/bes2600/ap.h | 9 +- + drivers/staging/bes2600/bes2600.h | 58 +- + drivers/staging/bes2600/bes2600_factory.c | 117 ++- + drivers/staging/bes2600/bes2600_factory.h | 12 +- + drivers/staging/bes2600/bes2600_plat.h | 9 +- + drivers/staging/bes2600/bes2600_sdio.c | 268 +++++-- + drivers/staging/bes2600/bes_chardev.c | 65 +- + drivers/staging/bes2600/bes_chardev.h | 11 +- + drivers/staging/bes2600/bes_fw.c | 43 +- + drivers/staging/bes2600/bes_fw_common.c | 9 +- + drivers/staging/bes2600/bes_fw_common.h | 9 +- + drivers/staging/bes2600/bes_log.h | 30 + + .../bes2600/bes_nl80211_testmode_msg.h | 9 +- + drivers/staging/bes2600/bes_pwr.c | 243 +++++- + drivers/staging/bes2600/bes_pwr.h | 33 +- + drivers/staging/bes2600/bh.c | 732 ++++-------------- + drivers/staging/bes2600/bh.h | 21 +- + drivers/staging/bes2600/debug.c | 29 +- + drivers/staging/bes2600/debug.h | 12 +- + drivers/staging/bes2600/epta_coex.c | 9 +- + drivers/staging/bes2600/epta_coex.h | 9 +- + drivers/staging/bes2600/epta_request.c | 9 +- + drivers/staging/bes2600/epta_request.h | 9 +- + drivers/staging/bes2600/fwio.c | 12 +- + drivers/staging/bes2600/fwio.h | 12 +- + drivers/staging/bes2600/ht.h | 9 +- + drivers/staging/bes2600/hwio.c | 23 +- + drivers/staging/bes2600/hwio.h | 15 +- + drivers/staging/bes2600/itp.c | 12 +- + drivers/staging/bes2600/itp.h | 9 +- + drivers/staging/bes2600/main.c | 66 +- + drivers/staging/bes2600/pm.c | 12 +- + drivers/staging/bes2600/pm.h | 12 +- + drivers/staging/bes2600/queue.c | 26 +- + drivers/staging/bes2600/queue.h | 12 +- + drivers/staging/bes2600/sbus.h | 31 +- + drivers/staging/bes2600/scan.c | 133 +++- + drivers/staging/bes2600/scan.h | 23 +- + drivers/staging/bes2600/sta.c | 204 +++-- + drivers/staging/bes2600/sta.h | 12 +- + drivers/staging/bes2600/tx_loop.c | 9 +- + drivers/staging/bes2600/tx_loop.h | 9 +- + drivers/staging/bes2600/txrx.c | 168 ++-- + drivers/staging/bes2600/txrx.h | 12 +- + drivers/staging/bes2600/wifi_testmode_cmd.c | 9 +- + drivers/staging/bes2600/wsm.c | 29 +- + drivers/staging/bes2600/wsm.h | 16 +- + 48 files changed, 1412 insertions(+), 1243 deletions(-) -diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile -index 300912b..788aee2 100644 ---- a/drivers/staging/bes2600/Makefile -+++ b/drivers/staging/bes2600/Makefile -@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116 - ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y) - FACTORY_CRC_CHECK ?= n - STANDARD_FACTORY_EFUSE_FLAG ?= y --FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt -+FACTORY_PATH ?= bes2600/bes2600_factory.txt - endif +diff --git a/drivers/staging/bes2600/ap.c b/drivers/staging/bes2600/ap.c +index 7b1e3b42c..16c0451e0 100644 +--- a/drivers/staging/bes2600/ap.c ++++ b/drivers/staging/bes2600/ap.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * mac80211 STA and AP API for mac80211 BES2600 drivers ++ * AP mode for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ - # basic function + #include "bes2600.h" +@@ -17,7 +14,6 @@ + #include + #include "epta_request.h" + #include "epta_coex.h" +-#include "txrx_opt.h" + + #ifdef AP_HT_CAP_UPDATE + #define HT_INFO_OFFSET 4 +@@ -66,8 +62,11 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); + struct bes2600_link_entry *entry; + struct sk_buff *skb; ++ struct sk_buff_head local_drain; + struct bes2600_common *hw_priv = hw->priv; + ++ __skb_queue_head_init(&local_drain); ++ + #ifdef P2P_MULTIVIF + WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); + #endif +@@ -96,9 +95,17 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + priv->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = BES2600_LINK_HARD; +- while ((skb = skb_dequeue(&entry->rx_queue))) +- ieee80211_rx_irqsafe(priv->hw, skb); ++ /* ++ * Patch C2: splice the rx_queue out under the lock then deliver ++ * after unlock. ieee80211_rx_ni() runs the mac80211 RX path ++ * synchronously (formerly ieee80211_rx_irqsafe deferred to a ++ * tasklet); calling it from inside spin_lock_bh would hold the ++ * lock across mac80211's full RX dispatch. ++ */ ++ skb_queue_splice_init(&entry->rx_queue, &local_drain); + spin_unlock_bh(&priv->ps_state_lock); ++ while ((skb = __skb_dequeue(&local_drain))) ++ ieee80211_rx_ni(priv->hw, skb); + #ifdef AP_AGGREGATE_FW_FIX + hw_priv->connected_sta_cnt++; + if(hw_priv->connected_sta_cnt>1) { +diff --git a/drivers/staging/bes2600/ap.h b/drivers/staging/bes2600/ap.h +index 6f2785288..f6e88c617 100644 +--- a/drivers/staging/bes2600/ap.h ++++ b/drivers/staging/bes2600/ap.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * mac80211 STA and AP API for mac80211 BES2600 drivers ++ * AP mode interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include + #ifndef AP_H_INCLUDED +diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h +index 0e60960bb..32bce5ebf 100644 +--- a/drivers/staging/bes2600/bes2600.h ++++ b/drivers/staging/bes2600/bes2600.h +@@ -1,18 +1,15 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Common private data for BES2600 drivers ++ * Common private data for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu + * +- * Based on the islsm (softmac prism54) driver, which is: ++ * Based on the islsm (softmac prism54) driver, which is + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_H +@@ -356,15 +353,23 @@ struct bes2600_common { + * Keeping in common structure for the time being. Will be moved to VIFF + * after the mechanism is clear */ + u8 ba_tid_mask; +- int ba_acc; /*TODO: Same as above */ +- int ba_cnt; /*TODO: Same as above */ +- int ba_cnt_rx; /*TODO: Same as above */ +- int ba_acc_rx; /*TODO: Same as above */ +- int ba_hist; /*TODO: Same as above */ +- struct timer_list ba_timer;/*TODO: Same as above */ +- spinlock_t ba_lock; /*TODO: Same as above */ +- bool ba_ena; /*TODO: Same as above */ +- struct work_struct ba_work; /*TODO: Same as above */ ++ /* ++ * Patch D: ba_lock removed. Per-frame TX/RX hot-path bumped these ++ * counters under spin_lock_bh; the lock did not protect any ++ * compound invariant that atomic ops can't satisfy. Counters are ++ * now atomic_t; ba_armed gates the once-per-window mod_timer ++ * arm via cmpxchg so concurrent TX/RX at a fresh window each ++ * try to claim the arm and exactly one succeeds. ++ */ ++ atomic_t ba_acc; ++ atomic_t ba_cnt; ++ atomic_t ba_cnt_rx; ++ atomic_t ba_acc_rx; ++ atomic_t ba_armed; ++ int ba_hist; ++ struct timer_list ba_timer; ++ atomic_t ba_ena; ++ struct work_struct ba_work; + bool is_BT_Present; + bool is_go_thru_go_neg; + u8 conf_listen_interval; +@@ -511,6 +516,9 @@ struct bes2600_common { + struct list_head coex_event_list; + spinlock_t coex_event_lock; + ++ /* Connection-loss-storm fast-recover (Trigger A). See sta.c. */ ++ struct work_struct connection_loss_storm_recover_work; ++ + /* member for low power */ + struct bes2600_pwr_t bes_power; + +@@ -596,6 +604,11 @@ struct bes2600_vif { + unsigned long rx_timestamp; + u32 cipherType; + ++ /* Decrypt-storm fast-recover (Trigger B). See txrx.c. */ ++ unsigned long decrypt_storm_window_start; ++ unsigned int decrypt_storm_count; ++ unsigned int decrypt_storm_recoveries; ++ struct work_struct decrypt_storm_recover_work; + + /* AP powersave */ + u32 link_id_map; +@@ -622,6 +635,10 @@ struct bes2600_vif { + /* CQM Implementation */ + struct delayed_work bss_loss_work; + struct delayed_work connection_loss_work; ++ /* Connection-loss-storm fast-recover (Trigger A). See sta.c. */ ++ unsigned long connection_loss_storm_window_start; ++ unsigned int connection_loss_storm_count; ++ unsigned int connection_loss_storm_recoveries; + struct work_struct tx_failure_work; + int delayed_link_loss; + spinlock_t bss_loss_lock; +@@ -856,4 +873,13 @@ int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv); + void bes2600_btusb_uninit(struct usb_interface *interface); + #endif + ++/* Decrypt-storm fast-recover helpers — see txrx.c. */ ++void bes2600_decrypt_storm_init(struct bes2600_vif *priv); ++void bes2600_decrypt_storm_account(struct bes2600_vif *priv); ++ ++/* Connection-loss-storm fast-recover helpers — see sta.c. */ ++void bes2600_connection_loss_storm_init(struct bes2600_vif *priv); ++bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv); ++void bes2600_connection_loss_storm_recover(struct work_struct *work); ++ + #endif /* BES2600_H */ diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c -index dc5d3da..8d60b7c 100644 +index dc5d3dac6..0d2bfa1c8 100644 --- a/drivers/staging/bes2600/bes2600_factory.c +++ b/drivers/staging/bes2600/bes2600_factory.c -@@ -12,6 +12,7 @@ +@@ -1,17 +1,15 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Factory calibration loader for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include #include #include @@ -87,7 +308,26 @@ index dc5d3da..8d60b7c 100644 #include #include #include -@@ -137,38 +138,32 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data) +@@ -30,6 +28,18 @@ + + static DEFINE_MUTEX(factory_lock); + ++/* ++ * struct device * for request_firmware() context. Set once at SDIO ++ * probe via bes2600_factory_set_dev(). NULL is tolerated (falls back ++ * to the udev-less firmware-class path) but loses per-device logging. ++ */ ++static struct device *bes2600_factory_dev; ++ ++void bes2600_factory_set_dev(struct device *dev) ++{ ++ bes2600_factory_dev = dev; ++} ++ + /* + * It is only used for temporary storage. + * Every time get the factory, it will read from the +@@ -137,66 +147,32 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data) */ static int factory_section_read_file(char *path, void *buffer) { @@ -107,7 +347,7 @@ index dc5d3da..8d60b7c 100644 - fp = filp_open(path, O_RDONLY, 0); //S_IRUSR - if (IS_ERR(fp)) { - bes_devel("BES2600 : can't open %s\n",path); -+ ret = request_firmware(&fw, path, NULL); ++ ret = request_firmware(&fw, path, bes2600_factory_dev); + if (ret) { + bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret); return -1; @@ -133,180 +373,125 @@ index dc5d3da..8d60b7c 100644 - ret = -1; - } - +- return ret; +-} +- +-/** +- * factory_section_write_file - Write data of specified length to file +- * @path: path of the file +- * @buffer: storage of write data +- * @size: length of data to write +- * +- * Return: length on success, negative error code otherwise. +- */ +-static int factory_section_write_file(char *path, void *buffer, int size) +-{ +- int ret = 0; +- struct file *fp; +- +- bes_devel("writing %s \n", path); +- +- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR); +- if (IS_ERR(fp)) { +- bes_devel("BES2600 : can't open %s\n",path); +- return -1; +- } +- +- ret = kernel_write(fp, buffer, size, &fp->f_pos); +- +- filp_close(fp,NULL); +- + memcpy(buffer, fw->data, fw->size); + ret = (int)fw->size; + release_firmware(fw); return ret; } --- -2.54.0 - -From 13dd191defab19294d843218833860d0e1e33dcd Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Wed, 22 Apr 2026 12:17:56 +0200 -Subject: [PATCH 02/29] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for - PineTab2 factory.txt format -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The shipped factory calibration file bes2600_factory.txt on PineTab2 -(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration -fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15), -bt (4). The file terminates with '%%\n' directly after edr_power. - -When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver -assembles STANDARD_FACTORY with an extra select_efuse_flag section -appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31): - - __STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\" - + \"%%%%\\n\" - -The PineTab2 factory.txt has no select_efuse_flag section, so sscanf -stops after field 30 and factory_parse() returns -1 with: - - bes2600_factory.txt parse fail - read and check bes2600/bes2600_factory.txt error - factory cali data get failed. - -This was latent until the preceding patch (use request_firmware() for -factory.txt read) fixed the path bug that masked the parse failure. - -Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable -at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips / -firmware packages that do ship the select_efuse_flag section. - -Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was -inconsistently wrapped in a conditional that keyed on -STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and -the call site in sta.c are ungated. With the flag now defaulting to -n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the -conditional wrapper around the prototype — the function exists and is -used regardless of the factory-parse flag. - -Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2 -6.19.10-danctnix1-1. With the flag defaulted off, factory_parse() -succeeds on the shipped factory.txt, factory_cali_data is populated, -and dmesg no longer shows the parse-fail / read-and-check-error / -factory-cali-data-get-failed sequence. - -Signed-off-by: Markus Fritsche ---- - bes2600/Makefile | 2 +- - bes2600/wsm.h | 2 -- - 2 files changed, 1 insertion(+), 3 deletions(-) - -diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile -index 788aee2..2dcba09 100644 ---- a/drivers/staging/bes2600/Makefile -+++ b/drivers/staging/bes2600/Makefile -@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116 - - ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y) - FACTORY_CRC_CHECK ?= n --STANDARD_FACTORY_EFUSE_FLAG ?= y -+STANDARD_FACTORY_EFUSE_FLAG ?= n - FACTORY_PATH ?= bes2600/bes2600_factory.txt - endif - -diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h -index 0673131..22845ac 100644 ---- a/drivers/staging/bes2600/wsm.h -+++ b/drivers/staging/bes2600/wsm.h -@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv); - - int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status); - --#if defined(STANDARD_FACTORY_EFUSE_FLAG) - int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type); --#endif - #endif /* BES2600_HWIO_H_INCLUDED */ --- -2.54.0 - -From 40a0a1a0c72ae5b4ee538f6e8a5d0def522606af Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Wed, 22 Apr 2026 13:18:38 +0200 -Subject: [PATCH 03/29] bes2600: thread struct device * through factory - request_firmware() call - -Follow-up to \"bes2600: use request_firmware() for factory.txt read\". -That patch switched the factory calibration read path from filp_open() -+ kernel_read() to request_firmware(), but passed dev=NULL to -request_firmware() because factory_section_read_file() did not have a -struct device * in scope. The resulting logs carry the -'(NULL device *):' prefix and do not propagate a udev association. - -Add a module-local static struct device * used as the firmware-class -load context, plus a small exported setter: - - static struct device *bes2600_factory_dev; - void bes2600_factory_set_dev(struct device *dev); - -Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(), -right after bes2600_platform_data_init() so the platform layer has -already had a chance to use the same struct device for its own -initialization. - -factory_section_read_file() now passes bes2600_factory_dev (instead -of NULL) to request_firmware(). When the factory read happens before -probe (not currently the case on PineTab2) the pointer is still NULL -and request_firmware() accepts that; no regression. - -No API changes to bes2600_get_factory_cali_data() callers. The -char *path parameter remains (it is the firmware-class name fed -straight to request_firmware()). - -Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2 -6.19.10-danctnix1-1. Driver probes, factory data is read, and any -post-c5 factory diagnostics now carry the SDIO device identity -instead of '(NULL device *)'. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes2600_factory.c | 14 +++++++++++++- - bes2600/bes2600_factory.h | 3 +++ - bes2600/bes2600_sdio.c | 4 ++++ - 3 files changed, 20 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c -index 8d60b7c..1cda447 100644 ---- a/drivers/staging/bes2600/bes2600_factory.c -+++ b/drivers/staging/bes2600/bes2600_factory.c -@@ -31,6 +31,18 @@ - - static DEFINE_MUTEX(factory_lock); +@@ -891,9 +867,22 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory) + #endif + } +/* -+ * struct device * for request_firmware() context. Set once at SDIO -+ * probe via bes2600_factory_set_dev(). NULL is tolerated (falls back -+ * to the udev-less firmware-class path) but loses per-device logging. ++ * Rebuild the serialised calibration blob in file_buffer from the live ++ * in-memory factory_save_p. Previously this function also persisted the ++ * blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that ++ * is not acceptable in mainline, so the persistence step has been removed. ++ * ++ * The in-memory factory_save_p remains authoritative for the duration of ++ * the session; on the next probe the firmware-class file is read back ++ * read-only via request_firmware(). If cross-reboot persistence of runtime ++ * calibration updates becomes a requirement, the expected route is a ++ * userspace-facing dump interface (debugfs read-only blob, or nl80211 ++ * vendor command) that lets userspace read the serialised form and store ++ * it under its own privileges. + */ -+static struct device *bes2600_factory_dev; -+ -+void bes2600_factory_set_dev(struct device *dev) -+{ -+ bes2600_factory_dev = dev; -+} -+ - /* - * It is only used for temporary storage. - * Every time get the factory, it will read from the -@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer) + static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p) + { +- int ret = 0; + int w_size; + u32 crc_len = sizeof(factory_data_t); + #ifndef STANDARD_FACTORY_EFUSE_FLAG +@@ -902,13 +891,11 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto - bes_devel("requesting firmware-class %s\n", path); + bes_devel("enter %s\n", __func__); + +- if (!file_buffer) { ++ if (!file_buffer) + return -ENOMEM; +- } + +- if (!factory_save_p) { ++ if (!factory_save_p) + return -ENOENT; +- } + + /* All initialized to space */ + memset(file_buffer, 32, FACTORY_MAX_SIZE); +@@ -920,22 +907,10 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto + w_size = factory_build(file_buffer, factory_save_p); + + if (w_size < 0 || w_size > FACTORY_MAX_SIZE) { +- bes_err("%s: build failed! ret = %d.", __func__, ret); ++ bes_err("%s: build failed! w_size = %d.", __func__, w_size); + return -ETXTBSY; + } + +-#ifdef FACTORY_SAVE_MULTI_PATH +- /* avoid trailing characters '\0' */ +- file_buffer[w_size] = 32; +- ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE); +-#else +- ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size); +-#endif +- if(ret < 0) { +- bes_err("%s: write failed! ret = %d.", __func__, ret); +- return ret; +- } +- + return 0; + } -- ret = request_firmware(&fw, path, NULL); -+ ret = request_firmware(&fw, path, bes2600_factory_dev); - if (ret) { - bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret); - return -1; diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h -index 3835b0d..7dbe9f8 100644 +index 3835b0d9c..0b1a321d7 100644 --- a/drivers/staging/bes2600/bes2600_factory.h +++ b/drivers/staging/bes2600/bes2600_factory.h -@@ -199,6 +199,9 @@ enum factory_cali_status { +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Factory calibration loader interface + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef __FACTORY_H__ + #define __FACTORY_H__ +@@ -199,6 +196,9 @@ enum factory_cali_status { /* just calibrate 11n, other protocols are automatically mapped */ #define WIFI_RF_11N_MODE 0x15 @@ -316,1088 +501,86 @@ index 3835b0d..7dbe9f8 100644 /* read wifi & bt factory cali value*/ u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path); void factory_little_endian_cvrt(u8 *data); +diff --git a/drivers/staging/bes2600/bes2600_plat.h b/drivers/staging/bes2600/bes2600_plat.h +index 63c32750e..ebec63591 100644 +--- a/drivers/staging/bes2600/bes2600_plat.h ++++ b/drivers/staging/bes2600/bes2600_plat.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Platform data for BES2600 SDIO bus + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef BES2600_PLAT_H_INCLUDED + #define BES2600_PLAT_H_INCLUDED diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index 13d4ff1..f172d53 100644 +index 13d4ff1e5..517e6f874 100644 --- a/drivers/staging/bes2600/bes2600_sdio.c +++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -30,6 +30,7 @@ +@@ -1,12 +1,13 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 SDIO driver for BES2600 device ++ * SDIO bus glue for BES2600 mac80211 driver ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. ++ * Derived from drivers/net/wireless/st/cw1200/cw1200_sdio.c ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin + * +- * Copyright (c) 2010, Bestechnic +- * Author: + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #define DEBUG 1 + #include +@@ -16,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -28,8 +30,10 @@ + #include + #include "bes2600.h" ++#include "bh.h" #include "sbus.h" #include "bes2600_plat.h" +#include "bes2600_factory.h" #include "hwio.h" #include "bes_chardev.h" #include "bes_log.h" -@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func, - if (ret) - goto err; - -+ /* wire struct device into factory.c for request_firmware() context */ -+ bes2600_factory_set_dev(dev); -+ - self->pdata = bes2600_get_platform_data(); - self->func = func; - self->dev = &func->dev; --- -2.54.0 - -From e8550e55fc7d3910ee690359d89d96c86cfb0347 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Wed, 22 Apr 2026 12:37:45 +0200 -Subject: [PATCH 04/29] bes2600: gate device LP-mode entry on successful - per-VIF firmware handshake - -bes2600_pwr_enter_lp_mode() drives the transition to low-power for each -associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on -pm_enter_cmpl for the firmware to acknowledge, then unconditionally -calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the -bus. - -Two bugs: - -1. A failed wsm_set_pm() only logs an error, then still falls into - wait_for_completion_timeout() on a completion the firmware will - never post (the set-mode command never reached it). The loop - therefore always blocks the full 5 s, logs a second error, and - proceeds. - -2. A genuine wait-timeout (firmware received the set-mode command but - never posted the indication) also only logs a warning. The code - then drops to bes2600_pwr_device_enter_lp_mode(), handing the - device subsystem an inconsistent view of mac-layer state. - -On PineTab2 (BES2600WM + RK3566) the second bug is the recurring -root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout' -message flooding dmesg every 5-10 s when the interface is associated -and idle. Sending the device to LP in that state cascades into the -SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work' -WARN splat. - -Fix: - - Add a 'timeouts' counter; bump it on both failure paths. - - Skip the wait_for_completion entirely when wsm_set_pm() failed - (there is no completion to wait for). - - Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF - handshake reached firmware-ACKed completion; otherwise return - -ETIMEDOUT and leave the device in its current power state. - -Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1. -Post-patch the handshake still fails on this particular firmware -revision (separate root-cause investigation outside this patch), but -the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg -and destabilising the SDIO path. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes_pwr.c | 20 +++++++++++++++++--- - 1 file changed, 17 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index e7a1045..f62ae22 100644 ---- a/drivers/staging/bes2600/bes_pwr.c -+++ b/drivers/staging/bes2600/bes_pwr.c -@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - int i = 0; - struct bes2600_vif *priv; - int ret = 0; -+ int timeouts = 0; - char ip_str[20]; - unsigned long status = 0; - -@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - if (ret) { - atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); - bes_err("%s, set operation mode fail\n", __func__); -+ timeouts++; -+ continue; - } - - /* wait power save mode changed indication */ - status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ); - atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); - reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); -- if (!status) -+ if (!status) { - bes_err("%s, wait pm ind timeout\n", __func__); -+ timeouts++; -+ } - } else { - bes_devel("skip enter lp mode\n"); - } - } - } - -- /* set device low power configuration */ -- bes2600_pwr_device_enter_lp_mode(hw_priv); +@@ -70,10 +74,12 @@ struct sbus_priv { + int rx_data_toggle; + #endif + #ifdef BES_SDIO_RX_MULTIPLE_ENABLE +- spinlock_t rx_queue_lock; +- struct sk_buff_head rx_queue; + /* -+ * Enter the device-end of the LP transition only if every per-VIF -+ * mac80211 handshake reached firmware-ACKed completion. Doing the -+ * device-LP setup while any VIF is still pending leaves the driver -+ * in an inconsistent state that cascades into SDIO TX errors on -+ * the BES2600. ++ * Patch C v3: rx_queue, rx_queue_lock, rx_work removed (no relay). ++ * The bh thread now reads RX inline; the rx_buffer scratch area ++ * stays. Counters/timestamps stay for debugfs visibility. + */ -+ if (timeouts == 0) -+ bes2600_pwr_device_enter_lp_mode(hw_priv); -+ else -+ ret = -ETIMEDOUT; - - return ret; - } --- -2.54.0 - -From cd5f85e10480f02e289ea731b5eeec571000562c Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Wed, 22 Apr 2026 12:55:18 +0200 -Subject: [PATCH 05/29] bes2600: remove userspace /dev/bes2600 character device - interface - -bes_chardev.c implemented a custom character device at /dev/bes2600 with -its own parser and command-dispatch table, exposing operations such as -'wifi on|off', 'bt on|off', 'change_fw_type ', 'bt_wakeup', -'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by -the in-tree driver - every kernel call site consumes the internal state -accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type, -etc) directly, not through the cdev. - -The cdev interface is a standing upstream blocker for two reasons: - - 1. Drivers under drivers/staging/ and drivers/net/wireless/ are - expected to expose tuning via the firmware/nl80211/debugfs - infrastructure rather than a private /dev node with an ad-hoc - parser. - - 2. The cdev handlers keep a global bes_cdev singleton alive whose - ->cdev, ->dev_id, ->class and ->device pointers exist only to be - torn down; they add no functionality that nl80211 or rfkill do - not already provide (wifi/bt on-off, module_param for fw_type). - -Remove the userspace interface: - - - open / read / write / release file_operations handlers and the - bes2600_chardev_fops instance - - bes2600_op_* command handlers and bes2600_op_map_tab dispatcher - - bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem - string helpers - - bes2600_load_uevent (its only caller was - bes2600_chrdev_wifi_force_close_work informing userspace of a - state it already gates via rfkill; that snprintf + - kobject_uevent_env block is gone too, the kernel-side - halt_device + switch_wifi(0) + chrdev_check_system_close - sequence remains) - - alloc_chrdev_region / cdev_init / cdev_add / class_create / - device_create in bes2600_chrdev_init plus the fail1/fail2/fail3 - unwind labels - - cdev_del / unregister_chrdev_region / device_destroy / - class_destroy in bes2600_chrdev_free - - cdev/dev_id/major/minor/class/device fields in struct bes_cdev - -What remains (unchanged behaviour): - - - fw_type module parameter - the primary user-facing knob for - signal/no-signal/BT mode switch - - All in-kernel bes2600_chrdev_* accessor functions called from - bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and - wifi_testmode_cmd.c (13 call sites) - - bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown - for the remaining bes_cdev state (waitqueues, workqueues, flags) - - DPD management (bes2600_chrdev_get_dpd_buffer / update / free) - - wifi_force_close worker, system-close logic, bus-probe state - machine - -Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2 -6.19.10-danctnix1-1. Driver continues to associate and pass traffic; -no kernel messages related to the cdev absence. Users that previously -wrote to /dev/bes2600 should switch to the fw_type module parameter -or (future patch c4) nl80211 testmode commands. - -Follow-ups: - - - c3.1: thread struct device * through bes2600_chrdev_is_signal_mode - and friends so the global bes2600_cdev singleton can be dropped - and the accessors scale to multi-device scenarios. - - c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode - commands to the firmware's patch_wifi_testMode entry. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes_chardev.c | 568 +----------------------------------------- - 1 file changed, 3 insertions(+), 565 deletions(-) - -diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c -index f89dcb8..e2e4f1b 100644 ---- a/drivers/staging/bes2600/bes_chardev.c -+++ b/drivers/staging/bes2600/bes_chardev.c -@@ -43,12 +43,6 @@ enum bus_probe_state { - }; - - struct bes_cdev { -- struct cdev cdev; -- dev_t dev_id; -- int major; -- int minor; -- struct class *class; -- struct device *device; - atomic_t num_proc; - wait_queue_head_t open_wq; - spinlock_t status_lock; -@@ -196,7 +190,7 @@ static int bes2600_switch_wifi(bool on) - return ret; - } - --static int bes2600_switch_bt(bool on) -+int bes2600_switch_bt(bool on) - { - int ret = 0; - long status = 0; -@@ -229,11 +223,11 @@ static int bes2600_switch_bt(bool on) - /* check if there is a error when bootup */ - ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0; - } else { -- bes_devel("bes2600 activate bt.\n"); -+ bes_info("enable BT\n"); - ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true); - } - } else { -- bes_devel("bes2600 deactivate bt.\n"); -+ bes_info("disable BT\n"); - bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false); - } - -@@ -249,392 +243,18 @@ static int bes2600_switch_bt(bool on) - return ret; - } - --/* -- * This is a global function so we don't have to make many changes to -- * the driver. -- * -- * @wifi: 1 to turn on, 0 to turn off. Otherwise, leave unchanged -- * @bt: 1 to turn on, 0 to turn off. Otherwise, leave unchanged -- */ --int bes2600_chrdev_switch_subsys_glb(int wifi, int bt) --{ -- int ret = 0; -- -- switch (wifi) { -- case 0: -- ret = bes2600_switch_wifi(false); -- break; -- case 1: -- ret = bes2600_switch_wifi(true); -- break; -- default: -- break; -- } -- -- if (ret) -- goto result; -- -- switch (bt) { -- case 0: -- ret = bes2600_switch_bt(false); -- break; -- case 1: -- ret = bes2600_switch_bt(true); -- break; -- default: -- break; -- } -- --result: -- return ret; --} --EXPORT_SYMBOL_GPL(bes2600_chrdev_switch_subsys_glb); -- --static int bes2600_get_cmd_and_ifname(const char *str, char **result) --{ -- int cmd_len = 0; -- int ifname_len = 0; -- char *sp = NULL; -- char *tmp_ptr = NULL; -- char *cmd_ptr = NULL; -- -- /* check if input arguments is valid */ -- if (!str || strncmp(str, "ifname:", 7) != 0) -- return -1; -- -- sp = strchr(str, ' '); -- if (strncmp(sp + 1, "cmd:", 4) != 0) -- return -1; -- -- /* extract interface name */ -- ifname_len = sp - str - 7; -- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL); -- if (!tmp_ptr) { -- return -2; -- } -- -- strncpy(tmp_ptr, str+7, ifname_len); -- tmp_ptr[ifname_len] = '\0'; -- result[0] = tmp_ptr; -- -- /* get command length */ -- cmd_ptr = strstr(str, "cmd:"); -- cmd_ptr += 4; -- sp = strchr(cmd_ptr, ' '); -- if (!sp) { /* the command don't have any parameter */ -- cmd_len = strlen(cmd_ptr); -- if (cmd_ptr[cmd_len - 1] == '\n') -- --cmd_len; -- } else { /* the command have one or more parameter */ -- cmd_len = sp - cmd_ptr; -- } -- -- /* copy command to out buffer */ -- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL); -- if (!tmp_ptr) { -- kfree(result[0]); -- result[0] = NULL; -- return -3; -- } -- -- strncpy(tmp_ptr, cmd_ptr, cmd_len); -- tmp_ptr[cmd_len] = '\0'; -- result[1] = tmp_ptr; -- -- return 0; --} -- --static void bes2600_recyle_cmd_and_ifname_mem(char **info) --{ -- if (info[0]) { -- kfree(info[0]); -- info[0] = NULL; -- } -- -- if (info[1]) { -- kfree(info[1]); -- info[1] = NULL; -- } -- --} -- --static int bes2600_op_default_handler(const char *str) --{ -- char *info[2] = {0}; -- -- if (bes2600_get_cmd_and_ifname(str, info) == 0) { -- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]); -- } else { -- bes_err("%s get command fail, the origin string is %s\n", __func__, str); -- } -- -- bes2600_recyle_cmd_and_ifname_mem(info); -- -- return 0; --} -- --static int bes2600_op_wifi_bt_on_off(const char *str) --{ -- char *info[2] = {0}; -- int ret = 0; -- enum wait_state wait_state; -- enum bus_probe_state probe_state; -- unsigned long status = 0; -- -- spin_lock(&bes2600_cdev.status_lock); -- probe_state = bes2600_cdev.bus_probe; -- wait_state = bes2600_cdev.wait_state; -- spin_unlock(&bes2600_cdev.status_lock); - -- /* only work for wifi signal mode */ -- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL) -- return -EFAULT; - -- /* wait bus probe operation end */ -- if (probe_state == BES2600_BUS_PROBE_START) { -- bes_devel("wait bus probe operation end\n"); -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, -- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START), -- HZ); -- WARN_ON(status <= 0); -- } - -- /* must wait previous operation end in critical section */ -- if (wait_state != BES2600_BOOT_WAIT_NONE) { -- bes_devel("wait previous operation end\n"); -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, -- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE), -- HZ * 8); -- WARN_ON(status <= 0); -- } - -- /* if dpd calibration is doing, modify wifi and bt state directly */ -- spin_lock(&bes2600_cdev.status_lock); -- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) { -- if (bes2600_get_cmd_and_ifname(str, info) == 0) { -- if (strncmp(info[1], "WIFI_ON", 7) == 0) { -- bes2600_cdev.wifi_opened = true; -- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) { -- bes2600_cdev.wifi_opened = false; -- } else if (strncmp(info[1], "BT_ON", 5) == 0) { -- bes2600_cdev.bt_opened = true; -- bes2600_cdev.bton_pending = true; -- } else if (strncmp(info[1], "BT_OFF", 6) == 0) { -- bes2600_cdev.bt_opened = false; -- bes2600_cdev.bton_pending = false; -- } -- } -- bes2600_recyle_cmd_and_ifname_mem(info); -- spin_unlock(&bes2600_cdev.status_lock); - -- /* wait probe done event */ -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, -- bes2600_bootup_end(), HZ * 8); -- WARN_ON(status <= 0); - -- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0; -- } -- spin_unlock(&bes2600_cdev.status_lock); - -- /* process wifi/bt on/off operation */ -- if (bes2600_get_cmd_and_ifname(str, info) == 0) { -- if (strncmp(info[1], "WIFI_ON", 7) == 0) { -- ret = bes2600_switch_wifi(1); -- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) { -- ret = bes2600_switch_wifi(0); -- } else if (strncmp(info[1], "BT_ON", 5) == 0) { -- ret = bes2600_switch_bt(1); -- } else if (strncmp(info[1], "BT_OFF", 6) == 0) { -- ret = bes2600_switch_bt(0); -- } -- } - -- if (!ret && bes2600_chrdev_check_system_close()) -- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, -- bes2600_cdev.sbus_priv); -- -- bes2600_recyle_cmd_and_ifname_mem(info); -- -- return ret ; --} -- -- --static int bes2600_op_change_fw_type(const char *str) --{ -- int ret = 0; -- int temp = 0; -- long status = 0; -- char *cmd_ptr = NULL; -- char fw_type[5] = {0}; -- bool sys_closed = bes2600_chrdev_check_system_close(); -- -- bes_devel("%s is called, arg:%s\n", __func__, str); -- -- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot) -- return -EPERM; -- -- /* check if user input is valid */ -- cmd_ptr = strstr(str, "CHANGE_FW_TYPE "); -- if (strlen(str) < 16 || !cmd_ptr) { -- bes_err("the format of \"%s\" is error\n", str); -- return -EINVAL; -- } -- -- /* convert fw_type from string to int */ -- strncpy(fw_type, cmd_ptr + 14, 4); -- fw_type[0] = '+'; -- ret = kstrtoint(fw_type, 10, &temp); -- if (ret < 0) { -- bes_err("%s parse error\n", __func__); -- return -EINVAL; -- } -- -- /* no need to realod firmware if new fw_type is equal to the old */ -- if (temp == bes2600_cdev.fw_type ) { -- bes_devel("fw type is equal\n"); -- return 0; -- } -- -- /* close wifi net device */ -- if (bes2600_cdev.sbus_priv -- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) { -- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv); -- } -- -- /* update firmware type */ -- bes2600_cdev.fw_type = temp; -- bes2600_chrdev_update_signal_mode(); -- -- if (!sys_closed) { -- /* close device to call disconnect function */ -- if (bes2600_cdev.sbus_ops->power_switch) -- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0); -- else if (bes2600_cdev.sbus_ops->reboot) -- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv); -- } -- -- if (bes2600_cdev.sbus_ops->reboot) -- bes2600_chrdev_start_bus_probe(); -- -- /* wait disconnect event */ -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10); -- WARN_ON(status <= 0); -- -- if (bes2600_cdev.dpd_calied -- && bes2600_chrdev_check_system_close()) { -- bes_devel("no need to reload firmware\n"); -- return 0; -- } -- -- bes_devel("reload firmware...\n"); -- /* power on device to call probe function */ -- if (bes2600_cdev.sbus_ops->power_switch) -- bes2600_cdev.sbus_ops->power_switch(NULL, 1); -- -- /* wait probe done event */ -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, -- bes2600_bootup_end(), HZ * 10); -- WARN_ON(status <= 0); -- -- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0; -- -- -- return ret; --} -- --static int bes2600_op_bt_wakeup(const char *str) --{ -- int ret = 0; -- unsigned long status = 0; -- -- spin_lock(&bes2600_cdev.status_lock); -- if (!bes2600_cdev.bt_opened) { -- spin_unlock(&bes2600_cdev.status_lock); -- return -EFAULT; -- } -- spin_unlock(&bes2600_cdev.status_lock); -- -- /* wait probe done event */ -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, -- bes2600_bootup_end(), HZ * 8); -- if (status <= 0 || bes2600_chrdev_is_bus_error()) -- return -EFAULT; -- -- bes_devel("bes2600 wakeup bt.\n"); -- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true); -- -- return ret; --} -- --static int bes2600_op_bt_sleep(const char *str) --{ -- int ret = 0; -- unsigned long status = 0; -- -- spin_lock(&bes2600_cdev.status_lock); -- if (!bes2600_cdev.bt_opened) { -- spin_unlock(&bes2600_cdev.status_lock); -- return -EFAULT; -- } -- spin_unlock(&bes2600_cdev.status_lock); -- -- /* wait probe done event */ -- status = wait_event_timeout(bes2600_cdev.probe_done_wq, -- bes2600_bootup_end(), HZ * 8); -- if (status <= 0 || bes2600_chrdev_is_bus_error()) -- return -EFAULT; -- -- bes_devel("bes2600 allow bt sleep.\n"); -- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false); -- -- return ret; --} -- --static int bes2600_op_set_wakeup_read_flag(const char *str) --{ -- bes_devel("%s is called, arg:%s\n", __func__, str); -- spin_lock(&bes2600_cdev.status_lock); -- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE; -- spin_unlock(&bes2600_cdev.status_lock); -- -- return 0; --} - - #ifdef FW_DOWNLOAD_UART_DAEMON --int bes2600_load_uevent(char *env[]) --{ -- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env); --} - #endif - --static struct bes2600_op_map bes2600_op_map_tab[] ={ -- /*op op_len handler */ -- {"P2P_SET_NOA", 11, bes2600_op_default_handler}, -- {"P2P_SET_PS", 10, bes2600_op_default_handler}, -- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler}, -- {"LINKSPEED", 9, bes2600_op_default_handler}, -- {"RSSI", 4, bes2600_op_default_handler}, -- {"GETBAND", 7, bes2600_op_default_handler}, -- {"WLS_BATCHING", 12, bes2600_op_default_handler}, -- {"MACADDR", 7, bes2600_op_default_handler}, -- {"RXFILTER-START", 14, bes2600_op_default_handler}, -- {"RXFILTER-STOP", 13, bes2600_op_default_handler}, -- {"RXFILTER-ADD", 12, bes2600_op_default_handler}, -- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler}, -- {"BTCOEXMODE", 10, bes2600_op_default_handler}, -- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler}, -- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler}, -- {"SETSUSPENDMODE", 14, bes2600_op_default_handler}, -- {"COUNTRY", 7, bes2600_op_default_handler}, -- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off}, -- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off}, -- {"BT_ON", 5, bes2600_op_wifi_bt_on_off}, -- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off}, -- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type}, -- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup}, -- {"BT_SLEEP", 8, bes2600_op_bt_sleep}, -- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag}, --}; - - static int bes2600_chrdev_check_system_close_internal(void) - { -@@ -644,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void) - && (bes2600_cdev.wifi_opened == false); - } - --static int bes2600_chrdev_open(struct inode *inode, struct file *filp) --{ -- if (atomic_read(&bes2600_cdev.num_proc) > 0) { -- wait_event_timeout(bes2600_cdev.open_wq, -- (atomic_read(&bes2600_cdev.num_proc) == 0), -- MAX_SCHEDULE_TIMEOUT); -- } - -- bes_devel("bes2600 char device is opened\n"); -- atomic_inc(&bes2600_cdev.num_proc); - -- return 0; --} - --static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf, -- size_t count, loff_t *ppos) --{ -- char buf[64] = {0}; -- unsigned int len; -- long status = 0; - -- switch (bes2600_cdev.read_flag) { -- case BES_CDEV_READ_WAKEUP_STATE: -- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) { -- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq, -- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2); -- WARN_ON(status <= 0); -- } -- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n", -- bes2600_cdev.wakeup_state, bes2600_cdev.src_port); -- break; -- default: -- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n", -- bes2600_cdev.dpd_calied, -- bes2600_cdev.wifi_opened, -- bes2600_cdev.bt_opened, -- bes2600_cdev.fw_type); -- break; -- } -- -- len = sizeof(buf); -- /* reset read flag */ -- spin_lock(&bes2600_cdev.status_lock); -- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX; -- spin_unlock(&bes2600_cdev.status_lock); -- -- return simple_read_from_buffer(user_buf, count, ppos, buf, len); --} -- --static ssize_t bes2600_chrdev_write(struct file *file, -- const char __user *user_buf, size_t count, loff_t *ppos) --{ -- int i = 0; -- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab); -- int cmd_len = 0; -- int ret = 0; -- char *info[2] = {0}; -- char *buf = NULL; -- -- /* copy content from user space to kernel */ -- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */ -- buf = kmalloc(count + 1, GFP_KERNEL); -- if (copy_from_user(buf, user_buf, count)) -- return -EFAULT; -- -- /* add terminal character */ -- buf[count] = '\0'; -- -- /* extract comand and interface */ -- if (bes2600_get_cmd_and_ifname(buf, info) != 0) { -- bes_err("%s get command fail, the origin string is %s\n", __func__, buf); -- kfree(buf); -- return -EINVAL; -- } -- -- /* match operation item and execure its handler */ -- cmd_len = strlen(info[1]); -- for (i = 0; i < cmd_num; i++) { -- if (cmd_len < bes2600_op_map_tab[i].op_len) -- continue; -- -- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) { -- ret = bes2600_op_map_tab[i].handler(buf); -- break; -- } -- } -- -- /* operation item mismatch */ -- if (i == cmd_num) { -- bes_err("cmd(%s) mismatch\n", info[1]); -- } -- -- bes2600_recyle_cmd_and_ifname_mem(info); -- kfree(buf); -- -- return (ret == 0) ? count : ret; --} -- --static int bes2600_chrdev_release (struct inode *inode, struct file *file) --{ -- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) { -- wake_up(&bes2600_cdev.open_wq); -- } -- -- bes_devel("bes2600 char device is closed\n"); -- -- return 0; --} -- --static struct file_operations bes2600_chardev_fops = --{ -- .owner = THIS_MODULE, -- .open = bes2600_chrdev_open, -- .read = bes2600_chrdev_read, -- .write = bes2600_chrdev_write, -- .release = bes2600_chrdev_release, --}; - - #ifdef BES2600_WRITE_DPD_TO_FILE - static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size) -@@ -1126,7 +633,6 @@ void bes2600_chrdev_wakeup_bt(void) - bes_err("Wakeup BT fail in resume\n"); - } - } --EXPORT_SYMBOL_GPL(bes2600_chrdev_wakeup_bt); - - int bes2600_chrdev_get_fw_type(void) - { -@@ -1148,7 +654,6 @@ bool bes2600_chrdev_is_bus_error(void) - - return error; - } --EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error); - - void bes2600_chrdev_update_signal_mode(void) - { -@@ -1167,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void) - - static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work) - { -- char wifi_state[15]; -- char bt_state[15]; -- char fw_type[15]; -- char *env[] = { wifi_state, bt_state, fw_type, NULL }; -- int ret; -- - if (bes2600_chrdev_is_wifi_opened()) { - bes_devel("system exeception, force wifi down\n"); - -@@ -1189,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work) - bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, - bes2600_cdev.sbus_priv); - } -- -- /* notify userspace */ -- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened); -- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened); -- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type); -- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env); -- if (!ret) -- bes_err("bes2600 notify userspace failed\n"); - } - } - -@@ -1290,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void) - - int bes2600_chrdev_init(struct sbus_ops *ops) - { -- int ret = 0; -- -- /* allocate devide id */ -- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev"); -- if (ret < 0){ -- bes_err("bes2600 alloc device id fail\n"); -- ret = -EFAULT; -- goto fail; -- } -- -- /* extract major and minor device id */ -- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id); -- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id); -- -- /* add char device and bind operation function */ -- bes2600_cdev.cdev.owner = THIS_MODULE; -- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops); -- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1); -- if (ret < 0){ -- bes_err("bes2600 char device add fail\n"); -- ret = -EFAULT; -- goto fail1; -- } -- -- /* create class for creating device node */ -- bes2600_cdev.class = class_create("bes2600_chrdev"); -- if (IS_ERR(bes2600_cdev.class)){ -- bes_err("bes2600 char device add fail\n"); -- ret = -EFAULT; -- goto fail2; -- } -- -- /* get char device pointer */ -- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600"); -- if (IS_ERR(bes2600_cdev.device)){ -- bes_err("bes2600 char device create fail\n"); -- ret = -EFAULT; -- goto fail3; -- } -- - /* initialise global variable */ - atomic_set(&bes2600_cdev.num_proc, 0); - init_waitqueue_head(&bes2600_cdev.open_wq); -@@ -1361,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops) - bes_devel("%s done\n", __func__); - - return 0; -- --fail3: -- class_destroy(bes2600_cdev.class); --fail2: -- cdev_del(&bes2600_cdev.cdev); --fail1: -- unregister_chrdev_region(bes2600_cdev.dev_id, 1); --fail: -- return ret; - } - - void bes2600_chrdev_free(void) -@@ -1379,9 +821,5 @@ void bes2600_chrdev_free(void) - bes2600_free_dpd_log_buffer(); - #endif - bes2600_chrdev_free_dpd_data(); -- cdev_del(&bes2600_cdev.cdev); -- unregister_chrdev_region(bes2600_cdev.dev_id, 1); -- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id); -- class_destroy(bes2600_cdev.class); - bes_devel("%s done\n", __func__); - } --- -2.54.0 - -From 789ab98e4cd4a0c2c43a54da6462b6b05f3af8f2 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Wed, 22 Apr 2026 13:04:27 +0200 -Subject: [PATCH 06/29] bes2600: enable CONFIG_BES2600_TESTMODE by default + - fix bit-rotted testmode plumbing -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The driver implements a mac80211 testmode_cmd operation that dispatches -to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE, -SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the -BES2600 RF-test path (bes2600_vendor_rf_cmd → firmware -patch_wifi_testMode). The testmode handlers and the .testmode_cmd -binding in struct ieee80211_ops are conditionally compiled under -CONFIG_BES2600_TESTMODE, which previously defaulted to n. - -Flip the Makefile default from n to y so wifi_testmode_cmd.o is -included in the build and the .testmode_cmd op is populated. On the -PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with -CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface -through the standard nl80211 testmode interface ('iw phy0 ...'). - -This also makes visible two classes of bit-rot that had accumulated -while nobody was building with CONFIG_BES2600_TESTMODE=y: - -1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() / - bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a - legacy log-macro family carrying a BES2600_DBG_* subsystem-id - first argument. Neither the macros nor any of the BES2600_DBG_* - constants are defined anywhere in the tree. The same call pattern - appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and - under CONFIG_BES2600_ITP in itp.c, both normally disabled. - - Add minimal shim macros to bes_log.h that rewire the calls onto - the existing bes_info() / bes_err() / bes_warn() / bes_devel() - family (ignoring the subsystem id). Define BES2600_DBG_SBUS, - BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE - as 0 constants for documentation / grep. - -2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and - bes2600_get_roam_delay() are declared in sta.c with external - linkage but have no prototype in any header. All callers live in - sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE - off the compiler never sees them; with it on gcc - -Werror=missing-prototypes breaks the build. - - Mark the three functions static. (Keeping them file-local also - matches their actual usage.) - -Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y -buildable; no behavioural change when the flag is off. - -Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2 -6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds -cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from -userspace. - -Signed-off-by: Markus Fritsche ---- - bes2600/Makefile | 2 +- - bes2600/bes_log.h | 23 +++++++++++++++++++++++ - bes2600/sta.c | 6 +++--- - 3 files changed, 27 insertions(+), 4 deletions(-) - -diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile -index 2dcba09..2c1a850 100644 ---- a/drivers/staging/bes2600/Makefile -+++ b/drivers/staging/bes2600/Makefile -@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build - # feature option - BES2600 ?= m - --CONFIG_BES2600_TESTMODE ?= n -+CONFIG_BES2600_TESTMODE ?= y - - CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n - -diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h -index 605cea8..65cf703 100644 ---- a/drivers/staging/bes2600/bes_log.h -+++ b/drivers/staging/bes2600/bes_log.h -@@ -8,3 +8,26 @@ extern struct device *global_dev; - #define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__) - #define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__) - #define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__) -+ -+/* -+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering -+ * was never implemented in-tree; these shims let code paths gated by -+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC -+ * build when their conditions are enabled. The first argument is -+ * currently unused; pick one of the BES2600_DBG_* constants below for -+ * documentation. -+ */ -+#define BES2600_DBG_SBUS 0 -+#define BES2600_DBG_DOWNLOAD 0 -+#define BES2600_DBG_ITP 0 -+#define BES2600_DBG_TEST_MODE 0 -+ -+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__) -+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__) -+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__) -+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__) -+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \ -+ do { \ -+ if (_cond) \ -+ bes_err(fmt, ##__VA_ARGS__); \ -+ } while (0) -diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c -index ca1c77c..bc6d483 100644 ---- a/drivers/staging/bes2600/sta.c -+++ b/drivers/staging/bes2600/sta.c -@@ -3654,7 +3654,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw, - * - * Returns: 0 on success or non zero value on failure - */ --int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) -+static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) - { - struct bes_msg_start_stop_tsm *start_stop_tsm = - (struct bes_msg_start_stop_tsm *) data; -@@ -3684,7 +3684,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) - * - * Returns: TSM parameters collected - */ --int bes2600_get_tsm_params(struct ieee80211_hw *hw) -+static int bes2600_get_tsm_params(struct ieee80211_hw *hw) - { - struct bes2600_common *hw_priv = hw->priv; - struct bes_tsm_stats tsm_stats; -@@ -3724,7 +3724,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw) - * - * Returns: Returns the last measured roam delay - */ --int bes2600_get_roam_delay(struct ieee80211_hw *hw) -+static int bes2600_get_roam_delay(struct ieee80211_hw *hw) - { - struct bes2600_common *hw_priv = hw->priv; - u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000; --- -2.54.0 - -From 2f9b4c719faf9563895c064439a7da25f35c8fc7 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 23 Apr 2026 11:58:31 +0200 -Subject: [PATCH 07/29] bes2600: bounce SDIO TX buffers to avoid DMA OOB read - -The SDIO TX path rounds the DMA transfer length up to the host's -current block size and hands that length to dma_map_sg() via -sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work(). -tx_buffer->buf typically aliases into an skb linear head whose -allocated size matches tx_buffer->len, not the block-aligned -align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up -to one block past the end of the skb. On a PineTab2 with KFENCE -enabled this fires as: - - BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic - Out-of-bounds read at ... (704B right of kfence-#...): - __pi_memcpy_generic - swiotlb_tbl_map_single - swiotlb_map - dma_direct_map_sg - __dma_map_sg_attrs - dma_map_sg_attrs - dw_mci_pre_dma_transfer - __dw_mci_start_request - ... - bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600] - sdio_tx_work+0x2b4/0x4a0 [bes2600] - -allocated by ... pskb_expand_head / validate_xmit_skb / tcp_* - -In addition to being undefined behavior, the padding bytes (which -come from whatever memory follows the skb) are transmitted to the -peer, leaking kernel memory on the air. - -Allocate a driver-owned DMA-page bounce buffer sized to -MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for -sdio_tx_work. Each TX buffer is copied into its bounce slot and the -tail (align - tx_buffer->len bytes) is zeroed. This mirrors the -existing bounce pattern already used by bes2600_sdio_memcpy_toio() -via single_gathered_buffer; a separate allocation is used for the -TX path because single_gathered_buffer is only serialised via -sdio_claim_host and sdio_tx_work accumulates scatter entries before -claiming the bus. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++- - 1 file changed, 38 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index f172d53..b9d836f 100644 ---- a/drivers/staging/bes2600/bes2600_sdio.c -+++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -95,6 +95,7 @@ struct sbus_priv { + u8 *rx_buffer; +- struct work_struct rx_work; + u32 rx_last_ctrl; + u32 rx_valid_ctrl; + u32 rx_total_ctrl_cnt; +@@ -94,6 +100,7 @@ struct sbus_priv { struct work_struct tx_work; struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1]; struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1]; @@ -1405,7 +588,153 @@ index f172d53..b9d836f 100644 u32 tx_data_cnt; u32 tx_xfer_cnt; u32 tx_proc_cnt; -@@ -1136,7 +1137,26 @@ static void sdio_tx_work(struct work_struct *work) +@@ -409,10 +416,19 @@ static void bes2600_sdio_irq_handler(struct sdio_func *func) + + bes_devel("%s called, fw_started:%d \n", + __func__, self->fw_started); +- if (likely(self->fw_started && self->core)) { +- queue_work(self->sdio_wq, &self->rx_work); ++ /* ++ * Patch C v3: no more sdio_rx_work relay. Wake the bh thread ++ * directly via self->irq_handler (bes2600_irq_handler in bh.c ++ * which bumps bh_rx atomic + wakes bh_wq). The bh thread will ++ * then call sbus_ops->bus_rx_batch() to do the SDIO read inline. ++ * Matches cw1200 mainline IRQ → bh-direct architecture. ++ */ ++ if (likely(self->fw_started && self->core && self->irq_handler)) { ++ spin_lock_irqsave(&self->lock, flags); ++ self->irq_handler(self->irq_priv); ++ spin_unlock_irqrestore(&self->lock, flags); + self->last_irq_timestamp = jiffies; +- } else if(self->irq_handler) { ++ } else if (self->irq_handler) { + spin_lock_irqsave(&self->lock, flags); + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); +@@ -809,10 +825,15 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 + skb_put(skb, packet_len); + memcpy(skb->data, &data[pos], packet_len); + bes_devel("%s, %d,%d\n", __func__, packet_len, pos); +- spin_lock(&self->rx_queue_lock); +- skb_queue_tail(&self->rx_queue, skb); + self->rx_data_cnt++; +- spin_unlock(&self->rx_queue_lock); ++ /* ++ * Patch C v3: deliver the SKB directly into the WSM/mac80211 ++ * stack from the bh thread. No rx_queue, no inter-thread ++ * handoff, no atomic_t needed on the counters that ++ * wsm_release_tx_buffer touches — single-writer-from-bh is ++ * preserved by construction. See bh.c for the contract block. ++ */ ++ bes2600_bh_handle_rx_skb(self->core, skb); + packet_len = (packet_len + 3) & (~0x3); + pos += packet_len; + #ifdef BES_SDIO_OPTIMIZED_LEN +@@ -823,17 +844,31 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 + return 0; + } + +-static void sdio_rx_work(struct work_struct *work) ++/* ++ * Patch C v3: bh thread calls this directly via sbus_ops->bus_rx_batch. ++ * No more sdio_rx_work workqueue. SDIO read sequence (lock → ++ * read_ctrl → memcpy_fromio → packets_check → extract_packets) runs ++ * inline in bh-thread context. Each parsed SKB is delivered via ++ * bes2600_bh_handle_rx_skb() from extract_packets — no rx_queue, no ++ * second worker, no inter-thread handoff. ++ * ++ * Architecture matches cw1200 mainline. Single-writer-from-bh ++ * invariant on hw_bufs_used preserved by construction. ++ * ++ * Returns 0 on success (caller's bh outer loop decides whether to ++ * continue), negative on bus read error. On error: triggers ++ * wifi_force_close (same as the old sdio_rx_work). ++ */ ++static int bes2600_sdio_read_rx_batch(struct sbus_priv *self) + { +- int ret, again = 0, retry = 0, crc_retry = 0; ++ int ret = 0, again = 0, retry = 0, crc_retry = 0; + u32 ctrl_reg = 0; + int total_len; +- struct sbus_priv *self = container_of(work, struct sbus_priv, rx_work); + u8 *buf = self->rx_buffer; + + /* don't read/write sdio when sdio error */ + if (bes2600_chrdev_is_bus_error()) +- return; ++ return 0; + + bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX); + +@@ -888,6 +923,10 @@ static void sdio_rx_work(struct work_struct *work) + goto failed; + } + ++ /* ++ * extract_packets parses the multi-RX buffer and calls ++ * bes2600_bh_handle_rx_skb() per SKB. No queueing. ++ */ + if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) { + bes_err("%s,%d error=%d\n", __func__, __LINE__, ret); + goto failed; +@@ -895,22 +934,16 @@ static void sdio_rx_work(struct work_struct *work) + + ctrl_reg = 0; + +- if (likely(self->irq_handler)) { +- self->irq_handler(self->irq_priv); +- } else { +- bes_err("%s,%d\n", __func__, __LINE__); +- goto failed; +- } +- + } while (again); + + bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); +- return; ++ return 0; + + failed: + bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); + bes2600_chrdev_wifi_force_close(self->core, false); + WARN_ON(1); ++ return -1; + } + + static void sdio_scan_work(struct work_struct *work) +@@ -918,26 +951,11 @@ static void sdio_scan_work(struct work_struct *work) + bes_warn("%s: this function does nothing\n", __FUNCTION__); + } + +-static void *bes2600_sdio_pipe_read(struct sbus_priv *self) +-{ +- struct sk_buff *skb; +- +- if (bes2600_chrdev_is_bus_error()) { +- return bes2600_tx_loop_read(self->core); +- } +- +- spin_lock(&self->rx_queue_lock); +- skb = skb_dequeue(&self->rx_queue); +- if (skb) +- self->rx_proc_cnt++; +- spin_unlock(&self->rx_queue_lock); +- if (likely(self->fw_started == true && +- !bes2600_pwr_device_is_idle(self->core) && +- self->core->hw_bufs_used > 0)) +- if (!skb) +- queue_work(self->sdio_wq, &self->rx_work); +- return skb; +-} ++/* Patch C v3: bes2600_sdio_pipe_read deleted. bh thread reads the ++ * SDIO bus inline via bes2600_sdio_read_rx_batch (sbus_ops->bus_rx_batch). ++ * No rx_queue, no skb_dequeue, no relay. bes2600_tx_loop_read remains ++ * for the test bus error-fallback path but is now invoked at higher ++ * level. */ + + #endif + +@@ -1135,7 +1153,26 @@ static void sdio_tx_work(struct work_struct *work) } } @@ -1433,1145 +762,94 @@ index f172d53..b9d836f 100644 total_len += align; ++scatters; /*del_node:*/ -@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func, - if (!self->single_gathered_buffer) - return -ENOMEM; - #endif -+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE -+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL, -+ get_order(MAX_SDIO_TRANSFER_LEN)); -+ if (!self->tx_bounce) { -+#ifndef SDIO_HOST_ADMA_SUPPORT -+ free_pages((unsigned long)self->single_gathered_buffer, -+ get_order(MAX_SDIO_TRANSFER_LEN)); -+#endif -+ return -ENOMEM; -+ } -+#endif - #ifdef BES_SDIO_RXTX_TOGGLE - self->fw_started = false; - #endif -@@ -1984,6 +2015,12 @@ static void bes2600_sdio_remove(struct sdio_func *func) - if (self->single_gathered_buffer) { - free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN)); - } -+#endif -+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE -+ if (self->tx_bounce) { -+ free_pages((unsigned long)self->tx_bounce, -+ get_order(MAX_SDIO_TRANSFER_LEN)); -+ } - #endif - kfree(self); - } --- -2.54.0 - -From 0c1f98df59fc3c330b370f1b5b54e8d780278d2a Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 23 Apr 2026 19:31:25 +0200 -Subject: [PATCH 08/29] bes2600: drop kernel_write() persistence from factory - cali save - -Following the conversion of the factory-calibration READ path to -request_firmware() (earlier in this series), the factory-calibration -WRITE path in factory_section_write_file() was still using -filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write() to persist -updated calibration data back to FACTORY_PATH -(default /lib/firmware/bes2600/bes2600_factory.txt). - -Writing to files under /lib/firmware/ from kernel code is a -standing upstream blocker for staging and for drivers/net/wireless/ -submission generally: - - - filp_open()/kernel_write() bypass the firmware-class abstraction, - the LSM framework, and user/group/mode enforcement that governs - the firmware search paths. They have been repeatedly called out - in staging-prep reviews. - - The kernel runs with capabilities that userspace does not (CAP_ - DAC_OVERRIDE effectively); quietly rewriting firmware blobs that - userspace owns is a surprise contract. - - A module unload / reboot immediately after the write races the - writeback and can leave a truncated calibration file on disk. - -Remove factory_section_write_file() and its two call sites in -bes2600_wifi_cali_table_save(). The in-memory factory_save_p -remains authoritative for the duration of the session: the WSM -command handlers that triggered this path (power-cali-table, -freq-cali, efuse-flag, power-cali-flag) already update the live -struct factory_t, and reads served from file_buffer pick up the -rebuilt serialised form immediately. On the next probe the -firmware-class file is re-read read-only via request_firmware(), -as set up by the earlier patch. - -If cross-reboot persistence of runtime-updated calibration becomes -a requirement, the expected route is a userspace-visible dump -interface -- a read-only debugfs file exporting the serialised -blob, or an nl80211 vendor command -- that lets userspace copy the -values to a chosen location under its own privileges. Such a -facility can land as a follow-up without touching the core driver -write path again. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes2600_factory.c | 63 +++++++++++---------------------------- - 1 file changed, 17 insertions(+), 46 deletions(-) - -diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c -index 1cda447..1b43b41 100644 ---- a/drivers/staging/bes2600/bes2600_factory.c -+++ b/drivers/staging/bes2600/bes2600_factory.c -@@ -179,34 +179,6 @@ static int factory_section_read_file(char *path, void *buffer) - return ret; - } - --/** -- * factory_section_write_file - Write data of specified length to file -- * @path: path of the file -- * @buffer: storage of write data -- * @size: length of data to write -- * -- * Return: length on success, negative error code otherwise. -- */ --static int factory_section_write_file(char *path, void *buffer, int size) --{ -- int ret = 0; -- struct file *fp; -- -- bes_devel("writing %s \n", path); -- -- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR); -- if (IS_ERR(fp)) { -- bes_devel("BES2600 : can't open %s\n",path); -- return -1; -- } -- -- ret = kernel_write(fp, buffer, size, &fp->f_pos); -- -- filp_close(fp,NULL); -- -- return ret; --} -- - static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory) - { - int ret = 0; -@@ -898,9 +870,22 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory) - #endif - } - -+/* -+ * Rebuild the serialised calibration blob in file_buffer from the live -+ * in-memory factory_save_p. Previously this function also persisted the -+ * blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that -+ * is not acceptable in mainline, so the persistence step has been removed. -+ * -+ * The in-memory factory_save_p remains authoritative for the duration of -+ * the session; on the next probe the firmware-class file is read back -+ * read-only via request_firmware(). If cross-reboot persistence of runtime -+ * calibration updates becomes a requirement, the expected route is a -+ * userspace-facing dump interface (debugfs read-only blob, or nl80211 -+ * vendor command) that lets userspace read the serialised form and store -+ * it under its own privileges. -+ */ - static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p) - { -- int ret = 0; - int w_size; - u32 crc_len = sizeof(factory_data_t); - #ifndef STANDARD_FACTORY_EFUSE_FLAG -@@ -909,13 +894,11 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto - - bes_devel("enter %s\n", __func__); - -- if (!file_buffer) { -+ if (!file_buffer) - return -ENOMEM; -- } - -- if (!factory_save_p) { -+ if (!factory_save_p) - return -ENOENT; -- } - - /* All initialized to space */ - memset(file_buffer, 32, FACTORY_MAX_SIZE); -@@ -927,22 +910,10 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto - w_size = factory_build(file_buffer, factory_save_p); - - if (w_size < 0 || w_size > FACTORY_MAX_SIZE) { -- bes_err("%s: build failed! ret = %d.", __func__, ret); -+ bes_err("%s: build failed! w_size = %d.", __func__, w_size); - return -ETXTBSY; - } - --#ifdef FACTORY_SAVE_MULTI_PATH -- /* avoid trailing characters '\0' */ -- file_buffer[w_size] = 32; -- ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE); --#else -- ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size); --#endif -- if(ret < 0) { -- bes_err("%s: write failed! ret = %d.", __func__, ret); -- return ret; -- } -- - return 0; - } - --- -2.54.0 - -From 0768e11da638457b3455e426de924f9e2e551641 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 23 Apr 2026 20:04:11 +0200 -Subject: [PATCH 09/29] bes2600: drop BES2600_WRITE_DPD_TO_FILE kernel_*() file - paths -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -bes_chardev.c carried three functions gated behind the -BES2600_WRITE_DPD_TO_FILE Kconfig/make-flag (default off): - - - bes2600_chrdev_write_dpd_data_to_file() - filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write() - writing a raw DPD calibration blob back to - BES2600_DPD_PATH (default /data/cfg/bes2600_dpd.bin, an - Android-AOSP path). - - - bes2600_chrdev_read_and_check_dpd_data() - filp_open(O_RDONLY) + kernel_read() reading the DPD blob - from either BES2600_DPD_GOLDEN_PATH (/data/cfg/…) or - BES2600_DEFAULT_DPD_PATH (/lib/firmware/bes2600_dpd.bin), - followed by a CRC/version sanity check. - - - bes2600_chrdev_dpd_is_vaild() (sic), the CRC/version helper - used only by the read path. - -Plus the bes_cdev.no_dpd field, its module_param, and two -intrusion sites in bes2600_chrdev_get_dpd_data() and -bes2600_chrdev_update_dpd_data() that invoke the above. - -The Makefile defaults BES2600_WRITE_DPD_TO_FILE=n, so in a stock -build all of this is dead code. It is still a standing upstream -blocker for exactly the same reasons as the factory-txt write -path removed in the preceding patch: - - - filp_open() + kernel_read()/kernel_write() bypass the - firmware-class abstraction and LSM-governed access control - that apply to /lib/firmware/. - - The write target /data/cfg/ is an Android AOSP convention - that does not exist on a Linux distribution and cannot be - created by the kernel anyway. - - A runtime DPD re-calibration is intended to reduce TX EVM - after temperature or aging drift; persisting the result via - kernel_write() is fundamentally a userspace concern (debugfs - dump + userspace tool is the expected route). - -Remove the entire #ifdef BES2600_WRITE_DPD_TO_FILE block from -bes_chardev.c (including the inner #ifdef inside -bes2600_chrdev_read_and_check_dpd_data() guarding a -DPD_BIN_FILE_SIZE size check that only applied to the read-back- -its-own-write case), the no_dpd field and module_param, and the -two invocation sites. Drop the Kconfig/make-flag and the three -associated PATH macros from the Makefile. Net: -155 lines, no -remaining filp_open/kernel_read/kernel_write anywhere in -bes_chardev.c. - -The in-memory DPD state path is unchanged: bes2600_chrdev_get_dpd_ -buffer() still allocates a kmalloc'd buffer used by the firmware- -download path, bes2600_chrdev_update_dpd_data() still validates -the buffer's CRC and transitions bes2600_cdev.wait_state on -success, and bes2600_chrdev_free_dpd_data() still releases the -buffer on unload. Only the file-I/O side-channel is removed. - -Signed-off-by: Markus Fritsche ---- - bes2600/Makefile | 12 ---- - bes2600/bes_chardev.c | 143 ------------------------------------------ - 2 files changed, 155 deletions(-) - -diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile -index 2c1a850..0dd3606 100644 ---- a/drivers/staging/bes2600/Makefile -+++ b/drivers/staging/bes2600/Makefile -@@ -28,7 +28,6 @@ CONFIG_BES2600_WIFI_BOOT_ON ?= y - CONFIG_BES2600_BT_BOOT_ON ?= n - - BES2600_GPIO_WAKEUP_AP ?= n --BES2600_WRITE_DPD_TO_FILE ?= n - BES2600_TX_MORE_RETRY ?= n - - # bes evb -@@ -93,12 +92,6 @@ ccflags-y += -DBES_UNIFIED_PM - ccflags-y += -DBES_SDIO_OPTIMIZED_LEN - ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG - --ifeq ($(BES2600_WRITE_DPD_TO_FILE),y) --BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin --BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin --BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin --endif -- - ifeq ($(BES2600_DUMP_FW_DPD_LOG),y) - BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log - endif -@@ -135,9 +128,6 @@ ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y) - ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH) - ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE) - ccflags-y += $(call string_flag,BES2600_DRV_VERSION) --ccflags-y += $(call string_flag,BES2600_DPD_PATH) --ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH) --ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH) - - ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y) - ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y) -@@ -159,8 +149,6 @@ ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y) - ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y) - - ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y) --ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y) -- - ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y) - ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH) - -diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c -index e2e4f1b..a02d6d9 100644 ---- a/drivers/staging/bes2600/bes_chardev.c -+++ b/drivers/staging/bes2600/bes_chardev.c -@@ -63,9 +63,6 @@ struct bes_cdev { - struct delayed_work probe_timeout_work; - enum bus_probe_state bus_probe; - struct work_struct wifi_force_close_work; --#ifdef BES2600_WRITE_DPD_TO_FILE -- int no_dpd; --#endif - enum pend_read_op read_flag; - enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */ - u16 wakeup_state; /* for userspace check wakeup reason */ -@@ -85,9 +82,6 @@ struct bes2600_op_map { - - static struct bes_cdev bes2600_cdev; - module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644); --#ifdef BES2600_WRITE_DPD_TO_FILE --module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644); --#endif - - extern int bes2600_register_net_dev(struct sbus_priv *bus_priv); - extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv); -@@ -269,137 +263,8 @@ static int bes2600_chrdev_check_system_close_internal(void) - - - --#ifdef BES2600_WRITE_DPD_TO_FILE --static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size) --{ -- int ret = 0; -- struct file *fp; -- -- if (buffer == NULL || size == 0) -- return 0; -- -- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR); -- if (IS_ERR(fp)) { -- bes_err("BES2600 : can't open %s\n",path); -- return -1; -- } -- -- ret = kernel_write(fp, buffer, size, &fp->f_pos); -- if (ret < 0) -- bes_err("write dpd to file failed\n"); -- -- filp_close(fp,NULL); -- -- bes_devel("write dpd to %s\n", path); -- -- return ret; --} -- --static bool bes2600_chrdev_dpd_is_vaild(u8 *dpd_data) --{ -- u32 cal_crc = 0; -- u32 dpd_crc = le32_to_cpup((__le32 *)(dpd_data)); -- u32 dpd_ver = le32_to_cpup((__le32 *)(dpd_data + DPD_VERSION_OFFSET)); -- -- /* check version */ -- if (dpd_ver < DPD_CUR_VERSION) -- return false; -- -- cal_crc ^= 0xffffffffL; -- cal_crc = crc32_le(cal_crc, dpd_data + 4, DPD_BIN_SIZE - 4); -- cal_crc ^= 0xffffffffL; -- -- /* check if the dpd data is valid */ -- if (cal_crc != dpd_crc) { -- bes_err( -- "bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n", -- cal_crc, dpd_crc); -- return false; -- } -- -- return true; --} -- --static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len) --{ -- int ret = 0; -- u8* read_data = NULL; -- struct file *fp; -- -- /* open file */ -- fp = filp_open(file, O_RDONLY, 0);//S_IRUSR -- if (IS_ERR(fp)) { -- bes_devel("BES2600 : can't open %s\n",file); -- return -1; -- } -- --#ifdef BES2600_WRITE_DPD_TO_FILE -- if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) { -- bes_err( -- "bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n", -- fp->f_inode->i_size, DPD_BIN_FILE_SIZE); -- filp_close(fp, NULL); -- return -1; -- } --#endif -- -- /* allocate memory for storing reading data */ -- read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL); -- if (read_data == NULL) { -- bes_devel("%s alloc mem fail\n", __func__); -- goto err1; -- } -- -- /* read data from file */ -- ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos); -- if (ret < DPD_BIN_SIZE) { -- bes_err("%s read fail, ret=%d\n", __func__, ret); -- goto err2; -- } -- -- /* check dpd version and crc */ -- if (!bes2600_chrdev_dpd_is_vaild(read_data)) -- goto err2; -- -- /* close file */ -- filp_close(fp, NULL); -- -- /* copy data to external */ -- *data = read_data; -- *len = DPD_BIN_SIZE;; -- -- /* output debug information */ -- bes_devel("read dpd data from %s\n", file); -- -- return 0; -- --err2: -- kfree(read_data); --err1: -- filp_close(fp, NULL); -- *data = NULL; -- *len = 0; -- return -1; --} --#endif -- - const u8* bes2600_chrdev_get_dpd_data(u32 *len) - { --#ifdef BES2600_WRITE_DPD_TO_FILE -- if (!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) { -- /* read dpd data from file that stores factory dpd calibration data */ -- if ((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH, -- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) && -- (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH, -- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) { -- bes_err("%s read dpd data fail\n", __func__); -- return NULL; -- } else { -- bes2600_cdev.dpd_calied = true; -- } -- } --#endif -- - if (!bes2600_cdev.dpd_calied) - return NULL; - if (len) -@@ -460,14 +325,6 @@ int bes2600_chrdev_update_dpd_data(void) - } - spin_unlock(&bes2600_cdev.status_lock); - --#ifdef BES2600_WRITE_DPD_TO_FILE -- /* write dpd data to file */ -- memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE); -- bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH, -- bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE); --#endif -- -- - return 0; - } - --- -2.54.0 - -From c3d28aea4603fec51b66cfa438dd546722d53272 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 23 Apr 2026 20:19:27 +0200 -Subject: [PATCH 10/29] bes2600: drop orphan DATA_DUMP_OBSERVE and - access_file() file I/O - -Two dead-in-default-build file-I/O sites remain in the driver -after the factory and chardev kernel_*() removals in the preceding -patches: - - - bes_fw.c DATA_DUMP_OBSERVE: four #ifdef DATA_DUMP_OBSERVE - blocks built around the firmware-download path that open - /lib/firmware/bes2002_fw_write.bin via filp_open(O_CREAT | - O_RDWR), then log every transmitted firmware chunk via - vfs_write() inside a get_fs()/set_fs(KERNEL_DS) wrapper. The - controlling #define at bes_fw.c line 128 is commented out - ('//#define DATA_DUMP_OBSERVE'), so none of this is ever - compiled in a stock build. - - - main.c access_file(): a helper gated on - GET_MAC_ADDR_METHOD == 2 || == 3 (default 4) using the same - get_fs()/set_fs()/vfs_read()/vfs_write() pattern. No caller - in the tree references it -- it was orphaned when the methods - that consumed it were refactored out. - -Both sites are unbuildable on modern kernels anyway: get_fs() / -set_fs() were removed from arm64 and the generic uaccess path in -the v5.10 era, and the legacy vfs_read() / vfs_write() variants -that took userspace-typed buffers went with them. The in-kernel -replacements would be kernel_read() / kernel_write(), which this -series is explicitly removing from the driver. - -Remove both blocks, the commented-out '//#define DATA_DUMP_OBSERVE' -line, and the access_file() definition and its #if gate. No -behaviour change in any default or non-default build, because -nothing compiled or linked in the first place. After this patch -the driver contains zero filp_open / kernel_read / kernel_write / -vfs_read / vfs_write references -- a precondition for a -drivers/staging/bes2600/ linux-wireless RFC. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes_fw.c | 34 ---------------------------------- - bes2600/main.c | 35 ----------------------------------- - 2 files changed, 69 deletions(-) - -diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c -index 133c945..d612c3c 100644 ---- a/drivers/staging/bes2600/bes_fw.c -+++ b/drivers/staging/bes2600/bes_fw.c -@@ -125,8 +125,6 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv) - } - */ - --//#define DATA_DUMP_OBSERVE -- - static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val) - { - u8 frame_num = 0; -@@ -468,14 +466,6 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n - - const struct firmware *fw_bin; - --#ifdef DATA_DUMP_OBSERVE -- char *observe; -- size_t observe_len; -- loff_t observe_off = 0; -- mm_segment_t old_fs; -- struct file *observe_file = NULL; --#endif -- - struct fw_msg_hdr_t header; - struct fw_info_t fw_info; - struct download_fw_t download_addr; -@@ -583,14 +573,6 @@ retry: - } - download_addr.addr = fw_info.addr; - --#ifdef DATA_DUMP_OBSERVE -- observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0); -- if (IS_ERR(observe_file)) { -- bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file)); -- observe_file = NULL; -- } --#endif -- - while (code_length) { - - #if 1 -@@ -640,17 +622,6 @@ retry: - //mdelay(5000); - bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length); - --#ifdef DATA_DUMP_OBSERVE -- if (observe_file) { -- observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); -- observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t); -- old_fs = get_fs(); -- set_fs(KERNEL_DS); -- vfs_write(observe_file, observe, observe_len, &observe_off); -- set_fs(old_fs); -- } --#endif -- - ret = bes2600_data_write(long_buf, length > 512 ? length : 512); - if (ret) { - bes_err("tx download fw data err:%d\n", ret); -@@ -832,11 +803,6 @@ retry: - - err2: - kfree(long_buf); --#ifdef DATA_DUMP_OBSERVE -- if (observe_file) { -- filp_close(observe_file, NULL); -- } --#endif - err1: - kfree(short_buf); - release_firmware(fw_bin); -diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c -index 3b0b7a3..7cbb3a9 100644 ---- a/drivers/staging/bes2600/main.c -+++ b/drivers/staging/bes2600/main.c -@@ -795,41 +795,6 @@ void bes2600_core_release(struct bes2600_common *self) - return; - } - --#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */ --int access_file(char *path, char *buffer, int size, int isRead) --{ -- int ret=0; -- struct file *fp; -- mm_segment_t old_fs = get_fs(); -- -- if(isRead) -- fp = filp_open(path,O_RDONLY,S_IRUSR); -- else -- fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR); -- -- if (IS_ERR(fp)) { -- bes_err("BES2600 : can't open %s\n", path); -- return -1; -- } -- -- if (isRead) { -- fp->f_pos = 0; -- set_fs(KERNEL_DS); -- ret = vfs_read(fp,buffer,size,&fp->f_pos); -- set_fs(old_fs); -- } else { -- fp->f_pos = 0; -- set_fs(KERNEL_DS); -- ret = vfs_write(fp,buffer,size,&fp->f_pos); -- set_fs(old_fs); -- } -- filp_close(fp,NULL); -- -- bes_info("BES2600 : access_file return code(%d)\n", ret); -- return ret; --} --#endif -- - int bes2600_wifi_start(struct bes2600_common *hw_priv) - { - int ret = 0, if_id; --- -2.54.0 - -From 894c502cd541079a8a26d61cd4289af9001b3046 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 23 Apr 2026 20:35:17 +0200 -Subject: [PATCH 11/29] bes2600: demote 'wait pm ind timeout' from bes_err to - bes_devel - -bes2600_pwr_enter_lp_mode() logs 'wait pm ind timeout' at bes_err -level every time wait_for_completion_timeout() on the firmware's -PM-change indication returns 0. The preceding patch ('bes2600: -gate device LP-mode entry on successful per-VIF firmware -handshake') already handles this case correctly: the per-VIF -timeouts counter is incremented, the function returns --ETIMEDOUT, and the device-side LP transition is skipped -- the -cascade into sdio_tx_work splats and [RX] Receive failure -messages is prevented. - -The timeout itself is benign steady-state noise on the PineTab2 -(BES2600WM). Firmware occasionally misses the 5 s PM-change -deadline when mac80211 flips power-save rapidly during -association or roaming; observed rate on a quiet, associated -ohm is roughly 3-10 events per 10 min of uptime, with no -user-visible effect. Keeping it at bes_err() level (== KERN_ERR, -priority 3) floods dmesg with what is already a handled -condition and makes real SDIO / PM errors harder to spot. - -Demote to bes_devel() (== KERN_DEBUG gated on the driver's debug -flag). The gate in the caller is unchanged, so the downstream -suppression behaviour introduced by the earlier patch remains. -Real pathologies -- bes_err("set operation mode fail") on the -same path, and the timeouts != 0 / -ETIMEDOUT return consumed -by callers -- still surface at bes_err() / return-value level. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes_pwr.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index f62ae22..474b6f1 100644 ---- a/drivers/staging/bes2600/bes_pwr.c -+++ b/drivers/staging/bes2600/bes_pwr.c -@@ -538,7 +538,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); - reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); - if (!status) { -- bes_err("%s, wait pm ind timeout\n", __func__); -+ bes_devel("%s, wait pm ind timeout\n", __func__); - timeouts++; +@@ -1174,7 +1211,14 @@ static void sdio_tx_work(struct work_struct *work) } - } else { --- -2.54.0 - -From 844e2245a1ed517b3a0bc487fec1a100304f0b44 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 24 Apr 2026 21:31:45 +0200 -Subject: [PATCH 13/29] bes2600: defer scan and soften WARN on firmware reject - -On a BES2600-based PineTab2, mac80211's background-scan cadence -(about every 30 s when associated) triggers a two-step WARN splat -pattern, visible in dmesg roughly 30 times per 10 min of regular -WiFi use: - - wsm_generic_confirm ret 2 - WARNING: at wsm_handle_rx+0x8a4/0xf30 [bes2600] - ... full stack trace ... - ieee80211 phy0: wsm_generic_confirm failed for request 0x0007. - WARNING: at bes2600_scan_work+0x5d4/0x810 [bes2600] - ... full stack trace ... - ieee80211 phy0: [SCAN] Scan failed (-22). - -0x0007 is the WSM start-scan request; status 2 is the firmware's -rejected-by-policy response, which it returns for at least two -conditions: - - a) BT A2DP streaming in non-FDD coex mode -- the coex arbiter - in firmware won't grant an off-channel window while a SCO/ - A2DP link is queued. - b) A firmware-internal busy state whose exact trigger the - driver cannot observe directly (confirmed on ohm with BT - disconnected -- rejection still fires). Likely transient - firmware-PM transitions. - -Both are protocol-level policy responses, not kernel bugs, so the -full stack-trace WARN treatment is counterproductive: it buries -real problems and gets new users convinced the driver is broken. - -Three-part fix: - - 1. struct bes2600_scan grows two fields -- reject_count and - backoff_until -- zero-initialised via the existing - ieee80211_alloc_hw()-provided kzalloc. - - 2. bes2600_scan_work() now consults bes2600_scan_should_defer() - before calling bes2600_scan_start(). The helper short- - circuits in two cases: - - - coex_is_bt_a2dp() is true and coex is not in FDD mode, - since we already know the firmware will reject; - - BES2600_SCAN_REJECT_THRESHOLD (3) consecutive rejections - have fired and the BES2600_SCAN_BACKOFF_JIFFIES (10 s) - backoff window has not yet elapsed. - - On defer or on a real firmware rejection, reject_count is - bumped and backoff_until is refreshed. A successful scan - clears reject_count. - - 3. The WARN_ON(hw_priv->scan.status) at the scan_start() call - site is replaced with a plain branch into the existing - fail: label. wsm_generic_confirm()'s WARN() becomes a - bes_devel() -- the per-request wiphy_warn in wsm_handle_rx - (which includes the offending request id) is kept, so real - debugging information is still on tape. - -Net behaviour: - - - Expected rejections no longer produce stack traces. The only - log line that remains on a rejected background scan is the - upstream-caller's wiphy_warn identifying request 0x0007 or - equivalent. - - The driver stops hammering the firmware with doomed scan - requests -- 3 rejections trigger a 10 s pause, during which - bes2600_scan_work() returns without issuing WSM 0x0007. - - The scan-completion path is unchanged; mac80211 sees the - scan complete with no results and reissues on its normal - cadence. - - Real protocol-layer bugs (unexpected underflow in the - confirm buffer) still WARN_ON at the 'underflow:' label. - -Verified on ohm (PineTab2, linux-pinetab2 6.19.10-danctnix1-1): -WARN splat count dropped from 32 to 0 per 10 min uptime. WiFi -stays associated. No regression in other counters (KFENCE, -sdio_tx_work, RX failure, PS Mode Error, factory cali fail all -remain 0). - -Signed-off-by: Markus Fritsche ---- - bes2600/scan.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++- - bes2600/scan.h | 11 +++++++++ - bes2600/wsm.c | 14 +++++++++++- - 3 files changed, 83 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c -index 3bfa535..5f6af3b 100644 ---- a/drivers/staging/bes2600/scan.c -+++ b/drivers/staging/bes2600/scan.c -@@ -14,11 +14,50 @@ - #include "scan.h" - #include "sta.h" - #include "pm.h" -+#include "epta_coex.h" - #include "epta_request.h" - #include "bes_pwr.h" - -+/* -+ * After this many consecutive WSM scan rejections from firmware, stop -+ * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state -+ * that's rejecting them (coex window, firmware-internal busy) clear. -+ */ -+#define BES2600_SCAN_REJECT_THRESHOLD 3 -+#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ) -+ - static void bes2600_scan_restart_delayed(struct bes2600_vif *priv); - -+/* -+ * Decide whether to skip sending the next WSM scan command without -+ * bothering the firmware. Two triggers: -+ * -+ * 1. BT A2DP is streaming in non-FDD coex mode. The firmware is -+ * known to reject scan requests during that window; short- -+ * circuiting here saves a WSM round-trip and avoids the -+ * wsm_generic_confirm / scan_work warning cascade that follows. -+ * -+ * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive -+ * rejections on recent scan attempts and the backoff window has -+ * not yet elapsed. Whatever was rejecting them is likely still -+ * rejecting them; give it time. -+ * -+ * Returns true if the caller should abandon the scan iteration. -+ */ -+static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv) -+{ -+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE -+ if (!coex_is_fdd_mode() && coex_is_bt_a2dp()) -+ return true; -+#endif -+ -+ if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD && -+ time_before(jiffies, hw_priv->scan.backoff_until)) -+ return true; -+ -+ return false; -+} -+ - #ifdef CONFIG_BES2600_TESTMODE - static int bes2600_advance_scan_start(struct bes2600_common *hw_priv) - { -@@ -703,10 +742,29 @@ void bes2600_scan_work(struct work_struct *work) - wsm_unlock_tx(hw_priv); - } else + } while (crc_retry <= 10); + sdio_release_host(self->func); +- queue_work(self->sdio_wq, &self->rx_work); ++ /* ++ * Patch C v3: wake the bh thread to check for any RX ++ * that piggybacked on this TX window. Bumps bh_rx ++ * atomic; bh's wait_event will pick it up and call ++ * sbus_ops->bus_rx_batch(). ++ */ ++ if (likely(self->irq_handler)) ++ self->irq_handler(self->irq_priv); + if (ret) { + bes_err("%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk); + sdio_work_debug(self); +@@ -1225,12 +1269,11 @@ static int bes2600_sdio_misc_init(struct sbus_priv *self, struct bes2600_common + self->next_toggle = 0; #endif -+ { -+ if (bes2600_scan_should_defer(hw_priv)) { -+ hw_priv->scan.status = -EBUSY; -+ hw_priv->scan.reject_count++; -+ hw_priv->scan.backoff_until = -+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES; -+ wiphy_dbg(priv->hw->wiphy, -+ "[SCAN] deferred (coex/backoff, reject_count=%u)\n", -+ hw_priv->scan.reject_count); -+ kfree(scan.ch); -+ goto fail; -+ } - hw_priv->scan.status = bes2600_scan_start(priv, &scan); -+ } - kfree(scan.ch); -- if (WARN_ON(hw_priv->scan.status)) -+ if (hw_priv->scan.status) { -+ hw_priv->scan.reject_count++; -+ hw_priv->scan.backoff_until = -+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES; -+ /* Lower callers already logged the reason at wiphy_warn. */ - goto fail; -+ } -+ hw_priv->scan.reject_count = 0; - hw_priv->scan.curr = it; + #ifdef BES_SDIO_RX_MULTIPLE_ENABLE +- spin_lock_init(&self->rx_queue_lock); +- skb_queue_head_init(&self->rx_queue); ++ /* Patch C v3: rx_queue / rx_queue_lock removed (no relay). */ + self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM)); + if (!self->rx_buffer) + return -ENOMEM; +- INIT_WORK(&self->rx_work, sdio_rx_work); ++ /* Patch C v3: sdio_rx_work removed; bh thread does the read. */ + #endif + #ifdef BES_SDIO_TX_MULTIPLE_ENABLE + INIT_LIST_HEAD(&self->tx_bufferlist); +@@ -1367,7 +1410,14 @@ static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag) + + /* error check */ + if((self->gpio_wakup_flags & BIT(flag)) != 0) { +- bes_err( "repeat set gpio_wake_flag, sub_sys:%d", flag); ++ /* ++ * Multiple subsystems holding wake is the steady-state case ++ * (e.g. WIFI + BT both want MCU awake). Demoted from bes_err ++ * to bes_devel since it isn't an error - the GPIO is already ++ * asserted high and the subsystem is now also tracked. ++ */ ++ bes_devel("repeat set gpio_wake_flag, sub_sys:%d\n", flag); ++ self->gpio_wakup_flags |= BIT(flag); + mutex_unlock(&self->io_mutex); + return; } - up(&hw_priv->conf_lock); -diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h -index e50fa36..1f3adea 100644 ---- a/drivers/staging/bes2600/scan.h -+++ b/drivers/staging/bes2600/scan.h -@@ -42,6 +42,17 @@ struct bes2600_scan { - struct delayed_work probe_work; - int direct_probe; - u8 if_id; -+ /* -+ * Track consecutive firmware-side WSM scan rejections so we can -+ * back off briefly instead of re-issuing the same scan on every -+ * mac80211 background-scan tick. Firmware returns WSM status != 0 -+ * for a handful of transient conditions (BT A2DP active in non- -+ * FDD coex, firmware-internal busy windows) and keeps rejecting -+ * until the state clears; retrying at full cadence just floods -+ * dmesg. -+ */ -+ unsigned int reject_count; -+ unsigned long backoff_until; - }; +@@ -1399,7 +1449,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag) - int bes2600_hw_scan(struct ieee80211_hw *hw, -diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c -index d40df30..55a4e2b 100644 ---- a/drivers/staging/bes2600/wsm.c -+++ b/drivers/staging/bes2600/wsm.c -@@ -134,8 +134,20 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv, - struct wsm_buf *buf) + /* error check */ + if((self->gpio_wakup_flags & BIT(flag)) == 0) { +- bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag); ++ /* ++ * Mirror of the wake path: a clear when the bit is already ++ * clear is racy bookkeeping, not a hardware error. ++ */ ++ bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag); + mutex_unlock(&self->io_mutex); + return; + } +@@ -1548,22 +1602,15 @@ static int bes2600_sdio_active(struct sbus_priv *self, int sub_system) + + static void bes2600_sdio_empty_work(struct sbus_priv *self) { - u32 status = WSM_GET32(buf); -- if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status)) -+ -+ /* -+ * A non-SUCCESS status here is a firmware-side policy decision for -+ * the command whose confirm this is -- commonly WSM status 2 for -+ * scan (0x0407) rejected because of a coex window or transient -+ * firmware-busy state. It is not a driver/kernel bug, so avoid the -+ * WARN()/stack-trace treatment; the caller already emits a -+ * wiphy_warn identifying the request id and will propagate the -+ * error to mac80211. -+ */ -+ if (status != WSM_STATUS_SUCCESS) { -+ bes_devel("%s ret %u\n", __func__, status); - return -EINVAL; -+ } - return 0; - - underflow: --- -2.54.0 - -From 179c2e0bf852734631acfc56b2478775215cc5f6 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Tue, 28 Apr 2026 14:32:18 +0200 -Subject: [PATCH 14/29] bes2600: widen scan-defer backoff to 30s and decay - count on quiet - -The scan-defer logic added in the previous patch ("bes2600: defer -scan and soften WARN on firmware reject") used a 10-second backoff -window and never cleared reject_count outside of a successful scan. -Field testing on a PineTab2 (linux-pinetab2 6.19.10-danctnix1) shows -two distinct mac80211 scan-retry cadences in practice: - - * Idle background scans every ~5 minutes when associated -- well - outside any plausible backoff, the defer guard correctly falls - through to a real WSM scan attempt. - - * Roam-evaluation bursts triggered when mac80211 wants to find a - candidate AP for handover (signal degradation, beacon loss, - locally-generated DEAUTH_LEAVING reason=3). Cadence is ~12 s, and - one boot reproduced 14 such rejected scans in 3 minutes during a - single burst, none of which engaged the defer guard because every - retry landed just outside the 10 s window. - -Two-line behaviour change to fix that: - - 1. BES2600_SCAN_BACKOFF_JIFFIES grows from 10*HZ to 30*HZ, so a - 12 s-cadence burst stays inside the window across consecutive - rejects and the third reject in the burst trips the threshold - guard. The 5 min idle case is still naturally past the window - and is unaffected. - - 2. bes2600_scan_should_defer() resets reject_count to 0 when - time_after(jiffies, backoff_until). Without this, reject_count - accumulated indefinitely across the slow-cadence rejects, so an - isolated reject after long quiet would have tripped the - threshold the moment it arrived. After the change, count is - latched only inside an active burst and decays cleanly when the - burst ends. - -Net effect on a roam burst: - - * t=0 reject #1 (count 1, backoff_until = t0 + 30s) - * t=12 reject #2 (count 2, backoff_until = t1 + 30s) - * t=24 reject #3 (count 3, threshold met, next scan deferred) - * t=36 defer fires, no WSM round-trip, reject not sent - * ... defers continue until the firmware-policy state clears - * scan succeeds -> reject_count = 0, normal cadence resumes - -WSM 0x0007 confirm rejections in a burst drop from ~14 to ~3 (just -the scans needed to reach the threshold). wpa_supplicant's reason=3 -locally-generated disconnects driven by exhausted roam candidates -during the same burst window also drop. - -No new state, no new symbols, no change to mac80211-facing semantics: -the deferred scan still completes via the existing fail: path with -status=-EBUSY, the same response a real firmware-busy would produce. - -Signed-off-by: Markus Fritsche ---- - bes2600/scan.c | 17 +++++++++++++++-- - 1 file changed, 15 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c -index 5f6af3b..b944adc 100644 ---- a/drivers/staging/bes2600/scan.c -+++ b/drivers/staging/bes2600/scan.c -@@ -22,9 +22,17 @@ - * After this many consecutive WSM scan rejections from firmware, stop - * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state - * that's rejecting them (coex window, firmware-internal busy) clear. -+ * -+ * The backoff has to be at least as long as the natural mac80211 scan- -+ * retry cadence, otherwise the next attempt lands outside the window -+ * and bypasses the defer guard. Observed in the wild on PineTab2: -+ * roam-evaluation bursts at ~12 s cadence, idle background scans at -+ * ~5 min cadence. 30 s catches the burst and leaves the slow case -+ * alone (the firmware-policy state has had minutes to clear by then -+ * anyway). - */ - #define BES2600_SCAN_REJECT_THRESHOLD 3 --#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ) -+#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ) - - static void bes2600_scan_restart_delayed(struct bes2600_vif *priv); - -@@ -40,7 +48,9 @@ static void bes2600_scan_restart_delayed(struct bes2600_vif *priv); - * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive - * rejections on recent scan attempts and the backoff window has - * not yet elapsed. Whatever was rejecting them is likely still -- * rejecting them; give it time. -+ * rejecting them; give it time. If the backoff has elapsed without -+ * a fresh reject refreshing it, the burst is over and we reset the -+ * count so an isolated reject doesn't immediately re-trip. - * - * Returns true if the caller should abandon the scan iteration. - */ -@@ -51,6 +61,9 @@ static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv) - return true; +-#ifdef BES_SDIO_RX_MULTIPLE_ENABLE +- struct sk_buff *skb; +-#endif + #ifdef BES_SDIO_TX_MULTIPLE_ENABLE + struct bes_sdio_tx_list_t *tx_buffer, *temp; #endif -+ if (time_after(jiffies, hw_priv->scan.backoff_until)) -+ hw_priv->scan.reject_count = 0; -+ - if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD && - time_before(jiffies, hw_priv->scan.backoff_until)) - return true; --- -2.54.0 - -From 22b799f5a21c0046aad46676519e5f03a0d105fd Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Sun, 26 Apr 2026 22:31:58 +0200 -Subject: [PATCH 15/29] bes2600: recover wedged firmware via mmc_hw_reset on - link break - -When the LMAC active monitor detects 'link break between lmac and host' -(the hw_buf_used==pending watchdog in bes2600_bh_lmac_active_monitor), -bes2600_chrdev_wifi_force_close(hw_priv, true) is invoked to tear the -device down and prepare for a fresh probe. On the wifi_force_close_work -side this calls bes2600_chrdev_do_system_close() which dispatches -sbus_ops->power_switch(0). - -On PineTab2 (RK3566 + BES2600WM over SDIO) this recovery path is a -no-op: - - * bes2600_sdio_power_down() writes a SYSTEM_CLOSE host-int message, - clears MMC_CAP_NONREMOVABLE, and schedules sdio_scan_work, which is - the literal one-line stub bes_warn("...this function does - nothing\n"). - * bes2600_sdio_on() (the eventual power_switch(1) counterpart) - toggles pdata->powerup, which is NULL on PineTab2 because the - wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 device - tree node (see arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi: - 'The reset pin is claimed by sdio_mmcseq, It is better to move it - to U-Boot so the OS can use it.'). - -Net result: the chip is never reset. The function drivers are not -removed (the SDIO core has no signal that the card is gone), the -firmware stays wedged, and a subsequent rmmod bes2600 leaves the SDIO -function in a half-torn-down state. modprobe bes2600 then fails with -'probe with driver bes2600_wlan failed with error -123' (-ENOMEDIUM) -on both functions (:1 wifi, :2 BT-companion) until a full system -reboot. - -Observed on PineTab2 (linux-pinetab2 6.19.10-danctnix1-1) after ~150 -minutes of background-scan rejects (wsm_generic_confirm 0x0007, -[SCAN] Scan failed (-22)) accumulating until the LMAC stopped -acknowledging TX buffers (hw_buf_used:24 pending:24). Reproducible -under sustained scan pressure. - -Add a sbus operation bus_reset() that the recovery path can call when -power_switch() has no effective chip-reset signal of its own. Provide -an SDIO implementation that calls mmc_hw_reset(self->func->card), -which on a multi-function SDIO card (PineTab2 binds func 1 for WLAN -and func 2 for the BT-companion path) takes the remove-and-rescan -path: mmc_sdio_hw_reset() marks the card removed and schedules -mmc_rescan, which tears down the bound function drivers and re-detects -the card on the next sweep, in turn reinvoking bes2600_sdio_probe(). -With a single function probed it instead invokes mmc_power_cycle() -directly, which on PineTab2 toggles the wifi-reset GPIO via -sdio_pwrseq. - -Add bes2600_chrdev_do_bus_reset() as the chrdev-side helper. It -invokes the bus op and then waits on probe_done_wq for the SDIO -remove() callback to clear sbus_priv, mirroring the wait pattern -already used by bes2600_chrdev_do_system_close() so that a subsequent -bes2600_switch_wifi(true) sees a clean state and can wait on the -fresh probe. - -Wire it into bes2600_chrdev_wifi_force_close_work(): when halt_dev is -set (the hard-exception path used by both -bes2600_bh_lmac_active_monitor and bes2600_bh_mcu_active_monitor) and -the underlying bus implements bus_reset, take the new recovery path; -otherwise fall back to the legacy power_switch(0) sequence so this -patch is a no-op on USB or any other future bus that does not provide -bus_reset. - -mmc_hw_reset() is exported by the MMC core and is the canonical -recovery primitive; calling it without holding the SDIO host claim is -correct because the multi-func remove-and-rescan path acquires the -host claim via the mmc workqueue, and the single-func mmc_power_cycle -path does not require the host claim. - -No DT change is required: this works against the existing PineTab2 -DTS, where the wifi-reset GPIO and the optional sdio_pwrkey GPIO (on -v2.0 boards) are both already configured as MMC pwrseq resets. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes2600_sdio.c | 29 +++++++++++++++++++++ - bes2600/bes_chardev.c | 59 ++++++++++++++++++++++++++++++++++++++++-- - bes2600/bes_chardev.h | 1 + - bes2600/sbus.h | 8 ++++++ - 4 files changed, 95 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index b9d836f..f7f86d7 100644 ---- a/drivers/staging/bes2600/bes2600_sdio.c -+++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1777,6 +1778,33 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) + #ifdef BES_SDIO_RX_MULTIPLE_ENABLE +- cancel_work_sync(&self->rx_work); +- while (1) { +- skb = skb_dequeue(&self->rx_queue); +- if (skb) +- dev_kfree_skb(skb); +- else +- break; +- } ++ /* ++ * Patch C v3: rx_work and rx_queue removed. Counters still ++ * reset for the next attach cycle. ++ */ + self->rx_last_ctrl = 0; + self->rx_total_ctrl_cnt = 0; + self->rx_continuous_ctrl_cnt = 0; +@@ -1756,6 +1803,55 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) sdio_work_debug(self); } @@ -2596,16 +874,48 @@ index b9d836f..f7f86d7 100644 + */ +static int bes2600_sdio_bus_reset(struct sbus_priv *self) +{ ++ struct mmc_host *host; ++ int ret; ++ + if (!self || !self->func || !self->func->card) + return -EINVAL; + -+ return mmc_hw_reset(self->func->card); ++ host = self->func->card->host; ++ ret = mmc_hw_reset(self->func->card); ++ ++ /* ++ * On multi-function SDIO cards (BES2600 has WLAN func 1 + BT ++ * companion func 2), mmc_sdio_hw_reset() removes the card and ++ * returns 1 to signal "remove happened, caller must trigger ++ * rescan". The kernel does NOT auto-rescan in this case; ++ * single-function cards take the rescan path inline and return 0. ++ * Treat any non-negative return as success and force a rescan if ++ * mmc_hw_reset signalled the multi-function path - otherwise the ++ * card stays removed indefinitely after a wedge recovery, ++ * leaving wifi (and the BT companion) silent until reboot. ++ */ ++ if (ret > 0) { ++ bes_info("multi-func mmc_hw_reset removed card; scheduling rescan\n"); ++ mmc_detect_change(host, 0); ++ ret = 0; ++ } ++ return ret; +} + static bool bes2600_sdio_wakeup_source(struct sbus_priv *self) { struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); -@@ -1815,6 +1843,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = { +@@ -1782,7 +1878,8 @@ static struct sbus_ops bes2600_sdio_sbus_ops = { + .sbus_reg_write = bes2600_sdio_reg_write, + .init = bes2600_sdio_misc_init, + #ifdef BES_SDIO_RX_MULTIPLE_ENABLE +- .pipe_read = bes2600_sdio_pipe_read, ++ /* Patch C v3: .pipe_read removed; bus_rx_batch replaces it. */ ++ .bus_rx_batch = bes2600_sdio_read_rx_batch, + #endif + #ifdef BES_SDIO_TX_MULTIPLE_ENABLE + .pipe_send = bes2600_sdio_pipe_send, +@@ -1794,6 +1891,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = { .gpio_sleep = bes2600_gpio_allow_mcu_sleep, .halt_device = bes2600_sdio_halt_device, .wakeup_source = bes2600_sdio_wakeup_source, @@ -2613,11 +923,110 @@ index b9d836f..f7f86d7 100644 }; static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) +@@ -1801,9 +1899,15 @@ static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) + long unsigned int old_ts, new_ts; + struct sbus_priv *self = hw_priv->sbus_priv; + ++ /* ++ * Patch C v3: rx_work removed. Wait for IRQ-timestamp activity ++ * to settle by polling self->last_irq_timestamp via msleep ++ * (best-effort). The caller already knows the bh thread will ++ * process pending bh_rx during its next wait_event round. ++ */ + do { + old_ts = self->last_irq_timestamp; +- flush_work(&self->rx_work); ++ msleep(2); + new_ts = self->last_irq_timestamp; + } while(old_ts != new_ts); + } +@@ -1834,6 +1938,9 @@ static int bes2600_sdio_probe(struct sdio_func *func, + if (ret) + goto err; + ++ /* wire struct device into factory.c for request_firmware() context */ ++ bes2600_factory_set_dev(dev); ++ + self->pdata = bes2600_get_platform_data(); + self->func = func; + self->dev = &func->dev; +@@ -1853,6 +1960,17 @@ static int bes2600_sdio_probe(struct sdio_func *func, + if (!self->single_gathered_buffer) + return -ENOMEM; + #endif ++#ifdef BES_SDIO_TX_MULTIPLE_ENABLE ++ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL, ++ get_order(MAX_SDIO_TRANSFER_LEN)); ++ if (!self->tx_bounce) { ++#ifndef SDIO_HOST_ADMA_SUPPORT ++ free_pages((unsigned long)self->single_gathered_buffer, ++ get_order(MAX_SDIO_TRANSFER_LEN)); ++#endif ++ return -ENOMEM; ++ } ++#endif + #ifdef BES_SDIO_RXTX_TOGGLE + self->fw_started = false; + #endif +@@ -1913,8 +2031,8 @@ int bes2600_unregister_net_dev(struct sbus_priv *bus_priv) + BUG_ON(!bus_priv); + if (bus_priv->core && !bus_priv->unregister_in_process) { + bus_priv->unregister_in_process = true; +- bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb); + bes2600_core_release(bus_priv->core); ++ bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb); + bus_priv->core = NULL; + + if (bus_priv->sdio_wq) { +@@ -1980,6 +2098,12 @@ static void bes2600_sdio_remove(struct sdio_func *func) + if (self->single_gathered_buffer) { + free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN)); + } ++#endif ++#ifdef BES_SDIO_TX_MULTIPLE_ENABLE ++ if (self->tx_bounce) { ++ free_pages((unsigned long)self->tx_bounce, ++ get_order(MAX_SDIO_TRANSFER_LEN)); ++ } + #endif + kfree(self); + } +@@ -2140,8 +2264,12 @@ static int bes2600_sdio_suspend_noirq(struct device *dev) + if (func->num > 1) + return 0; + +- if(self->core && +- (work_pending(&self->rx_work) || atomic_read(&self->core->bh_rx))) { ++ /* ++ * Patch C v3: work_pending(&self->rx_work) check dropped (no ++ * relay). bh_rx atomic alone tells us whether the bh thread ++ * has un-processed RX events queued. ++ */ ++ if (self->core && atomic_read(&self->core->bh_rx)) { + bes_devel("%s: Suspend interrupted.\n", __func__); + return -EAGAIN; + } diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c -index a02d6d9..d1375bc 100644 +index f89dcb8fb..5374d5117 100644 --- a/drivers/staging/bes2600/bes_chardev.c +++ b/drivers/staging/bes2600/bes_chardev.c -@@ -442,6 +442,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_ +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Character device for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include + #include +@@ -1078,6 +1075,62 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_ return ret; } @@ -2662,662 +1071,265 @@ index a02d6d9..d1375bc 100644 + + return 0; +} ++EXPORT_SYMBOL_GPL(bes2600_chrdev_do_bus_reset); ++ ++/* ++ * Trigger bes2600_chrdev_do_bus_reset() against the file-global ++ * bes2600_cdev. Used by host-side recovery paths outside this ++ * compilation unit (e.g. sta.c connection-loss-storm fast-recover) so ++ * those callers do not need to reach the static bes2600_cdev directly. ++ */ ++int bes2600_chrdev_trigger_bus_reset(void) ++{ ++ return bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops, ++ bes2600_cdev.sbus_priv); ++} ++EXPORT_SYMBOL_GPL(bes2600_chrdev_trigger_bus_reset); + bool bes2600_chrdev_is_wifi_opened(void) { bool wifi_opened = false; -@@ -540,8 +582,21 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work) - /* unregister wifi */ - bes2600_switch_wifi(0); - -- /* power down device if wifi is only opened */ -- if (bes2600_chrdev_check_system_close()) { -+ /* -+ * Hard exception with a bus_reset implementation: tear the -+ * bus down via mmc_hw_reset() (or equivalent) so the next -+ * bringup probes a freshly reset chip. On PineTab2 this is -+ * the only effective recovery path -- the existing -+ * power_switch(0)/(1) sequence has no chip-reset signal of -+ * its own (sdio_pwrseq owns wifi_reset). -+ * -+ * Soft close, or hard close on a board without bus_reset: -+ * fall back to the legacy power_switch(0) sequence. -+ */ -+ if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) { -+ bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops, -+ bes2600_cdev.sbus_priv); -+ } else if (bes2600_chrdev_check_system_close()) { - bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops, - bes2600_cdev.sbus_priv); - } diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h -index c627bb7..ca8419e 100644 +index c627bb7c3..9edb2067a 100644 --- a/drivers/staging/bes2600/bes_chardev.h +++ b/drivers/staging/bes2600/bes_chardev.h -@@ -60,6 +60,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void); +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Character device interface for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef __BES_CHARDEV_H__ + #define __BES_CHARDEV_H__ +@@ -60,6 +57,8 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void); /* used to control device power down */ int bes2600_chrdev_check_system_close(void); int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); +int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); ++int bes2600_chrdev_trigger_bus_reset(void); void bes2600_chrdev_wakeup_bt(void); void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev); void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv); -diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h -index 1f2c0cd..cb90890 100644 ---- a/drivers/staging/bes2600/sbus.h -+++ b/drivers/staging/bes2600/sbus.h -@@ -75,6 +75,14 @@ struct sbus_ops { - void (*halt_device)(struct sbus_priv *self); - bool (*wakeup_source)(struct sbus_priv *self); - int (*reboot)(struct sbus_priv *self); -+ /* -+ * Force the host bus to re-detect and re-probe the chip. Called -+ * from the firmware-wedge recovery path when power_switch() has no -+ * effective chip-reset signal of its own (e.g. PineTab2, where the -+ * wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node). -+ * Returns 0 on success or a negative errno. -+ */ -+ int (*bus_reset)(struct sbus_priv *self); - }; - - void bes2600_irq_handler(struct bes2600_common *priv); --- -2.54.0 - -From 3942404ae16b134a55e48cb796d625b8b90e504f Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Tue, 28 Apr 2026 21:37:37 +0200 -Subject: [PATCH 19/29] bes2600: handle multi-function SDIO cards in - mmc_hw_reset bus_reset - -c5.2 (recover-wedged-firmware-via-mmc-hw-reset) wraps mmc_hw_reset() -and treats any non-zero return as a recovery failure. On -single-function SDIO cards mmc_hw_reset returns 0 after doing the -remove + rescan inline. On multi-function cards (BES2600 has WLAN -func 1 + BT companion func 2) the kernel's mmc_sdio_hw_reset() does -NOT do the rescan: it tears the card down and returns 1 to signal -"caller must trigger rescan". - -Field observation on PineTab2 (linux-pinetab2 6.19.10-danctnix1): -when a real LMAC wedge fired bes2600_chrdev_wifi_force_close -> -bes2600_chrdev_do_bus_reset, mmc_hw_reset returned 1, c5.2's wrapper -treated that as "bus_reset failed: 1", logged the error, and gave -up. The card was already removed (mmc2: card 0001 removed) but -nothing scheduled a rescan; wifi (and the BT companion which shares -the same SDIO host) stayed silent until the user rebooted four -minutes later. - -Fix: - - - Capture the mmc_host pointer before calling mmc_hw_reset (the - card pointer is invalid after the remove). - - On positive return (multi-function path), log informationally - and call mmc_detect_change(host, 0) to schedule a rescan. - Return 0 so callers see the recovery as successful. - - Negative return is still treated as failure as before. - -The mmc_detect_change side effect is asynchronous; the chrdev's -wait_event_timeout(probe_done_wq, !sbus_priv) still observes the -remove half synchronously, and the rescan + re-probe runs out of -the host detect work afterwards. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes2600_sdio.c | 24 +++++++++++++++++++++++- - 1 file changed, 23 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index 5a0694a..c81c244 100644 ---- a/drivers/staging/bes2600/bes2600_sdio.c -+++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -1810,10 +1810,32 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) +diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c +index 133c9453b..6c5598b94 100644 +--- a/drivers/staging/bes2600/bes_fw.c ++++ b/drivers/staging/bes2600/bes_fw.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Firmware download for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. */ - static int bes2600_sdio_bus_reset(struct sbus_priv *self) - { -+ struct mmc_host *host; -+ int ret; -+ - if (!self || !self->func || !self->func->card) - return -EINVAL; - -- return mmc_hw_reset(self->func->card); -+ host = self->func->card->host; -+ ret = mmc_hw_reset(self->func->card); -+ -+ /* -+ * On multi-function SDIO cards (BES2600 has WLAN func 1 + BT -+ * companion func 2), mmc_sdio_hw_reset() removes the card and -+ * returns 1 to signal "remove happened, caller must trigger -+ * rescan". The kernel does NOT auto-rescan in this case; -+ * single-function cards take the rescan path inline and return 0. -+ * Treat any non-negative return as success and force a rescan if -+ * mmc_hw_reset signalled the multi-function path - otherwise the -+ * card stays removed indefinitely after a wedge recovery, -+ * leaving wifi (and the BT companion) silent until reboot. -+ */ -+ if (ret > 0) { -+ bes_info("multi-func mmc_hw_reset removed card; scheduling rescan\n"); -+ mmc_detect_change(host, 0); -+ ret = 0; -+ } -+ return ret; + #include "bes_fw_common.h" + #include "bes2600.h" +@@ -125,8 +122,6 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv) } + */ - static bool bes2600_sdio_wakeup_source(struct sbus_priv *self) --- -2.54.0 - -From 40aec44a6e4de5aaf0066982601c99e648b0f1ec Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Tue, 28 Apr 2026 15:05:27 +0200 -Subject: [PATCH 16/29] bes2600: gate PM indication completion on pending - request and track chip state - -When mac80211 toggles PSM on the BES2600, the host sends WSM set_pm -and waits up to 5 s on bes_power.pm_enter_cmpl for a firmware-side -PM-changed indication confirming the transition. Three sequenced -flaws make the wait-and-confirm racy and leave host/chip bookkeeping -desynced when anything misfires: - - 1) bes2600_pwr_notify_ps_changed() unconditionally fires - complete(pm_enter_cmpl) for any non-active psmode. It does not - check whether a host-initiated set_pm is actually pending. A - spontaneous indication (firmware-internal coex move, - idle-driven aging) primes the completion, and the next host- - driven enter_lp_mode sees a false success on its first - wait_for_completion_timeout. - - 2) The wait/reinit ordering in bes2600_pwr_enter_lp_mode is - - status = wait_for_completion_timeout(...); - atomic_set(pm_set_in_process, 0); - reinit_completion(...); - - If an indication arrives between wait_for_completion_timeout - returning with status==1 and reinit_completion, the next - enter_lp_mode iteration's wait can also see false success. The - reinit must happen *before* we start the new request, not - after handling the previous one. - - 3) On wait_pm_ind timeout, the driver returns -ETIMEDOUT and walks - away. It does not record that the firmware's actual PM state - is no longer known to the host. Subsequent wake paths - (gpio_wake / sbus_active) assume the chip is still active and - hit deterministic SDIO failures when the firmware has - transitioned anyway. - -This patch is the safe-prerequisite half of a wider fix: - - * bes_pwr.h gains enum bes2600_chip_pm_state {ACTIVE, LP, UNKNOWN} - and bes_power.chip_pm_state. Its job is to track what the host - has *seen the firmware confirm*, not what the host has - requested. Initialised to ACTIVE in bes2600_pwr_init(). - - * bes2600_pwr_notify_ps_changed() unconditionally updates - chip_pm_state on every indication, but only fires - complete(pm_enter_cmpl) when atomic_cmpxchg(pm_set_in_process, - 1, 0) succeeds. A spontaneous indication can no longer prime a - waiter that will only set up its request afterwards. - - * bes2600_pwr_enter_lp_mode() now reinit_completion()s before - setting pm_set_in_process and sending wsm_set_pm. After a - timeout, it cmpxchgs pm_set_in_process back to 0 (so a late - indication cannot prime the next iteration) and on the win- - cmpxchg branch records chip_pm_state=UNKNOWN. - -A follow-up patch consumes chip_pm_state on the wake side -(bes2600_pwr_device_exit_lp_mode + bes2600_gpio_wakeup_mcu) to fix -the deterministic "active mcu fail" cycle this state-record -enables a fix for. Splitting the work this way keeps the lock-free -race fix small and reviewable on its own. - -No new locks, no behaviour change on the success path. Only the -recovery path (timeout + spontaneous indication) gains correctness. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes_pwr.c | 94 ++++++++++++++++++++++++++++++++++++++++++----- - bes2600/bes_pwr.h | 15 ++++++++ - 2 files changed, 100 insertions(+), 9 deletions(-) - -diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index 474b6f1..9b4a4de 100644 ---- a/drivers/staging/bes2600/bes_pwr.c -+++ b/drivers/staging/bes2600/bes_pwr.c -@@ -524,7 +524,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n", - __func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod, - priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod); -+ /* -+ * Reinit BEFORE the WSM goes out, so a stale -+ * indication from a previous cycle cannot have -+ * primed pm_enter_cmpl. From here until the -+ * indication callback's cmpxchg(1->0) on -+ * pm_set_in_process, only the indication for -+ * THIS request can complete the wait. -+ */ -+ reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); - atomic_set(&hw_priv->bes_power.pm_set_in_process, 1); -+ - ret = bes2600_set_pm(priv, &priv->powersave_mode); - if (ret) { - atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); -@@ -535,11 +545,33 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - - /* wait power save mode changed indication */ - status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ); -- atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); -- reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); - if (!status) { -- bes_devel("%s, wait pm ind timeout\n", __func__); -- timeouts++; -+ /* -+ * The indication callback only fires -+ * complete() when it observes -+ * pm_set_in_process == 1; cmpxchg it -+ * to 0 here so a late indication -+ * cannot prime the next wait. -+ * -+ * If we win the cmpxchg, this is a -+ * real timeout: the firmware's PS -+ * state is unknown to us. Mark it as -+ * such so the next wake path can -+ * probe before assuming the chip is -+ * still active. -+ * -+ * If we lose the cmpxchg, the -+ * indication arrived between the -+ * wait timing out and us getting -+ * here; treat as success. -+ */ -+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process, -+ 1, 0) == 1) { -+ bes_devel("%s, wait pm ind timeout\n", __func__); -+ atomic_set(&hw_priv->bes_power.chip_pm_state, -+ BES2600_CHIP_PM_UNKNOWN); -+ timeouts++; -+ } - } - } else { - bes_devel("skip enter lp mode\n"); -@@ -554,10 +586,34 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - * in an inconsistent state that cascades into SDIO TX errors on - * the BES2600. - */ -- if (timeouts == 0) -+ if (timeouts == 0) { - bes2600_pwr_device_enter_lp_mode(hw_priv); -- else -+ } else { -+ /* -+ * device_enter_lp_mode() was skipped (one or more VIFs -+ * timed out waiting for the firmware indication) so its -+ * gpio_sleep(MCU) - which drops the wake-flag bit and, if -+ * no other subsystem holds the wake, drives the GPIO low - -+ * never ran. Without it the bit stays asserted, and the -+ * next bes2600_pwr_device_exit_lp_mode() calls -+ * gpio_wake(MCU) into a "bit already set" no-op: the GPIO -+ * never re-edges, sbus_active() exhausts its 200x2ms -+ * MCU_WAKEUP_READY budget against an unwoken chip, and -+ * the first TX after idle stalls for several seconds. -+ * -+ * Drop the MCU wake-flag bit explicitly here so the next -+ * wake injects a real GPIO edge. gpio_allow_mcu_sleep -+ * preserves multi-subsystem semantics: it only drives the -+ * GPIO low when no other subsystem still holds wake; if -+ * BT or another holder is keeping the chip awake, the -+ * GPIO stays high and the bit clear here is purely -+ * bookkeeping (so the next gpio_wake doesn't no-op). -+ */ -+ if (hw_priv->sbus_ops->gpio_sleep) -+ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, -+ GPIO_WAKE_FLAG_MCU); - ret = -ETIMEDOUT; -+ } - - return ret; - } -@@ -833,6 +889,7 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv) - hw_priv->bes_power.power_up_task = NULL; - mutex_init(&hw_priv->bes_power.pwr_mutex); - atomic_set(&hw_priv->bes_power.dev_state, 0); -+ atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN); - init_completion(&hw_priv->bes_power.pm_enter_cmpl); - sema_init(&hw_priv->bes_power.sync_lock, 1); - device_set_wakeup_capable(hw_priv->pdev, true); -@@ -1213,9 +1270,28 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event) - - void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) +-//#define DATA_DUMP_OBSERVE +- + static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val) { -- if((psmode & 0x01) != WSM_PSM_ACTIVE) { -- bes_devel("complete pm_enter_cmpl\n"); -- complete(&hw_priv->bes_power.pm_enter_cmpl); -+ /* -+ * The firmware sends a PM-changed indication for every transition, -+ * including ones we didn't ask for (firmware-internal coex moves, -+ * idle-driven aging). Update chip_pm_state unconditionally so the -+ * wake path can use it, but only fire pm_enter_cmpl when a host- -+ * initiated set_pm is actually in flight - otherwise a stale -+ * indication can prime a future wait against a freshly -+ * reinit_completion()'ed state. -+ */ -+ if ((psmode & 0x01) != WSM_PSM_ACTIVE) { -+ atomic_set(&hw_priv->bes_power.chip_pm_state, -+ BES2600_CHIP_PM_LP); -+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process, -+ 1, 0) == 1) { -+ bes_devel("complete pm_enter_cmpl\n"); -+ complete(&hw_priv->bes_power.pm_enter_cmpl); -+ } else { -+ bes_devel("PM ind (LP) without pending wait; state recorded\n"); -+ } -+ } else { -+ atomic_set(&hw_priv->bes_power.chip_pm_state, -+ BES2600_CHIP_PM_ACTIVE); + u8 frame_num = 0; +@@ -468,14 +463,6 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n + + const struct firmware *fw_bin; + +-#ifdef DATA_DUMP_OBSERVE +- char *observe; +- size_t observe_len; +- loff_t observe_off = 0; +- mm_segment_t old_fs; +- struct file *observe_file = NULL; +-#endif +- + struct fw_msg_hdr_t header; + struct fw_info_t fw_info; + struct download_fw_t download_addr; +@@ -583,14 +570,6 @@ const struct firmware *fw_bin; } - } + download_addr.addr = fw_info.addr; -diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h -index 1ba866c..6bc44ac 100644 ---- a/drivers/staging/bes2600/bes_pwr.h -+++ b/drivers/staging/bes2600/bes_pwr.h -@@ -64,6 +64,20 @@ enum power_down_state - POWER_DOWN_STATE_UNLOCKED, - }; +-#ifdef DATA_DUMP_OBSERVE +- observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0); +- if (IS_ERR(observe_file)) { +- bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file)); +- observe_file = NULL; +- } +-#endif +- + while (code_length) { + #if 1 +@@ -640,17 +619,6 @@ const struct firmware *fw_bin; + //mdelay(5000); + bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length); + +-#ifdef DATA_DUMP_OBSERVE +- if (observe_file) { +- observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)); +- observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t); +- old_fs = get_fs(); +- set_fs(KERNEL_DS); +- vfs_write(observe_file, observe, observe_len, &observe_off); +- set_fs(old_fs); +- } +-#endif +- + ret = bes2600_data_write(long_buf, length > 512 ? length : 512); + if (ret) { + bes_err("tx download fw data err:%d\n", ret); +@@ -832,11 +800,6 @@ const struct firmware *fw_bin; + + err2: + kfree(long_buf); +-#ifdef DATA_DUMP_OBSERVE +- if (observe_file) { +- filp_close(observe_file, NULL); +- } +-#endif + err1: + kfree(short_buf); + release_firmware(fw_bin); +diff --git a/drivers/staging/bes2600/bes_fw_common.c b/drivers/staging/bes2600/bes_fw_common.c +index 2e4745569..a0c1f9312 100644 +--- a/drivers/staging/bes2600/bes_fw_common.c ++++ b/drivers/staging/bes2600/bes_fw_common.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Firmware download common code for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include "bes_fw_common.h" + #include "bes_log.h" +diff --git a/drivers/staging/bes2600/bes_fw_common.h b/drivers/staging/bes2600/bes_fw_common.h +index 5c6561a39..dcd520058 100644 +--- a/drivers/staging/bes2600/bes_fw_common.h ++++ b/drivers/staging/bes2600/bes_fw_common.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Firmware download common interface + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef __BES_FW_COMMON_H__ + #define __BES_FW_COMMON_H__ +diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h +index 605cea8e9..7d3c4b8de 100644 +--- a/drivers/staging/bes2600/bes_log.h ++++ b/drivers/staging/bes2600/bes_log.h +@@ -1,3 +1,10 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ +/* -+ * Confirmed PM state of the firmware-side chip. Tracks what the host -+ * has *seen* the firmware acknowledge, not what the host has -+ * requested. UNKNOWN means a host-initiated transition timed out -+ * before the firmware indication arrived; the next wake path should -+ * treat it as "we don't know" and probe before issuing GPIO/SDIO -+ * wakeup ops. ++ * printk wrappers for BES2600 ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. ++ * + */ -+enum bes2600_chip_pm_state { -+ BES2600_CHIP_PM_ACTIVE = 0, -+ BES2600_CHIP_PM_LP, -+ BES2600_CHIP_PM_UNKNOWN, -+}; + extern struct device *global_dev; + + #ifdef CONFIG_BES2600_ENABLE_DEVEL_LOGS +@@ -8,3 +15,26 @@ extern struct device *global_dev; + #define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__) + #define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__) + #define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__) + - typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv); - typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv); ++/* ++ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering ++ * was never implemented in-tree; these shims let code paths gated by ++ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC ++ * build when their conditions are enabled. The first argument is ++ * currently unused; pick one of the BES2600_DBG_* constants below for ++ * documentation. ++ */ ++#define BES2600_DBG_SBUS 0 ++#define BES2600_DBG_DOWNLOAD 0 ++#define BES2600_DBG_ITP 0 ++#define BES2600_DBG_TEST_MODE 0 ++ ++#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__) ++#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__) ++#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__) ++#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__) ++#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \ ++ do { \ ++ if (_cond) \ ++ bes_err(fmt, ##__VA_ARGS__); \ ++ } while (0) +diff --git a/drivers/staging/bes2600/bes_nl80211_testmode_msg.h b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h +index b70a0dddc..c97c1ad78 100644 +--- a/drivers/staging/bes2600/bes_nl80211_testmode_msg.h ++++ b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Vendor testmode messages for BES2600 + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ -@@ -106,6 +120,7 @@ struct bes2600_pwr_t - bool ap_lp_bad; - struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM]; - atomic_t pm_set_in_process; -+ atomic_t chip_pm_state; - }; - - #ifdef CONFIG_BES2600_WOWLAN --- -2.54.0 - -From 7a65dc374c671e20bd6303959ff234a179bc9ff7 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Tue, 28 Apr 2026 15:23:34 +0200 -Subject: [PATCH 17/29] bes2600: short-circuit wake handshake when chip is - confirmed ACTIVE - -The previous patch ("bes2600: gate PM indication completion on pending -request and track chip state") added enum bes2600_chip_pm_state and the -chip_pm_state field tracking what the host has *seen the firmware -confirm*. This patch makes the wake side use it. - -Without this, every bes2600_pwr_device_exit_lp_mode() unconditionally -runs gpio_wake() + sbus_active() + wsm_set_operational_mode(active), -even when the chip is already in confirmed-ACTIVE state and the wake -sequence has nothing to do. The visible failure mode on PineTab2: - - bes2600_pwr_enter_lp_mode, wait pm ind timeout - repeat set gpio_wake_flag, sub_sys:0 - bes2600_sdio_active failed, subsys:0 - bes2600_pwr_device_exit_lp_mode, active mcu fail - -cycling every ~9 s, ~22 cycles in 10 minutes. Three pieces: - - 1. enter_lp_mode timed out (firmware indication lost). With c6.1, - chip_pm_state is now UNKNOWN. - 2. lock_device fires exit_lp_mode. - 3. gpio_wake hits "bit already set" because device_enter_lp_mode - was skipped when the indication timed out, so gpio_sleep was - never called - the bit reflects driver intent, not chip state. - gpio_wake silently no-ops (no GPIO edge), bit stays set. - 4. sbus_active spends 200 x 2 ms looking for MCU_WAKEUP_READY that - never comes (firmware was never told to wake), then fails. - 5. Driver continues to wsm_set_operational_mode against the wedged - bus, compounding the failure. - -This patch's three moves: - - * bes2600_pwr_device_exit_lp_mode() reads chip_pm_state at entry. - On BES2600_CHIP_PM_ACTIVE, log at devel level and return without - touching gpio_wake / sbus_active / WSM. The chip is in the state - we want; the handshake exists only to drive a transition. - - * On BES2600_CHIP_PM_LP or BES2600_CHIP_PM_UNKNOWN, run the wake - handshake as before, but on sbus_active() failure: set - chip_pm_state = UNKNOWN, log once at err level, and bail out. - Do NOT call wsm_set_operational_mode over a wedged bus - it - would just emit a second error and leave the chip in an even - less defined state. - - * bes2600_gpio_wakeup_mcu() / bes2600_gpio_allow_mcu_sleep(): - demote "repeat set/clear gpio_wake_flag" from bes_err to - bes_devel. Multi-subsystem wake-hold (e.g. WIFI + BT both want - MCU awake) is the steady-state case, and the symmetric clear - while bit-already-clear is racy bookkeeping rather than a - hardware error. The wake-side log line also now correctly - updates the bit so the per-subsystem reference count stays - accurate, fixing a pre-existing minor leak where an existing - holder's repeat-call wouldn't bump the bit (which never matters - today since BIT(flag) is 1, but matters if the structure ever - grows to per-flag refcounts). - -Net effect on the cycle: - - * If chip is genuinely ACTIVE (chip_pm_state == ACTIVE), wake skips - cleanly. Storm goes silent. - * If chip is genuinely LP, behaviour is unchanged. - * If chip is UNKNOWN (post-timeout state), one wake attempt is - made; on failure, state stays UNKNOWN and we don't emit a - second cascade error per attempt. Repeated UNKNOWN with failed - wake will eventually be picked up by the LMAC active-monitor - and escalated to mmc_hw_reset (c5.2). - -No new locks, no new state. Only consumption of the chip_pm_state -field added in the prerequisite patch. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes2600_sdio.c | 15 +++++++++-- - bes2600/bes_pwr.c | 56 ++++++++++++++++++++++++++++++++++++------ - 2 files changed, 62 insertions(+), 9 deletions(-) - -diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index f7f86d7..5a0694a 100644 ---- a/drivers/staging/bes2600/bes2600_sdio.c -+++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -1389,7 +1389,14 @@ static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag) - - /* error check */ - if((self->gpio_wakup_flags & BIT(flag)) != 0) { -- bes_err( "repeat set gpio_wake_flag, sub_sys:%d", flag); -+ /* -+ * Multiple subsystems holding wake is the steady-state case -+ * (e.g. WIFI + BT both want MCU awake). Demoted from bes_err -+ * to bes_devel since it isn't an error - the GPIO is already -+ * asserted high and the subsystem is now also tracked. -+ */ -+ bes_devel("repeat set gpio_wake_flag, sub_sys:%d\n", flag); -+ self->gpio_wakup_flags |= BIT(flag); - mutex_unlock(&self->io_mutex); - return; - } -@@ -1421,7 +1428,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag) - - /* error check */ - if((self->gpio_wakup_flags & BIT(flag)) == 0) { -- bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag); -+ /* -+ * Mirror of the wake path: a clear when the bit is already -+ * clear is racy bookkeeping, not a hardware error. -+ */ -+ bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag); - mutex_unlock(&self->io_mutex); - return; - } + #ifndef BES_NL80211_TESTMODE_MSG_H diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index 9b4a4de..b7b6c2f 100644 +index e7a104542..a3f954bf3 100644 --- a/drivers/staging/bes2600/bes_pwr.c +++ b/drivers/staging/bes2600/bes_pwr.c -@@ -621,19 +621,61 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv) - { - int ret = 0; -+ enum bes2600_chip_pm_state state; - struct wsm_operational_mode mode = { - .power_mode = wsm_power_mode_active, - .disableMoreFlagUsage = true, - }; - -- bes_devel("host lock lmac\n"); -- if(hw_priv->sbus_ops->gpio_wake) -- hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); -+ /* -+ * Consult chip_pm_state set by bes2600_pwr_notify_ps_changed(). -+ * If we last saw the firmware confirm ACTIVE, skip ONLY the -+ * gpio_wake + sbus_active wake handshake - the GPIO is already -+ * asserted high and the SDIO MCU subsystem is already running, -+ * so another sbus_active() round-trip just hits its 200x2ms -+ * timeout because the firmware has nothing to do. -+ * -+ * wsm_set_operational_mode() below is NOT part of the wake -+ * handshake; it is the operational-mode setter the firmware -+ * tracks per call. Skipping it leaves the chip's SDIO state -+ * machine without a fresh operational-mode update, which on -+ * PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read) -+ * within a few seconds of probe completion. So it must run -+ * unconditionally. -+ */ -+ state = atomic_read(&hw_priv->bes_power.chip_pm_state); -+ if (state == BES2600_CHIP_PM_ACTIVE) { -+ bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n"); -+ } else { -+ bes_devel("host lock lmac\n"); -+ if (hw_priv->sbus_ops->gpio_wake) -+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, -+ GPIO_WAKE_FLAG_MCU); - -- if(hw_priv->sbus_ops->sbus_active) { -- ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU); -- if (ret) -- bes_err("%s, active mcu fail\n", __func__); -+ if (hw_priv->sbus_ops->sbus_active) { -+ ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, -+ SUBSYSTEM_MCU); -+ if (ret) { -+ /* -+ * MCU_WAKEUP_READY did not arrive within -+ * the SDIO handshake window. Record state -+ * as UNKNOWN so the next exit_lp_mode call -+ * also runs the full wake sequence (no -+ * skip), but still send operational_mode -+ * below to match pre-c6 behaviour - the -+ * WSM may succeed even if the SDIO active -+ * confirm was lost, and if it fails too, -+ * we just emit a second devel-level error. -+ * Repeated UNKNOWN is the signal for the -+ * LMAC active-monitor to eventually -+ * escalate to bus_reset (c5.2's -+ * mmc_hw_reset path). -+ */ -+ bes_err("%s, active mcu fail\n", __func__); -+ atomic_set(&hw_priv->bes_power.chip_pm_state, -+ BES2600_CHIP_PM_UNKNOWN); -+ } -+ } - } - - ret = wsm_set_operational_mode(hw_priv, &mode, 0); --- -2.54.0 - -From dc1505f5bab24c5f0960dcc612ce51cd2e5aeddf Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Tue, 28 Apr 2026 16:54:06 +0200 -Subject: [PATCH 18/29] bes2600: self-detect when firmware does not honor PSM - and skip the cycle - -The c6 series fixed several host-side bookkeeping bugs around PSM -transitions, but didn't address the underlying contract: this chip's -firmware (BES2600 with the Bestechnic Dec 2023 build that ships on -PineTab2 and most danctnix images) silently drops every WSM_set_pm -request without emitting the corresponding PM_INDICATION. The driver's -own power_down_work delayed work calls bes2600_pwr_enter_lp_mode every -~10s; without firmware acknowledgment each call burns 5s on -wait_for_completion_timeout(pm_enter_cmpl, 5*HZ) and produces a -recurring three-line cascade in dmesg: - - bes2600_pwr_enter_lp_mode, wait pm ind timeout - bes2600_sdio_active failed, subsys:0 - bes2600_pwr_device_exit_lp_mode, active mcu fail - -Confirmed by tripwire instrumentation on PineTab2 (linux-pinetab2 -6.19.10-danctnix1, ohm) running the c5+c6 stack: zero -wsm_set_pm_indication() invocations across an entire boot, while -bes2600_pwr_enter_lp_mode timed out repeatedly, and -bes2600_sdio_active() consistently saw BES_SLAVE_STATUS_REG_ID return -0x2f (every "ready" bit set except MCU_WAKEUP_READY (bit 4) - the -firmware reports "I'm awake, there's nothing to wake from"). - -This patch makes the driver self-heal: - - * struct bes2600_pwr_t gains pm_unsupported (bool) and - pm_consecutive_timeouts (unsigned int). Both initialised to - 0/false. - - * bes2600_pwr_enter_lp_mode early-returns -EOPNOTSUPP when - pm_unsupported is set. Skips the per-VIF set_pm round-trip and - the wait_for_completion entirely. - - * On the cmpxchg-success branch of the timeout path, we increment - pm_consecutive_timeouts. When it crosses - BES2600_PM_UNSUPPORTED_THRESHOLD (3, ~15s of trying), we latch - pm_unsupported = true and force chip_pm_state = ACTIVE so that - bes2600_pwr_device_exit_lp_mode's c6.2 skip branch covers the - wake side (no gpio_wake / sbus_active / WSM_set_operational_mode - reissue past the first one). - - * bes2600_pwr_notify_ps_changed resets pm_consecutive_timeouts to 0 - on any incoming PM indication, and clears pm_unsupported if it - was previously latched. So a firmware update that fixes PM_IND - delivery automatically re-enables PSM transitions without a - driver rebuild. - -mac80211's PSM requests via bes2600_set_pm() still flow to the -firmware unchanged; they just don't have host-side timeouts so they -remain silent regardless of firmware acknowledgment. Power -consumption goes up if the firmware actually CAN do PSM (we'd be -keeping the chip awake unnecessarily), but on a chip where the -counter trips this trade-off is forced anyway: the chip stayed awake -under the broken cascade as well, just with constant SDIO churn. - -Net effect on dmesg: after ~15s of boot, the three-line cascade stops -firing entirely. The firmware-side wedge is observed once per boot -(captured by the pm_unsupported latch) instead of per-cycle. - -Signed-off-by: Markus Fritsche ---- - bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++- - bes2600/bes_pwr.h | 9 ++++++ - 2 files changed, 78 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index b7b6c2f..620acef 100644 ---- a/drivers/staging/bes2600/bes_pwr.c -+++ b/drivers/staging/bes2600/bes_pwr.c -@@ -467,6 +467,45 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv) +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Chip-side power state machine for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include + #include +@@ -467,14 +464,65 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv) bes_devel("device enter sleep\n"); } @@ -3363,7 +1375,9 @@ index b7b6c2f..620acef 100644 static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) { int i = 0; -@@ -476,6 +515,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) + struct bes2600_vif *priv; + int ret = 0; ++ int timeouts = 0; char ip_str[20]; unsigned long status = 0; @@ -3381,39 +1395,209 @@ index b7b6c2f..620acef 100644 /* set interface low power configuration */ bes2600_for_each_vif(hw_priv, priv, i) { #ifdef P2P_MULTIVIF -@@ -571,6 +621,9 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - atomic_set(&hw_priv->bes_power.chip_pm_state, - BES2600_CHIP_PM_UNKNOWN); - timeouts++; +@@ -523,27 +571,100 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) + bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n", + __func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod, + priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod); ++ /* ++ * Reinit BEFORE the WSM goes out, so a stale ++ * indication from a previous cycle cannot have ++ * primed pm_enter_cmpl. From here until the ++ * indication callback's cmpxchg(1->0) on ++ * pm_set_in_process, only the indication for ++ * THIS request can complete the wait. ++ */ ++ reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); + atomic_set(&hw_priv->bes_power.pm_set_in_process, 1); ++ + ret = bes2600_set_pm(priv, &priv->powersave_mode); + if (ret) { + atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); + bes_err("%s, set operation mode fail\n", __func__); ++ timeouts++; ++ continue; + } + + /* wait power save mode changed indication */ + status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ); +- atomic_set(&hw_priv->bes_power.pm_set_in_process, 0); +- reinit_completion(&hw_priv->bes_power.pm_enter_cmpl); +- if (!status) +- bes_err("%s, wait pm ind timeout\n", __func__); ++ if (!status) { ++ /* ++ * The indication callback only fires ++ * complete() when it observes ++ * pm_set_in_process == 1; cmpxchg it ++ * to 0 here so a late indication ++ * cannot prime the next wait. ++ * ++ * If we win the cmpxchg, this is a ++ * real timeout: the firmware's PS ++ * state is unknown to us. Mark it as ++ * such so the next wake path can ++ * probe before assuming the chip is ++ * still active. ++ * ++ * If we lose the cmpxchg, the ++ * indication arrived between the ++ * wait timing out and us getting ++ * here; treat as success. ++ */ ++ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process, ++ 1, 0) == 1) { ++ bes_devel("%s, wait pm ind timeout\n", __func__); ++ atomic_set(&hw_priv->bes_power.chip_pm_state, ++ BES2600_CHIP_PM_UNKNOWN); ++ timeouts++; + if (++hw_priv->bes_power.pm_consecutive_timeouts + >= BES2600_PM_UNSUPPORTED_THRESHOLD) + bes2600_pwr_latch_pm_unsupported(hw_priv); - } - } ++ } ++ } } else { -@@ -609,7 +662,8 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) - * GPIO stays high and the bit clear here is purely - * bookkeeping (so the next gpio_wake doesn't no-op). - */ -- if (hw_priv->sbus_ops->gpio_sleep) + bes_devel("skip enter lp mode\n"); + } + } + } + +- /* set device low power configuration */ +- bes2600_pwr_device_enter_lp_mode(hw_priv); ++ /* ++ * Enter the device-end of the LP transition only if every per-VIF ++ * mac80211 handshake reached firmware-ACKed completion. Doing the ++ * device-LP setup while any VIF is still pending leaves the driver ++ * in an inconsistent state that cascades into SDIO TX errors on ++ * the BES2600. ++ */ ++ if (timeouts == 0) { ++ bes2600_pwr_device_enter_lp_mode(hw_priv); ++ } else { ++ /* ++ * device_enter_lp_mode() was skipped (one or more VIFs ++ * timed out waiting for the firmware indication) so its ++ * gpio_sleep(MCU) - which drops the wake-flag bit and, if ++ * no other subsystem holds the wake, drives the GPIO low - ++ * never ran. Without it the bit stays asserted, and the ++ * next bes2600_pwr_device_exit_lp_mode() calls ++ * gpio_wake(MCU) into a "bit already set" no-op: the GPIO ++ * never re-edges, sbus_active() exhausts its 200x2ms ++ * MCU_WAKEUP_READY budget against an unwoken chip, and ++ * the first TX after idle stalls for several seconds. ++ * ++ * Drop the MCU wake-flag bit explicitly here so the next ++ * wake injects a real GPIO edge. gpio_allow_mcu_sleep ++ * preserves multi-subsystem semantics: it only drives the ++ * GPIO low when no other subsystem still holds wake; if ++ * BT or another holder is keeping the chip awake, the ++ * GPIO stays high and the bit clear here is purely ++ * bookkeeping (so the next gpio_wake doesn't no-op). ++ */ + if (!hw_priv->bes_power.pm_unsupported && + hw_priv->sbus_ops->gpio_sleep) - hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, - GPIO_WAKE_FLAG_MCU); - ret = -ETIMEDOUT; -@@ -932,6 +986,8 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv) ++ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, ++ GPIO_WAKE_FLAG_MCU); ++ ret = -ETIMEDOUT; ++ } + + return ret; + } +@@ -551,19 +672,61 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv) + static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv) + { + int ret = 0; ++ enum bes2600_chip_pm_state state; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_active, + .disableMoreFlagUsage = true, + }; + +- bes_devel("host lock lmac\n"); +- if(hw_priv->sbus_ops->gpio_wake) +- hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU); +- +- if(hw_priv->sbus_ops->sbus_active) { +- ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU); +- if (ret) +- bes_err("%s, active mcu fail\n", __func__); ++ /* ++ * Consult chip_pm_state set by bes2600_pwr_notify_ps_changed(). ++ * If we last saw the firmware confirm ACTIVE, skip ONLY the ++ * gpio_wake + sbus_active wake handshake - the GPIO is already ++ * asserted high and the SDIO MCU subsystem is already running, ++ * so another sbus_active() round-trip just hits its 200x2ms ++ * timeout because the firmware has nothing to do. ++ * ++ * wsm_set_operational_mode() below is NOT part of the wake ++ * handshake; it is the operational-mode setter the firmware ++ * tracks per call. Skipping it leaves the chip's SDIO state ++ * machine without a fresh operational-mode update, which on ++ * PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read) ++ * within a few seconds of probe completion. So it must run ++ * unconditionally. ++ */ ++ state = atomic_read(&hw_priv->bes_power.chip_pm_state); ++ if (state == BES2600_CHIP_PM_ACTIVE) { ++ bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n"); ++ } else { ++ bes_devel("host lock lmac\n"); ++ if (hw_priv->sbus_ops->gpio_wake) ++ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, ++ GPIO_WAKE_FLAG_MCU); ++ ++ if (hw_priv->sbus_ops->sbus_active) { ++ ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, ++ SUBSYSTEM_MCU); ++ if (ret) { ++ /* ++ * MCU_WAKEUP_READY did not arrive within ++ * the SDIO handshake window. Record state ++ * as UNKNOWN so the next exit_lp_mode call ++ * also runs the full wake sequence (no ++ * skip), but still send operational_mode ++ * below to match pre-c6 behaviour - the ++ * WSM may succeed even if the SDIO active ++ * confirm was lost, and if it fails too, ++ * we just emit a second devel-level error. ++ * Repeated UNKNOWN is the signal for the ++ * LMAC active-monitor to eventually ++ * escalate to bus_reset (c5.2's ++ * mmc_hw_reset path). ++ */ ++ bes_err("%s, active mcu fail\n", __func__); ++ atomic_set(&hw_priv->bes_power.chip_pm_state, ++ BES2600_CHIP_PM_UNKNOWN); ++ } ++ } + } + + ret = wsm_set_operational_mode(hw_priv, &mode, 0); +@@ -819,6 +982,9 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv) + hw_priv->bes_power.power_up_task = NULL; mutex_init(&hw_priv->bes_power.pwr_mutex); atomic_set(&hw_priv->bes_power.dev_state, 0); - atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN); ++ atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN); + hw_priv->bes_power.pm_unsupported = false; + hw_priv->bes_power.pm_consecutive_timeouts = 0; init_completion(&hw_priv->bes_power.pm_enter_cmpl); sema_init(&hw_priv->bes_power.sync_lock, 1); device_set_wakeup_capable(hw_priv->pdev, true); -@@ -1321,6 +1377,18 @@ void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) - * indication can prime a future wait against a freshly - * reinit_completion()'ed state. - */ +@@ -1199,9 +1365,40 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event) + + void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) + { +- if((psmode & 0x01) != WSM_PSM_ACTIVE) { +- bes_devel("complete pm_enter_cmpl\n"); +- complete(&hw_priv->bes_power.pm_enter_cmpl); ++ /* ++ * The firmware sends a PM-changed indication for every transition, ++ * including ones we didn't ask for (firmware-internal coex moves, ++ * idle-driven aging). Update chip_pm_state unconditionally so the ++ * wake path can use it, but only fire pm_enter_cmpl when a host- ++ * initiated set_pm is actually in flight - otherwise a stale ++ * indication can prime a future wait against a freshly ++ * reinit_completion()'ed state. ++ */ + /* + * Any PM indication, whatever its psmode, proves the firmware is + * actually emitting them. Reset the consecutive-timeout counter @@ -3426,1778 +1610,24 @@ index b7b6c2f..620acef 100644 + hw_priv->bes_power.pm_unsupported = false; + } + - if ((psmode & 0x01) != WSM_PSM_ACTIVE) { - atomic_set(&hw_priv->bes_power.chip_pm_state, - BES2600_CHIP_PM_LP); -diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h -index 6bc44ac..92de90b 100644 ---- a/drivers/staging/bes2600/bes_pwr.h -+++ b/drivers/staging/bes2600/bes_pwr.h -@@ -121,6 +121,15 @@ struct bes2600_pwr_t - struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM]; - atomic_t pm_set_in_process; - atomic_t chip_pm_state; -+ /* -+ * Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD -+ * consecutive enter_lp_mode timeouts with zero PM_INDICATIONs -+ * received from firmware. Indicates this chip's firmware does -+ * not honor host-driven PSM transitions; further attempts are -+ * skipped to avoid the 5s timeout cascade. -+ */ -+ bool pm_unsupported; -+ unsigned int pm_consecutive_timeouts; - }; - - #ifdef CONFIG_BES2600_WOWLAN --- -2.54.0 - -From 91640bd96d36dd5769b1325e1b2130a95277e0e7 Mon Sep 17 00:00:00 2001 -From: "Claude (noether)" -Date: Wed, 6 May 2026 19:50:52 +0200 -Subject: [PATCH 20/29] bes2600: pre-empt AP-deauth-6 with mac80211 reassoc on - decrypt-fail storm - -When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a burst -of received frames (typically because the host's PTK or GTK has fallen -out of sync with the AP), the AP eventually concludes that the STA is -not authenticated and emits an unprotected deauth-reason-6 ("Class 2 -frame received from non-authenticated station"). On the deployed -pinetab2 + bes2600 stack this AP-initiated deauth has been observed to -leave the link blackholed for up to 109 s before userspace finds a -different SSID/channel to recover on. (Receipts at -https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.) - -Add a sliding-window counter on each bes2600_vif: when 5 decrypt -failures fire within 5 s, schedule a worker that calls -ieee80211_connection_loss(vif). mac80211 then performs immediate -disassociation; userspace (NetworkManager / wpa_supplicant) reconnects -with fresh keys before the AP gets a chance to fire its unprotected -deauth. - -Predicted Phase 7 delta vs the unpatched baseline: -- decrypt-burst rate: unchanged (this does not address root cause) -- AP-deauth-6 rate: <= 0.2 of baseline -- conditional probability of >5s blackhole given a burst: - 100% -> <= 10% -- worst-case recovery time: 109s -> <5s - -Contract pin: ieee80211_connection_loss() per -include/net/mac80211.h: "may also be called if the connection needs to -be terminated for some other reason... will cause immediate change to -disassociated state, without connection recovery attempts." Userspace -recovery is the existing NM/wpa_supplicant path. The worker context -satisfies the implicit process-context expectation. - -Files touched: -- bes2600/bes2600.h: 4 new fields on struct bes2600_vif + 2 prototypes -- bes2600/txrx.c: new helpers + the call site at the existing - WSM_STATUS_DECRYPTFAILURE log point (the unconditional "goto drop" - branch in bes2600_rx_cb) -- bes2600/sta.c: bes2600_decrypt_storm_init() in bes2600_vif_setup; - cancel_work_sync() in bes2600_remove_interface, alongside the - existing per-vif cancel_*_work_sync block. Safe under the kernel - cancel_work_sync contract: the work_struct is INIT_WORK'd in setup, - so the call is valid; it blocks until any in-flight handler returns, - ensuring no use-after-free of priv when mac80211 frees the vif; and - it is idempotent (subsequent calls just return false). -- bes2600/debug.c: DecryptStormRecoveries seq_printf in the per-vif - status seq_file output - -Threshold (5/5s) is set well above the steady-state per-vif decrypt- -fail rate observed in measurement (~1/min even under sustained 1 MB/s -load), so a true storm is required to trip it. The cw1200/cw1260 -ancestor has no equivalent storm-recovery; this is a clean addition. - -checkpatch.pl --no-tree --strict: clean (0/0/0). - -Signed-off-by: Claude (noether) -Co-Authored-By: Claude Opus 4.7 (1M context) ---- - bes2600/bes2600.h | 9 ++++++ - bes2600/debug.c | 2 ++ - bes2600/sta.c | 2 ++ - bes2600/txrx.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 87 insertions(+) - -diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h -index 0e60960..66482f7 100644 ---- a/drivers/staging/bes2600/bes2600.h -+++ b/drivers/staging/bes2600/bes2600.h -@@ -596,6 +596,11 @@ struct bes2600_vif { - unsigned long rx_timestamp; - u32 cipherType; - -+ /* Decrypt-storm fast-recover (Trigger B). See txrx.c. */ -+ unsigned long decrypt_storm_window_start; -+ unsigned int decrypt_storm_count; -+ unsigned int decrypt_storm_recoveries; -+ struct work_struct decrypt_storm_recover_work; - - /* AP powersave */ - u32 link_id_map; -@@ -856,4 +861,8 @@ int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv); - void bes2600_btusb_uninit(struct usb_interface *interface); - #endif - -+/* Decrypt-storm fast-recover helpers — see txrx.c. */ -+void bes2600_decrypt_storm_init(struct bes2600_vif *priv); -+void bes2600_decrypt_storm_account(struct bes2600_vif *priv); -+ - #endif /* BES2600_H */ -diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c -index 5228b22..ca223dd 100644 ---- a/drivers/staging/bes2600/debug.c -+++ b/drivers/staging/bes2600/debug.c -@@ -542,6 +542,8 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v) - priv->listening ? " (listening)" : ""); - seq_printf(seq, "Assoc: %s\n", - bes2600_debug_join_status[priv->join_status]); -+ seq_printf(seq, "DecryptStormRecoveries: %u\n", -+ priv->decrypt_storm_recoveries); - if (priv->rx_filter.promiscuous) - seq_puts(seq, "Filter: promisc\n"); - else if (priv->rx_filter.fcs) -diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c -index bc6d483..139bdae 100644 ---- a/drivers/staging/bes2600/sta.c -+++ b/drivers/staging/bes2600/sta.c -@@ -464,6 +464,7 @@ void bes2600_remove_interface(struct ieee80211_hw *dev, - cancel_delayed_work_sync(&priv->join_timeout); - cancel_delayed_work_sync(&priv->set_cts_work); - cancel_delayed_work_sync(&priv->pending_offchanneltx_work); -+ cancel_work_sync(&priv->decrypt_storm_recover_work); - - timer_delete_sync(&priv->mcast_timeout); - /* TODO:COMBO: May be reset of these variables "delayed_link_loss and -@@ -2639,6 +2640,7 @@ int bes2600_vif_setup(struct bes2600_vif *priv) - - /* Setup per vif workitems and locks */ - spin_lock_init(&priv->vif_lock); -+ bes2600_decrypt_storm_init(priv); - INIT_WORK(&priv->join_work, bes2600_join_work); - INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout); - INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work); -diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c -index 017f0d8..f6a66d6 100644 ---- a/drivers/staging/bes2600/txrx.c -+++ b/drivers/staging/bes2600/txrx.c -@@ -26,6 +26,78 @@ - - #define BES2600_INVALID_RATE_ID (0xFF) - -+/* -+ * Decrypt-storm fast-recover (Trigger B). -+ * -+ * When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a -+ * burst of received frames (typically because the host's PTK or GTK -+ * has fallen out of sync with the AP), the AP eventually concludes that -+ * the STA is not authenticated and emits an unprotected deauth-reason-6 -+ * ("Class 2 frame received from non-authenticated station"). On the -+ * deployed pinetab2 + bes2600 stack this AP-initiated deauth has been -+ * observed to leave the link blackholed for up to 109 s before -+ * userspace finds a different SSID/channel to recover on. (Receipts at -+ * https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.) -+ * -+ * Recovery here pre-empts the AP: when we see THRESHOLD decrypt -+ * failures within WINDOW, we ask mac80211 for a clean reassoc via -+ * ieee80211_connection_loss(), which causes immediate disassociation -+ * and lets userspace auto-reconnect with fresh keys. -+ * -+ * mac80211 contract: ieee80211_connection_loss() may be called -+ * regardless of IEEE80211_HW_CONNECTION_MONITOR; it causes immediate -+ * disassociation without driver-side recovery attempts. See -+ * include/net/mac80211.h for the canonical doc-comment. -+ * -+ * The threshold is set well above the steady-state per-vif -+ * decrypt-fail rate observed in measurement (~1/min even under -+ * sustained 1 MB/s load), so a true storm is required to trip it. -+ */ -+#define BES2600_DECRYPT_STORM_THRESHOLD 5 -+#define BES2600_DECRYPT_STORM_WINDOW_MS 5000 -+ -+static void bes2600_decrypt_storm_recover_work(struct work_struct *work) -+{ -+ struct bes2600_vif *priv = container_of(work, struct bes2600_vif, -+ decrypt_storm_recover_work); -+ -+ if (!priv->vif) -+ return; -+ -+ bes_warn("[bes2600] decrypt-storm fast-recover: forcing reassoc\n"); -+ ieee80211_connection_loss(priv->vif); -+ priv->decrypt_storm_recoveries++; -+} -+ -+void bes2600_decrypt_storm_init(struct bes2600_vif *priv) -+{ -+ INIT_WORK(&priv->decrypt_storm_recover_work, -+ bes2600_decrypt_storm_recover_work); -+ priv->decrypt_storm_window_start = 0; -+ priv->decrypt_storm_count = 0; -+ priv->decrypt_storm_recoveries = 0; -+} -+ -+void bes2600_decrypt_storm_account(struct bes2600_vif *priv) -+{ -+ unsigned long now = jiffies; -+ unsigned long window = msecs_to_jiffies(BES2600_DECRYPT_STORM_WINDOW_MS); -+ -+ if (priv->decrypt_storm_window_start == 0 || -+ time_after(now, priv->decrypt_storm_window_start + window)) { -+ priv->decrypt_storm_window_start = now; -+ priv->decrypt_storm_count = 1; -+ return; -+ } -+ -+ if (++priv->decrypt_storm_count >= BES2600_DECRYPT_STORM_THRESHOLD) { -+ priv->decrypt_storm_count = 0; -+ /* Skew the window so we don't re-fire on the same storm. */ -+ priv->decrypt_storm_window_start = now + window; -+ schedule_work(&priv->decrypt_storm_recover_work); -+ } -+} -+ - #ifdef CONFIG_BES2600_TESTMODE - #include "bes_nl80211_testmode_msg.h" - #endif /* CONFIG_BES2600_TESTMODE */ -@@ -1694,6 +1766,8 @@ void bes2600_rx_cb(struct bes2600_vif *priv, - goto drop; - } else { - bes_warn("[RX] Receive failure: %d.\n", arg->status); -+ if (arg->status == WSM_STATUS_DECRYPTFAILURE) -+ bes2600_decrypt_storm_account(priv); - goto drop; - } - } --- -2.54.0 - -From 06fab777454d36ec5178730d8423285c2457d3ba Mon Sep 17 00:00:00 2001 -From: "Claude (noether)" -Date: Thu, 7 May 2026 11:30:09 +0200 -Subject: [PATCH 21/29] bes2600: bus_reset on connection-loss storm to dodge - assoc-comeback blackhole - -When mac80211 declares connection loss against this AP (typically driven -by inactivity-deauth or beacon-loss), the userspace reauth that follows -sometimes enters a long blackhole: the AP responds to auth with success -but defers assoc with the 802.11v "assoc comeback" timer; ohm retries -faster than the comeback grants permission; the AP eventually fires an -unprotected deauth-reason-6 ("Class 2 frame received from non- -authenticated station"), and recovery only completes via cross-SSID or -cross-channel fallback. Receipts: ~86 s blackhole observed in the -phase-7 rep on 2026-05-07 02:42, with three subsequent BSSIDs returning -assoc comeback timeouts before reason-9 (STA_REQ_ASSOC_WITHOUT_AUTH) -fired. Documented in marfrit/besser:notes/phase4-2026-05-07.md. - -When N=3 driver-side connection_loss decisions fire within a 60 s window -on the same vif, skip the ieee80211_connection_loss() path and trigger -the c5.2-introduced bes2600_chrdev_do_bus_reset() instead. The bus -reset removes and re-probes the chip; userspace re-associates with a -fresh chip state, dodging the AP's comeback-timer rejection cycle. - -Predicted Phase 7 delta vs current baseline: -- api_connection_loss rate: unchanged (we don't address the trigger) -- conditional probability of >5 s blackhole given event: <= 30 % -- worst-case recovery: 86 s -> < 10 s - -Contract pin: bes2600_chrdev_do_bus_reset(sbus_ops, sbus_priv) at -bes2600/bes_chardev.c:455, introduced by c5.2. The function is async- -returning: sbus_ops->bus_reset() schedules an SDIO rescan; the helper -waits up to 3 s for the remove() callback to clear sbus_priv, then -returns. Per-vif state is gone after this point, so the recover work -lives on bes2600_common (hw_priv) and uses the global bes2600_cdev for -the bus_reset call rather than dereferencing per-vif state. - -Threshold (3 / 60 s) is well above the steady-state per-vif -connection_loss rate observed in the patch-A phase-7 rep (0.86/h under -sustained load), so a true storm is required to trip it. - -Files touched: -- bes2600/bes2600.h: 3 counter fields on struct bes2600_vif, 1 - work_struct on struct bes2600_common, 3 prototypes -- bes2600/sta.c: 3 helpers + storm-account hook in - bes2600_connection_loss_work + storm-init in bes2600_vif_setup + - cancel_work_sync in the hw_priv shutdown path; #include bes_chardev.h - was already pulled in by an earlier c-stack patch -- bes2600/main.c: INIT_WORK alongside other hw_priv work_structs -- bes2600/debug.c: ConnectionLossStormRecoveries seq_printf in the - per-vif status seq_file output - -The cw1200/cw1260 ancestor has no equivalent; this is a clean -addition. checkpatch.pl --no-tree --strict: clean (0/0/0). - -Signed-off-by: Claude (noether) ---- - bes2600/bes2600.h | 12 +++++++ - bes2600/bes_chardev.c | 12 +++++++ - bes2600/bes_chardev.h | 1 + - bes2600/debug.c | 2 ++ - bes2600/main.c | 2 ++ - bes2600/sta.c | 82 +++++++++++++++++++++++++++++++++++++++++-- - 6 files changed, 109 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h -index 66482f7..ec41141 100644 ---- a/drivers/staging/bes2600/bes2600.h -+++ b/drivers/staging/bes2600/bes2600.h -@@ -511,6 +511,9 @@ struct bes2600_common { - struct list_head coex_event_list; - spinlock_t coex_event_lock; - -+ /* Connection-loss-storm fast-recover (Trigger A). See sta.c. */ -+ struct work_struct connection_loss_storm_recover_work; -+ - /* member for low power */ - struct bes2600_pwr_t bes_power; - -@@ -627,6 +630,10 @@ struct bes2600_vif { - /* CQM Implementation */ - struct delayed_work bss_loss_work; - struct delayed_work connection_loss_work; -+ /* Connection-loss-storm fast-recover (Trigger A). See sta.c. */ -+ unsigned long connection_loss_storm_window_start; -+ unsigned int connection_loss_storm_count; -+ unsigned int connection_loss_storm_recoveries; - struct work_struct tx_failure_work; - int delayed_link_loss; - spinlock_t bss_loss_lock; -@@ -865,4 +872,9 @@ void bes2600_btusb_uninit(struct usb_interface *interface); - void bes2600_decrypt_storm_init(struct bes2600_vif *priv); - void bes2600_decrypt_storm_account(struct bes2600_vif *priv); - -+/* Connection-loss-storm fast-recover helpers — see sta.c. */ -+void bes2600_connection_loss_storm_init(struct bes2600_vif *priv); -+bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv); -+void bes2600_connection_loss_storm_recover(struct work_struct *work); -+ - #endif /* BES2600_H */ -diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c -index d1375bc..224c62d 100644 ---- a/drivers/staging/bes2600/bes_chardev.c -+++ b/drivers/staging/bes2600/bes_chardev.c -@@ -484,6 +484,18 @@ int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_pri - return 0; - } - -+/* -+ * Trigger bes2600_chrdev_do_bus_reset() against the file-global -+ * bes2600_cdev. Used by host-side recovery paths outside this -+ * compilation unit (e.g. sta.c connection-loss-storm fast-recover) so -+ * those callers do not need to reach the static bes2600_cdev directly. -+ */ -+int bes2600_chrdev_trigger_bus_reset(void) -+{ -+ return bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops, -+ bes2600_cdev.sbus_priv); -+} -+ - bool bes2600_chrdev_is_wifi_opened(void) - { - bool wifi_opened = false; -diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h -index ca8419e..2a7cad7 100644 ---- a/drivers/staging/bes2600/bes_chardev.h -+++ b/drivers/staging/bes2600/bes_chardev.h -@@ -61,6 +61,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void); - int bes2600_chrdev_check_system_close(void); - int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); - int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv); -+int bes2600_chrdev_trigger_bus_reset(void); - void bes2600_chrdev_wakeup_bt(void); - void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev); - void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv); -diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c -index ca223dd..0d68392 100644 ---- a/drivers/staging/bes2600/debug.c -+++ b/drivers/staging/bes2600/debug.c -@@ -544,6 +544,8 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v) - bes2600_debug_join_status[priv->join_status]); - seq_printf(seq, "DecryptStormRecoveries: %u\n", - priv->decrypt_storm_recoveries); -+ seq_printf(seq, "ConnectionLossStormRecoveries: %u\n", -+ priv->connection_loss_storm_recoveries); - if (priv->rx_filter.promiscuous) - seq_puts(seq, "Filter: promisc\n"); - else if (priv->rx_filter.fcs) -diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c -index 7cbb3a9..ff82f4d 100644 ---- a/drivers/staging/bes2600/main.c -+++ b/drivers/staging/bes2600/main.c -@@ -489,6 +489,8 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) - spin_lock_init(&hw_priv->rtsvalue_lock); - INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work); - INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); -+ INIT_WORK(&hw_priv->connection_loss_storm_recover_work, -+ bes2600_connection_loss_storm_recover); - spin_lock_init(&hw_priv->event_queue_lock); - INIT_LIST_HEAD(&hw_priv->event_queue); - INIT_WORK(&hw_priv->event_handler, bes2600_event_handler); -diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c -index 139bdae..5868757 100644 ---- a/drivers/staging/bes2600/sta.c -+++ b/drivers/staging/bes2600/sta.c -@@ -268,6 +268,7 @@ void bes2600_stop(struct ieee80211_hw *dev, bool suspend) - cancel_work_sync(&hw_priv->coex_work); - coex_stop(hw_priv); - #endif -+ cancel_work_sync(&hw_priv->connection_loss_storm_recover_work); - - bes2600_wifi_stop(hw_priv); - -@@ -1675,6 +1676,70 @@ report: - spin_unlock(&priv->bss_loss_lock); - } - -+/* -+ * Connection-loss-storm fast-recover (Trigger A). -+ * -+ * bes2600_connection_loss_work below is the driver's own decision-point -+ * to give up on a BSS (after bss-loss detection accumulates beyond -+ * tolerance) and tell mac80211 via ieee80211_connection_loss(). On the -+ * deployed pinetab2 stack a single ieee80211_connection_loss() event -+ * sometimes triggers a userspace reauth blackhole (assoc-comeback -+ * timeouts followed by AP unprotected-deauth-reason-6) that ends only -+ * via cross-channel/cross-SSID fallback and can take 80+ s. Receipts at -+ * https://git.reauktion.de/marfrit/besser, notes/phase4-2026-05-07.md. -+ * -+ * When N connection-loss decisions land within WINDOW on the same vif, -+ * skip the ieee80211_connection_loss() path and trigger a chip-level -+ * bus_reset (the c5.2-introduced bes2600_chrdev_do_bus_reset). The chip -+ * is removed and re-probed; userspace re-associates from a fresh state, -+ * dodging the assoc-comeback loop. -+ * -+ * Threshold (3 / 60 s) is chosen well above the steady-state per-vif -+ * connection-loss rate observed in the patch-A Phase-7 rep -+ * (0.86/h under sustained load), so a true storm is required. -+ * -+ * The recover work_struct lives on bes2600_common (hw_priv) so that -+ * scheduling it does not race with vif teardown after bus_reset frees -+ * the per-vif state. -+ */ -+#define BES2600_CONNECTION_LOSS_STORM_THRESHOLD 3 -+#define BES2600_CONNECTION_LOSS_STORM_WINDOW_MS 60000 -+ -+void bes2600_connection_loss_storm_recover(struct work_struct *work) -+{ -+ bes_warn("[bes2600] connection-loss-storm fast-recover: bus_reset\n"); -+ bes2600_chrdev_trigger_bus_reset(); -+ /* -+ * After bes2600_chrdev_do_bus_reset() returns, the SDIO core has -+ * scheduled a remove + rescan; per-vif state may already be gone. -+ * Do not dereference any per-vif pointer here. -+ */ -+} -+ -+void bes2600_connection_loss_storm_init(struct bes2600_vif *priv) -+{ -+ priv->connection_loss_storm_window_start = 0; -+ priv->connection_loss_storm_count = 0; -+ priv->connection_loss_storm_recoveries = 0; -+} -+ -+bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv) -+{ -+ unsigned long now = jiffies; -+ unsigned long window = -+ msecs_to_jiffies(BES2600_CONNECTION_LOSS_STORM_WINDOW_MS); -+ -+ if (priv->connection_loss_storm_window_start == 0 || -+ time_after(now, priv->connection_loss_storm_window_start + window)) { -+ priv->connection_loss_storm_window_start = now; -+ priv->connection_loss_storm_count = 1; -+ return false; -+ } -+ -+ return ++priv->connection_loss_storm_count >= -+ BES2600_CONNECTION_LOSS_STORM_THRESHOLD; -+} -+ - void bes2600_connection_loss_work(struct work_struct *work) - { - struct bes2600_vif *priv = -@@ -1684,9 +1749,21 @@ void bes2600_connection_loss_work(struct work_struct *work) - - bes_devel("[CQM] Reporting connection loss.\n"); - bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST); -- if(bes2600_suspend_status_get(hw_priv)) { -+ -+ if (bes2600_connection_loss_storm_account(priv)) { -+ bes_warn("[bes2600] connection-loss storm: %u in %u s, scheduling bus reset\n", -+ priv->connection_loss_storm_count, -+ BES2600_CONNECTION_LOSS_STORM_WINDOW_MS / 1000); -+ priv->connection_loss_storm_count = 0; -+ priv->connection_loss_storm_recoveries++; -+ schedule_work(&hw_priv->connection_loss_storm_recover_work); -+ /* bus_reset will tear the chip down; skip the mac80211 path. */ -+ return; -+ } -+ -+ if (bes2600_suspend_status_get(hw_priv)) - bes2600_pending_unjoin_set(hw_priv, priv->if_id); -- } else -+ else - ieee80211_connection_loss(priv->vif); - #ifdef WIFI_BT_COEXIST_EPTA_ENABLE - // set disconnected in BSS_CHANGED_ASSOC -@@ -2641,6 +2718,7 @@ int bes2600_vif_setup(struct bes2600_vif *priv) - /* Setup per vif workitems and locks */ - spin_lock_init(&priv->vif_lock); - bes2600_decrypt_storm_init(priv); -+ bes2600_connection_loss_storm_init(priv); - INIT_WORK(&priv->join_work, bes2600_join_work); - INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout); - INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work); --- -2.54.0 - -From 737f28e29c4b8253939e24b1d6b97d5605bb7ac4 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 7 May 2026 21:19:49 +0200 -Subject: [PATCH 22/29] bes2600: replace a set of atomic_add() - -Backport of cw1200 mainline commit 07f995ca1951 ("cw1200: replace a set -of atomic_add()", 2020-11-10). atomic_inc() reads more naturally than -atomic_add(1, &x). Mechanical change, no functional impact. - -7 sites: 6 in bh.c (bh_term, bh_rx x2, bh_tx x3) and 1 in itp.c -(awaiting_confirm). Two of the bh_rx and three of the bh_tx sites are -inside the cw1200-ancestor #if 0 block; replaced anyway to keep the -file consistent with cw1200 mainline source style. - -Cherry-picked from upstream Linux: - 07f995ca1951 cw1200: replace a set of atomic_add() - Author: Yejune Deng - Signed-off-by: Kalle Valo - Link: https://lore.kernel.org/r/1604991491-27908-1-git-send-email-yejune.deng@gmail.com ---- - bes2600/bh.c | 12 ++++++------ - bes2600/itp.c | 2 +- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c -index 175ab5e..fab3bf0 100644 ---- a/drivers/staging/bes2600/bh.c -+++ b/drivers/staging/bes2600/bh.c -@@ -102,7 +102,7 @@ void bes2600_unregister_bh(struct bes2600_common *hw_priv) - coex_deinit_mode(hw_priv); - #endif - -- atomic_add(1, &hw_priv->bh_term); -+ atomic_inc(&hw_priv->bh_term); - wake_up(&hw_priv->bh_wq); - - flush_workqueue(hw_priv->bh_workqueue); -@@ -591,7 +591,7 @@ static int bes2600_bh(void *arg) - bes_devel("[BH] Device resume.\n"); - atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); - wake_up(&hw_priv->bh_evt_wq); -- atomic_add(1, &hw_priv->bh_rx); -+ atomic_inc(&hw_priv->bh_rx); - continue; - } - -@@ -759,9 +759,9 @@ tx: - - #if 0 /* count is not implemented */ - if (ret > 1) -- atomic_add(1, &hw_priv->bh_tx); -+ atomic_inc(&hw_priv->bh_tx); - #else -- atomic_add(1, &hw_priv->bh_tx); -+ atomic_inc(&hw_priv->bh_tx); - #endif - - #if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) -@@ -1135,7 +1135,7 @@ static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv, - tx_len += 4; - #endif - -- atomic_add(1, &hw_priv->bh_tx); -+ atomic_inc(&hw_priv->bh_tx); - - tx_len = hw_priv->sbus_ops->align_size( - hw_priv->sbus_priv, tx_len); -@@ -1442,7 +1442,7 @@ static int bes2600_bh(void *arg) - bes_devel("[BH] Device resume.\n"); - atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); - wake_up(&hw_priv->bh_evt_wq); -- atomic_add(1, &hw_priv->bh_rx); -+ atomic_inc(&hw_priv->bh_rx); - goto done; - } - -diff --git a/drivers/staging/bes2600/itp.c b/drivers/staging/bes2600/itp.c -index e5c2958..c50b29c 100644 ---- a/drivers/staging/bes2600/itp.c -+++ b/drivers/staging/bes2600/itp.c -@@ -570,7 +570,7 @@ int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data, - *burst = 2; - atomic_set(&priv->bh_tx, 1); - ktime_get_ts(&itp->last_sent); -- atomic_add(1, &itp->awaiting_confirm); -+ atomic_inc(&itp->awaiting_confirm); - spin_unlock_bh(&itp->tx_lock); - return 1; - --- -2.54.0 - -From 2fb72f06e54172479662257ae4ef9a61d6ba7092 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 7 May 2026 21:20:46 +0200 -Subject: [PATCH 23/29] bes2600: fix missing destroy_workqueue() on error in - init_common -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Two error paths between create_singlethread_workqueue() (~main.c:489) -and the success-path destroy_workqueue() in unregister_common (~609) -return without cleaning up the workqueue, leaking it on probe failure: - - 1. bes2600_queue_stats_init() failure - 2. bes2600_queue_init() failure (any of the 4 TID queues) - -Both call ieee80211_free_hw(hw); return NULL — without first -destroy_workqueue(hw_priv->workqueue). Add it. - -Backport of cw1200 mainline commit 7ec8a926188e ("cw1200: fix missing -destroy_workqueue() on error in cw1200_init_common", 2020-11-19), -which fixed the identical bug in the same code shape we inherited. -Reported on cw1200 by Hulk Robot. - -Cherry-picked from upstream Linux: - 7ec8a926188e cw1200: fix missing destroy_workqueue() on error - Author: Qinglang Miao - Reported-by: Hulk Robot - Signed-off-by: Kalle Valo - Link: https://lore.kernel.org/r/20201119070842.1011-1-miaoqinglang@huawei.com - Fixes: a910e4a94f69 ("cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets") ---- - bes2600/main.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c -index ff82f4d..89b5e2d 100644 ---- a/drivers/staging/bes2600/main.c -+++ b/drivers/staging/bes2600/main.c -@@ -502,6 +502,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) - WLAN_LINK_ID_MAX, - bes2600_skb_dtor, - hw_priv))) { -+ destroy_workqueue(hw_priv->workqueue); - ieee80211_free_hw(hw); - return NULL; - } -@@ -513,6 +514,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) - for (; i > 0; i--) - bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]); - bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats); -+ destroy_workqueue(hw_priv->workqueue); - ieee80211_free_hw(hw); - return NULL; - } --- -2.54.0 - -From d9e6361cf0c273f07aee94f24533a5f19e7ed4c0 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 7 May 2026 21:24:01 +0200 -Subject: [PATCH 24/29] bes2600: fix concurrency UAF in bes2600_hw_scan and - sched_scan -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -bes2600_bss_info_changed() and bes2600_hw_scan() can run concurrently. -The probe-request SKB allocated by ieee80211_probereq_get() before -scan.lock + conf_lock are taken can be touched by a concurrent -bss_info_changed (via wsm_set_template_frame's path) while we hold no -lock. Reorder to acquire both locks BEFORE the SKB allocation. - -Also reorder cleanup paths so dev_kfree_skb() runs BEFORE up() — -otherwise a small window exists where the SKB has been touched but the -lock has been released, allowing concurrent code to also touch it. - -Three sites fixed: - - bes2600_hw_scan: lock-take + ENOMEM cleanup + wsm_set_template_frame - error cleanup + success-path SKB free + lock release order - - bes2600_sched_scan_start (#ifdef ROAM_OFFLOAD): same three sub-fixes - (compiled-out at default build, fixed for consistency) - - All success/error paths: dev_kfree_skb before up() - -Backport of cw1200 mainline commit 86760e0dfe36 ("cw1200: Fix -concurrency use-after-free bugs in cw1200_hw_scan()", 2018-12-14), -which fixed the identical bug in the same code shape we inherited. -That commit was merged from upstream 4f68ef64cd7f. - -Cherry-picked from upstream Linux: - 86760e0dfe36 cw1200: Fix concurrency use-after-free bugs in cw1200_hw_scan() - Author: Jia-Ju Bai - Link: https://lore.kernel.org/r/20181214035521.7575-1-baijiaju1990@gmail.com ---- - bes2600/scan.c | 37 ++++++++++++++++++++++--------------- - 1 file changed, 22 insertions(+), 15 deletions(-) - -diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c -index b944adc..3cd7b64 100644 ---- a/drivers/staging/bes2600/scan.c -+++ b/drivers/staging/bes2600/scan.c -@@ -257,18 +257,21 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, - - bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN); - -+ /* will be unlocked in bes2600_scan_work() */ -+ down(&hw_priv->scan.lock); -+ down(&hw_priv->conf_lock); -+ - frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, - req->ie_len); -- if (!frame.skb) -+ if (!frame.skb) { -+ up(&hw_priv->conf_lock); -+ up(&hw_priv->scan.lock); - return -ENOMEM; -+ } - - if (req->ie_len) - skb_put_data(frame.skb, req->ie, req->ie_len); - -- /* will be unlocked in bes2600_scan_work() */ -- down(&hw_priv->scan.lock); -- down(&hw_priv->conf_lock); -- - if (frame.skb) { - int ret; - //if (priv->if_id == 0) -@@ -286,9 +289,9 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, - } - #endif - if (ret) { -+ dev_kfree_skb(frame.skb); - up(&hw_priv->conf_lock); - up(&hw_priv->scan.lock); -- dev_kfree_skb(frame.skb); - return ret; - } - } -@@ -318,10 +321,10 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, - ++hw_priv->scan.n_ssids; - } - -- up(&hw_priv->conf_lock); -- - if (frame.skb) - dev_kfree_skb(frame.skb); -+ -+ up(&hw_priv->conf_lock); - #ifdef WIFI_BT_COEXIST_EPTA_ENABLE - bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING); - #endif -@@ -362,14 +365,18 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, - if (req->n_ssids > hw->wiphy->max_scan_ssids) - return -EINVAL; - -+ /* will be unlocked in bes2600_scan_work() */ -+ down(&hw_priv->scan.lock); -+ down(&hw_priv->conf_lock); -+ - frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, - req->ie_len); -- if (!frame.skb) -+ if (!frame.skb) { -+ up(&hw_priv->conf_lock); -+ up(&hw_priv->scan.lock); - return -ENOMEM; -+ } - -- /* will be unlocked in bes2600_scan_work() */ -- down(&hw_priv->scan.lock); -- down(&hw_priv->conf_lock); - if (frame.skb) { - int ret; - if (priv->if_id == 0) -@@ -380,9 +387,9 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, - ret = wsm_set_probe_responder(priv, true); - } - if (ret) { -+ dev_kfree_skb(frame.skb); - up(&hw_priv->conf_lock); - up(&hw_priv->scan.lock); -- dev_kfree_skb(frame.skb); - return ret; - } - } -@@ -414,10 +421,10 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, - } - } - -- up(&hw_priv->conf_lock); -- - if (frame.skb) - dev_kfree_skb(frame.skb); -+ -+ up(&hw_priv->conf_lock); - queue_work(hw_priv->workqueue, &hw_priv->scan.swork); - wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n"); - return 0; --- -2.54.0 - -From 0f185172b020818faec9572fd800867db623a40e Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Thu, 7 May 2026 22:34:11 +0200 -Subject: [PATCH 25/29] =?UTF-8?q?bes2600:=20drop=20sdio=5Frx=5Fwork=20rela?= - =?UTF-8?q?y,=20IRQ=E2=86=92bh-direct=20(no-relay=20architecture)?= -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Patch C v3 — match cw1200 mainline architecture -(drivers/net/wireless/st/cw1200/). Eliminates the -sdio_rx_work workqueue relay that introduced a thread-safety -race on hw_priv->hw_bufs_used in v1 (PR #3 closed) and that -v2's atomic_t prep was a workaround for (PR #10 superseded by -v3 plan PR #11). - -Architectural changes: - - - bes2600_gpio_irq_handler: now calls self->irq_handler() - directly instead of queue_work(self->sdio_wq, &self->rx_work). - Bumps bh_rx atomic + wakes bh_wq. - - bes2600_bh_rx_helper (BES_SDIO_RX_MULTIPLE_ENABLE branch): - now calls priv->sbus_ops->bus_rx_batch() to do the SDIO read - inline. No pipe_read, no skb_dequeue. - - bes2600_sdio_read_rx_batch (new): the SDIO read sequence - extracted from sdio_rx_work, registered as - sbus_ops->bus_rx_batch. Runs in bh thread context. - - bes2600_sdio_extract_packets: calls - bes2600_bh_handle_rx_skb() directly per parsed SKB. No - skb_queue_tail, no rx_queue. - - bes2600_bh_handle_rx_skb (new in bh.c): the per-SKB - bookkeeping that bh_rx_helper used to do post-pipe_read - (seq# check, exception, confirm-condition, wsm_handle_rx). - Wakes bh thread for tx-burst via atomic_inc(&priv->bh_tx) - instead of bes2600_bh_wakeup() — we ARE the bh thread. - - Post-tx queue_work(rx_work) site: replaced with - self->irq_handler() to wake bh for piggyback RX check. - -Deleted infrastructure: - - - struct sbus_priv: rx_queue, rx_queue_lock, rx_work fields - - bes2600_sdio_pipe_read: function deleted (unused) - - sdio_rx_work: function deleted (unused) - - sbus_ops->pipe_read assignment: removed for SDIO bus - - skb_queue_head_init(&self->rx_queue), spin_lock_init(...), - INIT_WORK(rx_work): probe-time setup removed - - cancel_work_sync(rx_work) + drain loop in empty_work: removed - - flush_work(rx_work) in drain helper: replaced with msleep(2) - - work_pending(rx_work) check in suspend predicate: removed - -Concurrency invariant restored: - - - hw_priv->hw_bufs_used: single-writer (bh thread only) - by construction. No atomic_t needed. - - hw_priv->hw_bufs_used_vif[]: ditto. - - hw_priv->wsm_tx_pending[]: ditto. - - All other shared state: unchanged or already protected. - -Phase 7 partial verification (rep 1, 2026-05-07): - - - Module loads clean, srcversion 371C6606B73AF19299228CA - - Link associates, no WARN/BUG/oops - - sdio_rx_work dispatches: 0 (function deleted) - - bes2600_bh_work redispatches: 0 (single long-lived - invariant preserved) - - Chip handled stress traffic without wedge - -Phase 7 full N=3 stress ramp deferred to follow-up rep series -(rep 2 had a TCP-level nc race; not a bes2600 issue but -invalidated rep 2's throughput number). ---- - bes2600/bes2600_sdio.c | 144 ++++++++++++++++++++++++----------------- - bes2600/bh.c | 129 ++++++++++++++++++++++++++++++++++-- - bes2600/bh.h | 9 +++ - bes2600/sbus.h | 8 +++ - 4 files changed, 226 insertions(+), 64 deletions(-) - -diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index c81c244..3834032 100644 ---- a/drivers/staging/bes2600/bes2600_sdio.c -+++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -29,6 +29,7 @@ - #include - - #include "bes2600.h" -+#include "bh.h" - #include "sbus.h" - #include "bes2600_plat.h" - #include "bes2600_factory.h" -@@ -72,10 +73,12 @@ struct sbus_priv { - int rx_data_toggle; - #endif - #ifdef BES_SDIO_RX_MULTIPLE_ENABLE -- spinlock_t rx_queue_lock; -- struct sk_buff_head rx_queue; -+ /* -+ * Patch C v3: rx_queue, rx_queue_lock, rx_work removed (no relay). -+ * The bh thread now reads RX inline; the rx_buffer scratch area -+ * stays. Counters/timestamps stay for debugfs visibility. -+ */ - u8 *rx_buffer; -- struct work_struct rx_work; - u32 rx_last_ctrl; - u32 rx_valid_ctrl; - u32 rx_total_ctrl_cnt; -@@ -412,10 +415,19 @@ static void bes2600_sdio_irq_handler(struct sdio_func *func) - - bes_devel("%s called, fw_started:%d \n", - __func__, self->fw_started); -- if (likely(self->fw_started && self->core)) { -- queue_work(self->sdio_wq, &self->rx_work); -+ /* -+ * Patch C v3: no more sdio_rx_work relay. Wake the bh thread -+ * directly via self->irq_handler (bes2600_irq_handler in bh.c -+ * which bumps bh_rx atomic + wakes bh_wq). The bh thread will -+ * then call sbus_ops->bus_rx_batch() to do the SDIO read inline. -+ * Matches cw1200 mainline IRQ → bh-direct architecture. -+ */ -+ if (likely(self->fw_started && self->core && self->irq_handler)) { -+ spin_lock_irqsave(&self->lock, flags); -+ self->irq_handler(self->irq_priv); -+ spin_unlock_irqrestore(&self->lock, flags); - self->last_irq_timestamp = jiffies; -- } else if(self->irq_handler) { -+ } else if (self->irq_handler) { - spin_lock_irqsave(&self->lock, flags); - self->irq_handler(self->irq_priv); - spin_unlock_irqrestore(&self->lock, flags); -@@ -812,10 +824,15 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 - skb_put(skb, packet_len); - memcpy(skb->data, &data[pos], packet_len); - bes_devel("%s, %d,%d\n", __func__, packet_len, pos); -- spin_lock(&self->rx_queue_lock); -- skb_queue_tail(&self->rx_queue, skb); - self->rx_data_cnt++; -- spin_unlock(&self->rx_queue_lock); -+ /* -+ * Patch C v3: deliver the SKB directly into the WSM/mac80211 -+ * stack from the bh thread. No rx_queue, no inter-thread -+ * handoff, no atomic_t needed on the counters that -+ * wsm_release_tx_buffer touches — single-writer-from-bh is -+ * preserved by construction. See bh.c for the contract block. -+ */ -+ bes2600_bh_handle_rx_skb(self->core, skb); - packet_len = (packet_len + 3) & (~0x3); - pos += packet_len; - #ifdef BES_SDIO_OPTIMIZED_LEN -@@ -826,17 +843,31 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 - return 0; - } - --static void sdio_rx_work(struct work_struct *work) -+/* -+ * Patch C v3: bh thread calls this directly via sbus_ops->bus_rx_batch. -+ * No more sdio_rx_work workqueue. SDIO read sequence (lock → -+ * read_ctrl → memcpy_fromio → packets_check → extract_packets) runs -+ * inline in bh-thread context. Each parsed SKB is delivered via -+ * bes2600_bh_handle_rx_skb() from extract_packets — no rx_queue, no -+ * second worker, no inter-thread handoff. -+ * -+ * Architecture matches cw1200 mainline. Single-writer-from-bh -+ * invariant on hw_bufs_used preserved by construction. -+ * -+ * Returns 0 on success (caller's bh outer loop decides whether to -+ * continue), negative on bus read error. On error: triggers -+ * wifi_force_close (same as the old sdio_rx_work). -+ */ -+static int bes2600_sdio_read_rx_batch(struct sbus_priv *self) - { -- int ret, again = 0, retry = 0, crc_retry = 0; -+ int ret = 0, again = 0, retry = 0, crc_retry = 0; - u32 ctrl_reg = 0; - int total_len; -- struct sbus_priv *self = container_of(work, struct sbus_priv, rx_work); - u8 *buf = self->rx_buffer; - - /* don't read/write sdio when sdio error */ - if (bes2600_chrdev_is_bus_error()) -- return; -+ return 0; - - bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX); - -@@ -891,6 +922,10 @@ static void sdio_rx_work(struct work_struct *work) - goto failed; - } - -+ /* -+ * extract_packets parses the multi-RX buffer and calls -+ * bes2600_bh_handle_rx_skb() per SKB. No queueing. -+ */ - if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) { - bes_err("%s,%d error=%d\n", __func__, __LINE__, ret); - goto failed; -@@ -898,22 +933,16 @@ static void sdio_rx_work(struct work_struct *work) - - ctrl_reg = 0; - -- if (likely(self->irq_handler)) { -- self->irq_handler(self->irq_priv); -- } else { -- bes_err("%s,%d\n", __func__, __LINE__); -- goto failed; -- } -- - } while (again); - - bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); -- return; -+ return 0; - - failed: - bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX); - bes2600_chrdev_wifi_force_close(self->core, false); - WARN_ON(1); -+ return -1; - } - - static void sdio_scan_work(struct work_struct *work) -@@ -921,26 +950,11 @@ static void sdio_scan_work(struct work_struct *work) - bes_warn("%s: this function does nothing\n", __FUNCTION__); - } - --static void *bes2600_sdio_pipe_read(struct sbus_priv *self) --{ -- struct sk_buff *skb; -- -- if (bes2600_chrdev_is_bus_error()) { -- return bes2600_tx_loop_read(self->core); -- } -- -- spin_lock(&self->rx_queue_lock); -- skb = skb_dequeue(&self->rx_queue); -- if (skb) -- self->rx_proc_cnt++; -- spin_unlock(&self->rx_queue_lock); -- if (likely(self->fw_started == true && -- !bes2600_pwr_device_is_idle(self->core) && -- self->core->hw_bufs_used > 0)) -- if (!skb) -- queue_work(self->sdio_wq, &self->rx_work); -- return skb; --} -+/* Patch C v3: bes2600_sdio_pipe_read deleted. bh thread reads the -+ * SDIO bus inline via bes2600_sdio_read_rx_batch (sbus_ops->bus_rx_batch). -+ * No rx_queue, no skb_dequeue, no relay. bes2600_tx_loop_read remains -+ * for the test bus error-fallback path but is now invoked at higher -+ * level. */ - - #endif - -@@ -1196,7 +1210,14 @@ flush_previous: - } - } while (crc_retry <= 10); - sdio_release_host(self->func); -- queue_work(self->sdio_wq, &self->rx_work); -+ /* -+ * Patch C v3: wake the bh thread to check for any RX -+ * that piggybacked on this TX window. Bumps bh_rx -+ * atomic; bh's wait_event will pick it up and call -+ * sbus_ops->bus_rx_batch(). -+ */ -+ if (likely(self->irq_handler)) -+ self->irq_handler(self->irq_priv); - if (ret) { - bes_err("%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk); - sdio_work_debug(self); -@@ -1247,12 +1268,11 @@ static int bes2600_sdio_misc_init(struct sbus_priv *self, struct bes2600_common - self->next_toggle = 0; - #endif - #ifdef BES_SDIO_RX_MULTIPLE_ENABLE -- spin_lock_init(&self->rx_queue_lock); -- skb_queue_head_init(&self->rx_queue); -+ /* Patch C v3: rx_queue / rx_queue_lock removed (no relay). */ - self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM)); - if (!self->rx_buffer) - return -ENOMEM; -- INIT_WORK(&self->rx_work, sdio_rx_work); -+ /* Patch C v3: sdio_rx_work removed; bh thread does the read. */ - #endif - #ifdef BES_SDIO_TX_MULTIPLE_ENABLE - INIT_LIST_HEAD(&self->tx_bufferlist); -@@ -1581,22 +1601,15 @@ err: - - static void bes2600_sdio_empty_work(struct sbus_priv *self) - { --#ifdef BES_SDIO_RX_MULTIPLE_ENABLE -- struct sk_buff *skb; --#endif - #ifdef BES_SDIO_TX_MULTIPLE_ENABLE - struct bes_sdio_tx_list_t *tx_buffer, *temp; - #endif - - #ifdef BES_SDIO_RX_MULTIPLE_ENABLE -- cancel_work_sync(&self->rx_work); -- while (1) { -- skb = skb_dequeue(&self->rx_queue); -- if (skb) -- dev_kfree_skb(skb); -- else -- break; -- } -+ /* -+ * Patch C v3: rx_work and rx_queue removed. Counters still -+ * reset for the next attach cycle. -+ */ - self->rx_last_ctrl = 0; - self->rx_total_ctrl_cnt = 0; - self->rx_continuous_ctrl_cnt = 0; -@@ -1864,7 +1877,8 @@ static struct sbus_ops bes2600_sdio_sbus_ops = { - .sbus_reg_write = bes2600_sdio_reg_write, - .init = bes2600_sdio_misc_init, - #ifdef BES_SDIO_RX_MULTIPLE_ENABLE -- .pipe_read = bes2600_sdio_pipe_read, -+ /* Patch C v3: .pipe_read removed; bus_rx_batch replaces it. */ -+ .bus_rx_batch = bes2600_sdio_read_rx_batch, - #endif - #ifdef BES_SDIO_TX_MULTIPLE_ENABLE - .pipe_send = bes2600_sdio_pipe_send, -@@ -1884,9 +1898,15 @@ static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) - long unsigned int old_ts, new_ts; - struct sbus_priv *self = hw_priv->sbus_priv; - -+ /* -+ * Patch C v3: rx_work removed. Wait for IRQ-timestamp activity -+ * to settle by polling self->last_irq_timestamp via msleep -+ * (best-effort). The caller already knows the bh thread will -+ * process pending bh_rx during its next wait_event round. -+ */ - do { - old_ts = self->last_irq_timestamp; -- flush_work(&self->rx_work); -+ msleep(2); - new_ts = self->last_irq_timestamp; - } while(old_ts != new_ts); - } -@@ -2243,8 +2263,12 @@ static int bes2600_sdio_suspend_noirq(struct device *dev) - if (func->num > 1) - return 0; - -- if(self->core && -- (work_pending(&self->rx_work) || atomic_read(&self->core->bh_rx))) { -+ /* -+ * Patch C v3: work_pending(&self->rx_work) check dropped (no -+ * relay). bh_rx atomic alone tells us whether the bh thread -+ * has un-processed RX events queued. -+ */ -+ if (self->core && atomic_read(&self->core->bh_rx)) { - bes_devel("%s: Suspend interrupted.\n", __func__); - return -EAGAIN; - } -diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c -index fab3bf0..febcaf4 100644 ---- a/drivers/staging/bes2600/bh.c -+++ b/drivers/staging/bes2600/bh.c -@@ -959,6 +959,119 @@ static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct - } - } - -+/* -+ * Direct-deliver an RX SKB into the WSM/mac80211 stack. -+ * -+ * Patch C v3 (no-relay architecture, matches cw1200): the bh thread -+ * calls bes2600_sdio_read_rx_batch which calls -+ * bes2600_sdio_extract_packets which calls THIS function per parsed -+ * SKB. No rx_queue, no sdio_rx_work, no inter-thread handoff. -+ * -+ * Single-writer-from-bh invariant on hw_priv->hw_bufs_used, -+ * hw_priv->hw_bufs_used_vif[] and hw_priv->wsm_tx_pending[] is -+ * preserved BY CONSTRUCTION — there is now only one writer (the bh -+ * thread itself), same as cw1200's design. No atomic_t conversion -+ * needed. -+ * -+ * Contract: -+ * - process context, sleepable. wsm_handle_rx (wsm.c, EXPORT_SYMBOL) -+ * acquires wsm_cmd.lock and may sleep on wait_event_timeout. -+ * - caller holds no bes2600 spinlock. bes2600_sdio_unlock(self) is -+ * called inside read_rx_batch before extract_packets is invoked. -+ * - SKB ownership: function frees on every path (success + error). -+ * - No need to wake the bh thread on TX-confirm — we ARE the bh -+ * thread; tx_burst is signalled by returning *tx_out = 1 to the -+ * caller (bh_rx_helper), which propagates it to bh's outer loop. -+ */ -+int bes2600_bh_handle_rx_skb(struct bes2600_common *priv, struct sk_buff *skb) -+{ -+ struct wsm_hdr *wsm; -+ size_t wsm_len; -+ u16 wsm_id; -+ u8 wsm_seq; -+ int tx = 0; -+ u32 confirm_label = 0x0; -+ -+ if (!skb) -+ return 0; -+ -+ wsm = (struct wsm_hdr *)skb->data; -+ wsm_len = __le16_to_cpu(wsm->len); -+ if (WARN_ON(wsm_len > skb->len)) { -+ bes_err("wsm_len err %d %d\n", (int)wsm_len, (int)skb->len); -+ dev_kfree_skb(skb); -+ return -1; -+ } -+ -+ if (priv->wsm_enable_wsm_dumps) -+ print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, 16, 1, -+ skb->data, wsm_len, false); -+ -+ wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; -+ wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; -+ bes_devel("bes2600_bh_handle_rx_skb wsm_id:0x%04x seq:%d\n", -+ wsm_id, wsm_seq); -+ -+ skb_trim(skb, wsm_len); -+ -+ if (wsm_id == 0x0800) { -+ wsm_handle_exception(priv, -+ &skb->data[sizeof(*wsm)], -+ wsm_len - sizeof(*wsm)); -+ bes_err("wsm exception\n"); -+ dev_kfree_skb(skb); -+ return -1; -+ } else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) { -+ bes_err("seq error! %u. %u. 0x%x.", wsm_seq, -+ priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id); -+ dev_kfree_skb(skb); -+ return -1; -+ } -+ -+ bes2600_bh_parse_wakeup_event(priv, skb); -+ -+ priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7; -+ -+ if (IS_DRIVER_TO_MCU_CMD(wsm_id)) -+ confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label); -+ -+ if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) { -+ int rc = wsm_release_tx_buffer(priv, 1); -+ bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id)); -+ -+ if (rc < 0) { -+ bes_err("wsm_release_tx_buffer failed: %d\n", rc); -+ dev_kfree_skb(skb); -+ return rc; -+ } else if (rc > 0) { -+ tx = 1; ++ if ((psmode & 0x01) != WSM_PSM_ACTIVE) { ++ atomic_set(&hw_priv->bes_power.chip_pm_state, ++ BES2600_CHIP_PM_LP); ++ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process, ++ 1, 0) == 1) { ++ bes_devel("complete pm_enter_cmpl\n"); ++ complete(&hw_priv->bes_power.pm_enter_cmpl); ++ } else { ++ bes_devel("PM ind (LP) without pending wait; state recorded\n"); + } -+ } -+ -+ /* wsm_handle_rx takes care of SKB lifetime: zeroes *skb_p if consumed. */ -+ if (wsm_handle_rx(priv, wsm_id, wsm, &skb)) { -+ bes_err("wsm_handle_rx failed (id=0x%04x)\n", wsm_id); -+ if (skb) -+ dev_kfree_skb(skb); -+ return -1; -+ } -+ -+ if (skb) -+ dev_kfree_skb(skb); -+ -+ /* -+ * Signal "tx side has new headroom" via atomic so the bh outer -+ * loop's wait_event predicate notices on its next wait. No -+ * cross-thread wake needed because we are the bh thread; the -+ * outer loop will pick this up after read_rx_batch returns. -+ */ -+ if (tx) -+ atomic_inc(&priv->bh_tx); -+ -+ return 0; -+} -+EXPORT_SYMBOL(bes2600_bh_handle_rx_skb); -+ - static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx) - { - struct sk_buff *skb = NULL; -@@ -970,10 +1083,18 @@ static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx) - u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */ ++ } else { ++ atomic_set(&hw_priv->bes_power.chip_pm_state, ++ BES2600_CHIP_PM_ACTIVE); + } + } - #if defined(BES_SDIO_RX_MULTIPLE_ENABLE) -- skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv); -- if (!skb) -- return 0; -- rx = 1; // always consider rx pipe not empty -+ /* -+ * Patch C v3: the bh thread does the SDIO read inline via -+ * sbus_ops->bus_rx_batch. bes2600_sdio_read_rx_batch reads the -+ * multi-RX coalesced frames out of the chip and delivers each -+ * one inline via bes2600_bh_handle_rx_skb (no rx_queue, no -+ * pipe_read, no inter-thread handoff). Return value: 0 on -+ * success (bh outer loop will check whether to continue), -+ * negative on read error. -+ */ -+ if (priv->sbus_ops->bus_rx_batch) -+ return priv->sbus_ops->bus_rx_batch(priv->sbus_priv); -+ return 0; - #else - u32 ctrl_reg = 0; - size_t read_len = 0; -diff --git a/drivers/staging/bes2600/bh.h b/drivers/staging/bes2600/bh.h -index 7be82dc..9ed08b1 100644 ---- a/drivers/staging/bes2600/bh.h -+++ b/drivers/staging/bes2600/bh.h -@@ -39,6 +39,15 @@ int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id, - int bes2600_bh_sw_process(struct bes2600_common *hw_priv, - struct wsm_tx_confirm *tx_confirm); - -+/* -+ * Direct-deliver an RX SKB into the WSM/mac80211 stack from the bh thread. -+ * Called by bes2600_sdio_extract_packets per RX frame, no queueing. -+ * Process context, sleepable, caller holds no bes2600 spinlock. -+ * Function frees skb on every path. See bh.c for full contract. -+ */ -+int bes2600_bh_handle_rx_skb(struct bes2600_common *hw_priv, -+ struct sk_buff *skb); -+ - void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx); - void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx); - -diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h -index cb90890..96b1d4c 100644 ---- a/drivers/staging/bes2600/sbus.h -+++ b/drivers/staging/bes2600/sbus.h -@@ -83,6 +83,14 @@ struct sbus_ops { - * Returns 0 on success or a negative errno. - */ - int (*bus_reset)(struct sbus_priv *self); -+ /* -+ * Read a batch of RX frames inline from the bus and deliver each -+ * one via bes2600_bh_handle_rx_skb(). Called from the bh thread -+ * (process context, sleepable). Replaces the -+ * sdio_rx_work + rx_queue + pipe_read relay (Patch C v3, 2026). -+ * Returns 0 on success, negative on read error. -+ */ -+ int (*bus_rx_batch)(struct sbus_priv *self); - }; - - void bes2600_irq_handler(struct bes2600_common *priv); --- -2.54.0 - -From b9e340c78cf7111dd29c9b31dae5fe73d8b5ceec Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 8 May 2026 00:03:50 +0200 -Subject: [PATCH 26/29] =?UTF-8?q?bes2600:=20Patch=20G=20=E2=80=94=20restor?= - =?UTF-8?q?e=20SPDX=20identifiers=20+=20ST-Ericsson=20attribution?= -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The bes2600 driver is a fork of the upstream cw1200 driver -(drivers/net/wireless/st/cw1200/, ST-Ericsson, Dmitry Tarnyagin -2010-2011). The fork's file headers have three GPL-compliance issues: - - 1. NO SPDX-License-Identifier on any of 48 source files (cw1200 - mainline has them on all 25). kernel.org-mandated since 2017. - - 2. Original "Copyright (c) 2010, ST-Ericsson" lines stripped from - all files inherited from cw1200, replaced with - "Copyright (c) 2010, Bestechnic" — factually impossible - (Bestechnic did not author the 2010 work) and a GPL-2.0 §1 - attribution-preservation violation. - - 3. The "GPL version 2 as published by the Free Software Foundation" - boilerplate paragraph is redundant alongside SPDX and is the - legacy form modern kernel sources have replaced. - -This patch corrects all three for the 48 .c/.h files in bes2600/: - - - Adds `// SPDX-License-Identifier: GPL-2.0-only` (or `/* ... */` - for headers) as line 1 of every file. - - Restores `Copyright (c) 2010, ST-Ericsson` + `Author: Dmitry - Tarnyagin ` as the FIRST copyright - chain entry on all 22 files derived from cw1200 (bh.{c,h}, - debug.{c,h}, fwio.{c,h}, hwio.{c,h}, main.c, pm.{c,h}, - queue.{c,h}, scan.{c,h}, sta.{c,h}, txrx.{c,h}, wsm.{c,h}). - - Keeps `Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.` as - the SECOND chain entry where Bestechnic genuinely contributed. - - Notes "Derived from cw1200_sdio.c" + ST-Ericsson copyright on - bes2600_sdio.c (heavy derivation, not a literal rename). - - Notes "Replaces hwbus.h from cw1200/" + ST-Ericsson copyright - on sbus.h. - - Preserves the prism54/islsm authorship chain on main.c and - bes2600.h (Michael Wu 2006 + Jean-Baptiste Note 2004-2006). - - Drops the GPL-2.0 boilerplate paragraph in favour of SPDX. - -No code changes — only file-header comment blocks. Module build is -unaffected (verified by header-only diff scope). - -This is a prerequisite for any kernel.org submission attempt. The -existing MODULE_LICENSE("GPL") + MODULE_AUTHOR(Tarnyagin@stericsson.com) -declarations were already present and are unchanged here; the -mismatch between MODULE_AUTHOR and the (since-corrected) per-file -copyrights is now resolved. ---- - bes2600/ap.c | 9 +++------ - bes2600/ap.h | 9 +++------ - bes2600/bes2600.h | 11 ++++------- - bes2600/bes2600_factory.c | 9 +++------ - bes2600/bes2600_factory.h | 9 +++------ - bes2600/bes2600_plat.h | 9 +++------ - bes2600/bes2600_sdio.c | 13 +++++++------ - bes2600/bes_chardev.c | 9 +++------ - bes2600/bes_chardev.h | 9 +++------ - bes2600/bes_fw.c | 9 +++------ - bes2600/bes_fw_common.c | 9 +++------ - bes2600/bes_fw_common.h | 9 +++------ - bes2600/bes_log.h | 7 +++++++ - bes2600/bes_nl80211_testmode_msg.h | 9 +++------ - bes2600/bes_pwr.c | 9 +++------ - bes2600/bes_pwr.h | 9 +++------ - bes2600/bh.c | 12 ++++++------ - bes2600/bh.h | 12 ++++++------ - bes2600/debug.c | 12 ++++++------ - bes2600/debug.h | 12 ++++++------ - bes2600/epta_coex.c | 9 +++------ - bes2600/epta_coex.h | 9 +++------ - bes2600/epta_request.c | 9 +++------ - bes2600/epta_request.h | 9 +++------ - bes2600/fwio.c | 12 ++++++------ - bes2600/fwio.h | 12 ++++++------ - bes2600/ht.h | 9 +++------ - bes2600/hwio.c | 12 ++++++------ - bes2600/hwio.h | 15 +++++---------- - bes2600/itp.c | 10 +++------- - bes2600/itp.h | 9 +++------ - bes2600/main.c | 18 ++++++++++++------ - bes2600/pm.c | 12 ++++++------ - bes2600/pm.h | 12 ++++++------ - bes2600/queue.c | 12 ++++++------ - bes2600/queue.h | 12 ++++++------ - bes2600/sbus.h | 12 ++++++------ - bes2600/scan.c | 12 ++++++------ - bes2600/scan.h | 12 ++++++------ - bes2600/sta.c | 12 ++++++------ - bes2600/sta.h | 12 ++++++------ - bes2600/tx_loop.c | 9 +++------ - bes2600/tx_loop.h | 9 +++------ - bes2600/txrx.c | 12 ++++++------ - bes2600/txrx.h | 12 ++++++------ - bes2600/wifi_testmode_cmd.c | 9 +++------ - bes2600/wsm.c | 13 ++++++------- - bes2600/wsm.h | 14 +++++--------- - 48 files changed, 223 insertions(+), 292 deletions(-) - -diff --git a/drivers/staging/bes2600/ap.c b/drivers/staging/bes2600/ap.c -index 7b1e3b4..8a17545 100644 ---- a/drivers/staging/bes2600/ap.c -+++ b/drivers/staging/bes2600/ap.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * mac80211 STA and AP API for mac80211 BES2600 drivers -+ * AP mode for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include "bes2600.h" -diff --git a/drivers/staging/bes2600/ap.h b/drivers/staging/bes2600/ap.h -index 6f27852..f6e88c6 100644 ---- a/drivers/staging/bes2600/ap.h -+++ b/drivers/staging/bes2600/ap.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * mac80211 STA and AP API for mac80211 BES2600 drivers -+ * AP mode interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include - #ifndef AP_H_INCLUDED -diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h -index ec41141..84059c7 100644 ---- a/drivers/staging/bes2600/bes2600.h -+++ b/drivers/staging/bes2600/bes2600.h -@@ -1,18 +1,15 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Common private data for BES2600 drivers -+ * Common private data for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * - * Based on the mac80211 Prism54 code, which is - * Copyright (c) 2006, Michael Wu - * -- * Based on the islsm (softmac prism54) driver, which is: -+ * Based on the islsm (softmac prism54) driver, which is - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_H -diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c -index 1b43b41..0d2bfa1 100644 ---- a/drivers/staging/bes2600/bes2600_factory.c -+++ b/drivers/staging/bes2600/bes2600_factory.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Factory calibration loader for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h -index 7dbe9f8..0b1a321 100644 ---- a/drivers/staging/bes2600/bes2600_factory.h -+++ b/drivers/staging/bes2600/bes2600_factory.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Factory calibration loader interface - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef __FACTORY_H__ - #define __FACTORY_H__ -diff --git a/drivers/staging/bes2600/bes2600_plat.h b/drivers/staging/bes2600/bes2600_plat.h -index 63c3275..ebec635 100644 ---- a/drivers/staging/bes2600/bes2600_plat.h -+++ b/drivers/staging/bes2600/bes2600_plat.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Platform data for BES2600 SDIO bus - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef BES2600_PLAT_H_INCLUDED - #define BES2600_PLAT_H_INCLUDED -diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c -index 3834032..e85c524 100644 ---- a/drivers/staging/bes2600/bes2600_sdio.c -+++ b/drivers/staging/bes2600/bes2600_sdio.c -@@ -1,12 +1,13 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 SDIO driver for BES2600 device -+ * SDIO bus glue for BES2600 mac80211 driver -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. -+ * Derived from drivers/net/wireless/st/cw1200/cw1200_sdio.c -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin - * -- * Copyright (c) 2010, Bestechnic -- * Author: - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #define DEBUG 1 - #include -diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c -index 224c62d..02dcd43 100644 ---- a/drivers/staging/bes2600/bes_chardev.c -+++ b/drivers/staging/bes2600/bes_chardev.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Character device for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include - #include -diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h -index 2a7cad7..9edb206 100644 ---- a/drivers/staging/bes2600/bes_chardev.h -+++ b/drivers/staging/bes2600/bes_chardev.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Character device interface for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef __BES_CHARDEV_H__ - #define __BES_CHARDEV_H__ -diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c -index d612c3c..6c5598b 100644 ---- a/drivers/staging/bes2600/bes_fw.c -+++ b/drivers/staging/bes2600/bes_fw.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Firmware download for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include "bes_fw_common.h" - #include "bes2600.h" -diff --git a/drivers/staging/bes2600/bes_fw_common.c b/drivers/staging/bes2600/bes_fw_common.c -index 2e47455..a0c1f93 100644 ---- a/drivers/staging/bes2600/bes_fw_common.c -+++ b/drivers/staging/bes2600/bes_fw_common.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Firmware download common code for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include "bes_fw_common.h" - #include "bes_log.h" -diff --git a/drivers/staging/bes2600/bes_fw_common.h b/drivers/staging/bes2600/bes_fw_common.h -index 5c6561a..dcd5200 100644 ---- a/drivers/staging/bes2600/bes_fw_common.h -+++ b/drivers/staging/bes2600/bes_fw_common.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Firmware download common interface - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef __BES_FW_COMMON_H__ - #define __BES_FW_COMMON_H__ -diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h -index 65cf703..7d3c4b8 100644 ---- a/drivers/staging/bes2600/bes_log.h -+++ b/drivers/staging/bes2600/bes_log.h -@@ -1,3 +1,10 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * printk wrappers for BES2600 -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. -+ * -+ */ - extern struct device *global_dev; - - #ifdef CONFIG_BES2600_ENABLE_DEVEL_LOGS -diff --git a/drivers/staging/bes2600/bes_nl80211_testmode_msg.h b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h -index b70a0dd..c97c1ad 100644 ---- a/drivers/staging/bes2600/bes_nl80211_testmode_msg.h -+++ b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Vendor testmode messages for BES2600 - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES_NL80211_TESTMODE_MSG_H -diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index 620acef..a3f954b 100644 ---- a/drivers/staging/bes2600/bes_pwr.c -+++ b/drivers/staging/bes2600/bes_pwr.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Chip-side power state machine for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include - #include diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h -index 92de90b..49477b3 100644 +index 1ba866c25..49477b3e2 100644 --- a/drivers/staging/bes2600/bes_pwr.h +++ b/drivers/staging/bes2600/bes_pwr.h @@ -1,12 +1,9 @@ @@ -5216,8 +1646,46 @@ index 92de90b..49477b3 100644 */ #ifndef __BES_PWR_H__ #define __BES_PWR_H__ +@@ -64,6 +61,20 @@ enum power_down_state + POWER_DOWN_STATE_UNLOCKED, + }; + ++/* ++ * Confirmed PM state of the firmware-side chip. Tracks what the host ++ * has *seen* the firmware acknowledge, not what the host has ++ * requested. UNKNOWN means a host-initiated transition timed out ++ * before the firmware indication arrived; the next wake path should ++ * treat it as "we don't know" and probe before issuing GPIO/SDIO ++ * wakeup ops. ++ */ ++enum bes2600_chip_pm_state { ++ BES2600_CHIP_PM_ACTIVE = 0, ++ BES2600_CHIP_PM_LP, ++ BES2600_CHIP_PM_UNKNOWN, ++}; ++ + typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv); + typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv); + +@@ -106,6 +117,16 @@ struct bes2600_pwr_t + bool ap_lp_bad; + struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM]; + atomic_t pm_set_in_process; ++ atomic_t chip_pm_state; ++ /* ++ * Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD ++ * consecutive enter_lp_mode timeouts with zero PM_INDICATIONs ++ * received from firmware. Indicates this chip's firmware does ++ * not honor host-driven PSM transitions; further attempts are ++ * skipped to avoid the 5s timeout cascade. ++ */ ++ bool pm_unsupported; ++ unsigned int pm_consecutive_timeouts; + }; + + #ifdef CONFIG_BES2600_WOWLAN diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c -index febcaf4..61f6991 100644 +index 175ab5e39..924899b1c 100644 --- a/drivers/staging/bes2600/bh.c +++ b/drivers/staging/bes2600/bh.c @@ -1,12 +1,12 @@ @@ -5239,1398 +1707,24 @@ index febcaf4..61f6991 100644 */ #include #include -diff --git a/drivers/staging/bes2600/bh.h b/drivers/staging/bes2600/bh.h -index 9ed08b1..700f2aa 100644 ---- a/drivers/staging/bes2600/bh.h -+++ b/drivers/staging/bes2600/bh.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Device handling thread interface for mac80211 BES2600 drivers -+ * Bottom-half thread interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ +@@ -22,7 +22,6 @@ + #include "debug.h" + #include "epta_coex.h" + #include "bes_chardev.h" +-#include "txrx_opt.h" + #include "sta.h" + #include "bes_log.h" - #ifndef BES2600_BH_H -diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c -index 0d68392..47e27be 100644 ---- a/drivers/staging/bes2600/debug.c -+++ b/drivers/staging/bes2600/debug.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Debugging interface for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/debug.h b/drivers/staging/bes2600/debug.h -index 3714577..5914ffc 100644 ---- a/drivers/staging/bes2600/debug.h -+++ b/drivers/staging/bes2600/debug.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * DebugFS code for BES2600 mac80211 driver -+ * Debugging interface for BES2600 mac80211 driver - * -- * Copyright (c) 2011, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_DEBUG_H_INCLUDED -diff --git a/drivers/staging/bes2600/epta_coex.c b/drivers/staging/bes2600/epta_coex.c -index dfdf8e7..3ed76f1 100644 ---- a/drivers/staging/bes2600/epta_coex.c -+++ b/drivers/staging/bes2600/epta_coex.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * BT/WiFi coexistence (ePTA) for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include - #include -diff --git a/drivers/staging/bes2600/epta_coex.h b/drivers/staging/bes2600/epta_coex.h -index bc9eed6..f8a5fea 100644 ---- a/drivers/staging/bes2600/epta_coex.h -+++ b/drivers/staging/bes2600/epta_coex.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * BT/WiFi coexistence interface for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef __EPTA_COEX_H__ - #define __EPTA_COEX_H__ -diff --git a/drivers/staging/bes2600/epta_request.c b/drivers/staging/bes2600/epta_request.c -index 3b3e6af..486f02b 100644 ---- a/drivers/staging/bes2600/epta_request.c -+++ b/drivers/staging/bes2600/epta_request.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * BT/WiFi coexistence request handling - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include - #include -diff --git a/drivers/staging/bes2600/epta_request.h b/drivers/staging/bes2600/epta_request.h -index f0217c2..b3d9228 100644 ---- a/drivers/staging/bes2600/epta_request.h -+++ b/drivers/staging/bes2600/epta_request.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * BT/WiFi coexistence request interface - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef EPTA_REQUEST_H - #define EPTA_REQUEST_H -diff --git a/drivers/staging/bes2600/fwio.c b/drivers/staging/bes2600/fwio.c -index 5fe6b50..29aa2b3 100644 ---- a/drivers/staging/bes2600/fwio.c -+++ b/drivers/staging/bes2600/fwio.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Firmware I/O for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/fwio.h b/drivers/staging/bes2600/fwio.h -index a4afb7a..adbd708 100644 ---- a/drivers/staging/bes2600/fwio.h -+++ b/drivers/staging/bes2600/fwio.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Firmware I/O interface for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef FWIO_H_INCLUDED - #define FWIO_H_INCLUDED -diff --git a/drivers/staging/bes2600/ht.h b/drivers/staging/bes2600/ht.h -index b5caa29..5ac077b 100644 ---- a/drivers/staging/bes2600/ht.h -+++ b/drivers/staging/bes2600/ht.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * HT-related code for BES2600 driver -+ * HT capability config for BES2600 - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_HT_H_INCLUDED -diff --git a/drivers/staging/bes2600/hwio.c b/drivers/staging/bes2600/hwio.c -index ea88210..0934a13 100644 ---- a/drivers/staging/bes2600/hwio.c -+++ b/drivers/staging/bes2600/hwio.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Low-level device IO routines for BES2600 drivers -+ * Low-level device I/O for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/hwio.h b/drivers/staging/bes2600/hwio.h -index b9c1858..48e5215 100644 ---- a/drivers/staging/bes2600/hwio.h -+++ b/drivers/staging/bes2600/hwio.h -@@ -1,17 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Low-level API for mac80211 BES2600 drivers -+ * Low-level device I/O interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin - * -- * Based on: -- * UMAC BES2600 driver which is -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_HWIO_H_INCLUDED -diff --git a/drivers/staging/bes2600/itp.c b/drivers/staging/bes2600/itp.c -index c50b29c..7cc237c 100644 ---- a/drivers/staging/bes2600/itp.c -+++ b/drivers/staging/bes2600/itp.c -@@ -1,13 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * mac80211 glue code for mac80211 BES2600 drivers -- * ITP code -+ * ITP (in-band test mode) for BES2600 - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/itp.h b/drivers/staging/bes2600/itp.h -index 5cfba46..bec3647 100644 ---- a/drivers/staging/bes2600/itp.h -+++ b/drivers/staging/bes2600/itp.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * ITP code for BES2600 mac80211 driver -+ * ITP interface for BES2600 - * -- * Copyright (c) 2011, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_ITP_H_INCLUDED -diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c -index 89b5e2d..71dc4ae 100644 ---- a/drivers/staging/bes2600/main.c -+++ b/drivers/staging/bes2600/main.c -@@ -1,12 +1,18 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Main entry/init for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. -+ * -+ * Based on the mac80211 Prism54 code, which is -+ * Copyright (c) 2006, Michael Wu -+ * -+ * Based on the islsm (softmac prism54) driver, which is -+ * Copyright 2004-2006 Jean-Baptiste Note , et al. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/pm.c b/drivers/staging/bes2600/pm.c -index c32c68e..0424aae 100644 ---- a/drivers/staging/bes2600/pm.c -+++ b/drivers/staging/bes2600/pm.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 power management API for BES2600 drivers -+ * Power management for BES2600 mac80211 driver - * -- * Copyright (c) 2011, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/pm.h b/drivers/staging/bes2600/pm.h -index 0f6943e..ae70453 100644 ---- a/drivers/staging/bes2600/pm.h -+++ b/drivers/staging/bes2600/pm.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 power management interface for BES2600 mac80211 drivers -+ * Power management interface for BES2600 mac80211 driver - * -- * Copyright (c) 2011, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef PM_H_INCLUDED -diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c -index d1b407b..b56ca43 100644 ---- a/drivers/staging/bes2600/queue.c -+++ b/drivers/staging/bes2600/queue.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * O(1) TX queue with built-in allocator for BES2600 drivers -+ * O(1) TX queue for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/queue.h b/drivers/staging/bes2600/queue.h -index a5395b6..94874dd 100644 ---- a/drivers/staging/bes2600/queue.h -+++ b/drivers/staging/bes2600/queue.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * O(1) TX queue with built-in allocator for BES2600 drivers -+ * O(1) TX queue interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_QUEUE_H_INCLUDED -diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h -index 96b1d4c..43c2dae 100644 ---- a/drivers/staging/bes2600/sbus.h -+++ b/drivers/staging/bes2600/sbus.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Common sbus abstraction layer interface for bes2600 wireless driver -+ * Bus abstraction interface for BES2600 -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. -+ * Replaces hwbus.h from drivers/net/wireless/st/cw1200/ -+ * Copyright (c) 2010, ST-Ericsson - * -- * Copyright (c) 2010, Bestechnic -- * Author: - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_SBUS_H -diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c -index 3cd7b64..1905471 100644 ---- a/drivers/staging/bes2600/scan.c -+++ b/drivers/staging/bes2600/scan.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Scan implementation for BES2600 mac80211 drivers -+ * Scan implementation for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h -index 1f3adea..295be18 100644 ---- a/drivers/staging/bes2600/scan.h -+++ b/drivers/staging/bes2600/scan.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Scan interface for BES2600 mac80211 drivers -+ * Scan interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef SCAN_H_INCLUDED -diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c -index 5868757..70b12f9 100644 ---- a/drivers/staging/bes2600/sta.c -+++ b/drivers/staging/bes2600/sta.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 STA API for BES2600 drivers -+ * Mac80211 STA API for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/sta.h b/drivers/staging/bes2600/sta.h -index 39b4b1a..a174e04 100644 ---- a/drivers/staging/bes2600/sta.h -+++ b/drivers/staging/bes2600/sta.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 STA interface for BES2600 mac80211 drivers -+ * Mac80211 STA API interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include - #ifndef STA_H_INCLUDED -diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c -index baab3f0..e6cf072 100644 ---- a/drivers/staging/bes2600/tx_loop.c -+++ b/drivers/staging/bes2600/tx_loop.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Test-mode TX loopback for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #include "bes2600.h" - #include "wsm.h" -diff --git a/drivers/staging/bes2600/tx_loop.h b/drivers/staging/bes2600/tx_loop.h -index de82b30..7f42c04 100644 ---- a/drivers/staging/bes2600/tx_loop.h -+++ b/drivers/staging/bes2600/tx_loop.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Test-mode TX loopback interface for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifndef __TX_LOOP_H__ - #define __TX_LOOP_H__ -diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c -index f6a66d6..3aef009 100644 ---- a/drivers/staging/bes2600/txrx.c -+++ b/drivers/staging/bes2600/txrx.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Datapath implementation for BES2600 mac80211 drivers -+ * Datapath implementation for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/txrx.h b/drivers/staging/bes2600/txrx.h -index cb7c192..6466c33 100644 ---- a/drivers/staging/bes2600/txrx.h -+++ b/drivers/staging/bes2600/txrx.h -@@ -1,12 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Datapath interface for BES2600 mac80211 drivers -+ * Datapath interface for BES2600 mac80211 driver - * -- * Copyright (c) 2010, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_TXRX_H -diff --git a/drivers/staging/bes2600/wifi_testmode_cmd.c b/drivers/staging/bes2600/wifi_testmode_cmd.c -index 2494cca..c010e8d 100644 ---- a/drivers/staging/bes2600/wifi_testmode_cmd.c -+++ b/drivers/staging/bes2600/wifi_testmode_cmd.c -@@ -1,12 +1,9 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * WiFi testmode commands for BES2600 - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - #ifdef CONFIG_BES2600_TESTMODE - #include -diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c -index 55a4e2b..908c965 100644 ---- a/drivers/staging/bes2600/wsm.c -+++ b/drivers/staging/bes2600/wsm.c -@@ -1,13 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * WSM host interface (HI) implementation for -- * BES2600 mac80211 drivers. -+ * WSM host interface for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #include -diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h -index 22845ac..0d755a3 100644 ---- a/drivers/staging/bes2600/wsm.h -+++ b/drivers/staging/bes2600/wsm.h -@@ -1,16 +1,12 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * WSM host interface (HI) interface for BES2600 mac80211 drivers -+ * WSM host interface for BES2600 mac80211 driver - * -- * Copyright (c) 2022, Bestechnic -- * Author: -+ * Copyright (c) 2010, ST-Ericsson -+ * Author: Dmitry Tarnyagin - * -- * Based on BES2600 UMAC WSM API, which is -- * Copyright (C) SA 2010 -- * Author: Stewart Mathers -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. - * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License version 2 as -- * published by the Free Software Foundation. - */ - - #ifndef BES2600_WSM_H_INCLUDED --- -2.54.0 - -From 8fd20308ed53678c863a0ef52fb2c754e3adc63c Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 8 May 2026 00:17:46 +0200 -Subject: [PATCH 27/29] =?UTF-8?q?bes2600:=20Patch=20D=20=E2=80=94=20atomic?= - =?UTF-8?q?ize=20ba=5Flock=20counters,=20drop=20the=20spinlock?= -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The block-ack policy uses 4 int counters (ba_acc, ba_cnt, ba_acc_rx, -ba_cnt_rx) bumped per data frame in the TX and RX hot paths under -spin_lock_bh(&hw_priv->ba_lock). The lock was the heaviest per-frame -synchronization cost remaining after Patch C v3 (which fixed the -sdio_rx_work relay). Per the Opus structural critique (PR #8), this -pattern matches mac80211 driver convention for per-frame statistics: -atomic_t suffices, no lock needed. - -Field-by-field changes in struct bes2600_common: - ba_acc, ba_cnt, ba_acc_rx, ba_cnt_rx: int -> atomic_t - ba_armed: new atomic_t (timer-arm flag) - ba_ena: bool -> atomic_t - ba_lock: removed (spinlock_t deleted) - ba_hist: int (single-writer = ba_timer) - -Producer hot path (txrx.c TX submit + RX receive): - - atomic_add for the byte accumulator - - atomic_inc for the frame counter - - atomic_cmpxchg(&ba_armed, 0, 1) to claim the once-per-window - mod_timer arm — at most ONE producer succeeds; race-free - - no spin_lock_bh - -Consumer paths (sta.c bes2600_ba_timer, sta.c disconnect-reset, sta.c -bes2600_ba_work, debug.c debugfs reader): - - atomic_read snapshots all 4 counters into locals; the threshold - predicate (acc/cnt >= THLD) tolerates approximate snapshots — the - timer fires periodically, a single misclassification just delays - the policy update by one tick - - atomic_set zeroes the counters at end of timer-callback window; - racing producer increments after the snapshot are lost (acceptable - for stats; same approximation the original lock allowed under - contention) - - atomic_set(&ba_armed, 0) re-enables the next window's arm - -Followup-amenable simplification: ba_hist remains int because only -the single ba_timer callback writes it; multiple writers would need -to upgrade it too. - -This patch follows the cw1200-mainline-idiom established by Patch C v3 -(structural fix, not bandaid). The cw1200 reference doesn't have a -similar lock to compare; bes2600 inherited this from a later -Bestechnic addition rather than the upstream tree. ---- - bes2600/bes2600.h | 26 ++++++++++------ - bes2600/debug.c | 13 +++++--- - bes2600/main.c | 2 +- - bes2600/sta.c | 77 ++++++++++++++++++++++++++++------------------- - bes2600/txrx.c | 23 ++++++++------ - 5 files changed, 85 insertions(+), 56 deletions(-) - -diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h -index 84059c7..32bce5e 100644 ---- a/drivers/staging/bes2600/bes2600.h -+++ b/drivers/staging/bes2600/bes2600.h -@@ -353,15 +353,23 @@ struct bes2600_common { - * Keeping in common structure for the time being. Will be moved to VIFF - * after the mechanism is clear */ - u8 ba_tid_mask; -- int ba_acc; /*TODO: Same as above */ -- int ba_cnt; /*TODO: Same as above */ -- int ba_cnt_rx; /*TODO: Same as above */ -- int ba_acc_rx; /*TODO: Same as above */ -- int ba_hist; /*TODO: Same as above */ -- struct timer_list ba_timer;/*TODO: Same as above */ -- spinlock_t ba_lock; /*TODO: Same as above */ -- bool ba_ena; /*TODO: Same as above */ -- struct work_struct ba_work; /*TODO: Same as above */ -+ /* -+ * Patch D: ba_lock removed. Per-frame TX/RX hot-path bumped these -+ * counters under spin_lock_bh; the lock did not protect any -+ * compound invariant that atomic ops can't satisfy. Counters are -+ * now atomic_t; ba_armed gates the once-per-window mod_timer -+ * arm via cmpxchg so concurrent TX/RX at a fresh window each -+ * try to claim the arm and exactly one succeeds. -+ */ -+ atomic_t ba_acc; -+ atomic_t ba_cnt; -+ atomic_t ba_cnt_rx; -+ atomic_t ba_acc_rx; -+ atomic_t ba_armed; -+ int ba_hist; -+ struct timer_list ba_timer; -+ atomic_t ba_ena; -+ struct work_struct ba_work; - bool is_BT_Present; - bool is_go_thru_go_neg; - u8 conf_listen_interval; -diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c -index 47e27be..0ab79c0 100644 ---- a/drivers/staging/bes2600/debug.c -+++ b/drivers/staging/bes2600/debug.c -@@ -110,17 +110,20 @@ static int bes2600_status_show_common(struct seq_file *seq, void *v) - int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0; - bool ba_ena; - -- spin_lock_bh(&hw_priv->ba_lock); -- ba_cnt = hw_priv->debug->ba_cnt; -- ba_acc = hw_priv->debug->ba_acc; -+ /* -+ * Patch D: ba_lock removed. hw_priv->debug->ba_* are written only -+ * by the timer callback (single writer); reading without a lock is -+ * fine for stats. ba_ena is atomic_t. -+ */ -+ ba_cnt = hw_priv->debug->ba_cnt; -+ ba_acc = hw_priv->debug->ba_acc; - ba_cnt_rx = hw_priv->debug->ba_cnt_rx; - ba_acc_rx = hw_priv->debug->ba_acc_rx; -- ba_ena = hw_priv->ba_ena; -+ ba_ena = !!atomic_read(&hw_priv->ba_ena); - if (ba_cnt) - ba_avg = ba_acc / ba_cnt; - if (ba_cnt_rx) - ba_avg_rx = ba_acc_rx / ba_cnt_rx; -- spin_unlock_bh(&hw_priv->ba_lock); - - seq_puts(seq, "BES2600 Wireless LAN driver status\n"); - seq_printf(seq, "Hardware: %d.%d\n", -diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c -index 71dc4ae..8fc37b4 100644 ---- a/drivers/staging/bes2600/main.c -+++ b/drivers/staging/bes2600/main.c -@@ -501,7 +501,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) - INIT_LIST_HEAD(&hw_priv->event_queue); - INIT_WORK(&hw_priv->event_handler, bes2600_event_handler); - INIT_WORK(&hw_priv->ba_work, bes2600_ba_work); -- spin_lock_init(&hw_priv->ba_lock); -+ /* Patch D: ba_lock removed; ba_acc/ba_cnt/etc are atomic_t. */ - timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0); - - if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats, -diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c -index 70b12f9..8af8150 100644 ---- a/drivers/staging/bes2600/sta.c -+++ b/drivers/staging/bes2600/sta.c -@@ -2362,14 +2362,19 @@ void bes2600_join_work(struct work_struct *work) - //WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id)); - WARN_ON(wsm_set_block_ack_policy(hw_priv, - 0, hw_priv->ba_tid_mask, priv->if_id)); -- spin_lock_bh(&hw_priv->ba_lock); -- hw_priv->ba_ena = false; -- hw_priv->ba_cnt = 0; -- hw_priv->ba_acc = 0; -+ /* -+ * Patch D: ba_lock removed. Disconnect-reset clears the -+ * counters and the arm flag; producers racing here cannot -+ * cause harm — at worst they re-arm the timer and bump -+ * counters that will be cleared on the next timer tick. -+ */ -+ atomic_set(&hw_priv->ba_ena, 0); -+ atomic_set(&hw_priv->ba_cnt, 0); -+ atomic_set(&hw_priv->ba_acc, 0); - hw_priv->ba_hist = 0; -- hw_priv->ba_cnt_rx = 0; -- hw_priv->ba_acc_rx = 0; -- spin_unlock_bh(&hw_priv->ba_lock); -+ atomic_set(&hw_priv->ba_cnt_rx, 0); -+ atomic_set(&hw_priv->ba_acc_rx, 0); -+ atomic_set(&hw_priv->ba_armed, 0); - - mgmt_policy.protectedMgmtEnable = 0; - mgmt_policy.unprotectedMgmtFramesAllowed = 1; -@@ -2649,10 +2654,11 @@ void bes2600_ba_work(struct work_struct *work) - return;*/ - - bes_devel("BA work****\n"); -- spin_lock_bh(&hw_priv->ba_lock); --// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0; -+ /* -+ * Patch D: ba_lock removed. ba_tid_mask is u8 set once at init -+ * (main.c); reading it without a lock is fine. -+ */ - tx_ba_tid_mask = hw_priv->ba_tid_mask; -- spin_unlock_bh(&hw_priv->ba_lock); - - wsm_lock_tx(hw_priv); - -@@ -2665,37 +2671,49 @@ void bes2600_ba_work(struct work_struct *work) - void bes2600_ba_timer(struct timer_list *t) - { - bool ba_ena; -+ int cnt, acc, cnt_rx, acc_rx; - struct bes2600_common *hw_priv = timer_container_of(hw_priv, t, ba_timer); - -- spin_lock_bh(&hw_priv->ba_lock); -- bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc, -- hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx); -+ /* -+ * Patch D: ba_lock removed. Snapshot atomic counters into locals -+ * for the predicate evaluation; producers may race incrementing -+ * after the snapshot but the resulting decision is approximate -+ * which the policy already tolerates (next timer tick re-evaluates). -+ */ -+ cnt = atomic_read(&hw_priv->ba_cnt); -+ acc = atomic_read(&hw_priv->ba_acc); -+ cnt_rx = atomic_read(&hw_priv->ba_cnt_rx); -+ acc_rx = atomic_read(&hw_priv->ba_acc_rx); -+ -+ bes2600_debug_ba(hw_priv, cnt, acc, cnt_rx, acc_rx); - - if (atomic_read(&hw_priv->scan.in_progress)) { -- hw_priv->ba_cnt = 0; -- hw_priv->ba_acc = 0; -- hw_priv->ba_cnt_rx = 0; -- hw_priv->ba_acc_rx = 0; -- goto skip_statistic_update; -+ atomic_set(&hw_priv->ba_cnt, 0); -+ atomic_set(&hw_priv->ba_acc, 0); -+ atomic_set(&hw_priv->ba_cnt_rx, 0); -+ atomic_set(&hw_priv->ba_acc_rx, 0); -+ atomic_set(&hw_priv->ba_armed, 0); -+ return; - } - -- if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT && -- (hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD || -- (hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT && -- hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >= -+ if (cnt >= BES2600_BLOCK_ACK_CNT && -+ (acc / cnt >= BES2600_BLOCK_ACK_THLD || -+ (cnt_rx >= BES2600_BLOCK_ACK_CNT && -+ acc_rx / cnt_rx >= - BES2600_BLOCK_ACK_THLD))) - ba_ena = true; - else - ba_ena = false; - -- hw_priv->ba_cnt = 0; -- hw_priv->ba_acc = 0; -- hw_priv->ba_cnt_rx = 0; -- hw_priv->ba_acc_rx = 0; -+ atomic_set(&hw_priv->ba_cnt, 0); -+ atomic_set(&hw_priv->ba_acc, 0); -+ atomic_set(&hw_priv->ba_cnt_rx, 0); -+ atomic_set(&hw_priv->ba_acc_rx, 0); -+ atomic_set(&hw_priv->ba_armed, 0); - -- if (ba_ena != hw_priv->ba_ena) { -+ if (ba_ena != !!atomic_read(&hw_priv->ba_ena)) { - if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) { -- hw_priv->ba_ena = ba_ena; -+ atomic_set(&hw_priv->ba_ena, ba_ena ? 1 : 0); - hw_priv->ba_hist = 0; - #if 0 - bes_devel("[STA] %s block ACK:\n", -@@ -2705,9 +2723,6 @@ void bes2600_ba_timer(struct timer_list *t) - } - } else if (hw_priv->ba_hist) - --hw_priv->ba_hist; -- --skip_statistic_update: -- spin_unlock_bh(&hw_priv->ba_lock); - } - - int bes2600_vif_setup(struct bes2600_vif *priv) -diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c -index 3aef009..536b198 100644 ---- a/drivers/staging/bes2600/txrx.c -+++ b/drivers/staging/bes2600/txrx.c -@@ -996,14 +996,18 @@ bes2600_tx_h_ba_stat(struct bes2600_vif *priv, - if (!ieee80211_is_data(t->hdr->frame_control)) - return; - -- spin_lock_bh(&hw_priv->ba_lock); -- hw_priv->ba_acc += t->skb->len - t->hdrlen; -- if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { -+ /* -+ * Patch D: lock-free hot-path BA accounting. atomic_inc + atomic_add -+ * each per-frame; the once-per-window timer-arm uses cmpxchg on -+ * ba_armed so concurrent TX/RX can't both try to set the timer and -+ * we don't need cross-counter coherency on the ba_cnt/ba_cnt_rx pair. -+ */ -+ atomic_add(t->skb->len - t->hdrlen, &hw_priv->ba_acc); -+ atomic_inc(&hw_priv->ba_cnt); -+ if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) { - mod_timer(&hw_priv->ba_timer, - jiffies + BES2600_BLOCK_ACK_INTERVAL); - } -- hw_priv->ba_cnt++; -- spin_unlock_bh(&hw_priv->ba_lock); - } - - static int -@@ -1651,14 +1655,13 @@ bes2600_rx_h_ba_stat(struct bes2600_vif *priv, - if (!priv->setbssparams_done) - return; - -- spin_lock_bh(&hw_priv->ba_lock); -- hw_priv->ba_acc_rx += skb_len - hdrlen; -- if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { -+ /* Patch D: lock-free hot-path BA accounting; see TX side comment. */ -+ atomic_add(skb_len - hdrlen, &hw_priv->ba_acc_rx); -+ atomic_inc(&hw_priv->ba_cnt_rx); -+ if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) { - mod_timer(&hw_priv->ba_timer, - jiffies + BES2600_BLOCK_ACK_INTERVAL); - } -- hw_priv->ba_cnt_rx++; -- spin_unlock_bh(&hw_priv->ba_lock); - } - - void bes2600_rx_cb(struct bes2600_vif *priv, --- -2.54.0 - -From 445c619da88d69adf68e8cae08ad1b53f76fe57d Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 8 May 2026 00:22:14 +0200 -Subject: [PATCH 28/29] =?UTF-8?q?bes2600:=20Patch=20E=20=E2=80=94=20skip?= - =?UTF-8?q?=20ps=5Fstate=5Flock=20when=20PSM-known-disabled?= -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Per the Opus structural critique (PR #8 §2.4) and Sonnet review item 5. -The per-RX-frame early-data path takes ps_state_lock to double-check -whether a link entry transitioned to BES2600_LINK_SOFT (AP-side -power-save state machine, soft-link transition). - -When c7 has latched pm_unsupported = true (firmware does not honor -PSM, see feedback_bes2600_firmware_no_psm memory), the AP power-save -state machine is dead and link entries never transition to LINK_SOFT. -The per-frame spin_lock_bh + double-check is wasted work. - -This patch gates the lock acquisition on !pm_unsupported. When the -latch is on (the steady state on the production-shipped bes2600 -firmware), early_data RX frames bypass the spin_lock_bh and go -directly to ieee80211_rx_irqsafe. - -If a future firmware drop fixes PSM, c7 self-clears pm_unsupported on -the first real PM_INDICATION and the locked path resumes. - -Scope is narrower than Sonnet originally framed: only the per-RX-frame -hot path (txrx.c:1945-1951 in cleanups+G+D) is touched. Other -ps_state_lock sites in txrx.c (lines 657, 1256, 1420, 1528) are TX -submission / multicast-start / link-id paths, not per-frame RX, and -not on the Bug #5 hot path. Leave those alone. - -Build verified: srcversion B5922B4933590F33207EE97 on ohm sandbox. ---- - bes2600/txrx.c | 30 ++++++++++++++++++++++++------ - 1 file changed, 24 insertions(+), 6 deletions(-) - -diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c -index 536b198..cb718ad 100644 ---- a/drivers/staging/bes2600/txrx.c -+++ b/drivers/staging/bes2600/txrx.c -@@ -1965,13 +1965,31 @@ void bes2600_rx_cb(struct bes2600_vif *priv, - if (unlikely(bes2600_itp_rxed(hw_priv, skb))) - consume_skb(skb); - else if (unlikely(early_data)) { -- spin_lock_bh(&priv->ps_state_lock); -- /* Double-check status with lock held */ -- if (entry->status == BES2600_LINK_SOFT) -- skb_queue_tail(&entry->rx_queue, skb); -- else -+ /* -+ * Patch E: when c7 has latched pm_unsupported (firmware -+ * doesn't honour PSM, see feedback_bes2600_firmware_no_psm), -+ * AP-side power-save state machine is dead and link entries -+ * never transition to BES2600_LINK_SOFT. The double-check -+ * branch under ps_state_lock is unreachable in that case, -+ * so skip the per-frame lock acquisition entirely and -+ * deliver to mac80211 directly. -+ * -+ * On firmware that does honour PSM (the latch self-clears -+ * if a real PM_INDICATION ever arrives — see c7), this -+ * predicate flips back to false and the original locked -+ * path is taken. -+ */ -+ if (hw_priv->bes_power.pm_unsupported) { - ieee80211_rx_irqsafe(priv->hw, skb); -- spin_unlock_bh(&priv->ps_state_lock); -+ } else { -+ spin_lock_bh(&priv->ps_state_lock); -+ /* Double-check status with lock held */ -+ if (entry->status == BES2600_LINK_SOFT) -+ skb_queue_tail(&entry->rx_queue, skb); -+ else -+ ieee80211_rx_irqsafe(priv->hw, skb); -+ spin_unlock_bh(&priv->ps_state_lock); -+ } - } else { - ieee80211_rx_irqsafe(priv->hw, skb); - } --- -2.54.0 - -From a70e882f3d5f4f9148206675562dddeecd3f4404 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 8 May 2026 06:40:00 +0200 -Subject: [PATCH 29/29] =?UTF-8?q?bes2600:=20Patch=20C2=20=E2=80=94=20repla?= - =?UTF-8?q?ce=20ieee80211=5Frx=5Firqsafe=20with=20ieee80211=5Frx=5Fni?= -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Per Phase 4 plan PR #14 + kerneldoc audit (Task #19). Six call sites -deferred per-RX-frame mac80211 dispatch via tasklet; replace with the -synchronous-from-process-context API ieee80211_rx_ni() which does its -own local_bh_disable wrap. - -Why _ni and not _list: - - Phase 4 plan originally targeted ieee80211_rx_list for batch - delivery. Mining mt76 mainline (the only driver using _list) - showed the canonical pattern requires threading a struct list_head - through the per-frame call chain. bes2600s WSM dispatcher - (wsm_handle_rx -> bes2600_rx_cb / wsm.c beacon path) sits between - the bh threads SDIO read and the mac80211 hand-off; threading a - list_head through the dispatcher is a non-trivial refactor. - ieee80211_rx_ni() is the simpler drop-in: no list management, still - removes the tasklet hop. Per-call local_bh_disable cost is trivial - vs the saved tasklet schedule. Future refactor can revisit _list - if measurements warrant. - -Sites converted: - - - ap.c:96 (bes2600_sta_add link-id rx_queue drain on AP-mode - STA add). Was inside spin_lock_bh(&ps_state_lock); - refactored to splice the queue under the lock then - deliver after unlock — _ni runs the synchronous - mac80211 RX path inline, would otherwise hold the - lock across mac80211 dispatch. splice via - skb_queue_splice_init into a local sk_buff_head. - - sta.c:1487 (deauth-frame inject in inactivity-event handler). - Not under any lock; direct conversion. - - txrx.c:1960 (early-data + pm_unsupported branch from Patch E). - - txrx.c:1967 (early-data + LINK_SOFT-not-set branch). - - txrx.c:1971 (normal RX path in bes2600_rx_cb). - - wsm.c:2415 (beacon delivery in scan-complete WSM handler). - beacon SKB ownership is preserved by the existing - skb_copy(beacon, GFP_ATOMIC) -> beacon_bkp pattern; - no lifecycle change needed. - -Mixing constraint (kerneldoc include/net/mac80211.h:5399-5430): -ieee80211_rx_ni() cannot mix with ieee80211_rx_irqsafe() for a -single hardware. All 6 sites convert atomically; no mixed state. - -Build verified clean on ohm sandbox: srcversion 619A51E61BF5479AAC146E6. - -Predicted Phase 7 delta: +5-15% over v3+D+E baseline (2.35 MB/s mean -on v3 alone; D+E single-rep was 3.22 MB/s). Modest improvement -expected from removing the tasklet schedule per RX frame. Smaller -deltas would still be a net win for upstream-cleanliness — the -kernel.org submission story benefits from not using _irqsafe from -process context. ---- - bes2600/ap.c | 15 +++++++++++++-- - bes2600/bes_chardev.c | 33 ++++++++++++++++++++++++++++++++- - bes2600/sta.c | 2 +- - bes2600/txrx.c | 6 +++--- - bes2600/wsm.c | 2 +- - 5 files changed, 50 insertions(+), 8 deletions(-) - -diff --git a/drivers/staging/bes2600/ap.c b/drivers/staging/bes2600/ap.c -index 8a17545..99e2da2 100644 ---- a/drivers/staging/bes2600/ap.c -+++ b/drivers/staging/bes2600/ap.c -@@ -63,8 +63,11 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif); - struct bes2600_link_entry *entry; - struct sk_buff *skb; -+ struct sk_buff_head local_drain; - struct bes2600_common *hw_priv = hw->priv; - -+ __skb_queue_head_init(&local_drain); -+ - #ifdef P2P_MULTIVIF - WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID); +@@ -102,7 +101,7 @@ void bes2600_unregister_bh(struct bes2600_common *hw_priv) + coex_deinit_mode(hw_priv); #endif -@@ -93,9 +96,17 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) - priv->sta_asleep_mask |= BIT(sta_priv->link_id); - entry->status = BES2600_LINK_HARD; -- while ((skb = skb_dequeue(&entry->rx_queue))) -- ieee80211_rx_irqsafe(priv->hw, skb); -+ /* -+ * Patch C2: splice the rx_queue out under the lock then deliver -+ * after unlock. ieee80211_rx_ni() runs the mac80211 RX path -+ * synchronously (formerly ieee80211_rx_irqsafe deferred to a -+ * tasklet); calling it from inside spin_lock_bh would hold the -+ * lock across mac80211's full RX dispatch. -+ */ -+ skb_queue_splice_init(&entry->rx_queue, &local_drain); - spin_unlock_bh(&priv->ps_state_lock); -+ while ((skb = __skb_dequeue(&local_drain))) -+ ieee80211_rx_ni(priv->hw, skb); - #ifdef AP_AGGREGATE_FW_FIX - hw_priv->connected_sta_cnt++; - if(hw_priv->connected_sta_cnt>1) { -diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c -index 02dcd43..844f1d0 100644 ---- a/drivers/staging/bes2600/bes_chardev.c -+++ b/drivers/staging/bes2600/bes_chardev.c -@@ -181,7 +181,7 @@ static int bes2600_switch_wifi(bool on) - return ret; - } --int bes2600_switch_bt(bool on) -+static int bes2600_switch_bt(bool on) - { - int ret = 0; - long status = 0; -@@ -234,6 +234,36 @@ int bes2600_switch_bt(bool on) - return ret; - } +- atomic_add(1, &hw_priv->bh_term); ++ atomic_inc(&hw_priv->bh_term); + wake_up(&hw_priv->bh_wq); -+/* -+ * Re-added for danctnix's bes2600_btuart.c (a danctnix-only file) which -+ * relies on the chardev utility API for BT power switching and bus-error -+ * checks. The userspace /dev/bes2600 chardev itself is removed by the -+ * remove-chardev-user-interface series; these in-kernel helpers stay. -+ * -+ * @wifi: 1 to turn on, 0 to turn off. Otherwise, leave unchanged -+ * @bt: 1 to turn on, 0 to turn off. Otherwise, leave unchanged -+ */ -+int bes2600_chrdev_switch_subsys_glb(int wifi, int bt) -+{ -+ int ret = 0; -+ -+ switch (wifi) { -+ case 0: ret = bes2600_switch_wifi(false); break; -+ case 1: ret = bes2600_switch_wifi(true); break; -+ default: break; -+ } -+ if (ret) -+ return ret; -+ -+ switch (bt) { -+ case 0: ret = bes2600_switch_bt(false); break; -+ case 1: ret = bes2600_switch_bt(true); break; -+ default: break; -+ } -+ return ret; -+} -+EXPORT_SYMBOL_GPL(bes2600_chrdev_switch_subsys_glb); -+ - - - -@@ -562,6 +592,7 @@ bool bes2600_chrdev_is_bus_error(void) - - return error; - } -+EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error); - - void bes2600_chrdev_update_signal_mode(void) - { -diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c -index 8af8150..2b63ff2 100644 ---- a/drivers/staging/bes2600/sta.c -+++ b/drivers/staging/bes2600/sta.c -@@ -1500,7 +1500,7 @@ void bes2600_event_handler(struct work_struct *work) - IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS); - deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; - deauth->seq_ctrl = 0; -- ieee80211_rx_irqsafe(priv->hw, skb); -+ ieee80211_rx_ni(priv->hw, skb); - bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); - queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); - break; -diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c -index cb718ad..9074972 100644 ---- a/drivers/staging/bes2600/txrx.c -+++ b/drivers/staging/bes2600/txrx.c -@@ -1980,18 +1980,18 @@ void bes2600_rx_cb(struct bes2600_vif *priv, - * path is taken. - */ - if (hw_priv->bes_power.pm_unsupported) { -- ieee80211_rx_irqsafe(priv->hw, skb); -+ ieee80211_rx_ni(priv->hw, skb); - } else { - spin_lock_bh(&priv->ps_state_lock); - /* Double-check status with lock held */ - if (entry->status == BES2600_LINK_SOFT) - skb_queue_tail(&entry->rx_queue, skb); - else -- ieee80211_rx_irqsafe(priv->hw, skb); -+ ieee80211_rx_ni(priv->hw, skb); - spin_unlock_bh(&priv->ps_state_lock); - } - } else { -- ieee80211_rx_irqsafe(priv->hw, skb); -+ ieee80211_rx_ni(priv->hw, skb); - } - *skb_p = NULL; - -diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c -index 908c965..2424181 100644 ---- a/drivers/staging/bes2600/wsm.c -+++ b/drivers/staging/bes2600/wsm.c -@@ -2412,7 +2412,7 @@ int wsm_handle_rx(struct bes2600_common *hw_priv, int id, - if (!hw_priv->beacon_bkp) - hw_priv->beacon_bkp = \ - skb_copy(hw_priv->beacon, GFP_ATOMIC); -- ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); -+ ieee80211_rx_ni(hw_priv->hw, hw_priv->beacon); - hw_priv->beacon = hw_priv->beacon_bkp; - - hw_priv->beacon_bkp = NULL; --- -2.54.0 - -From 1b5374d35bcc75e0f393e3d841288f91812eb7dc Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 8 May 2026 08:23:20 +0200 -Subject: [PATCH] =?UTF-8?q?bes2600:=20Patch=20H=20=E2=80=94=20bh.c=20hygie?= - =?UTF-8?q?ne=20cleanup=20(drop=20fossil=20blocks,=20dead=20stubs)?= -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Per Opus structural critique §4.1 (#if 0 graveyard), §4.3 (asm -volatile("nop") placeholder), §4.4 (BUG_ON in steady-state hot -path). Pure source-tree cleanup, no functional change. - -Removed: - - 1. bh.c lines 319-395 (76-line #if 0 block) — dead helper - functions inherited from cw1200 ancestor: - bes2600_bh_read_ctrl_reg, bes2600_get_skb, bes2600_put_skb, - bes2600_device_wakeup. Compiled out for years. - - 2. bh.c lines 405-873 + line 1659 (the outer #if 0 / #else / - #endif) — 468-line cw1200-ancestor bes2600_bh() function body, - preserved verbatim alongside the active impl. Same function - name, same goto labels. Maintenance hazard removed. - - 3. bh.c done: label body — `__bes2600_irq_enable(1)` placeholder - (commented out) + `asm volatile ("nop")` filler. Both - no-ops on bes2600 silicon. - - 4. bh.c post-loop "Explicitly disable device interrupts" block - (sbus lock + __bes2600_irq_enable(0) + sbus unlock) — the - stub call wrapped in lock/unlock ceremony. Dead. - - 5. hwio.c __bes2600_irq_enable() function definition — - `int __bes2600_irq_enable(int enable) { return 0; }`. Stub. - Removed entirely. - - 6. sbus.h __bes2600_irq_enable() forward declaration. - -Replaced: - - 7. bh.c bes2600_bh outer-loop BUG_ON(hw_bufs_used > numInpChBufs) - -> WARN_ON_ONCE. The BUG_ON ran every bh-loop iteration; - tripping it on a bookkeeping bug locks the kernel up during - normal operation — the wrong response to a (recoverable) - accounting drift. WARN_ON_ONCE surfaces the issue without - taking the system down. - -Why __bes2600_irq_enable was a stub on bes2600: - - cw1200 has the same-named function (drivers/net/wireless/st/cw1200/ - hwio.c:267) that does real work — reads ST90TDS_CONFIG_REG_ID and - toggles the ST90TDS_CONF_IRQ_RDY_ENABLE bit. bes2600 inherited - the function name + signature when forked, but the bes2600 chip's - IRQ enable is managed by sdio_claim_irq + chip-side firmware, not - by a driver-side enable register. Bestechnic kept the function as - a no-op stub (return 0). Patch H removes the dead infrastructure. - -Diff scope: - - - bes2600/bh.c -578/+27 (mostly deletions) - - bes2600/hwio.c -7/+7 (stub function -> comment block) - - bes2600/sbus.h -2/+1 (declaration -> comment) - - net: -578/+28 across 3 files - -Build verification deferred — ohm offline. Pure-deletion change, -no semantic risk; the deleted code was either #if 0-gated -(never compiled) or stub-implementations (always returned 0). ---- - bes2600/bh.c | 578 ++----------------------------------------------- - bes2600/hwio.c | 11 +- - bes2600/sbus.h | 3 +- - 3 files changed, 28 insertions(+), 564 deletions(-) - -diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c -index 61f6991..67dfad4 100644 ---- a/drivers/staging/bes2600/bh.c -+++ b/drivers/staging/bes2600/bh.c -@@ -317,83 +317,6 @@ int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count) + flush_workqueue(hw_priv->bh_workqueue); +@@ -317,83 +316,6 @@ int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count) } #endif @@ -6714,7 +1808,7 @@ index 61f6991..67dfad4 100644 /* Must be called from BH thraed. */ void bes2600_enable_powersave(struct bes2600_vif *priv, -@@ -403,475 +326,6 @@ void bes2600_enable_powersave(struct bes2600_vif *priv, +@@ -403,475 +325,6 @@ void bes2600_enable_powersave(struct bes2600_vif *priv, priv->powersave_enabled = enable; } @@ -6906,7 +2000,7 @@ index 61f6991..67dfad4 100644 - bes_devel("[BH] Device resume.\n"); - atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); - wake_up(&hw_priv->bh_evt_wq); -- atomic_inc(&hw_priv->bh_rx); +- atomic_add(1, &hw_priv->bh_rx); - continue; - } - @@ -7074,9 +2168,9 @@ index 61f6991..67dfad4 100644 - -#if 0 /* count is not implemented */ - if (ret > 1) -- atomic_inc(&hw_priv->bh_tx); +- atomic_add(1, &hw_priv->bh_tx); -#else -- atomic_inc(&hw_priv->bh_tx); +- atomic_add(1, &hw_priv->bh_tx); -#endif - -#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) @@ -7190,7 +2284,190 @@ index 61f6991..67dfad4 100644 extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg); -@@ -1599,7 +1053,15 @@ static int bes2600_bh(void *arg) +@@ -959,6 +412,119 @@ static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct + } + } + ++/* ++ * Direct-deliver an RX SKB into the WSM/mac80211 stack. ++ * ++ * Patch C v3 (no-relay architecture, matches cw1200): the bh thread ++ * calls bes2600_sdio_read_rx_batch which calls ++ * bes2600_sdio_extract_packets which calls THIS function per parsed ++ * SKB. No rx_queue, no sdio_rx_work, no inter-thread handoff. ++ * ++ * Single-writer-from-bh invariant on hw_priv->hw_bufs_used, ++ * hw_priv->hw_bufs_used_vif[] and hw_priv->wsm_tx_pending[] is ++ * preserved BY CONSTRUCTION — there is now only one writer (the bh ++ * thread itself), same as cw1200's design. No atomic_t conversion ++ * needed. ++ * ++ * Contract: ++ * - process context, sleepable. wsm_handle_rx (wsm.c, EXPORT_SYMBOL) ++ * acquires wsm_cmd.lock and may sleep on wait_event_timeout. ++ * - caller holds no bes2600 spinlock. bes2600_sdio_unlock(self) is ++ * called inside read_rx_batch before extract_packets is invoked. ++ * - SKB ownership: function frees on every path (success + error). ++ * - No need to wake the bh thread on TX-confirm — we ARE the bh ++ * thread; tx_burst is signalled by returning *tx_out = 1 to the ++ * caller (bh_rx_helper), which propagates it to bh's outer loop. ++ */ ++int bes2600_bh_handle_rx_skb(struct bes2600_common *priv, struct sk_buff *skb) ++{ ++ struct wsm_hdr *wsm; ++ size_t wsm_len; ++ u16 wsm_id; ++ u8 wsm_seq; ++ int tx = 0; ++ u32 confirm_label = 0x0; ++ ++ if (!skb) ++ return 0; ++ ++ wsm = (struct wsm_hdr *)skb->data; ++ wsm_len = __le16_to_cpu(wsm->len); ++ if (WARN_ON(wsm_len > skb->len)) { ++ bes_err("wsm_len err %d %d\n", (int)wsm_len, (int)skb->len); ++ dev_kfree_skb(skb); ++ return -1; ++ } ++ ++ if (priv->wsm_enable_wsm_dumps) ++ print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, 16, 1, ++ skb->data, wsm_len, false); ++ ++ wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; ++ wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; ++ bes_devel("bes2600_bh_handle_rx_skb wsm_id:0x%04x seq:%d\n", ++ wsm_id, wsm_seq); ++ ++ skb_trim(skb, wsm_len); ++ ++ if (wsm_id == 0x0800) { ++ wsm_handle_exception(priv, ++ &skb->data[sizeof(*wsm)], ++ wsm_len - sizeof(*wsm)); ++ bes_err("wsm exception\n"); ++ dev_kfree_skb(skb); ++ return -1; ++ } else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) { ++ bes_err("seq error! %u. %u. 0x%x.", wsm_seq, ++ priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id); ++ dev_kfree_skb(skb); ++ return -1; ++ } ++ ++ bes2600_bh_parse_wakeup_event(priv, skb); ++ ++ priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7; ++ ++ if (IS_DRIVER_TO_MCU_CMD(wsm_id)) ++ confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label); ++ ++ if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) { ++ int rc = wsm_release_tx_buffer(priv, 1); ++ bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id)); ++ ++ if (rc < 0) { ++ bes_err("wsm_release_tx_buffer failed: %d\n", rc); ++ dev_kfree_skb(skb); ++ return rc; ++ } else if (rc > 0) { ++ tx = 1; ++ } ++ } ++ ++ /* wsm_handle_rx takes care of SKB lifetime: zeroes *skb_p if consumed. */ ++ if (wsm_handle_rx(priv, wsm_id, wsm, &skb)) { ++ bes_err("wsm_handle_rx failed (id=0x%04x)\n", wsm_id); ++ if (skb) ++ dev_kfree_skb(skb); ++ return -1; ++ } ++ ++ if (skb) ++ dev_kfree_skb(skb); ++ ++ /* ++ * Signal "tx side has new headroom" via atomic so the bh outer ++ * loop's wait_event predicate notices on its next wait. No ++ * cross-thread wake needed because we are the bh thread; the ++ * outer loop will pick this up after read_rx_batch returns. ++ */ ++ if (tx) ++ atomic_inc(&priv->bh_tx); ++ ++ return 0; ++} ++EXPORT_SYMBOL(bes2600_bh_handle_rx_skb); ++ + static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx) + { + struct sk_buff *skb = NULL; +@@ -970,10 +536,18 @@ static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx) + u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */ + + #if defined(BES_SDIO_RX_MULTIPLE_ENABLE) +- skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv); +- if (!skb) +- return 0; +- rx = 1; // always consider rx pipe not empty ++ /* ++ * Patch C v3: the bh thread does the SDIO read inline via ++ * sbus_ops->bus_rx_batch. bes2600_sdio_read_rx_batch reads the ++ * multi-RX coalesced frames out of the chip and delivers each ++ * one inline via bes2600_bh_handle_rx_skb (no rx_queue, no ++ * pipe_read, no inter-thread handoff). Return value: 0 on ++ * success (bh outer loop will check whether to continue), ++ * negative on read error. ++ */ ++ if (priv->sbus_ops->bus_rx_batch) ++ return priv->sbus_ops->bus_rx_batch(priv->sbus_priv); ++ return 0; + #else + u32 ctrl_reg = 0; + size_t read_len = 0; +@@ -1135,7 +709,7 @@ static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv, + tx_len += 4; + #endif + +- atomic_add(1, &hw_priv->bh_tx); ++ atomic_inc(&hw_priv->bh_tx); + + tx_len = hw_priv->sbus_ops->align_size( + hw_priv->sbus_priv, tx_len); +@@ -1260,8 +834,6 @@ int bes2600_bh_sw_process(struct bes2600_common *hw_priv, + delta_time = jiffies + ((unsigned long)0xffffffff - timestamp); + else + delta_time = jiffies - timestamp; +- bes2600_add_tx_delta_time(delta_time); +- bes2600_add_tx_ac_delta_time(queue_id, delta_time); + + if (bes2600_need_retry_type(skb, tx_confirm->status) == 0) + return -1; +@@ -1270,12 +842,8 @@ int bes2600_bh_sw_process(struct bes2600_common *hw_priv, + return -1; + + if (txpriv->retry_count < CW1200_MAX_SW_RETRY_CNT ) { +- struct bes2600_vif *priv = +- __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + txpriv->retry_count++; + +- bes2600_tx_status(priv,skb); +- + bes2600_pwr_set_busy_event_with_timeout_async( + hw_priv, BES_PWR_LOCK_ON_TX, BES_PWR_EVENT_TX_TIMEOUT); + +@@ -1442,7 +1010,7 @@ static int bes2600_bh(void *arg) + bes_devel("[BH] Device resume.\n"); + atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED); + wake_up(&hw_priv->bh_evt_wq); +- atomic_add(1, &hw_priv->bh_rx); ++ atomic_inc(&hw_priv->bh_rx); + goto done; + } + +@@ -1478,7 +1046,15 @@ static int bes2600_bh(void *arg) tx = 0; @@ -7207,7 +2484,7 @@ index 61f6991..67dfad4 100644 tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used; tx_allowed = tx_burst > 0; -@@ -1643,18 +1105,19 @@ static int bes2600_bh(void *arg) +@@ -1522,18 +1098,19 @@ static int bes2600_bh(void *arg) goto tx; done: @@ -7237,16 +2514,303 @@ index 61f6991..67dfad4 100644 if (!term) { bes_err("[BH] Fatal error, exiting.\n"); sdio_work_debug(hw_priv->sbus_priv); -@@ -1663,4 +1126,3 @@ static int bes2600_bh(void *arg) +@@ -1542,4 +1119,3 @@ static int bes2600_bh(void *arg) } return 0; } -#endif +diff --git a/drivers/staging/bes2600/bh.h b/drivers/staging/bes2600/bh.h +index 7be82dc58..700f2aa07 100644 +--- a/drivers/staging/bes2600/bh.h ++++ b/drivers/staging/bes2600/bh.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Device handling thread interface for mac80211 BES2600 drivers ++ * Bottom-half thread interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_BH_H +@@ -39,6 +39,15 @@ int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id, + int bes2600_bh_sw_process(struct bes2600_common *hw_priv, + struct wsm_tx_confirm *tx_confirm); + ++/* ++ * Direct-deliver an RX SKB into the WSM/mac80211 stack from the bh thread. ++ * Called by bes2600_sdio_extract_packets per RX frame, no queueing. ++ * Process context, sleepable, caller holds no bes2600 spinlock. ++ * Function frees skb on every path. See bh.c for full contract. ++ */ ++int bes2600_bh_handle_rx_skb(struct bes2600_common *hw_priv, ++ struct sk_buff *skb); ++ + void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx); + void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx); + +diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c +index 5228b2279..0ab79c025 100644 +--- a/drivers/staging/bes2600/debug.c ++++ b/drivers/staging/bes2600/debug.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Debugging interface for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -110,17 +110,20 @@ static int bes2600_status_show_common(struct seq_file *seq, void *v) + int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0; + bool ba_ena; + +- spin_lock_bh(&hw_priv->ba_lock); +- ba_cnt = hw_priv->debug->ba_cnt; +- ba_acc = hw_priv->debug->ba_acc; ++ /* ++ * Patch D: ba_lock removed. hw_priv->debug->ba_* are written only ++ * by the timer callback (single writer); reading without a lock is ++ * fine for stats. ba_ena is atomic_t. ++ */ ++ ba_cnt = hw_priv->debug->ba_cnt; ++ ba_acc = hw_priv->debug->ba_acc; + ba_cnt_rx = hw_priv->debug->ba_cnt_rx; + ba_acc_rx = hw_priv->debug->ba_acc_rx; +- ba_ena = hw_priv->ba_ena; ++ ba_ena = !!atomic_read(&hw_priv->ba_ena); + if (ba_cnt) + ba_avg = ba_acc / ba_cnt; + if (ba_cnt_rx) + ba_avg_rx = ba_acc_rx / ba_cnt_rx; +- spin_unlock_bh(&hw_priv->ba_lock); + + seq_puts(seq, "BES2600 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", +@@ -542,6 +545,10 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v) + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Assoc: %s\n", + bes2600_debug_join_status[priv->join_status]); ++ seq_printf(seq, "DecryptStormRecoveries: %u\n", ++ priv->decrypt_storm_recoveries); ++ seq_printf(seq, "ConnectionLossStormRecoveries: %u\n", ++ priv->connection_loss_storm_recoveries); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) +diff --git a/drivers/staging/bes2600/debug.h b/drivers/staging/bes2600/debug.h +index 371457755..5914ffc6e 100644 +--- a/drivers/staging/bes2600/debug.h ++++ b/drivers/staging/bes2600/debug.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * DebugFS code for BES2600 mac80211 driver ++ * Debugging interface for BES2600 mac80211 driver + * +- * Copyright (c) 2011, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_DEBUG_H_INCLUDED +diff --git a/drivers/staging/bes2600/epta_coex.c b/drivers/staging/bes2600/epta_coex.c +index dfdf8e70a..3ed76f1ff 100644 +--- a/drivers/staging/bes2600/epta_coex.c ++++ b/drivers/staging/bes2600/epta_coex.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * BT/WiFi coexistence (ePTA) for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include + #include +diff --git a/drivers/staging/bes2600/epta_coex.h b/drivers/staging/bes2600/epta_coex.h +index bc9eed6cc..f8a5fea45 100644 +--- a/drivers/staging/bes2600/epta_coex.h ++++ b/drivers/staging/bes2600/epta_coex.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * BT/WiFi coexistence interface for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef __EPTA_COEX_H__ + #define __EPTA_COEX_H__ +diff --git a/drivers/staging/bes2600/epta_request.c b/drivers/staging/bes2600/epta_request.c +index 3b3e6af97..486f02ba7 100644 +--- a/drivers/staging/bes2600/epta_request.c ++++ b/drivers/staging/bes2600/epta_request.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * BT/WiFi coexistence request handling + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include + #include +diff --git a/drivers/staging/bes2600/epta_request.h b/drivers/staging/bes2600/epta_request.h +index f0217c2c8..b3d922827 100644 +--- a/drivers/staging/bes2600/epta_request.h ++++ b/drivers/staging/bes2600/epta_request.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * BT/WiFi coexistence request interface + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef EPTA_REQUEST_H + #define EPTA_REQUEST_H +diff --git a/drivers/staging/bes2600/fwio.c b/drivers/staging/bes2600/fwio.c +index 5fe6b507a..29aa2b37c 100644 +--- a/drivers/staging/bes2600/fwio.c ++++ b/drivers/staging/bes2600/fwio.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Firmware I/O for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +diff --git a/drivers/staging/bes2600/fwio.h b/drivers/staging/bes2600/fwio.h +index a4afb7ab1..adbd708f3 100644 +--- a/drivers/staging/bes2600/fwio.h ++++ b/drivers/staging/bes2600/fwio.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Firmware I/O interface for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef FWIO_H_INCLUDED + #define FWIO_H_INCLUDED +diff --git a/drivers/staging/bes2600/ht.h b/drivers/staging/bes2600/ht.h +index b5caa2919..5ac077bf2 100644 +--- a/drivers/staging/bes2600/ht.h ++++ b/drivers/staging/bes2600/ht.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * HT-related code for BES2600 driver ++ * HT capability config for BES2600 + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_HT_H_INCLUDED diff --git a/drivers/staging/bes2600/hwio.c b/drivers/staging/bes2600/hwio.c -index 0934a13..1a63e4f 100644 +index ea88210e8..1a63e4f00 100644 --- a/drivers/staging/bes2600/hwio.c +++ b/drivers/staging/bes2600/hwio.c -@@ -324,7 +324,10 @@ out: +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Low-level device IO routines for BES2600 drivers ++ * Low-level device I/O for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -324,7 +324,10 @@ int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len) } #endif @@ -7261,11 +2825,399 @@ index 0934a13..1a63e4f 100644 + * silicon manages SDIO IRQ via sdio_claim_irq and chip-side + * firmware — there is no driver-side enable register to write. + */ +diff --git a/drivers/staging/bes2600/hwio.h b/drivers/staging/bes2600/hwio.h +index b9c1858df..48e521513 100644 +--- a/drivers/staging/bes2600/hwio.h ++++ b/drivers/staging/bes2600/hwio.h +@@ -1,17 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Low-level API for mac80211 BES2600 drivers ++ * Low-level device I/O interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin + * +- * Based on: +- * UMAC BES2600 driver which is +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_HWIO_H_INCLUDED +diff --git a/drivers/staging/bes2600/itp.c b/drivers/staging/bes2600/itp.c +index e5c2958b5..7cc237c47 100644 +--- a/drivers/staging/bes2600/itp.c ++++ b/drivers/staging/bes2600/itp.c +@@ -1,13 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * mac80211 glue code for mac80211 BES2600 drivers +- * ITP code ++ * ITP (in-band test mode) for BES2600 + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -570,7 +566,7 @@ int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data, + *burst = 2; + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); +- atomic_add(1, &itp->awaiting_confirm); ++ atomic_inc(&itp->awaiting_confirm); + spin_unlock_bh(&itp->tx_lock); + return 1; + +diff --git a/drivers/staging/bes2600/itp.h b/drivers/staging/bes2600/itp.h +index 5cfba4689..bec364788 100644 +--- a/drivers/staging/bes2600/itp.h ++++ b/drivers/staging/bes2600/itp.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * ITP code for BES2600 mac80211 driver ++ * ITP interface for BES2600 + * +- * Copyright (c) 2011, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_ITP_H_INCLUDED +diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c +index 3b0b7a3d7..5fd663e2c 100644 +--- a/drivers/staging/bes2600/main.c ++++ b/drivers/staging/bes2600/main.c +@@ -1,12 +1,18 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Main entry/init for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. ++ * ++ * Based on the mac80211 Prism54 code, which is ++ * Copyright (c) 2006, Michael Wu ++ * ++ * Based on the islsm (softmac prism54) driver, which is ++ * Copyright 2004-2006 Jean-Baptiste Note , et al. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -32,7 +38,6 @@ + #include "pm.h" + #include "bes2600_factory.h" + #include "bes_chardev.h" +-#include "txrx_opt.h" + + MODULE_AUTHOR("Dmitry Tarnyagin "); + MODULE_DESCRIPTION("Softmac BES2600 common code"); +@@ -199,11 +204,7 @@ static const struct ieee80211_iface_limit bes2600_if_limits[] = { + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) }, + #ifdef P2P_MULTIVIF +- /* +- * HACK: Disable P2P_DEVICE implementation for BES2600 +- * as the code is a little buggy. +- */ +- //{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, ++ { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, + #endif + }; + +@@ -489,17 +490,20 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) + spin_lock_init(&hw_priv->rtsvalue_lock); + INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work); + INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); ++ INIT_WORK(&hw_priv->connection_loss_storm_recover_work, ++ bes2600_connection_loss_storm_recover); + spin_lock_init(&hw_priv->event_queue_lock); + INIT_LIST_HEAD(&hw_priv->event_queue); + INIT_WORK(&hw_priv->event_handler, bes2600_event_handler); + INIT_WORK(&hw_priv->ba_work, bes2600_ba_work); +- spin_lock_init(&hw_priv->ba_lock); ++ /* Patch D: ba_lock removed; ba_acc/ba_cnt/etc are atomic_t. */ + timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0); + + if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats, + WLAN_LINK_ID_MAX, + bes2600_skb_dtor, + hw_priv))) { ++ destroy_workqueue(hw_priv->workqueue); + ieee80211_free_hw(hw); + return NULL; + } +@@ -511,6 +515,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len) + for (; i > 0; i--) + bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]); + bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats); ++ destroy_workqueue(hw_priv->workqueue); + ieee80211_free_hw(hw); + return NULL; + } +@@ -795,41 +800,6 @@ void bes2600_core_release(struct bes2600_common *self) + return; + } + +-#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */ +-int access_file(char *path, char *buffer, int size, int isRead) +-{ +- int ret=0; +- struct file *fp; +- mm_segment_t old_fs = get_fs(); +- +- if(isRead) +- fp = filp_open(path,O_RDONLY,S_IRUSR); +- else +- fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR); +- +- if (IS_ERR(fp)) { +- bes_err("BES2600 : can't open %s\n", path); +- return -1; +- } +- +- if (isRead) { +- fp->f_pos = 0; +- set_fs(KERNEL_DS); +- ret = vfs_read(fp,buffer,size,&fp->f_pos); +- set_fs(old_fs); +- } else { +- fp->f_pos = 0; +- set_fs(KERNEL_DS); +- ret = vfs_write(fp,buffer,size,&fp->f_pos); +- set_fs(old_fs); +- } +- filp_close(fp,NULL); +- +- bes_info("BES2600 : access_file return code(%d)\n", ret); +- return ret; +-} +-#endif +- + int bes2600_wifi_start(struct bes2600_common *hw_priv) + { + int ret = 0, if_id; +diff --git a/drivers/staging/bes2600/pm.c b/drivers/staging/bes2600/pm.c +index c32c68efe..0424aae6d 100644 +--- a/drivers/staging/bes2600/pm.c ++++ b/drivers/staging/bes2600/pm.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 power management API for BES2600 drivers ++ * Power management for BES2600 mac80211 driver + * +- * Copyright (c) 2011, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +diff --git a/drivers/staging/bes2600/pm.h b/drivers/staging/bes2600/pm.h +index 0f6943ecd..ae704537e 100644 +--- a/drivers/staging/bes2600/pm.h ++++ b/drivers/staging/bes2600/pm.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 power management interface for BES2600 mac80211 drivers ++ * Power management interface for BES2600 mac80211 driver + * +- * Copyright (c) 2011, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef PM_H_INCLUDED +diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c +index d1b407b31..5881fa91c 100644 +--- a/drivers/staging/bes2600/queue.c ++++ b/drivers/staging/bes2600/queue.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * O(1) TX queue with built-in allocator for BES2600 drivers ++ * O(1) TX queue for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -119,9 +119,10 @@ static void bes2600_queue_register_post_gc(struct list_head *gc_list, + struct bes2600_queue_item *item) + { + struct bes2600_queue_item *gc_item; +- gc_item = kmemdup(item, sizeof(struct bes2600_queue_item), ++ gc_item = kmalloc(sizeof(struct bes2600_queue_item), + GFP_ATOMIC); + BUG_ON(!gc_item); ++ memcpy(gc_item, item, sizeof(struct bes2600_queue_item)); + list_add_tail(&gc_item->head, gc_list); + } + +@@ -130,9 +131,9 @@ static void bes2600_queue_pending_record(struct list_head *pending_record_list, + { + struct bes2600_queue_item *record_item; + +- record_item = kmemdup(pending_item, sizeof(struct bes2600_queue_item), +- GFP_ATOMIC); ++ record_item = kmalloc(sizeof(struct bes2600_queue_item),GFP_ATOMIC); + BUG_ON(!record_item); ++ memcpy(record_item, pending_item, sizeof(struct bes2600_queue_item)); + record_item->skb = skb_clone(pending_item->skb, GFP_ATOMIC); + list_add_tail(&record_item->head, pending_record_list); + } +@@ -217,7 +218,7 @@ int bes2600_queue_stats_init(struct bes2600_queue_stats *stats, + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + for (i = 0; i < CW12XX_MAX_VIFS; i++) { +- stats->link_map_cache[i] = kcalloc(map_capacity, sizeof(int), ++ stats->link_map_cache[i] = kzalloc(map_capacity * sizeof(int), + GFP_KERNEL); + if (!stats->link_map_cache[i]) { + for (; i >= 0; i--) +@@ -248,14 +249,14 @@ int bes2600_queue_init(struct bes2600_queue *queue, + queue->queue_all_lock = false; + spin_lock_init(&queue->lock); + timer_setup(&queue->gc, bes2600_queue_gc, 0); +- queue->pool = kcalloc(capacity, sizeof(struct bes2600_queue_item), ++ queue->pool = kzalloc(sizeof(struct bes2600_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + for (i = 0; i < CW12XX_MAX_VIFS; i++) { + queue->link_map_cache[i] = +- kcalloc(stats->map_capacity, sizeof(int), ++ kzalloc(stats->map_capacity * sizeof(int), + GFP_KERNEL); + if (!queue->link_map_cache[i]) { + for (; i >= 0; i--) +@@ -409,6 +410,7 @@ int bes2600_queue_put(struct bes2600_queue *queue, + struct timespec64 tmval; + #endif /*CONFIG_BES2600_TESTMODE*/ + ++ LIST_HEAD(gc_list); + struct bes2600_queue_stats *stats = queue->stats; + /* TODO:COMBO: Add interface ID info to queue item */ + +diff --git a/drivers/staging/bes2600/queue.h b/drivers/staging/bes2600/queue.h +index a5395b633..94874dd27 100644 +--- a/drivers/staging/bes2600/queue.h ++++ b/drivers/staging/bes2600/queue.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * O(1) TX queue with built-in allocator for BES2600 drivers ++ * O(1) TX queue interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_QUEUE_H_INCLUDED diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h -index 43c2dae..4193084 100644 +index 1f2c0cda7..41930847f 100644 --- a/drivers/staging/bes2600/sbus.h +++ b/drivers/staging/bes2600/sbus.h -@@ -95,7 +95,6 @@ struct sbus_ops { +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Common sbus abstraction layer interface for bes2600 wireless driver ++ * Bus abstraction interface for BES2600 ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. ++ * Replaces hwbus.h from drivers/net/wireless/st/cw1200/ ++ * Copyright (c) 2010, ST-Ericsson + * +- * Copyright (c) 2010, Bestechnic +- * Author: + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_SBUS_H +@@ -75,11 +75,26 @@ struct sbus_ops { + void (*halt_device)(struct sbus_priv *self); + bool (*wakeup_source)(struct sbus_priv *self); + int (*reboot)(struct sbus_priv *self); ++ /* ++ * Force the host bus to re-detect and re-probe the chip. Called ++ * from the firmware-wedge recovery path when power_switch() has no ++ * effective chip-reset signal of its own (e.g. PineTab2, where the ++ * wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node). ++ * Returns 0 on success or a negative errno. ++ */ ++ int (*bus_reset)(struct sbus_priv *self); ++ /* ++ * Read a batch of RX frames inline from the bus and deliver each ++ * one via bes2600_bh_handle_rx_skb(). Called from the bh thread ++ * (process context, sleepable). Replaces the ++ * sdio_rx_work + rx_queue + pipe_read relay (Patch C v3, 2026). ++ * Returns 0 on success, negative on read error. ++ */ ++ int (*bus_rx_batch)(struct sbus_priv *self); + }; void bes2600_irq_handler(struct bes2600_common *priv); @@ -7274,8 +3226,1099 @@ index 43c2dae..4193084 100644 +/* Patch H: __bes2600_irq_enable removed (was a stub). */ #endif /* BES2600_SBUS_H */ +diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c +index 3bfa53564..fb1d29861 100644 +--- a/drivers/staging/bes2600/scan.c ++++ b/drivers/staging/bes2600/scan.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Scan implementation for BES2600 mac80211 drivers ++ * Scan implementation for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -14,11 +14,63 @@ + #include "scan.h" + #include "sta.h" + #include "pm.h" ++#include "epta_coex.h" + #include "epta_request.h" + #include "bes_pwr.h" + ++/* ++ * After this many consecutive WSM scan rejections from firmware, stop ++ * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state ++ * that's rejecting them (coex window, firmware-internal busy) clear. ++ * ++ * The backoff has to be at least as long as the natural mac80211 scan- ++ * retry cadence, otherwise the next attempt lands outside the window ++ * and bypasses the defer guard. Observed in the wild on PineTab2: ++ * roam-evaluation bursts at ~12 s cadence, idle background scans at ++ * ~5 min cadence. 30 s catches the burst and leaves the slow case ++ * alone (the firmware-policy state has had minutes to clear by then ++ * anyway). ++ */ ++#define BES2600_SCAN_REJECT_THRESHOLD 3 ++#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ) ++ + static void bes2600_scan_restart_delayed(struct bes2600_vif *priv); + ++/* ++ * Decide whether to skip sending the next WSM scan command without ++ * bothering the firmware. Two triggers: ++ * ++ * 1. BT A2DP is streaming in non-FDD coex mode. The firmware is ++ * known to reject scan requests during that window; short- ++ * circuiting here saves a WSM round-trip and avoids the ++ * wsm_generic_confirm / scan_work warning cascade that follows. ++ * ++ * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive ++ * rejections on recent scan attempts and the backoff window has ++ * not yet elapsed. Whatever was rejecting them is likely still ++ * rejecting them; give it time. If the backoff has elapsed without ++ * a fresh reject refreshing it, the burst is over and we reset the ++ * count so an isolated reject doesn't immediately re-trip. ++ * ++ * Returns true if the caller should abandon the scan iteration. ++ */ ++static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv) ++{ ++#ifdef WIFI_BT_COEXIST_EPTA_ENABLE ++ if (!coex_is_fdd_mode() && coex_is_bt_a2dp()) ++ return true; ++#endif ++ ++ if (time_after(jiffies, hw_priv->scan.backoff_until)) ++ hw_priv->scan.reject_count = 0; ++ ++ if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD && ++ time_before(jiffies, hw_priv->scan.backoff_until)) ++ return true; ++ ++ return false; ++} ++ + #ifdef CONFIG_BES2600_TESTMODE + static int bes2600_advance_scan_start(struct bes2600_common *hw_priv) + { +@@ -205,18 +257,21 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, + + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN); + ++ /* will be unlocked in bes2600_scan_work() */ ++ down(&hw_priv->scan.lock); ++ down(&hw_priv->conf_lock); ++ + frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, + req->ie_len); +- if (!frame.skb) ++ if (!frame.skb) { ++ up(&hw_priv->conf_lock); ++ up(&hw_priv->scan.lock); + return -ENOMEM; ++ } + + if (req->ie_len) + skb_put_data(frame.skb, req->ie, req->ie_len); + +- /* will be unlocked in bes2600_scan_work() */ +- down(&hw_priv->scan.lock); +- down(&hw_priv->conf_lock); +- + if (frame.skb) { + int ret; + //if (priv->if_id == 0) +@@ -234,9 +289,9 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, + } + #endif + if (ret) { ++ dev_kfree_skb(frame.skb); + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); +- dev_kfree_skb(frame.skb); + return ret; + } + } +@@ -266,10 +321,10 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, + ++hw_priv->scan.n_ssids; + } + +- up(&hw_priv->conf_lock); +- + if (frame.skb) + dev_kfree_skb(frame.skb); ++ ++ up(&hw_priv->conf_lock); + #ifdef WIFI_BT_COEXIST_EPTA_ENABLE + bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING); + #endif +@@ -310,14 +365,18 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, + if (req->n_ssids > hw->wiphy->max_scan_ssids) + return -EINVAL; + ++ /* will be unlocked in bes2600_scan_work() */ ++ down(&hw_priv->scan.lock); ++ down(&hw_priv->conf_lock); ++ + frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, + req->ie_len); +- if (!frame.skb) ++ if (!frame.skb) { ++ up(&hw_priv->conf_lock); ++ up(&hw_priv->scan.lock); + return -ENOMEM; ++ } + +- /* will be unlocked in bes2600_scan_work() */ +- down(&hw_priv->scan.lock); +- down(&hw_priv->conf_lock); + if (frame.skb) { + int ret; + if (priv->if_id == 0) +@@ -328,9 +387,9 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, + ret = wsm_set_probe_responder(priv, true); + } + if (ret) { ++ dev_kfree_skb(frame.skb); + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); +- dev_kfree_skb(frame.skb); + return ret; + } + } +@@ -362,10 +421,10 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw, + } + } + +- up(&hw_priv->conf_lock); +- + if (frame.skb) + dev_kfree_skb(frame.skb); ++ ++ up(&hw_priv->conf_lock); + queue_work(hw_priv->workqueue, &hw_priv->scan.swork); + wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n"); + return 0; +@@ -533,10 +592,10 @@ void bes2600_scan_work(struct work_struct *work) + "[SCAN] Scan failed (%d).\n", + hw_priv->scan.status); + else if (hw_priv->scan.req) +- wiphy_dbg(priv->hw->wiphy, ++ wiphy_info(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); + else +- wiphy_dbg(priv->hw->wiphy, ++ wiphy_info(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); + + #ifdef WIFI_BT_COEXIST_EPTA_ENABLE +@@ -621,9 +680,8 @@ void bes2600_scan_work(struct work_struct *work) + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } +- scan.ch = kcalloc((it - hw_priv->scan.curr), +- sizeof(struct wsm_scan_ch), +- GFP_KERNEL); ++ scan.ch = kzalloc((it - hw_priv->scan.curr) * ++ sizeof(struct wsm_scan_ch), GFP_KERNEL); + if (!scan.ch) { + hw_priv->scan.status = -ENOMEM; + goto fail; +@@ -703,10 +761,29 @@ void bes2600_scan_work(struct work_struct *work) + wsm_unlock_tx(hw_priv); + } else + #endif ++ { ++ if (bes2600_scan_should_defer(hw_priv)) { ++ hw_priv->scan.status = -EBUSY; ++ hw_priv->scan.reject_count++; ++ hw_priv->scan.backoff_until = ++ jiffies + BES2600_SCAN_BACKOFF_JIFFIES; ++ wiphy_dbg(priv->hw->wiphy, ++ "[SCAN] deferred (coex/backoff, reject_count=%u)\n", ++ hw_priv->scan.reject_count); ++ kfree(scan.ch); ++ goto fail; ++ } + hw_priv->scan.status = bes2600_scan_start(priv, &scan); ++ } + kfree(scan.ch); +- if (WARN_ON(hw_priv->scan.status)) ++ if (hw_priv->scan.status) { ++ hw_priv->scan.reject_count++; ++ hw_priv->scan.backoff_until = ++ jiffies + BES2600_SCAN_BACKOFF_JIFFIES; ++ /* Lower callers already logged the reason at wiphy_warn. */ + goto fail; ++ } ++ hw_priv->scan.reject_count = 0; + hw_priv->scan.curr = it; + } + up(&hw_priv->conf_lock); +@@ -906,7 +983,7 @@ void bes2600_scan_complete_cb(struct bes2600_common *hw_priv, + // recover EPTA timer after scan wsm msg complete, in case of epta state error + // bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING_COMP); + #endif +- wiphy_dbg(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status); ++ wiphy_info(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status); + + if(hw_priv->scan.status == -ETIMEDOUT) + wiphy_warn(hw_priv->hw->wiphy, +diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h +index e50fa363b..295be1850 100644 +--- a/drivers/staging/bes2600/scan.h ++++ b/drivers/staging/bes2600/scan.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Scan interface for BES2600 mac80211 drivers ++ * Scan interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef SCAN_H_INCLUDED +@@ -42,6 +42,17 @@ struct bes2600_scan { + struct delayed_work probe_work; + int direct_probe; + u8 if_id; ++ /* ++ * Track consecutive firmware-side WSM scan rejections so we can ++ * back off briefly instead of re-issuing the same scan on every ++ * mac80211 background-scan tick. Firmware returns WSM status != 0 ++ * for a handful of transient conditions (BT A2DP active in non- ++ * FDD coex, firmware-internal busy windows) and keeps rejecting ++ * until the state clears; retrying at full cadence just floods ++ * dmesg. ++ */ ++ unsigned int reject_count; ++ unsigned long backoff_until; + }; + + int bes2600_hw_scan(struct ieee80211_hw *hw, +diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c +index ca1c77c5f..e8c085918 100644 +--- a/drivers/staging/bes2600/sta.c ++++ b/drivers/staging/bes2600/sta.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 STA API for BES2600 drivers ++ * Mac80211 STA API for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -42,8 +42,6 @@ + #include "bes2600_factory.h" + #endif + +-#include "txrx_opt.h" +- + #define WEP_ENCRYPT_HDR_SIZE 4 + #define WEP_ENCRYPT_TAIL_SIZE 4 + #define WPA_ENCRYPT_HDR_SIZE 8 +@@ -268,6 +266,7 @@ void bes2600_stop(struct ieee80211_hw *dev, bool suspend) + cancel_work_sync(&hw_priv->coex_work); + coex_stop(hw_priv); + #endif ++ cancel_work_sync(&hw_priv->connection_loss_storm_recover_work); + + bes2600_wifi_stop(hw_priv); + +@@ -377,23 +376,9 @@ void bes2600_remove_interface(struct ieee80211_hw *dev, + atomic_set(&priv->enabled, 0); + down(&hw_priv->scan.lock); + down(&hw_priv->conf_lock); +- +- /* +- * There's a chance remove_interface will run again on the same +- * (already removed) interface. +- * +- * Currently this only happens when NetworkManager creates a P2P_DEVICE +- * alongside a STA. +- * +- * But there can be other cases where this may run as well. So if that +- * happens, let's throw a warning and decrease the vifs count by one. +- */ +- if (WARN_ON(!__cw12xx_hwpriv_to_vifpriv(hw_priv, priv->if_id))) { ++ if (!__cw12xx_hwpriv_to_vifpriv(hw_priv, priv->if_id)) { + bes_devel(" !!! %s: interface addr %pM already removed\n", + __func__, vif->addr); +- +- atomic_dec(&hw_priv->num_vifs); +- + up(&hw_priv->conf_lock); + up(&hw_priv->scan.lock); + return; +@@ -464,6 +449,7 @@ void bes2600_remove_interface(struct ieee80211_hw *dev, + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->set_cts_work); + cancel_delayed_work_sync(&priv->pending_offchanneltx_work); ++ cancel_work_sync(&priv->decrypt_storm_recover_work); + + timer_delete_sync(&priv->mcast_timeout); + /* TODO:COMBO: May be reset of these variables "delayed_link_loss and +@@ -1498,7 +1484,7 @@ void bes2600_event_handler(struct work_struct *work) + IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS); + deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; + deauth->seq_ctrl = 0; +- ieee80211_rx_irqsafe(priv->hw, skb); ++ ieee80211_rx_ni(priv->hw, skb); + bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); + queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); + break; +@@ -1674,6 +1660,70 @@ void bes2600_bss_loss_work(struct work_struct *work) + spin_unlock(&priv->bss_loss_lock); + } + ++/* ++ * Connection-loss-storm fast-recover (Trigger A). ++ * ++ * bes2600_connection_loss_work below is the driver's own decision-point ++ * to give up on a BSS (after bss-loss detection accumulates beyond ++ * tolerance) and tell mac80211 via ieee80211_connection_loss(). On the ++ * deployed pinetab2 stack a single ieee80211_connection_loss() event ++ * sometimes triggers a userspace reauth blackhole (assoc-comeback ++ * timeouts followed by AP unprotected-deauth-reason-6) that ends only ++ * via cross-channel/cross-SSID fallback and can take 80+ s. Receipts at ++ * https://git.reauktion.de/marfrit/besser, notes/phase4-2026-05-07.md. ++ * ++ * When N connection-loss decisions land within WINDOW on the same vif, ++ * skip the ieee80211_connection_loss() path and trigger a chip-level ++ * bus_reset (the c5.2-introduced bes2600_chrdev_do_bus_reset). The chip ++ * is removed and re-probed; userspace re-associates from a fresh state, ++ * dodging the assoc-comeback loop. ++ * ++ * Threshold (3 / 60 s) is chosen well above the steady-state per-vif ++ * connection-loss rate observed in the patch-A Phase-7 rep ++ * (0.86/h under sustained load), so a true storm is required. ++ * ++ * The recover work_struct lives on bes2600_common (hw_priv) so that ++ * scheduling it does not race with vif teardown after bus_reset frees ++ * the per-vif state. ++ */ ++#define BES2600_CONNECTION_LOSS_STORM_THRESHOLD 3 ++#define BES2600_CONNECTION_LOSS_STORM_WINDOW_MS 60000 ++ ++void bes2600_connection_loss_storm_recover(struct work_struct *work) ++{ ++ bes_warn("[bes2600] connection-loss-storm fast-recover: bus_reset\n"); ++ bes2600_chrdev_trigger_bus_reset(); ++ /* ++ * After bes2600_chrdev_do_bus_reset() returns, the SDIO core has ++ * scheduled a remove + rescan; per-vif state may already be gone. ++ * Do not dereference any per-vif pointer here. ++ */ ++} ++ ++void bes2600_connection_loss_storm_init(struct bes2600_vif *priv) ++{ ++ priv->connection_loss_storm_window_start = 0; ++ priv->connection_loss_storm_count = 0; ++ priv->connection_loss_storm_recoveries = 0; ++} ++ ++bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv) ++{ ++ unsigned long now = jiffies; ++ unsigned long window = ++ msecs_to_jiffies(BES2600_CONNECTION_LOSS_STORM_WINDOW_MS); ++ ++ if (priv->connection_loss_storm_window_start == 0 || ++ time_after(now, priv->connection_loss_storm_window_start + window)) { ++ priv->connection_loss_storm_window_start = now; ++ priv->connection_loss_storm_count = 1; ++ return false; ++ } ++ ++ return ++priv->connection_loss_storm_count >= ++ BES2600_CONNECTION_LOSS_STORM_THRESHOLD; ++} ++ + void bes2600_connection_loss_work(struct work_struct *work) + { + struct bes2600_vif *priv = +@@ -1683,9 +1733,21 @@ void bes2600_connection_loss_work(struct work_struct *work) + + bes_devel("[CQM] Reporting connection loss.\n"); + bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST); +- if(bes2600_suspend_status_get(hw_priv)) { ++ ++ if (bes2600_connection_loss_storm_account(priv)) { ++ bes_warn("[bes2600] connection-loss storm: %u in %u s, scheduling bus reset\n", ++ priv->connection_loss_storm_count, ++ BES2600_CONNECTION_LOSS_STORM_WINDOW_MS / 1000); ++ priv->connection_loss_storm_count = 0; ++ priv->connection_loss_storm_recoveries++; ++ schedule_work(&hw_priv->connection_loss_storm_recover_work); ++ /* bus_reset will tear the chip down; skip the mac80211 path. */ ++ return; ++ } ++ ++ if (bes2600_suspend_status_get(hw_priv)) + bes2600_pending_unjoin_set(hw_priv, priv->if_id); +- } else ++ else + ieee80211_connection_loss(priv->vif); + #ifdef WIFI_BT_COEXIST_EPTA_ENABLE + // set disconnected in BSS_CHANGED_ASSOC +@@ -2185,8 +2247,6 @@ void bes2600_join_work(struct work_struct *work) + wsm_unlock_tx(hw_priv); + return; + } +- +- rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + dtimie = ieee80211_bss_get_ie(bss, WLAN_EID_TIM); + if (dtimie) +@@ -2270,8 +2330,6 @@ void bes2600_join_work(struct work_struct *work) + bes2600_rate_mask_to_wsm(hw_priv, 0xFF0); + } + +- rcu_read_unlock(); +- + bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_JOIN); + wsm_flush_tx(hw_priv); + +@@ -2284,14 +2342,19 @@ void bes2600_join_work(struct work_struct *work) + //WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id)); + WARN_ON(wsm_set_block_ack_policy(hw_priv, + 0, hw_priv->ba_tid_mask, priv->if_id)); +- spin_lock_bh(&hw_priv->ba_lock); +- hw_priv->ba_ena = false; +- hw_priv->ba_cnt = 0; +- hw_priv->ba_acc = 0; ++ /* ++ * Patch D: ba_lock removed. Disconnect-reset clears the ++ * counters and the arm flag; producers racing here cannot ++ * cause harm — at worst they re-arm the timer and bump ++ * counters that will be cleared on the next timer tick. ++ */ ++ atomic_set(&hw_priv->ba_ena, 0); ++ atomic_set(&hw_priv->ba_cnt, 0); ++ atomic_set(&hw_priv->ba_acc, 0); + hw_priv->ba_hist = 0; +- hw_priv->ba_cnt_rx = 0; +- hw_priv->ba_acc_rx = 0; +- spin_unlock_bh(&hw_priv->ba_lock); ++ atomic_set(&hw_priv->ba_cnt_rx, 0); ++ atomic_set(&hw_priv->ba_acc_rx, 0); ++ atomic_set(&hw_priv->ba_armed, 0); + + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; +@@ -2571,10 +2634,11 @@ void bes2600_ba_work(struct work_struct *work) + return;*/ + + bes_devel("BA work****\n"); +- spin_lock_bh(&hw_priv->ba_lock); +-// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0; ++ /* ++ * Patch D: ba_lock removed. ba_tid_mask is u8 set once at init ++ * (main.c); reading it without a lock is fine. ++ */ + tx_ba_tid_mask = hw_priv->ba_tid_mask; +- spin_unlock_bh(&hw_priv->ba_lock); + + wsm_lock_tx(hw_priv); + +@@ -2587,37 +2651,49 @@ void bes2600_ba_work(struct work_struct *work) + void bes2600_ba_timer(struct timer_list *t) + { + bool ba_ena; ++ int cnt, acc, cnt_rx, acc_rx; + struct bes2600_common *hw_priv = timer_container_of(hw_priv, t, ba_timer); + +- spin_lock_bh(&hw_priv->ba_lock); +- bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc, +- hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx); ++ /* ++ * Patch D: ba_lock removed. Snapshot atomic counters into locals ++ * for the predicate evaluation; producers may race incrementing ++ * after the snapshot but the resulting decision is approximate ++ * which the policy already tolerates (next timer tick re-evaluates). ++ */ ++ cnt = atomic_read(&hw_priv->ba_cnt); ++ acc = atomic_read(&hw_priv->ba_acc); ++ cnt_rx = atomic_read(&hw_priv->ba_cnt_rx); ++ acc_rx = atomic_read(&hw_priv->ba_acc_rx); ++ ++ bes2600_debug_ba(hw_priv, cnt, acc, cnt_rx, acc_rx); + + if (atomic_read(&hw_priv->scan.in_progress)) { +- hw_priv->ba_cnt = 0; +- hw_priv->ba_acc = 0; +- hw_priv->ba_cnt_rx = 0; +- hw_priv->ba_acc_rx = 0; +- goto skip_statistic_update; ++ atomic_set(&hw_priv->ba_cnt, 0); ++ atomic_set(&hw_priv->ba_acc, 0); ++ atomic_set(&hw_priv->ba_cnt_rx, 0); ++ atomic_set(&hw_priv->ba_acc_rx, 0); ++ atomic_set(&hw_priv->ba_armed, 0); ++ return; + } + +- if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT && +- (hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD || +- (hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT && +- hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >= ++ if (cnt >= BES2600_BLOCK_ACK_CNT && ++ (acc / cnt >= BES2600_BLOCK_ACK_THLD || ++ (cnt_rx >= BES2600_BLOCK_ACK_CNT && ++ acc_rx / cnt_rx >= + BES2600_BLOCK_ACK_THLD))) + ba_ena = true; + else + ba_ena = false; + +- hw_priv->ba_cnt = 0; +- hw_priv->ba_acc = 0; +- hw_priv->ba_cnt_rx = 0; +- hw_priv->ba_acc_rx = 0; ++ atomic_set(&hw_priv->ba_cnt, 0); ++ atomic_set(&hw_priv->ba_acc, 0); ++ atomic_set(&hw_priv->ba_cnt_rx, 0); ++ atomic_set(&hw_priv->ba_acc_rx, 0); ++ atomic_set(&hw_priv->ba_armed, 0); + +- if (ba_ena != hw_priv->ba_ena) { ++ if (ba_ena != !!atomic_read(&hw_priv->ba_ena)) { + if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) { +- hw_priv->ba_ena = ba_ena; ++ atomic_set(&hw_priv->ba_ena, ba_ena ? 1 : 0); + hw_priv->ba_hist = 0; + #if 0 + bes_devel("[STA] %s block ACK:\n", +@@ -2627,9 +2703,6 @@ void bes2600_ba_timer(struct timer_list *t) + } + } else if (hw_priv->ba_hist) + --hw_priv->ba_hist; +- +-skip_statistic_update: +- spin_unlock_bh(&hw_priv->ba_lock); + } + + int bes2600_vif_setup(struct bes2600_vif *priv) +@@ -2639,6 +2712,8 @@ int bes2600_vif_setup(struct bes2600_vif *priv) + + /* Setup per vif workitems and locks */ + spin_lock_init(&priv->vif_lock); ++ bes2600_decrypt_storm_init(priv); ++ bes2600_connection_loss_storm_init(priv); + INIT_WORK(&priv->join_work, bes2600_join_work); + INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout); + INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work); +@@ -2809,7 +2884,6 @@ void bes2600_dynamic_opt_txrx_work(struct work_struct *work) + if (priv != NULL && priv->join_status > BES2600_JOIN_STATUS_MONITOR) { + multivif_connected = true; + } +- bes2600_txrx_opt_multivif_connected_handler(hw_priv, multivif_connected); + } + + +@@ -3654,7 +3728,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw, + * + * Returns: 0 on success or non zero value on failure + */ +-int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) ++static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) + { + struct bes_msg_start_stop_tsm *start_stop_tsm = + (struct bes_msg_start_stop_tsm *) data; +@@ -3684,7 +3758,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data) + * + * Returns: TSM parameters collected + */ +-int bes2600_get_tsm_params(struct ieee80211_hw *hw) ++static int bes2600_get_tsm_params(struct ieee80211_hw *hw) + { + struct bes2600_common *hw_priv = hw->priv; + struct bes_tsm_stats tsm_stats; +@@ -3724,7 +3798,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw) + * + * Returns: Returns the last measured roam delay + */ +-int bes2600_get_roam_delay(struct ieee80211_hw *hw) ++static int bes2600_get_roam_delay(struct ieee80211_hw *hw) + { + struct bes2600_common *hw_priv = hw->priv; + u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000; +diff --git a/drivers/staging/bes2600/sta.h b/drivers/staging/bes2600/sta.h +index 39b4b1a10..a174e04f5 100644 +--- a/drivers/staging/bes2600/sta.h ++++ b/drivers/staging/bes2600/sta.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 STA interface for BES2600 mac80211 drivers ++ * Mac80211 STA API interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include + #ifndef STA_H_INCLUDED +diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c +index baab3f0c2..e6cf072d1 100644 +--- a/drivers/staging/bes2600/tx_loop.c ++++ b/drivers/staging/bes2600/tx_loop.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Test-mode TX loopback for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #include "bes2600.h" + #include "wsm.h" +diff --git a/drivers/staging/bes2600/tx_loop.h b/drivers/staging/bes2600/tx_loop.h +index de82b302c..7f42c04b8 100644 +--- a/drivers/staging/bes2600/tx_loop.h ++++ b/drivers/staging/bes2600/tx_loop.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Test-mode TX loopback interface for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifndef __TX_LOOP_H__ + #define __TX_LOOP_H__ +diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c +index 017f0d89c..de521a3be 100644 +--- a/drivers/staging/bes2600/txrx.c ++++ b/drivers/staging/bes2600/txrx.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Datapath implementation for BES2600 mac80211 drivers ++ * Datapath implementation for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -21,11 +21,82 @@ + #include "debug.h" + #include "sta.h" + #include "sbus.h" +-#include "txrx_opt.h" + #include "bes_log.h" + + #define BES2600_INVALID_RATE_ID (0xFF) + ++/* ++ * Decrypt-storm fast-recover (Trigger B). ++ * ++ * When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a ++ * burst of received frames (typically because the host's PTK or GTK ++ * has fallen out of sync with the AP), the AP eventually concludes that ++ * the STA is not authenticated and emits an unprotected deauth-reason-6 ++ * ("Class 2 frame received from non-authenticated station"). On the ++ * deployed pinetab2 + bes2600 stack this AP-initiated deauth has been ++ * observed to leave the link blackholed for up to 109 s before ++ * userspace finds a different SSID/channel to recover on. (Receipts at ++ * https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.) ++ * ++ * Recovery here pre-empts the AP: when we see THRESHOLD decrypt ++ * failures within WINDOW, we ask mac80211 for a clean reassoc via ++ * ieee80211_connection_loss(), which causes immediate disassociation ++ * and lets userspace auto-reconnect with fresh keys. ++ * ++ * mac80211 contract: ieee80211_connection_loss() may be called ++ * regardless of IEEE80211_HW_CONNECTION_MONITOR; it causes immediate ++ * disassociation without driver-side recovery attempts. See ++ * include/net/mac80211.h for the canonical doc-comment. ++ * ++ * The threshold is set well above the steady-state per-vif ++ * decrypt-fail rate observed in measurement (~1/min even under ++ * sustained 1 MB/s load), so a true storm is required to trip it. ++ */ ++#define BES2600_DECRYPT_STORM_THRESHOLD 5 ++#define BES2600_DECRYPT_STORM_WINDOW_MS 5000 ++ ++static void bes2600_decrypt_storm_recover_work(struct work_struct *work) ++{ ++ struct bes2600_vif *priv = container_of(work, struct bes2600_vif, ++ decrypt_storm_recover_work); ++ ++ if (!priv->vif) ++ return; ++ ++ bes_warn("[bes2600] decrypt-storm fast-recover: forcing reassoc\n"); ++ ieee80211_connection_loss(priv->vif); ++ priv->decrypt_storm_recoveries++; ++} ++ ++void bes2600_decrypt_storm_init(struct bes2600_vif *priv) ++{ ++ INIT_WORK(&priv->decrypt_storm_recover_work, ++ bes2600_decrypt_storm_recover_work); ++ priv->decrypt_storm_window_start = 0; ++ priv->decrypt_storm_count = 0; ++ priv->decrypt_storm_recoveries = 0; ++} ++ ++void bes2600_decrypt_storm_account(struct bes2600_vif *priv) ++{ ++ unsigned long now = jiffies; ++ unsigned long window = msecs_to_jiffies(BES2600_DECRYPT_STORM_WINDOW_MS); ++ ++ if (priv->decrypt_storm_window_start == 0 || ++ time_after(now, priv->decrypt_storm_window_start + window)) { ++ priv->decrypt_storm_window_start = now; ++ priv->decrypt_storm_count = 1; ++ return; ++ } ++ ++ if (++priv->decrypt_storm_count >= BES2600_DECRYPT_STORM_THRESHOLD) { ++ priv->decrypt_storm_count = 0; ++ /* Skew the window so we don't re-fire on the same storm. */ ++ priv->decrypt_storm_window_start = now + window; ++ schedule_work(&priv->decrypt_storm_recover_work); ++ } ++} ++ + #ifdef CONFIG_BES2600_TESTMODE + #include "bes_nl80211_testmode_msg.h" + #endif /* CONFIG_BES2600_TESTMODE */ +@@ -924,14 +995,18 @@ bes2600_tx_h_ba_stat(struct bes2600_vif *priv, + if (!ieee80211_is_data(t->hdr->frame_control)) + return; + +- spin_lock_bh(&hw_priv->ba_lock); +- hw_priv->ba_acc += t->skb->len - t->hdrlen; +- if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { ++ /* ++ * Patch D: lock-free hot-path BA accounting. atomic_inc + atomic_add ++ * each per-frame; the once-per-window timer-arm uses cmpxchg on ++ * ba_armed so concurrent TX/RX can't both try to set the timer and ++ * we don't need cross-counter coherency on the ba_cnt/ba_cnt_rx pair. ++ */ ++ atomic_add(t->skb->len - t->hdrlen, &hw_priv->ba_acc); ++ atomic_inc(&hw_priv->ba_cnt); ++ if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) { + mod_timer(&hw_priv->ba_timer, + jiffies + BES2600_BLOCK_ACK_INTERVAL); + } +- hw_priv->ba_cnt++; +- spin_unlock_bh(&hw_priv->ba_lock); + } + + static int +@@ -1474,35 +1549,14 @@ void bes2600_skb_dtor(struct bes2600_common *hw_priv, + struct bes2600_vif *priv = + __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + +- +- if (!skb) +- return; +- +- /* +- * There should be no reason for skb buffer being larger +- * than the offset.. +- */ +- if(WARN_ON(txpriv->offset > skb->len)) { +- ieee80211_free_txskb(hw_priv->hw, skb); +- return; +- } +- +- bes_devel("%s: txpriv->offset: %d - skb->len: %d\n", +- __func__, txpriv->offset, skb->len); +- + skb_pull(skb, txpriv->offset); + if (priv && txpriv->rate_id != BES2600_INVALID_RATE_ID) { + bes2600_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(hw_priv, txpriv->rate_id); + } +- if (likely(!bes2600_is_itp(hw_priv))) { +- if (priv) { +- /* The interface may be already removed */ +- bes2600_tx_status(priv, skb); +- } ++ if (likely(!bes2600_is_itp(hw_priv))) + ieee80211_tx_status_skb(hw_priv->hw, skb); +- } + + } + #ifdef CONFIG_BES2600_TESTMODE +@@ -1579,14 +1633,13 @@ bes2600_rx_h_ba_stat(struct bes2600_vif *priv, + if (!priv->setbssparams_done) + return; + +- spin_lock_bh(&hw_priv->ba_lock); +- hw_priv->ba_acc_rx += skb_len - hdrlen; +- if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { ++ /* Patch D: lock-free hot-path BA accounting; see TX side comment. */ ++ atomic_add(skb_len - hdrlen, &hw_priv->ba_acc_rx); ++ atomic_inc(&hw_priv->ba_cnt_rx); ++ if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) { + mod_timer(&hw_priv->ba_timer, + jiffies + BES2600_BLOCK_ACK_INTERVAL); + } +- hw_priv->ba_cnt_rx++; +- spin_unlock_bh(&hw_priv->ba_lock); + } + + void bes2600_rx_cb(struct bes2600_vif *priv, +@@ -1694,6 +1747,8 @@ void bes2600_rx_cb(struct bes2600_vif *priv, + goto drop; + } else { + bes_warn("[RX] Receive failure: %d.\n", arg->status); ++ if (arg->status == WSM_STATUS_DECRYPTFAILURE) ++ bes2600_decrypt_storm_account(priv); + goto drop; + } + } +@@ -1871,7 +1926,6 @@ void bes2600_rx_cb(struct bes2600_vif *priv, + + if (ieee80211_is_data(frame->frame_control)) { + bes2600_rx_h_ba_stat(priv, hdrlen, skb->len); +- bes2600_rx_status(priv, skb); + } + + #ifdef CONFIG_BES2600_TESTMODE +@@ -1888,15 +1942,33 @@ void bes2600_rx_cb(struct bes2600_vif *priv, + if (unlikely(bes2600_itp_rxed(hw_priv, skb))) + consume_skb(skb); + else if (unlikely(early_data)) { +- spin_lock_bh(&priv->ps_state_lock); +- /* Double-check status with lock held */ +- if (entry->status == BES2600_LINK_SOFT) +- skb_queue_tail(&entry->rx_queue, skb); +- else +- ieee80211_rx_irqsafe(priv->hw, skb); +- spin_unlock_bh(&priv->ps_state_lock); ++ /* ++ * Patch E: when c7 has latched pm_unsupported (firmware ++ * doesn't honour PSM, see feedback_bes2600_firmware_no_psm), ++ * AP-side power-save state machine is dead and link entries ++ * never transition to BES2600_LINK_SOFT. The double-check ++ * branch under ps_state_lock is unreachable in that case, ++ * so skip the per-frame lock acquisition entirely and ++ * deliver to mac80211 directly. ++ * ++ * On firmware that does honour PSM (the latch self-clears ++ * if a real PM_INDICATION ever arrives — see c7), this ++ * predicate flips back to false and the original locked ++ * path is taken. ++ */ ++ if (hw_priv->bes_power.pm_unsupported) { ++ ieee80211_rx_ni(priv->hw, skb); ++ } else { ++ spin_lock_bh(&priv->ps_state_lock); ++ /* Double-check status with lock held */ ++ if (entry->status == BES2600_LINK_SOFT) ++ skb_queue_tail(&entry->rx_queue, skb); ++ else ++ ieee80211_rx_ni(priv->hw, skb); ++ spin_unlock_bh(&priv->ps_state_lock); ++ } + } else { +- ieee80211_rx_irqsafe(priv->hw, skb); ++ ieee80211_rx_ni(priv->hw, skb); + } + *skb_p = NULL; + +diff --git a/drivers/staging/bes2600/txrx.h b/drivers/staging/bes2600/txrx.h +index cb7c192d1..6466c3370 100644 +--- a/drivers/staging/bes2600/txrx.h ++++ b/drivers/staging/bes2600/txrx.h +@@ -1,12 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Datapath interface for BES2600 mac80211 drivers ++ * Datapath interface for BES2600 mac80211 driver + * +- * Copyright (c) 2010, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_TXRX_H +diff --git a/drivers/staging/bes2600/wifi_testmode_cmd.c b/drivers/staging/bes2600/wifi_testmode_cmd.c +index 2494ccac7..c010e8d6d 100644 +--- a/drivers/staging/bes2600/wifi_testmode_cmd.c ++++ b/drivers/staging/bes2600/wifi_testmode_cmd.c +@@ -1,12 +1,9 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * WiFi testmode commands for BES2600 + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + #ifdef CONFIG_BES2600_TESTMODE + #include +diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c +index d40df3063..242418114 100644 +--- a/drivers/staging/bes2600/wsm.c ++++ b/drivers/staging/bes2600/wsm.c +@@ -1,13 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * WSM host interface (HI) implementation for +- * BES2600 mac80211 drivers. ++ * WSM host interface for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin ++ * ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #include +@@ -134,8 +133,20 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv, + struct wsm_buf *buf) + { + u32 status = WSM_GET32(buf); +- if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status)) ++ ++ /* ++ * A non-SUCCESS status here is a firmware-side policy decision for ++ * the command whose confirm this is -- commonly WSM status 2 for ++ * scan (0x0407) rejected because of a coex window or transient ++ * firmware-busy state. It is not a driver/kernel bug, so avoid the ++ * WARN()/stack-trace treatment; the caller already emits a ++ * wiphy_warn identifying the request id and will propagate the ++ * error to mac80211. ++ */ ++ if (status != WSM_STATUS_SUCCESS) { ++ bes_devel("%s ret %u\n", __func__, status); + return -EINVAL; ++ } + return 0; + + underflow: +@@ -2401,7 +2412,7 @@ int wsm_handle_rx(struct bes2600_common *hw_priv, int id, + if (!hw_priv->beacon_bkp) + hw_priv->beacon_bkp = \ + skb_copy(hw_priv->beacon, GFP_ATOMIC); +- ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); ++ ieee80211_rx_ni(hw_priv->hw, hw_priv->beacon); + hw_priv->beacon = hw_priv->beacon_bkp; + + hw_priv->beacon_bkp = NULL; +diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h +index 067313162..0d755a362 100644 +--- a/drivers/staging/bes2600/wsm.h ++++ b/drivers/staging/bes2600/wsm.h +@@ -1,16 +1,12 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * WSM host interface (HI) interface for BES2600 mac80211 drivers ++ * WSM host interface for BES2600 mac80211 driver + * +- * Copyright (c) 2022, Bestechnic +- * Author: ++ * Copyright (c) 2010, ST-Ericsson ++ * Author: Dmitry Tarnyagin + * +- * Based on BES2600 UMAC WSM API, which is +- * Copyright (C) SA 2010 +- * Author: Stewart Mathers ++ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. + */ + + #ifndef BES2600_WSM_H_INCLUDED +@@ -2236,7 +2232,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv); + + int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status); + +-#if defined(STANDARD_FACTORY_EFUSE_FLAG) + int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type); +-#endif + #endif /* BES2600_HWIO_H_INCLUDED */ -- -2.54.0 +2.53.0 From 093a5038b8b68f316d976b7cb69609ca7f24f322 Mon Sep 17 00:00:00 2001 From: Markus Fritsche diff --git a/arch/linux-pinetab2-danctnix-besser/PKGBUILD b/arch/linux-pinetab2-danctnix-besser/PKGBUILD index fa1f7f71c..aa8486c66 100644 --- a/arch/linux-pinetab2-danctnix-besser/PKGBUILD +++ b/arch/linux-pinetab2-danctnix-besser/PKGBUILD @@ -29,7 +29,7 @@ pkgbase=linux-pinetab2-danctnix-besser pkgver=7.0.danctnix1 -pkgrel=6 +pkgrel=5 pkgdesc='PineTab2 (BESser bes2600 driver patchset, kernel-agent managed)' _srcname=linux-pinetab2 _srctag=v${pkgver%.*}-${pkgver##*.} @@ -68,7 +68,7 @@ b2sums=('3d9795083c8938f80f480de0d10bfd9c525640e59d5c7f22983de3f12ee42c84c31be90 'SKIP' '71fe98221e802b315e54b4b10d3e8c8f376695a36bae3541d876e5776a37f3fa33c8f8dfa6e51fcbd6f5396add02e5166634165f2351836a0ea0453c172fe56c' 'SKIP' - 'eb179c03f35a4dbaec2e40036f0033ef04985bb6b14ab22419d68e5caaa5874f2ad14e158f7c5b05added97f60fecde8fb8b7f2a6ced33e031e37352fe776ca6' + '50397711a6a3ba522283685a9e7397aeed6663f353f7cba214d4bb88bc98516065b2fca9a36ce13c52644617879f69f39c5305e86db5d9fb25c4dae5434eb9c4' '656a998ab40cb85ee4c00f087b071a91632a6c091da2c84b0f74236b51d2dea6e9db6886625f80ad81dc249d8494ec47cd79d6dd9ea4f5e44f3cde857f861e10') export KBUILD_BUILD_HOST=archlinux diff --git a/arch/linux-pinetab2-danctnix-besser/manifest.lock b/arch/linux-pinetab2-danctnix-besser/manifest.lock index 088011e64..2e7fb5cb6 100644 --- a/arch/linux-pinetab2-danctnix-besser/manifest.lock +++ b/arch/linux-pinetab2-danctnix-besser/manifest.lock @@ -4,174 +4,34 @@ baseline: upstream_compat: linux-7.0 url: https://codeberg.org/DanctNIX/linux-pinetab2 cumulative: - b2sum: eb179c03f35a4dbaec2e40036f0033ef04985bb6b14ab22419d68e5caaa5874f2ad14e158f7c5b05added97f60fecde8fb8b7f2a6ced33e031e37352fe776ca6 + b2sum: 50397711a6a3ba522283685a9e7397aeed6663f353f7cba214d4bb88bc98516065b2fca9a36ce13c52644617879f69f39c5305e86db5d9fb25c4dae5434eb9c4 path: cumulative.patch - size: 279554 -generated_at: '2026-05-19T13:05:46.476359+00:00' + size: 162716 +generated_at: '2026-05-18T17:16:06.455474+00:00' host: ohm ka_promote_version: 1 manifest: path: fleet/ohm.yaml - sha256: 9ac04ddd3170418b7b2d2cf7b31ac225a31ed19be4f03e8477bf28b585bae257 + sha256: da59ac2c965e5ad9c5004a115b10a37abf47ed3ecc8b7f5ab426470d2ee7b442 resolved_patches: - apply_order: 1 from_series: true - include: driver/bes2600/factory-series/0001-bes2600-use-request_firmware-for-factory.txt-read.patch - sha256: a1bc2d13b258709fa37c9ff428dfdc0659464b436470fa2ec69b07edf7592f6f - size: 5456 + include: driver/bes2600/cumulative-c5x-danctnix/0001-bes2600-besser-cumulative-series.patch + sha256: e477a170567487fef84fe13be5b0a1f0498247ff1f201000d0085a2e49ff9026 + size: 148149 - apply_order: 2 from_series: true - include: driver/bes2600/factory-series/0002-bes2600-default-STANDARD_FACTORY_EFUSE_FLAG-off-for-PineTab2.patch - sha256: 577d7024ce0b342c4381365872fc29e75a93427ad61223907fead8b829b5a86c - size: 3499 -- apply_order: 3 - from_series: true - include: driver/bes2600/factory-thread-dev/0001-bes2600-thread-struct-device-through-factory-request_firmware.patch - sha256: e3fac725e6addc11147341836600c2c5cd0116abba960f34ba50bb8094581c75 - size: 4406 -- apply_order: 4 - from_series: true - include: driver/bes2600/pm-gate-on-handshake/0001-bes2600-gate-device-LP-mode-entry-on-successful-handshake.patch - sha256: 9842c0dd66f59fe28898041ba5a816be56965b0665f202410cd461c3e6565474 - size: 3914 -- apply_order: 5 - from_series: true - include: driver/bes2600/remove-chardev-user-interface/0001-bes2600-remove-userspace-dev-bes2600-character-device-interface.patch - sha256: c67d340ae5923aada613ea9c5133e3efa3aeb7986749f4bf3619d1752a1b61fb - size: 22445 -- apply_order: 6 - from_series: true - include: driver/bes2600/enable-testmode/0001-bes2600-enable-CONFIG_BES2600_TESTMODE-by-default-fix-bitrot.patch - sha256: 5dee74e8753d332fd380882994ea43aa907d1ff97466b0c48aedf38d4076e446 - size: 6152 -- apply_order: 7 - from_series: true - include: driver/bes2600/tx-sdio-dma-oob-danctnix/0001-bes2600-bounce-SDIO-TX-buffers-to-avoid-DMA-OOB-read.patch - sha256: 0dce2fe35450b8376c2d2a7c007119f28c888c1c30b489a67841039caedeebfc - size: 4544 -- apply_order: 8 - from_series: true - include: driver/bes2600/factory-drop-kernel-write-danctnix/0001-bes2600-drop-kernel_write-persistence-from-factory-cali-save.patch - sha256: a7995b38e210af16b73d284a58ab39b8aecac36ff4a671af3d894b1983f961b3 - size: 5704 -- apply_order: 9 - from_series: true - include: driver/bes2600/drop-dpd-file-paths-danctnix/0001-bes2600-drop-BES2600_WRITE_DPD_TO_FILE-kernel-file-paths.patch - sha256: 0cd8780c245c97c65e4845e42d712c6256a0449658641aea18e4c7d400f63e41 - size: 9661 -- apply_order: 10 - from_series: true - include: driver/bes2600/drop-orphan-file-io-danctnix/0001-bes2600-drop-orphan-DATA_DUMP_OBSERVE-and-access_file-IO.patch - sha256: fd8c297223e6a985c2898f919ae1ab27eb56ab44f09f44d84d75eb35a187527b - size: 5327 -- apply_order: 11 - from_series: true - include: driver/bes2600/pm-timeout-silence-danctnix/0001-bes2600-demote-wait-pm-ind-timeout-from-bes_err-to-bes_devel.patch - sha256: 3a4fd3255facbcef0419e0e0332cb980316529aa5c225b35bcfd244a42736667 - size: 2332 -- apply_order: 12 - from_series: true - include: driver/bes2600/scan-defer-on-reject-danctnix/0001-bes2600-defer-scan-and-soften-WARN-on-firmware-reject.patch - sha256: 55e16c176bc147c371a20f57b3a57da38c719d3b42417e88f9de243e10102d35 - size: 8393 -- apply_order: 13 - from_series: true - include: driver/bes2600/scan-defer-backoff-tune-danctnix/0001-bes2600-widen-scan-defer-backoff-30s-and-decay-on-quiet.patch - sha256: 70a5b25baaf41c8090701b069c30cbad378883d828bdd06e4eb560a35bc077f1 - size: 4924 -- apply_order: 14 - from_series: true - include: driver/bes2600/lmac-recover-via-mmc-hw-reset-danctnix/0001-bes2600-recover-wedged-firmware-via-mmc_hw_reset-on-link-break.patch - sha256: 3decf33c9684b3aba64004d5ad97ae3d54e1d6dc176d0b0ae539036c65e6dc6c - size: 10604 -- apply_order: 15 - from_series: true - include: driver/bes2600/lmac-recover-via-mmc-hw-reset-danctnix/0002-bes2600-handle-multi-function-SDIO-cards-in-mmc_hw_reset-bus_reset.patch - sha256: a1acfcc401afc699a9c3676b6df2ec0f092e78826a32616268f90b509d538e33 - size: 3321 -- apply_order: 16 - from_series: true - include: driver/bes2600/pm-state-resync-danctnix/0001-bes2600-gate-PM-indication-completion-on-pending-request-and-track-state.patch - sha256: 049cf3ff9c01fdd10ff73bd18497e14ef0cd8fd1a65486ba86fbc6c1935a5f8e - size: 10269 -- apply_order: 17 - from_series: true - include: driver/bes2600/pm-wake-consume-state-danctnix/0001-bes2600-short-circuit-wake-handshake-when-chip-confirmed-ACTIVE.patch - sha256: c9d19a73816f4c82b418dcd18008176bbb0c49fd4138be53cad45ae142224112 - size: 8100 -- apply_order: 18 - from_series: true - include: driver/bes2600/pm-detect-firmware-unsupported-danctnix/0001-bes2600-self-detect-firmware-does-not-honor-PSM-skip-cycle.patch - sha256: 196dc9d51ffea268718a290d434b6237fb60119f10c2b050a58724c8a775c7a8 - size: 9041 -- apply_order: 19 - from_series: true - include: driver/bes2600/decrypt-storm-fast-recover-danctnix/0001-bes2600-pre-empt-AP-deauth-6-mac80211-reassoc-on-decrypt-fail-storm.patch - sha256: b57ed316005f402c95ccae8ab24ac761bdf34162d73f108f5790af8f8ad2d1fe - size: 9249 -- apply_order: 20 - from_series: true - include: driver/bes2600/connection-loss-fast-recover-danctnix/0001-bes2600-bus_reset-on-connection-loss-storm-to-dodge-assoc-comeback-blackhole.patch - sha256: cd1eaff97c3f08c58e7b1588e19a12200e8bb2a1f39afe554284f1d818610a67 - size: 12184 -- apply_order: 21 - from_series: true - include: driver/bes2600/cw1200-fix-backports-danctnix/0001-bes2600-replace-atomic_add-with-atomic_inc-cw1200-backport.patch - sha256: 3876c9e512f556c7f2e8d4cfaba1d7df2945ee48af8edfab5f8d09d9de9adf23 - size: 3080 -- apply_order: 22 - from_series: true - include: driver/bes2600/cw1200-fix-backports-danctnix/0002-bes2600-fix-missing-destroy_workqueue-on-error-in-init_common.patch - sha256: 2b82ecb127748349780404479205b952337c244e715278e6d40471c6ecad7602 - size: 2230 -- apply_order: 23 - from_series: true - include: driver/bes2600/cw1200-fix-backports-danctnix/0003-bes2600-fix-concurrency-UAF-in-bes2600_hw_scan-and-sched_scan.patch - sha256: 4c1850ad003ddcac543d3d61edd15c18ccd0cc601367cf4c6dd31e1fbb39ab16 - size: 4476 -- apply_order: 24 - from_series: true - include: driver/bes2600/sdio-rx-no-relay-danctnix/0001-bes2600-drop-sdio_rx_work-relay-IRQ-bh-direct-no-relay-architecture.patch - sha256: f1182150c5893f2497f942900b34c9c4aeb8d5901d9786ae2753dcce38ed6c78 - size: 19313 -- apply_order: 25 - from_series: true - include: driver/bes2600/license-spdx-restore-attribution-danctnix/0001-bes2600-Patch-G-restore-SPDX-identifiers-ST-Ericsson-attribution.patch - sha256: 91dadab0b58f8b8ad2dca80fd04796d478ecb83ce94a1e4b6e97ef8634d97ef1 - size: 41521 -- apply_order: 26 - from_series: true - include: driver/bes2600/ba-lock-atomic-danctnix/0001-bes2600-Patch-D-atomicize-ba_lock-counters-drop-the-spinlock.patch - sha256: a5d4ed2bf545458a756e65670c7eed31997bd0be9262344a10313bee31ea4963 - size: 11987 -- apply_order: 27 - from_series: true - include: driver/bes2600/ps-state-lock-skip-pm-disabled-danctnix/0001-bes2600-Patch-E-skip-ps_state_lock-when-PSM-known-disabled.patch - sha256: 18040a563b37cc95c558703f01bfbf6b7fa23a52f2f4f0f8f1254ad4fa0fe0d6 - size: 3396 -- apply_order: 28 - from_series: true - include: driver/bes2600/rx-list-batch-delivery-danctnix/0001-bes2600-Patch-C2-replace-ieee80211_rx_irqsafe-with-ieee80211_rx_ni.patch - sha256: ffeffd085a9d052c126a717b845d50120ea302e76c12e53c0c3c891291cababf - size: 8377 -- apply_order: 29 - from_series: true - include: driver/bes2600/bh-c-fossil-cleanup-danctnix/0001-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blocks-dead-stubs.patch - sha256: 8fb0c799e3a8ee5ad7bfb647fceaf370c6a1a5f24d8621776fd07bf18a976f81 - size: 21082 -- apply_order: 30 - from_series: true - include: driver/bes2600/scan-filter-5ghz-danctnix/0001-bes2600-filter-5-GHz-scans-at-the-driver-boundary.patch + include: driver/bes2600/scan-filter-5ghz-danctnix/0001-bes2600-filter-5ghz-scan-and-allow-single-channel.patch sha256: 31e67569e00daead0784214aced1e077d3270cf1407baa0b330d474e17ec3931 size: 7735 -- apply_order: 31 +- apply_order: 3 from_series: true - include: arch/arm64/scs-arm-neon-build-fix/0001-arm64-xor-neon-ffixed-x18-build-fix.patch - sha256: 105e32edc54743d8107c4dcd846833ae97d2df5f918aebc9fe3e67d6f23249cc - size: 1562 -- apply_order: 32 + include: arch/arm64/xor-neon-ffixed-x18-scs-build-fix-danctnix/0001-arm64-xor-neon-ffixed-x18-build-fix.patch + sha256: a49c50f0ebffc499970c24908b832c3e61c96ed87de35b3a82178eff587f94f1 + size: 1574 +- apply_order: 4 from_series: true include: driver/bes2600/queue-pending-record-lock-bh-danctnix/0001-bes2600-take-pending-record-lock-with-bh.patch - sha256: e0894371c43f750590e1704ae3c77b27b6910548afa4a5b61ebc4d9919580ca2 - size: 5270 + sha256: 089862e5f6da5783ed0db979144e4fa07cff7f743809a0bebd715c75a3bb8eb5 + size: 5258 schema_version: 1