From fd0f5a8b71b9add312f9ca2fc775bf41018cf2c2 Mon Sep 17 00:00:00 2001 From: "Claude (noether)" Date: Wed, 20 May 2026 20:37:30 +0200 Subject: [PATCH] danctnix-besser: replace cumulative patch with per-series (pkgrel=4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the single squashed 0001-bes2600-besser-cumulative-series.patch with 20 individual per-commit patches matching the bes2600/besser-danctnix-v3 branch in marfrit/bes2600-dkms. Also remove the duplicate 0003-arm64 entry that was a bug in pkgrel=3. Patch list: 0001 c5.1 defer scan and soften WARN on firmware reject 0002 c5.1.1 widen scan-defer backoff to 30s and decay reject_count 0003 c5.2 recover wedged firmware via mmc_hw_reset on link break 0004 c6.1 gate PM indication completion on pending request 0005 c6.2 short-circuit wake handshake when chip confirmed ACTIVE 0006 c7 self-detect when firmware does not honor PSM and skip 0007 c5.2.1 handle multi-function SDIO cards in mmc_hw_reset 0008 Patch A pre-empt AP-deauth-6 with reassoc on decrypt-fail storm 0009 Patch B bus_reset on connection-loss storm 0010 Patch F3 atomicize atomic_add() calls 0011 Patch F2 fix missing destroy_workqueue() on error in init_common 0012 Patch F1 fix concurrency UAF in bes2600_hw_scan / sched_scan 0013 Patch C v3 drop sdio_rx_work relay, IRQ→bh-direct 0014 Patch G restore SPDX identifiers + ST-Ericsson attribution 0015 Patch D atomicize ba_lock counters, drop the spinlock 0016 Patch E skip ps_state_lock when PSM-known-disabled 0017 Patch C2 replace ieee80211_rx_irqsafe with ieee80211_rx_ni 0018 Patch H bh.c hygiene cleanup (drop fossil blocks, dead stubs) 0019 besser#18 pending_record_lock SOFTIRQ-safe fix 0020 danctnix-flavor: export bus_reset helpers for bes2600_btuart Build pending (pkgrel=4 makepkg in progress on boltzmann). Signed-off-by: Claude (noether) --- ...001-bes2600-besser-cumulative-series.patch | 4322 ----------------- ...an-and-soften-WARN-on-firmware-rejec.patch | 226 + ...an-defer-backoff-to-30s-and-decay-co.patch | 109 + ...wedged-firmware-via-mmc_hw_reset-on-.patch | 251 + ...indication-completion-on-pending-req.patch | 261 + ...rcuit-wake-handshake-when-chip-is-co.patch | 190 + ...ect-when-firmware-does-not-honor-PSM.patch | 209 + ...ulti-function-SDIO-cards-in-mmc_hw_r.patch | 83 + ...-AP-deauth-6-with-mac80211-reassoc-o.patch | 221 + ...t-on-connection-loss-storm-to-dodge-.patch | 279 ++ ...-bes2600-replace-a-set-of-atomic_add.patch | 92 + ...ing-destroy_workqueue-on-error-in-in.patch | 58 + ...urrency-UAF-in-bes2600_hw_scan-and-s.patch | 144 + ...o_rx_work-relay-IRQ-bh-direct-no-rel.patch | 540 ++ ...restore-SPDX-identifiers-ST-Ericsson.patch | 1154 +++++ ...atomicize-ba_lock-counters-drop-the-.patch | 313 ++ ...skip-ps_state_lock-when-PSM-known-di.patch | 83 + ...-replace-ieee80211_rx_irqsafe-with-i.patch | 157 + ...bh.c-hygiene-cleanup-drop-fossil-blo.patch | 725 +++ ...ding_record_lock-with-_bh-to-fix-SOF.patch | 121 + ...us_reset-helpers-for-danctnix-bes260.patch | 47 + danctnix-besser-pkgbuild/kernel/PKGBUILD | 46 +- 22 files changed, 5304 insertions(+), 4327 deletions(-) delete mode 100644 danctnix-besser-pkgbuild/kernel/0001-bes2600-besser-cumulative-series.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0001-bes2600-defer-scan-and-soften-WARN-on-firmware-rejec.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0002-bes2600-widen-scan-defer-backoff-to-30s-and-decay-co.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0003-bes2600-recover-wedged-firmware-via-mmc_hw_reset-on-.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0004-bes2600-gate-PM-indication-completion-on-pending-req.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0005-bes2600-short-circuit-wake-handshake-when-chip-is-co.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0006-bes2600-self-detect-when-firmware-does-not-honor-PSM.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0007-bes2600-handle-multi-function-SDIO-cards-in-mmc_hw_r.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0008-bes2600-pre-empt-AP-deauth-6-with-mac80211-reassoc-o.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0009-bes2600-bus_reset-on-connection-loss-storm-to-dodge-.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0010-bes2600-replace-a-set-of-atomic_add.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0011-bes2600-fix-missing-destroy_workqueue-on-error-in-in.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0012-bes2600-fix-concurrency-UAF-in-bes2600_hw_scan-and-s.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0013-bes2600-drop-sdio_rx_work-relay-IRQ-bh-direct-no-rel.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0014-bes2600-Patch-G-restore-SPDX-identifiers-ST-Ericsson.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0015-bes2600-Patch-D-atomicize-ba_lock-counters-drop-the-.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0016-bes2600-Patch-E-skip-ps_state_lock-when-PSM-known-di.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0017-bes2600-Patch-C2-replace-ieee80211_rx_irqsafe-with-i.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0018-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blo.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0019-bes2600-take-pending_record_lock-with-_bh-to-fix-SOF.patch create mode 100644 danctnix-besser-pkgbuild/kernel/0020-bes2600-export-bus_reset-helpers-for-danctnix-bes260.patch diff --git a/danctnix-besser-pkgbuild/kernel/0001-bes2600-besser-cumulative-series.patch b/danctnix-besser-pkgbuild/kernel/0001-bes2600-besser-cumulative-series.patch deleted file mode 100644 index a0adf6fd3..000000000 --- a/danctnix-besser-pkgbuild/kernel/0001-bes2600-besser-cumulative-series.patch +++ /dev/null @@ -1,4322 +0,0 @@ -From 4e176b8f930373bc02382c903e6d739ab2d5fd47 Mon Sep 17 00:00:00 2001 -From: Markus Fritsche -Date: Fri, 8 May 2026 10:07:47 +0200 -Subject: [PATCH] bes2600: BESser cumulative patch series (16 commits squashed) - -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: - - - 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) - -Patch series squashed: - - 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) - -Net: 48 files changed, ~1500 insertions, ~2000 deletions. - -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. - -For per-patch history see git.reauktion.de/marfrit/bes2600-dkms cleanups -branch. - -Signed-off-by: Markus Fritsche ---- - 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/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. - */ - - #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 dc5d3dac6..0d2bfa1c8 100644 ---- a/drivers/staging/bes2600/bes2600_factory.c -+++ b/drivers/staging/bes2600/bes2600_factory.c -@@ -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 -+#include - #include - #include - #include -@@ -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) - { -- int ret = 0; -- struct file *fp; -+ const struct firmware *fw; -+ int ret; - - if (!path || !buffer) { - bes_err("%s NULL pointer err\n", __func__); - return -1; - } - -- bes_devel("reading %s \n", path); -+ bes_devel("requesting firmware-class %s\n", path); - -- 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, bes2600_factory_dev); -+ if (ret) { -+ bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret); - return -1; - } - -- if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) { -- bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n", -- fp->f_inode->i_size, FACTORY_MAX_SIZE); -- filp_close(fp, NULL); -+ if (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) { -+ bes_err("bes2600_factory.txt size check failed, read_size: %zu max_size: %d\n", -+ fw->size, FACTORY_MAX_SIZE); -+ release_firmware(fw); - return -1; - } - -- ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos); -- -- filp_close(fp, NULL); -- -- if (ret != fp->f_inode->i_size) { -- bes_err("bes2600_factory.txt read fail\n"); -- 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; - } - -@@ -891,9 +867,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 -@@ -902,13 +891,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); -@@ -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; - } - -diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h -index 3835b0d9c..0b1a321d7 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__ -@@ -199,6 +196,9 @@ enum factory_cali_status { - /* just calibrate 11n, other protocols are automatically mapped */ - #define WIFI_RF_11N_MODE 0x15 - -+/* set the struct device * used for request_firmware() context */ -+void bes2600_factory_set_dev(struct device *dev); -+ - /* 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 13d4ff1e5..517e6f874 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 -@@ -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" -@@ -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; -+ /* -+ * 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; -@@ -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]; -+ u8 *tx_bounce; - u32 tx_data_cnt; - u32 tx_xfer_cnt; - u32 tx_proc_cnt; -@@ -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) - } - } - -- sg_set_buf(&sg[scatters], tx_buffer->buf, align); -+ /* -+ * The transfer length is rounded up to the SDIO block -+ * size, but tx_buffer->buf is only tx_buffer->len bytes -+ * long (it usually aliases into an skb linear head). -+ * Copy into a driver-owned bounce buffer and zero-pad -+ * to the aligned size; otherwise DMA reads past the -+ * skb and leaks adjacent kernel memory on the wire -- -+ * observed as KFENCE OOB reads from -+ * bes_sdio_memcpy_to_io_helper via dma_map_sg. -+ */ -+ if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN)) -+ goto flush_previous; -+ memcpy(self->tx_bounce + total_len, -+ tx_buffer->buf, tx_buffer->len); -+ if (align > tx_buffer->len) -+ memset(self->tx_bounce + total_len + -+ tx_buffer->len, 0, -+ align - tx_buffer->len); -+ sg_set_buf(&sg[scatters], -+ self->tx_bounce + total_len, align); - total_len += align; - ++scatters; - /*del_node:*/ -@@ -1174,7 +1211,14 @@ static void sdio_tx_work(struct work_struct *work) - } - } 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 - #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; - } -@@ -1399,7 +1449,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; - } -@@ -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) - { --#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; -@@ -1756,6 +1803,55 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) - sdio_work_debug(self); - } - -+/* -+ * Trigger an SDIO bus reset via mmc_hw_reset(). -+ * -+ * With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and -+ * func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the -+ * remove-and-rescan path: it 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. -+ * -+ * In both cases the chip ends up in a freshly reset state, which is the -+ * goal of the recovery path. -+ * -+ * mmc_hw_reset() must be called without holding the SDIO host claim -- -+ * the multi-func remove-and-rescan path acquires the host claim via the -+ * mmc workqueue. -+ */ -+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; -+ -+ 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(); -@@ -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, -+ .bus_reset = bes2600_sdio_bus_reset, - }; - - 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 f89dcb8fb..5374d5117 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 -@@ -1078,6 +1075,62 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_ - return ret; - } - -+/* -+ * Hard-reset the bus and wait for the bus core to remove the chip. -+ * -+ * Used by the firmware-wedge recovery path on platforms where the normal -+ * power_switch(0) sequence has no effective chip-reset signal. The bus -+ * implementation triggers an asynchronous re-detect; this helper waits for -+ * the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a -+ * subsequent bes2600_switch_wifi(true) sees a clean state and can wait on -+ * the fresh probe. -+ */ -+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv) -+{ -+ int ret; -+ long status; -+ -+ if (!sbus_ops || !priv) -+ return -EINVAL; -+ -+ if (!sbus_ops->bus_reset) -+ return -EOPNOTSUPP; -+ -+ bes_info("trigger bus reset to recover wedged firmware.\n"); -+ -+ ret = sbus_ops->bus_reset(priv); -+ if (ret) { -+ bes_err("bus_reset failed: %d\n", ret); -+ return ret; -+ } -+ -+ /* -+ * The bus reset is asynchronous: the bus core schedules a rescan -+ * which removes the bound function drivers and then re-detects the -+ * chip. Wait for the remove callback to clear sbus_priv. Do not -+ * dereference 'priv' after this point -- it may already be freed. -+ */ -+ status = wait_event_timeout(bes2600_cdev.probe_done_wq, -+ !bes2600_cdev.sbus_priv, HZ * 3); -+ WARN_ON(status <= 0); -+ -+ 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; -diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h -index c627bb7c3..9edb2067a 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__ -@@ -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/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. - */ - #include "bes_fw_common.h" - #include "bes2600.h" -@@ -125,8 +122,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 +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; - --#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 */ -+/* -+ * printk wrappers for BES2600 -+ * -+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd. -+ * -+ */ - 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__) -+ -+/* -+ * 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. - */ - - #ifndef BES_NL80211_TESTMODE_MSG_H -diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c -index e7a104542..a3f954bf3 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 -@@ -467,14 +464,65 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv) - bes_devel("device enter sleep\n"); - } - -+/* -+ * Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero -+ * PM_INDICATIONs received) before we conclude the firmware does not -+ * honor host-driven PSM and switch to a sticky skip path. -+ */ -+#define BES2600_PM_UNSUPPORTED_THRESHOLD 3 -+ -+/* -+ * Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the -+ * c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode. -+ * Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode -+ * timeouts with zero PM_INDICATIONs. -+ */ -+static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv) -+{ -+ bes_warn("PSM not honored (%u timeouts), switching to skip mode\n", -+ hw_priv->bes_power.pm_consecutive_timeouts); -+ hw_priv->bes_power.pm_unsupported = true; -+ atomic_set(&hw_priv->bes_power.chip_pm_state, -+ BES2600_CHIP_PM_ACTIVE); -+ -+ /* -+ * Hold the MCU wake-flag bit permanently. Without this, every -+ * sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX) -+ * when gpio_wakup_flags == 0, drives the GPIO high and msleeps -+ * 10 ms per RX. With ~50 RX/s of beacons + multicast that's -+ * ~50%% of the bes_sdio workqueue thread blocked in msleep, -+ * which directly caps RX throughput. Holding the MCU bit makes -+ * those calls bit-only bookkeeping (gpio_wakeup = (flags == 0) -+ * stays false, no GPIO toggle, no msleep). The bit is never -+ * cleared once pm_unsupported is set because -+ * bes2600_pwr_device_enter_lp_mode is unreachable under the -+ * early-return. -+ */ -+ if (hw_priv->sbus_ops->gpio_wake) -+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, -+ GPIO_WAKE_FLAG_MCU); -+} -+ - 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; - -+ /* -+ * Sticky early-return when we've previously concluded the firmware -+ * doesn't honor PSM. Each attempt would otherwise burn 5s on a -+ * doomed wait_for_completion_timeout and produce a noisy three-line -+ * cascade in dmesg every time power_down_work retries (every -+ * ~10s). The chip stays in active mode, which on this firmware is -+ * the de-facto state anyway. -+ */ -+ if (hw_priv->bes_power.pm_unsupported) -+ return -EOPNOTSUPP; -+ - /* set interface low power configuration */ - bes2600_for_each_vif(hw_priv, priv, i) { - #ifdef P2P_MULTIVIF -@@ -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 { - 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; -+ } - - 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); -+ 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); -@@ -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 -+ * so a transient stall doesn't permanently disable PSM, and clear -+ * pm_unsupported if a previous run had latched it. -+ */ -+ hw_priv->bes_power.pm_consecutive_timeouts = 0; -+ if (hw_priv->bes_power.pm_unsupported) { -+ bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n"); -+ 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); -+ 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); - } - } - -diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h -index 1ba866c25..49477b3e2 100644 ---- a/drivers/staging/bes2600/bes_pwr.h -+++ b/drivers/staging/bes2600/bes_pwr.h -@@ -1,12 +1,9 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ - /* -- * Mac80211 driver for BES2600 device -+ * Chip-side power state machine 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_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 175ab5e39..924899b1c 100644 ---- a/drivers/staging/bes2600/bh.c -+++ b/drivers/staging/bes2600/bh.c -@@ -1,12 +1,12 @@ -+// SPDX-License-Identifier: GPL-2.0-only - /* -- * Mac80211 driver for BES2600 device -+ * Bottom-half thread 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 - #include -@@ -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" - -@@ -102,7 +101,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); -@@ -317,83 +316,6 @@ int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count) - } - #endif - --#if 0 --static struct sk_buff *bes2600_get_skb(struct bes2600_common *hw_priv, size_t len) --{ -- struct sk_buff *skb; -- size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; -- -- if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) { -- skb = dev_alloc_skb(alloc_len -- + WSM_TX_EXTRA_HEADROOM -- + 8 /* TKIP IV */ -- + 12 /* TKIP ICV + MIC */ -- - 2 /* Piggyback */); -- /* In AP mode RXed SKB can be looped back as a broadcast. -- * Here we reserve enough space for headers. */ -- skb_reserve(skb, WSM_TX_EXTRA_HEADROOM -- + 8 /* TKIP IV */ -- - WSM_RX_EXTRA_HEADROOM); -- } else { -- skb = hw_priv->skb_cache; -- hw_priv->skb_cache = NULL; -- } -- return skb; --} -- --static void bes2600_put_skb(struct bes2600_common *hw_priv, struct sk_buff *skb) --{ -- if (hw_priv->skb_cache) -- dev_kfree_skb(skb); -- else -- hw_priv->skb_cache = skb; --} -- --static int bes2600_bh_read_ctrl_reg(struct bes2600_common *hw_priv, -- u16 *ctrl_reg) --{ -- int ret; -- -- ret = bes2600_reg_read_16(hw_priv, -- ST90TDS_CONTROL_REG_ID, ctrl_reg); -- if (ret) { -- ret = bes2600_reg_read_16(hw_priv, -- ST90TDS_CONTROL_REG_ID, ctrl_reg); -- if (ret) -- bes_err("[BH] Failed to read control register.\n"); -- } -- -- return ret; --} -- --static int bes2600_device_wakeup(struct bes2600_common *hw_priv) --{ -- u16 ctrl_reg; -- int ret; -- -- bes_devel("[BH] Device wakeup.\n"); -- -- /* To force the device to be always-on, the host sets WLAN_UP to 1 */ -- ret = bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID, -- ST90TDS_CONT_WUP_BIT); -- if (WARN_ON(ret)) -- return ret; -- -- ret = bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); -- if (WARN_ON(ret)) -- return ret; -- -- /* If the device returns WLAN_RDY as 1, the device is active and will -- * remain active. */ -- if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { -- bes_devel("[BH] Device awake.\n"); -- return 1; -- } -- -- return 0; --} -- --#endif - - /* Must be called from BH thraed. */ - void bes2600_enable_powersave(struct bes2600_vif *priv, -@@ -403,475 +325,6 @@ void bes2600_enable_powersave(struct bes2600_vif *priv, - priv->powersave_enabled = enable; - } - --#if 0 --#define INTERRUPT_WORKAROUND --static int bes2600_bh(void *arg) --{ -- struct bes2600_common *hw_priv = arg; -- struct bes2600_vif *priv = NULL; -- struct sk_buff *skb_rx = NULL; -- size_t read_len = 0; -- int rx, tx, term, suspend; -- struct wsm_hdr *wsm; -- size_t wsm_len; -- int wsm_id; -- u8 wsm_seq; -- int rx_resync = 1; -- u16 ctrl_reg = 0; -- int tx_allowed; -- int pending_tx = 0; -- int tx_burst; -- int rx_burst = 0; -- long status; --#if defined(CONFIG_BES2600_WSM_DUMPS) -- size_t wsm_dump_max = -1; --#endif -- u32 dummy; -- bool powersave_enabled; -- int i; -- int vif_selected; -- -- for (;;) { -- powersave_enabled = 1; -- spin_lock(&hw_priv->vif_list_lock); -- bes2600_for_each_vif(hw_priv, priv, i) { --#ifdef P2P_MULTIVIF -- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv) --#else -- if (!priv) --#endif -- continue; -- powersave_enabled &= !!priv->powersave_enabled; -- } -- spin_unlock(&hw_priv->vif_list_lock); -- if (!hw_priv->hw_bufs_used -- && powersave_enabled -- && !hw_priv->device_can_sleep -- && !atomic_read(&hw_priv->recent_scan)) { -- status = HZ/8; -- bes_devel("[BH] No Device wakedown.\n"); --#ifndef FPGA_SETUP -- WARN_ON(bes2600_reg_write_16(hw_priv, -- ST90TDS_CONTROL_REG_ID, 0)); -- hw_priv->device_can_sleep = true; --#endif -- } else if (hw_priv->hw_bufs_used) -- /* Interrupt loss detection */ -- status = HZ/8; -- else -- status = HZ/8; -- -- /* Dummy Read for SDIO retry mechanism*/ -- if (((atomic_read(&hw_priv->bh_rx) == 0) && -- (atomic_read(&hw_priv->bh_tx) == 0))) -- bes2600_reg_read(hw_priv, ST90TDS_CONFIG_REG_ID, -- &dummy, sizeof(dummy)); --#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT) -- wsm_dump_max = hw_priv->wsm_dump_max_size; --#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */ -- --#ifdef INTERRUPT_WORKAROUND -- /* If a packet has already been txed to the device then read the -- control register for a probable interrupt miss before going -- further to wait for interrupt; if the read length is non-zero -- then it means there is some data to be received */ -- if (hw_priv->hw_bufs_used) { -- bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); -- if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) -- { -- rx = 1; -- goto test; -- } -- } --#endif -- -- status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({ -- rx = atomic_xchg(&hw_priv->bh_rx, 0); -- tx = atomic_xchg(&hw_priv->bh_tx, 0); -- term = atomic_xchg(&hw_priv->bh_term, 0); -- suspend = pending_tx ? -- 0 : atomic_read(&hw_priv->bh_suspend); -- (rx || tx || term || suspend || hw_priv->bh_error); -- }), status); -- -- if (status < 0 || term || hw_priv->bh_error) -- break; -- --#ifdef INTERRUPT_WORKAROUND -- if (!status) { -- bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); -- if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) -- { -- bes_err("MISS 1\n"); -- rx = 1; -- goto test; -- } -- } --#endif -- if (!status && hw_priv->hw_bufs_used) { -- unsigned long timestamp = jiffies; -- long timeout; -- bool pending = false; -- int i; -- -- wiphy_warn(hw_priv->hw->wiphy, "Missed interrupt?\n"); -- rx = 1; -- -- /* Get a timestamp of "oldest" frame */ -- for (i = 0; i < 4; ++i) -- pending |= bes2600_queue_get_xmit_timestamp( -- &hw_priv->tx_queue[i], -- ×tamp, -1, -- hw_priv->pending_frame_id); -- -- /* Check if frame transmission is timed out. -- * Add an extra second with respect to possible -- * interrupt loss. */ -- timeout = timestamp + -- WSM_CMD_LAST_CHANCE_TIMEOUT + -- 1 * HZ - -- jiffies; -- -- /* And terminate BH tread if the frame is "stuck" */ -- if (pending && timeout < 0) { -- //wiphy_warn(priv->hw->wiphy, -- // "Timeout waiting for TX confirm.\n"); -- bes_devel("bes2600_bh: Timeout waiting for TX confirm.\n"); -- break; -- } -- --#if defined(CONFIG_BES2600_DUMP_ON_ERROR) -- BUG_ON(1); --#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ -- } else if (!status) { -- if (!hw_priv->device_can_sleep -- && !atomic_read(&hw_priv->recent_scan)) { -- bes_devel("[BH] Device wakedown. Timeout.\n"); --#ifndef FPGA_SETUP -- WARN_ON(bes2600_reg_write_16(hw_priv, -- ST90TDS_CONTROL_REG_ID, 0)); -- hw_priv->device_can_sleep = true; --#endif -- } -- continue; -- } else if (suspend) { -- bes_devel("[BH] Device suspend.\n"); -- powersave_enabled = 1; -- spin_lock(&hw_priv->vif_list_lock); -- bes2600_for_each_vif(hw_priv, priv, i) { --#ifdef P2P_MULTIVIF -- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv) --#else -- if (!priv) --#endif -- continue; -- powersave_enabled &= !!priv->powersave_enabled; -- } -- spin_unlock(&hw_priv->vif_list_lock); -- if (powersave_enabled) { -- bes_devel("[BH] No Device wakedown. Suspend.\n"); --#ifndef FPGA_SETUP -- WARN_ON(bes2600_reg_write_16(hw_priv, -- ST90TDS_CONTROL_REG_ID, 0)); -- hw_priv->device_can_sleep = true; --#endif -- } -- -- atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED); -- wake_up(&hw_priv->bh_evt_wq); -- status = wait_event_interruptible(hw_priv->bh_wq, -- BES2600_BH_RESUME == atomic_read( -- &hw_priv->bh_suspend)); -- if (status < 0) { -- wiphy_err(hw_priv->hw->wiphy, -- "%s: Failed to wait for resume: %ld.\n", -- __func__, status); -- break; -- } -- 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); -- continue; -- } -- --test: -- tx += pending_tx; -- pending_tx = 0; -- -- if (rx) { -- size_t alloc_len; -- u8 *data; -- --#ifdef INTERRUPT_WORKAROUND -- if(!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)) --#endif -- if (WARN_ON(bes2600_bh_read_ctrl_reg( -- hw_priv, &ctrl_reg))) -- break; --rx: -- read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; -- if (!read_len) { -- rx_burst = 0; -- goto tx; -- } -- -- if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || -- (read_len > EFFECTIVE_BUF_SIZE))) { -- bes_devel("Invalid read len: %d", read_len); -- break; -- } -- -- /* Add SIZE of PIGGYBACK reg (CONTROL Reg) -- * to the NEXT Message length + 2 Bytes for SKB */ -- read_len = read_len + 2; -- --#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) -- alloc_len = hw_priv->sbus_ops->align_size( -- hw_priv->sbus_priv, read_len); --#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ -- /* Platform's SDIO workaround */ -- alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); -- if (read_len & (SDIO_BLOCK_SIZE - 1)) -- alloc_len += SDIO_BLOCK_SIZE; --#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ -- -- /* Check if not exceeding BES2600 capabilities */ -- if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) -- bes_devel("Read aligned len: %d\n", alloc_len); -- -- skb_rx = bes2600_get_skb(hw_priv, alloc_len); -- if (WARN_ON(!skb_rx)) -- break; -- -- skb_trim(skb_rx, 0); -- skb_put(skb_rx, read_len); -- data = skb_rx->data; -- if (WARN_ON(!data)) -- break; -- -- if (WARN_ON(bes2600_data_read(hw_priv, data, alloc_len))) -- break; -- -- /* Piggyback */ -- ctrl_reg = __le16_to_cpu( -- ((__le16 *)data)[alloc_len / 2 - 1]); -- -- wsm = (struct wsm_hdr *)data; -- wsm_len = __le32_to_cpu(wsm->len); -- if (WARN_ON(wsm_len > read_len)) -- break; -- --#if defined(CONFIG_BES2600_WSM_DUMPS) -- if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { -- u16 msgid, ifid; -- u16 *p = (u16 *)data; -- msgid = (*(p + 1)) & 0xC3F; -- ifid = (*(p + 1)) >> 6; -- ifid &= 0xF; -- bes_devel("[DUMP] <<< msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p); -- print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, data, min(wsm_len, wsm_dump_max)); -- } --#endif /* CONFIG_BES2600_WSM_DUMPS */ -- -- wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; -- wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; -- -- skb_trim(skb_rx, wsm_len); -- -- if (unlikely(wsm_id == 0x0800)) { -- wsm_handle_exception(hw_priv, -- &data[sizeof(*wsm)], -- wsm_len - sizeof(*wsm)); -- break; -- } else if (unlikely(!rx_resync)) { -- if (WARN_ON(wsm_seq != hw_priv->wsm_rx_seq)) { --#if defined(CONFIG_BES2600_DUMP_ON_ERROR) -- BUG_ON(1); --#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ -- break; -- } -- } -- hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7; -- rx_resync = 0; -- -- if (wsm_id & 0x0400) { -- int rc = wsm_release_tx_buffer(hw_priv, 1); -- if (WARN_ON(rc < 0)) -- break; -- else if (rc > 0) -- tx = 1; -- } -- -- /* bes2600_wsm_rx takes care on SKB livetime */ -- if (WARN_ON(wsm_handle_rx(hw_priv, wsm_id, wsm, -- &skb_rx))) -- break; -- -- if (skb_rx) { -- bes2600_put_skb(hw_priv, skb_rx); -- skb_rx = NULL; -- } -- -- read_len = 0; -- -- if (rx_burst) { -- bes2600_debug_rx_burst(hw_priv); -- --rx_burst; -- goto rx; -- } -- } -- --tx: -- BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); -- tx_burst = hw_priv->wsm_caps.numInpChBufs - -- hw_priv->hw_bufs_used; -- tx_allowed = tx_burst > 0; -- if (tx && tx_allowed) { -- size_t tx_len; -- u8 *data; -- int ret; -- -- if (hw_priv->device_can_sleep) { -- ret = bes2600_device_wakeup(hw_priv); -- if (WARN_ON(ret < 0)) -- break; -- else if (ret) -- hw_priv->device_can_sleep = false; -- else { -- /* Wait for "awake" interrupt */ -- pending_tx = tx; -- continue; -- } -- } -- -- wsm_alloc_tx_buffer(hw_priv); -- ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst, -- &vif_selected); -- if (ret <= 0) { -- wsm_release_tx_buffer(hw_priv, 1); -- if (WARN_ON(ret < 0)) -- break; -- } else { -- wsm = (struct wsm_hdr *)data; -- BUG_ON(tx_len < sizeof(*wsm)); -- BUG_ON(__le32_to_cpu(wsm->len) != tx_len); -- --#if 0 /* count is not implemented */ -- if (ret > 1) -- atomic_add(1, &hw_priv->bh_tx); --#else -- atomic_add(1, &hw_priv->bh_tx); --#endif -- --#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) -- if (tx_len <= 8) -- tx_len = 16; -- tx_len = hw_priv->sbus_ops->align_size( -- hw_priv->sbus_priv, tx_len); --#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ -- /* HACK!!! Platform limitation. -- * It is also supported by upper layer: -- * there is always enough space at the -- * end of the buffer. */ -- if (tx_len & (SDIO_BLOCK_SIZE - 1)) { -- tx_len &= ~(SDIO_BLOCK_SIZE - 1); -- tx_len += SDIO_BLOCK_SIZE; -- } --#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ -- -- /* Check if not exceeding BES2600 -- capabilities */ -- if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) -- bes_devel("Write aligned len: %d\n", tx_len); -- -- wsm->id &= __cpu_to_le32( -- ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); -- wsm->id |= cpu_to_le32(WSM_TX_SEQ( -- hw_priv->wsm_tx_seq)); -- -- if (WARN_ON(bes2600_data_write(hw_priv, -- data, tx_len))) { -- wsm_release_tx_buffer(hw_priv, 1); -- break; -- } -- -- if (vif_selected != -1) { -- hw_priv->hw_bufs_used_vif[ -- vif_selected]++; -- } -- --#if defined(CONFIG_BES2600_WSM_DUMPS) -- if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { -- u16 msgid, ifid; -- u16 *p = (u16 *)data; -- msgid = (*(p + 1)) & 0x3F; -- ifid = (*(p + 1)) >> 6; -- ifid &= 0xF; -- if (msgid == 0x0006) -- bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d MIB 0x%.4X\n", msgid, ifid, *p, *(p + 2)); -- else -- bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p); -- print_hex_dump(KERN_DEBUG, "--> ", DUMP_PREFIX_NONE, data, min(__le32_to_cpu(wsm->len), wsm_dump_max)); -- } --#endif /* CONFIG_BES2600_WSM_DUMPS */ -- -- wsm_txed(hw_priv, data); -- hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) -- & WSM_TX_SEQ_MAX; -- -- if (tx_burst > 1) { -- bes2600_debug_tx_burst(hw_priv); -- ++rx_burst; -- goto tx; -- } -- } -- } -- -- if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) -- goto rx; -- } -- -- if (skb_rx) { -- bes2600_put_skb(hw_priv, skb_rx); -- skb_rx = NULL; -- } -- -- -- if (!term) { -- bes_devel("[BH] Fatal error, exitting.\n"); --#if defined(CONFIG_BES2600_DUMP_ON_ERROR) -- BUG_ON(1); --#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ -- hw_priv->bh_error = 1; --#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) -- spin_lock(&hw_priv->vif_list_lock); -- bes2600_for_each_vif(hw_priv, priv, i) { -- if (!priv) -- continue; -- ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); -- } -- spin_unlock(&hw_priv->vif_list_lock); -- bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ); --#endif -- /* TODO: schedule_work(recovery) */ --#ifndef HAS_PUT_TASK_STRUCT -- /* The only reason of having this stupid code here is -- * that __put_task_struct is not exported by kernel. */ -- for (;;) { -- int status = wait_event_interruptible(hw_priv->bh_wq, ({ -- term = atomic_xchg(&hw_priv->bh_term, 0); -- (term); -- })); -- -- if (status || term) -- break; -- } --#endif -- } -- return 0; --} --#else - - extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg); - -@@ -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; - -- BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); -+ /* -+ * Patch H: BUG_ON -> WARN_ON_ONCE in the steady-state -+ * hot path. The original BUG_ON ran every bh-loop -+ * iteration; tripping it on a bookkeeping bug locks -+ * the kernel up during normal operation, which is -+ * the wrong response. WARN_ON_ONCE surfaces the -+ * issue without taking the system down. -+ */ -+ WARN_ON_ONCE(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); - tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used; - tx_allowed = tx_burst > 0; - -@@ -1522,18 +1098,19 @@ static int bes2600_bh(void *arg) - goto tx; - - done: -- /* Re-enable device interrupts */ -- //hw_priv->sbus_ops->lock(hw_priv->sbus_priv); -- //__bes2600_irq_enable(1); -- //hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); -- asm volatile ("nop"); -+ /* -+ * Patch H: dropped the dead `__bes2600_irq_enable(1)` / -+ * `asm volatile("nop")` placeholder that used to sit here. -+ * `__bes2600_irq_enable()` is a stub that returns 0 on -+ * bes2600 silicon — the IRQ is managed by sdio_claim_irq -+ * and chip-side firmware, not by a driver-side enable bit. -+ * (cw1200 inherited the function from a different chip -+ * shape; bes2600 kept the stub but the call sites are -+ * meaningless.) -+ */ -+ ; - } - -- /* Explicitly disable device interrupts */ -- hw_priv->sbus_ops->lock(hw_priv->sbus_priv); -- __bes2600_irq_enable(0); -- hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); -- - if (!term) { - bes_err("[BH] Fatal error, exiting.\n"); - sdio_work_debug(hw_priv->sbus_priv); -@@ -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 ea88210e8..1a63e4f00 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 -@@ -324,7 +324,10 @@ int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len) - } - #endif - --int __bes2600_irq_enable(int enable) --{ -- return 0; --} -+/* -+ * Patch H: __bes2600_irq_enable stub removed. It was a no-op -+ * (always returned 0) inherited from cw1200 where the analogous -+ * function manipulates the chip's IRQ-enable register. bes2600 -+ * 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 1f2c0cda7..41930847f 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 -@@ -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); - --/* This MUST be wrapped with hwbus_ops->lock/unlock! */ --int __bes2600_irq_enable(int enable); -+/* 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.53.0 - diff --git a/danctnix-besser-pkgbuild/kernel/0001-bes2600-defer-scan-and-soften-WARN-on-firmware-rejec.patch b/danctnix-besser-pkgbuild/kernel/0001-bes2600-defer-scan-and-soften-WARN-on-firmware-rejec.patch new file mode 100644 index 000000000..a8da6e16b --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0001-bes2600-defer-scan-and-soften-WARN-on-firmware-rejec.patch @@ -0,0 +1,226 @@ +From 4fec8b2ecc006ab4aff589fc6742e251d6af96f0 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Fri, 24 Apr 2026 21:31:45 +0200 +Subject: [PATCH 01/20] 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 + #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); +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; + }; + + 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) + { + 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0002-bes2600-widen-scan-defer-backoff-to-30s-and-decay-co.patch b/danctnix-besser-pkgbuild/kernel/0002-bes2600-widen-scan-defer-backoff-to-30s-and-decay-co.patch new file mode 100644 index 000000000..c73dce4b1 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0002-bes2600-widen-scan-defer-backoff-to-30s-and-decay-co.patch @@ -0,0 +1,109 @@ +From bdb0450bdf6f51d91ee0ca850048d65d81864e77 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Tue, 28 Apr 2026 14:32:18 +0200 +Subject: [PATCH 02/20] 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; + #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 + diff --git a/danctnix-besser-pkgbuild/kernel/0003-bes2600-recover-wedged-firmware-via-mmc_hw_reset-on-.patch b/danctnix-besser-pkgbuild/kernel/0003-bes2600-recover-wedged-firmware-via-mmc_hw_reset-on-.patch new file mode 100644 index 000000000..a32f3e91e --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0003-bes2600-recover-wedged-firmware-via-mmc_hw_reset-on-.patch @@ -0,0 +1,251 @@ +From e0f664cbc9e23098da3f119f2f4cb399279c129b Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Sun, 26 Apr 2026 22:31:58 +0200 +Subject: [PATCH 03/20] 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 13d4ff1..8552b12 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 +@@ -1756,6 +1757,33 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) + sdio_work_debug(self); + } + ++/* ++ * Trigger an SDIO bus reset via mmc_hw_reset(). ++ * ++ * With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and ++ * func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the ++ * remove-and-rescan path: it 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. ++ * ++ * In both cases the chip ends up in a freshly reset state, which is the ++ * goal of the recovery path. ++ * ++ * mmc_hw_reset() must be called without holding the SDIO host claim -- ++ * the multi-func remove-and-rescan path acquires the host claim via the ++ * mmc workqueue. ++ */ ++static int bes2600_sdio_bus_reset(struct sbus_priv *self) ++{ ++ if (!self || !self->func || !self->func->card) ++ return -EINVAL; ++ ++ return mmc_hw_reset(self->func->card); ++} ++ + static bool bes2600_sdio_wakeup_source(struct sbus_priv *self) + { + struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data(); +@@ -1794,6 +1822,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, ++ .bus_reset = bes2600_sdio_bus_reset, + }; + + static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv) +diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c +index f89dcb8..a74bf60 100644 +--- a/drivers/staging/bes2600/bes_chardev.c ++++ b/drivers/staging/bes2600/bes_chardev.c +@@ -1078,6 +1078,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_ + return ret; + } + ++/* ++ * Hard-reset the bus and wait for the bus core to remove the chip. ++ * ++ * Used by the firmware-wedge recovery path on platforms where the normal ++ * power_switch(0) sequence has no effective chip-reset signal. The bus ++ * implementation triggers an asynchronous re-detect; this helper waits for ++ * the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a ++ * subsequent bes2600_switch_wifi(true) sees a clean state and can wait on ++ * the fresh probe. ++ */ ++int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv) ++{ ++ int ret; ++ long status; ++ ++ if (!sbus_ops || !priv) ++ return -EINVAL; ++ ++ if (!sbus_ops->bus_reset) ++ return -EOPNOTSUPP; ++ ++ bes_info("trigger bus reset to recover wedged firmware.\n"); ++ ++ ret = sbus_ops->bus_reset(priv); ++ if (ret) { ++ bes_err("bus_reset failed: %d\n", ret); ++ return ret; ++ } ++ ++ /* ++ * The bus reset is asynchronous: the bus core schedules a rescan ++ * which removes the bound function drivers and then re-detects the ++ * chip. Wait for the remove callback to clear sbus_priv. Do not ++ * dereference 'priv' after this point -- it may already be freed. ++ */ ++ status = wait_event_timeout(bes2600_cdev.probe_done_wq, ++ !bes2600_cdev.sbus_priv, HZ * 3); ++ WARN_ON(status <= 0); ++ ++ return 0; ++} ++ + bool bes2600_chrdev_is_wifi_opened(void) + { + bool wifi_opened = false; +@@ -1184,8 +1226,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 +--- 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); + /* 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); + 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0004-bes2600-gate-PM-indication-completion-on-pending-req.patch b/danctnix-besser-pkgbuild/kernel/0004-bes2600-gate-PM-indication-completion-on-pending-req.patch new file mode 100644 index 000000000..3042c6086 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0004-bes2600-gate-PM-indication-completion-on-pending-req.patch @@ -0,0 +1,261 @@ +From 7c4ad3b1d6614347dd7d9df87875f899acdffa79 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Tue, 28 Apr 2026 15:05:27 +0200 +Subject: [PATCH 04/20] 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 | 106 ++++++++++++++++++++++++++++++++++++++++++---- + bes2600/bes_pwr.h | 15 +++++++ + 2 files changed, 112 insertions(+), 9 deletions(-) + +diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c +index e7a1045..4c6bd78 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; + +@@ -523,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); +@@ -532,18 +543,75 @@ 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_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++; ++ } ++ } + } else { + 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->sbus_ops->gpio_sleep) ++ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, ++ GPIO_WAKE_FLAG_MCU); ++ ret = -ETIMEDOUT; ++ } + + return ret; + } +@@ -819,6 +887,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); +@@ -1199,9 +1268,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) + { +- 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); + } + } + +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, + }; + ++/* ++ * 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 +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 + diff --git a/danctnix-besser-pkgbuild/kernel/0005-bes2600-short-circuit-wake-handshake-when-chip-is-co.patch b/danctnix-besser-pkgbuild/kernel/0005-bes2600-short-circuit-wake-handshake-when-chip-is-co.patch new file mode 100644 index 000000000..94520f864 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0005-bes2600-short-circuit-wake-handshake-when-chip-is-co.patch @@ -0,0 +1,190 @@ +From 51d46a2e2597ade0786b7af49bf1b687490f9dc9 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Tue, 28 Apr 2026 15:23:34 +0200 +Subject: [PATCH 05/20] 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 8552b12..deefba9 100644 +--- a/drivers/staging/bes2600/bes2600_sdio.c ++++ b/drivers/staging/bes2600/bes2600_sdio.c +@@ -1368,7 +1368,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; + } +@@ -1400,7 +1407,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; + } +diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c +index 4c6bd78..5798e8a 100644 +--- a/drivers/staging/bes2600/bes_pwr.c ++++ b/drivers/staging/bes2600/bes_pwr.c +@@ -619,19 +619,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 + diff --git a/danctnix-besser-pkgbuild/kernel/0006-bes2600-self-detect-when-firmware-does-not-honor-PSM.patch b/danctnix-besser-pkgbuild/kernel/0006-bes2600-self-detect-when-firmware-does-not-honor-PSM.patch new file mode 100644 index 000000000..798859f4a --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0006-bes2600-self-detect-when-firmware-does-not-honor-PSM.patch @@ -0,0 +1,209 @@ +From 9a0a4c0a4687cc0a70a34be57a74a0fbc327b066 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Tue, 28 Apr 2026 16:54:06 +0200 +Subject: [PATCH 06/20] 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 5798e8a..ec91485 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) + bes_devel("device enter sleep\n"); + } + ++/* ++ * Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero ++ * PM_INDICATIONs received) before we conclude the firmware does not ++ * honor host-driven PSM and switch to a sticky skip path. ++ */ ++#define BES2600_PM_UNSUPPORTED_THRESHOLD 3 ++ ++/* ++ * Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the ++ * c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode. ++ * Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode ++ * timeouts with zero PM_INDICATIONs. ++ */ ++static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv) ++{ ++ bes_warn("PSM not honored (%u timeouts), switching to skip mode\n", ++ hw_priv->bes_power.pm_consecutive_timeouts); ++ hw_priv->bes_power.pm_unsupported = true; ++ atomic_set(&hw_priv->bes_power.chip_pm_state, ++ BES2600_CHIP_PM_ACTIVE); ++ ++ /* ++ * Hold the MCU wake-flag bit permanently. Without this, every ++ * sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX) ++ * when gpio_wakup_flags == 0, drives the GPIO high and msleeps ++ * 10 ms per RX. With ~50 RX/s of beacons + multicast that's ++ * ~50%% of the bes_sdio workqueue thread blocked in msleep, ++ * which directly caps RX throughput. Holding the MCU bit makes ++ * those calls bit-only bookkeeping (gpio_wakeup = (flags == 0) ++ * stays false, no GPIO toggle, no msleep). The bit is never ++ * cleared once pm_unsupported is set because ++ * bes2600_pwr_device_enter_lp_mode is unreachable under the ++ * early-return. ++ */ ++ if (hw_priv->sbus_ops->gpio_wake) ++ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, ++ GPIO_WAKE_FLAG_MCU); ++} ++ + 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) + char ip_str[20]; + unsigned long status = 0; + ++ /* ++ * Sticky early-return when we've previously concluded the firmware ++ * doesn't honor PSM. Each attempt would otherwise burn 5s on a ++ * doomed wait_for_completion_timeout and produce a noisy three-line ++ * cascade in dmesg every time power_down_work retries (every ++ * ~10s). The chip stays in active mode, which on this firmware is ++ * the de-facto state anyway. ++ */ ++ if (hw_priv->bes_power.pm_unsupported) ++ return -EOPNOTSUPP; ++ + /* set interface low power configuration */ + bes2600_for_each_vif(hw_priv, priv, i) { + #ifdef P2P_MULTIVIF +@@ -569,6 +619,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++; ++ if (++hw_priv->bes_power.pm_consecutive_timeouts ++ >= BES2600_PM_UNSUPPORTED_THRESHOLD) ++ bes2600_pwr_latch_pm_unsupported(hw_priv); + } + } + } else { +@@ -607,7 +660,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) ++ 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; +@@ -930,6 +984,8 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv) + 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); ++ 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); +@@ -1319,6 +1375,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. + */ ++ /* ++ * Any PM indication, whatever its psmode, proves the firmware is ++ * actually emitting them. Reset the consecutive-timeout counter ++ * so a transient stall doesn't permanently disable PSM, and clear ++ * pm_unsupported if a previous run had latched it. ++ */ ++ hw_priv->bes_power.pm_consecutive_timeouts = 0; ++ if (hw_priv->bes_power.pm_unsupported) { ++ bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n"); ++ 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0007-bes2600-handle-multi-function-SDIO-cards-in-mmc_hw_r.patch b/danctnix-besser-pkgbuild/kernel/0007-bes2600-handle-multi-function-SDIO-cards-in-mmc_hw_r.patch new file mode 100644 index 000000000..d4113665a --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0007-bes2600-handle-multi-function-SDIO-cards-in-mmc_hw_r.patch @@ -0,0 +1,83 @@ +From d48f2ae73ca17761d7a64aa645b4629641c8be5d Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Tue, 28 Apr 2026 21:37:37 +0200 +Subject: [PATCH 07/20] 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 deefba9..c0b67b0 100644 +--- a/drivers/staging/bes2600/bes2600_sdio.c ++++ b/drivers/staging/bes2600/bes2600_sdio.c +@@ -1789,10 +1789,32 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self) + */ + 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) +-- +2.54.0 + diff --git a/danctnix-besser-pkgbuild/kernel/0008-bes2600-pre-empt-AP-deauth-6-with-mac80211-reassoc-o.patch b/danctnix-besser-pkgbuild/kernel/0008-bes2600-pre-empt-AP-deauth-6-with-mac80211-reassoc-o.patch new file mode 100644 index 000000000..3bba35d55 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0008-bes2600-pre-empt-AP-deauth-6-with-mac80211-reassoc-o.patch @@ -0,0 +1,221 @@ +From 3b4239ad2b7976eab04ccae748e36fb78422874f Mon Sep 17 00:00:00 2001 +From: "Claude (noether)" +Date: Wed, 6 May 2026 19:50:52 +0200 +Subject: [PATCH 08/20] 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 ca1c77c..ee9fd81 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0009-bes2600-bus_reset-on-connection-loss-storm-to-dodge-.patch b/danctnix-besser-pkgbuild/kernel/0009-bes2600-bus_reset-on-connection-loss-storm-to-dodge-.patch new file mode 100644 index 000000000..46c446773 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0009-bes2600-bus_reset-on-connection-loss-storm-to-dodge-.patch @@ -0,0 +1,279 @@ +From a7e232738d50c797bb2be1e71cbe1578a1d46dda Mon Sep 17 00:00:00 2001 +From: "Claude (noether)" +Date: Thu, 7 May 2026 11:30:09 +0200 +Subject: [PATCH 09/20] 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 a74bf60..df6b911 100644 +--- a/drivers/staging/bes2600/bes_chardev.c ++++ b/drivers/staging/bes2600/bes_chardev.c +@@ -1120,6 +1120,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 3b0b7a3..000329c 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 ee9fd81..ec67d38 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0010-bes2600-replace-a-set-of-atomic_add.patch b/danctnix-besser-pkgbuild/kernel/0010-bes2600-replace-a-set-of-atomic_add.patch new file mode 100644 index 000000000..8445842bb --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0010-bes2600-replace-a-set-of-atomic_add.patch @@ -0,0 +1,92 @@ +From d9268b433abc035c6e3f63a26191df5855b09b61 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Thu, 7 May 2026 21:19:49 +0200 +Subject: [PATCH 10/20] 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0011-bes2600-fix-missing-destroy_workqueue-on-error-in-in.patch b/danctnix-besser-pkgbuild/kernel/0011-bes2600-fix-missing-destroy_workqueue-on-error-in-in.patch new file mode 100644 index 000000000..ebe9b7672 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0011-bes2600-fix-missing-destroy_workqueue-on-error-in-in.patch @@ -0,0 +1,58 @@ +From 77f966df25d24a2fb85d235bcaa6248ddc394822 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Thu, 7 May 2026 21:20:46 +0200 +Subject: [PATCH 11/20] 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 000329c..f9f5f3b 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0012-bes2600-fix-concurrency-UAF-in-bes2600_hw_scan-and-s.patch b/danctnix-besser-pkgbuild/kernel/0012-bes2600-fix-concurrency-UAF-in-bes2600_hw_scan-and-s.patch new file mode 100644 index 000000000..1f83e1da0 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0012-bes2600-fix-concurrency-UAF-in-bes2600_hw_scan-and-s.patch @@ -0,0 +1,144 @@ +From 9e38ac552302b6a6bbbeeb27339b8f8ca190110f Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Thu, 7 May 2026 21:24:01 +0200 +Subject: [PATCH 12/20] 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0013-bes2600-drop-sdio_rx_work-relay-IRQ-bh-direct-no-rel.patch b/danctnix-besser-pkgbuild/kernel/0013-bes2600-drop-sdio_rx_work-relay-IRQ-bh-direct-no-rel.patch new file mode 100644 index 000000000..69b4e0cff --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0013-bes2600-drop-sdio_rx_work-relay-IRQ-bh-direct-no-rel.patch @@ -0,0 +1,540 @@ +From 73191b7bc1b607d0331b590c0c54c848c078a088 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Thu, 7 May 2026 22:34:11 +0200 +Subject: [PATCH 13/20] =?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 c0b67b0..ba1e1c3 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 "hwio.h" +@@ -71,10 +72,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; +@@ -410,10 +413,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); +@@ -810,10 +822,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 +@@ -824,17 +841,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); + +@@ -889,6 +920,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; +@@ -896,22 +931,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) +@@ -919,26 +948,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 + +@@ -1175,7 +1189,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); +@@ -1226,12 +1247,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); +@@ -1560,22 +1580,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; +@@ -1843,7 +1856,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, +@@ -1863,9 +1877,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); + } +@@ -2202,8 +2222,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; ++ } ++ } ++ ++ /* 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 */ + + #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 + diff --git a/danctnix-besser-pkgbuild/kernel/0014-bes2600-Patch-G-restore-SPDX-identifiers-ST-Ericsson.patch b/danctnix-besser-pkgbuild/kernel/0014-bes2600-Patch-G-restore-SPDX-identifiers-ST-Ericsson.patch new file mode 100644 index 000000000..402ba818c --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0014-bes2600-Patch-G-restore-SPDX-identifiers-ST-Ericsson.patch @@ -0,0 +1,1154 @@ +From a02f8b7629546d8d1c48f0f51076990eec444507 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Fri, 8 May 2026 00:03:50 +0200 +Subject: [PATCH 14/20] =?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 dc5d3da..dcf15b4 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 3835b0d..7cebf78 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 ba1e1c3..b943882 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 df6b911..801e4bf 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 133c945..fa04f79 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 605cea8..eda463b 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 ec91485..1735749 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 +--- a/drivers/staging/bes2600/bes_pwr.h ++++ b/drivers/staging/bes2600/bes_pwr.h +@@ -1,12 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ + /* +- * Mac80211 driver for BES2600 device ++ * Chip-side power state machine 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_PWR_H__ + #define __BES_PWR_H__ +diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c +index febcaf4..61f6991 100644 +--- a/drivers/staging/bes2600/bh.c ++++ b/drivers/staging/bes2600/bh.c +@@ -1,12 +1,12 @@ ++// SPDX-License-Identifier: GPL-2.0-only + /* +- * Mac80211 driver for BES2600 device ++ * Bottom-half thread 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 + #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. + */ + + #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 f9f5f3b..02a79c0 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 ec67d38..2ba9a0a 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 0673131..4108bbb 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0015-bes2600-Patch-D-atomicize-ba_lock-counters-drop-the-.patch b/danctnix-besser-pkgbuild/kernel/0015-bes2600-Patch-D-atomicize-ba_lock-counters-drop-the-.patch new file mode 100644 index 000000000..d6f4482b1 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0015-bes2600-Patch-D-atomicize-ba_lock-counters-drop-the-.patch @@ -0,0 +1,313 @@ +From 93f2aab65682d0ea1938607e7426257e9758d6c0 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Fri, 8 May 2026 00:17:46 +0200 +Subject: [PATCH 15/20] =?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 02a79c0..76ca668 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 2ba9a0a..412b2c4 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0016-bes2600-Patch-E-skip-ps_state_lock-when-PSM-known-di.patch b/danctnix-besser-pkgbuild/kernel/0016-bes2600-Patch-E-skip-ps_state_lock-when-PSM-known-di.patch new file mode 100644 index 000000000..d409002ac --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0016-bes2600-Patch-E-skip-ps_state_lock-when-PSM-known-di.patch @@ -0,0 +1,83 @@ +From dd01be0162846b61c6695887ce9e421b69e099d4 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Fri, 8 May 2026 00:22:14 +0200 +Subject: [PATCH 16/20] =?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 + diff --git a/danctnix-besser-pkgbuild/kernel/0017-bes2600-Patch-C2-replace-ieee80211_rx_irqsafe-with-i.patch b/danctnix-besser-pkgbuild/kernel/0017-bes2600-Patch-C2-replace-ieee80211_rx_irqsafe-with-i.patch new file mode 100644 index 000000000..e19c60a5b --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0017-bes2600-Patch-C2-replace-ieee80211_rx_irqsafe-with-i.patch @@ -0,0 +1,157 @@ +From 447240cbe8dee9d865683508f7d814e7ffe1d970 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Fri, 8 May 2026 06:40:00 +0200 +Subject: [PATCH 17/20] =?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/sta.c | 2 +- + bes2600/txrx.c | 6 +++--- + bes2600/wsm.c | 2 +- + 4 files changed, 18 insertions(+), 7 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); + #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/sta.c b/drivers/staging/bes2600/sta.c +index 412b2c4..476d875 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 + diff --git a/danctnix-besser-pkgbuild/kernel/0018-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blo.patch b/danctnix-besser-pkgbuild/kernel/0018-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blo.patch new file mode 100644 index 000000000..c95b29f33 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0018-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blo.patch @@ -0,0 +1,725 @@ +From dc13f5d64fd4267bd85bef5fbf945b64f21a1c93 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Fri, 8 May 2026 08:23:20 +0200 +Subject: [PATCH 18/20] =?UTF-8?q?bes2600:=20Patch=20H=20=E2=80=94=20bh.c?= + =?UTF-8?q?=20hygiene=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) + } + #endif + +-#if 0 +-static struct sk_buff *bes2600_get_skb(struct bes2600_common *hw_priv, size_t len) +-{ +- struct sk_buff *skb; +- size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; +- +- if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) { +- skb = dev_alloc_skb(alloc_len +- + WSM_TX_EXTRA_HEADROOM +- + 8 /* TKIP IV */ +- + 12 /* TKIP ICV + MIC */ +- - 2 /* Piggyback */); +- /* In AP mode RXed SKB can be looped back as a broadcast. +- * Here we reserve enough space for headers. */ +- skb_reserve(skb, WSM_TX_EXTRA_HEADROOM +- + 8 /* TKIP IV */ +- - WSM_RX_EXTRA_HEADROOM); +- } else { +- skb = hw_priv->skb_cache; +- hw_priv->skb_cache = NULL; +- } +- return skb; +-} +- +-static void bes2600_put_skb(struct bes2600_common *hw_priv, struct sk_buff *skb) +-{ +- if (hw_priv->skb_cache) +- dev_kfree_skb(skb); +- else +- hw_priv->skb_cache = skb; +-} +- +-static int bes2600_bh_read_ctrl_reg(struct bes2600_common *hw_priv, +- u16 *ctrl_reg) +-{ +- int ret; +- +- ret = bes2600_reg_read_16(hw_priv, +- ST90TDS_CONTROL_REG_ID, ctrl_reg); +- if (ret) { +- ret = bes2600_reg_read_16(hw_priv, +- ST90TDS_CONTROL_REG_ID, ctrl_reg); +- if (ret) +- bes_err("[BH] Failed to read control register.\n"); +- } +- +- return ret; +-} +- +-static int bes2600_device_wakeup(struct bes2600_common *hw_priv) +-{ +- u16 ctrl_reg; +- int ret; +- +- bes_devel("[BH] Device wakeup.\n"); +- +- /* To force the device to be always-on, the host sets WLAN_UP to 1 */ +- ret = bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID, +- ST90TDS_CONT_WUP_BIT); +- if (WARN_ON(ret)) +- return ret; +- +- ret = bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); +- if (WARN_ON(ret)) +- return ret; +- +- /* If the device returns WLAN_RDY as 1, the device is active and will +- * remain active. */ +- if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { +- bes_devel("[BH] Device awake.\n"); +- return 1; +- } +- +- return 0; +-} +- +-#endif + + /* 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, + priv->powersave_enabled = enable; + } + +-#if 0 +-#define INTERRUPT_WORKAROUND +-static int bes2600_bh(void *arg) +-{ +- struct bes2600_common *hw_priv = arg; +- struct bes2600_vif *priv = NULL; +- struct sk_buff *skb_rx = NULL; +- size_t read_len = 0; +- int rx, tx, term, suspend; +- struct wsm_hdr *wsm; +- size_t wsm_len; +- int wsm_id; +- u8 wsm_seq; +- int rx_resync = 1; +- u16 ctrl_reg = 0; +- int tx_allowed; +- int pending_tx = 0; +- int tx_burst; +- int rx_burst = 0; +- long status; +-#if defined(CONFIG_BES2600_WSM_DUMPS) +- size_t wsm_dump_max = -1; +-#endif +- u32 dummy; +- bool powersave_enabled; +- int i; +- int vif_selected; +- +- for (;;) { +- powersave_enabled = 1; +- spin_lock(&hw_priv->vif_list_lock); +- bes2600_for_each_vif(hw_priv, priv, i) { +-#ifdef P2P_MULTIVIF +- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv) +-#else +- if (!priv) +-#endif +- continue; +- powersave_enabled &= !!priv->powersave_enabled; +- } +- spin_unlock(&hw_priv->vif_list_lock); +- if (!hw_priv->hw_bufs_used +- && powersave_enabled +- && !hw_priv->device_can_sleep +- && !atomic_read(&hw_priv->recent_scan)) { +- status = HZ/8; +- bes_devel("[BH] No Device wakedown.\n"); +-#ifndef FPGA_SETUP +- WARN_ON(bes2600_reg_write_16(hw_priv, +- ST90TDS_CONTROL_REG_ID, 0)); +- hw_priv->device_can_sleep = true; +-#endif +- } else if (hw_priv->hw_bufs_used) +- /* Interrupt loss detection */ +- status = HZ/8; +- else +- status = HZ/8; +- +- /* Dummy Read for SDIO retry mechanism*/ +- if (((atomic_read(&hw_priv->bh_rx) == 0) && +- (atomic_read(&hw_priv->bh_tx) == 0))) +- bes2600_reg_read(hw_priv, ST90TDS_CONFIG_REG_ID, +- &dummy, sizeof(dummy)); +-#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT) +- wsm_dump_max = hw_priv->wsm_dump_max_size; +-#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */ +- +-#ifdef INTERRUPT_WORKAROUND +- /* If a packet has already been txed to the device then read the +- control register for a probable interrupt miss before going +- further to wait for interrupt; if the read length is non-zero +- then it means there is some data to be received */ +- if (hw_priv->hw_bufs_used) { +- bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); +- if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) +- { +- rx = 1; +- goto test; +- } +- } +-#endif +- +- status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({ +- rx = atomic_xchg(&hw_priv->bh_rx, 0); +- tx = atomic_xchg(&hw_priv->bh_tx, 0); +- term = atomic_xchg(&hw_priv->bh_term, 0); +- suspend = pending_tx ? +- 0 : atomic_read(&hw_priv->bh_suspend); +- (rx || tx || term || suspend || hw_priv->bh_error); +- }), status); +- +- if (status < 0 || term || hw_priv->bh_error) +- break; +- +-#ifdef INTERRUPT_WORKAROUND +- if (!status) { +- bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg); +- if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) +- { +- bes_err("MISS 1\n"); +- rx = 1; +- goto test; +- } +- } +-#endif +- if (!status && hw_priv->hw_bufs_used) { +- unsigned long timestamp = jiffies; +- long timeout; +- bool pending = false; +- int i; +- +- wiphy_warn(hw_priv->hw->wiphy, "Missed interrupt?\n"); +- rx = 1; +- +- /* Get a timestamp of "oldest" frame */ +- for (i = 0; i < 4; ++i) +- pending |= bes2600_queue_get_xmit_timestamp( +- &hw_priv->tx_queue[i], +- ×tamp, -1, +- hw_priv->pending_frame_id); +- +- /* Check if frame transmission is timed out. +- * Add an extra second with respect to possible +- * interrupt loss. */ +- timeout = timestamp + +- WSM_CMD_LAST_CHANCE_TIMEOUT + +- 1 * HZ - +- jiffies; +- +- /* And terminate BH tread if the frame is "stuck" */ +- if (pending && timeout < 0) { +- //wiphy_warn(priv->hw->wiphy, +- // "Timeout waiting for TX confirm.\n"); +- bes_devel("bes2600_bh: Timeout waiting for TX confirm.\n"); +- break; +- } +- +-#if defined(CONFIG_BES2600_DUMP_ON_ERROR) +- BUG_ON(1); +-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ +- } else if (!status) { +- if (!hw_priv->device_can_sleep +- && !atomic_read(&hw_priv->recent_scan)) { +- bes_devel("[BH] Device wakedown. Timeout.\n"); +-#ifndef FPGA_SETUP +- WARN_ON(bes2600_reg_write_16(hw_priv, +- ST90TDS_CONTROL_REG_ID, 0)); +- hw_priv->device_can_sleep = true; +-#endif +- } +- continue; +- } else if (suspend) { +- bes_devel("[BH] Device suspend.\n"); +- powersave_enabled = 1; +- spin_lock(&hw_priv->vif_list_lock); +- bes2600_for_each_vif(hw_priv, priv, i) { +-#ifdef P2P_MULTIVIF +- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv) +-#else +- if (!priv) +-#endif +- continue; +- powersave_enabled &= !!priv->powersave_enabled; +- } +- spin_unlock(&hw_priv->vif_list_lock); +- if (powersave_enabled) { +- bes_devel("[BH] No Device wakedown. Suspend.\n"); +-#ifndef FPGA_SETUP +- WARN_ON(bes2600_reg_write_16(hw_priv, +- ST90TDS_CONTROL_REG_ID, 0)); +- hw_priv->device_can_sleep = true; +-#endif +- } +- +- atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED); +- wake_up(&hw_priv->bh_evt_wq); +- status = wait_event_interruptible(hw_priv->bh_wq, +- BES2600_BH_RESUME == atomic_read( +- &hw_priv->bh_suspend)); +- if (status < 0) { +- wiphy_err(hw_priv->hw->wiphy, +- "%s: Failed to wait for resume: %ld.\n", +- __func__, status); +- break; +- } +- 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); +- continue; +- } +- +-test: +- tx += pending_tx; +- pending_tx = 0; +- +- if (rx) { +- size_t alloc_len; +- u8 *data; +- +-#ifdef INTERRUPT_WORKAROUND +- if(!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)) +-#endif +- if (WARN_ON(bes2600_bh_read_ctrl_reg( +- hw_priv, &ctrl_reg))) +- break; +-rx: +- read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; +- if (!read_len) { +- rx_burst = 0; +- goto tx; +- } +- +- if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || +- (read_len > EFFECTIVE_BUF_SIZE))) { +- bes_devel("Invalid read len: %d", read_len); +- break; +- } +- +- /* Add SIZE of PIGGYBACK reg (CONTROL Reg) +- * to the NEXT Message length + 2 Bytes for SKB */ +- read_len = read_len + 2; +- +-#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) +- alloc_len = hw_priv->sbus_ops->align_size( +- hw_priv->sbus_priv, read_len); +-#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ +- /* Platform's SDIO workaround */ +- alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); +- if (read_len & (SDIO_BLOCK_SIZE - 1)) +- alloc_len += SDIO_BLOCK_SIZE; +-#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ +- +- /* Check if not exceeding BES2600 capabilities */ +- if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) +- bes_devel("Read aligned len: %d\n", alloc_len); +- +- skb_rx = bes2600_get_skb(hw_priv, alloc_len); +- if (WARN_ON(!skb_rx)) +- break; +- +- skb_trim(skb_rx, 0); +- skb_put(skb_rx, read_len); +- data = skb_rx->data; +- if (WARN_ON(!data)) +- break; +- +- if (WARN_ON(bes2600_data_read(hw_priv, data, alloc_len))) +- break; +- +- /* Piggyback */ +- ctrl_reg = __le16_to_cpu( +- ((__le16 *)data)[alloc_len / 2 - 1]); +- +- wsm = (struct wsm_hdr *)data; +- wsm_len = __le32_to_cpu(wsm->len); +- if (WARN_ON(wsm_len > read_len)) +- break; +- +-#if defined(CONFIG_BES2600_WSM_DUMPS) +- if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { +- u16 msgid, ifid; +- u16 *p = (u16 *)data; +- msgid = (*(p + 1)) & 0xC3F; +- ifid = (*(p + 1)) >> 6; +- ifid &= 0xF; +- bes_devel("[DUMP] <<< msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p); +- print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, data, min(wsm_len, wsm_dump_max)); +- } +-#endif /* CONFIG_BES2600_WSM_DUMPS */ +- +- wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; +- wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; +- +- skb_trim(skb_rx, wsm_len); +- +- if (unlikely(wsm_id == 0x0800)) { +- wsm_handle_exception(hw_priv, +- &data[sizeof(*wsm)], +- wsm_len - sizeof(*wsm)); +- break; +- } else if (unlikely(!rx_resync)) { +- if (WARN_ON(wsm_seq != hw_priv->wsm_rx_seq)) { +-#if defined(CONFIG_BES2600_DUMP_ON_ERROR) +- BUG_ON(1); +-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ +- break; +- } +- } +- hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7; +- rx_resync = 0; +- +- if (wsm_id & 0x0400) { +- int rc = wsm_release_tx_buffer(hw_priv, 1); +- if (WARN_ON(rc < 0)) +- break; +- else if (rc > 0) +- tx = 1; +- } +- +- /* bes2600_wsm_rx takes care on SKB livetime */ +- if (WARN_ON(wsm_handle_rx(hw_priv, wsm_id, wsm, +- &skb_rx))) +- break; +- +- if (skb_rx) { +- bes2600_put_skb(hw_priv, skb_rx); +- skb_rx = NULL; +- } +- +- read_len = 0; +- +- if (rx_burst) { +- bes2600_debug_rx_burst(hw_priv); +- --rx_burst; +- goto rx; +- } +- } +- +-tx: +- BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); +- tx_burst = hw_priv->wsm_caps.numInpChBufs - +- hw_priv->hw_bufs_used; +- tx_allowed = tx_burst > 0; +- if (tx && tx_allowed) { +- size_t tx_len; +- u8 *data; +- int ret; +- +- if (hw_priv->device_can_sleep) { +- ret = bes2600_device_wakeup(hw_priv); +- if (WARN_ON(ret < 0)) +- break; +- else if (ret) +- hw_priv->device_can_sleep = false; +- else { +- /* Wait for "awake" interrupt */ +- pending_tx = tx; +- continue; +- } +- } +- +- wsm_alloc_tx_buffer(hw_priv); +- ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst, +- &vif_selected); +- if (ret <= 0) { +- wsm_release_tx_buffer(hw_priv, 1); +- if (WARN_ON(ret < 0)) +- break; +- } else { +- wsm = (struct wsm_hdr *)data; +- BUG_ON(tx_len < sizeof(*wsm)); +- BUG_ON(__le32_to_cpu(wsm->len) != tx_len); +- +-#if 0 /* count is not implemented */ +- if (ret > 1) +- atomic_inc(&hw_priv->bh_tx); +-#else +- atomic_inc(&hw_priv->bh_tx); +-#endif +- +-#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES) +- if (tx_len <= 8) +- tx_len = 16; +- tx_len = hw_priv->sbus_ops->align_size( +- hw_priv->sbus_priv, tx_len); +-#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ +- /* HACK!!! Platform limitation. +- * It is also supported by upper layer: +- * there is always enough space at the +- * end of the buffer. */ +- if (tx_len & (SDIO_BLOCK_SIZE - 1)) { +- tx_len &= ~(SDIO_BLOCK_SIZE - 1); +- tx_len += SDIO_BLOCK_SIZE; +- } +-#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */ +- +- /* Check if not exceeding BES2600 +- capabilities */ +- if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) +- bes_devel("Write aligned len: %d\n", tx_len); +- +- wsm->id &= __cpu_to_le32( +- ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); +- wsm->id |= cpu_to_le32(WSM_TX_SEQ( +- hw_priv->wsm_tx_seq)); +- +- if (WARN_ON(bes2600_data_write(hw_priv, +- data, tx_len))) { +- wsm_release_tx_buffer(hw_priv, 1); +- break; +- } +- +- if (vif_selected != -1) { +- hw_priv->hw_bufs_used_vif[ +- vif_selected]++; +- } +- +-#if defined(CONFIG_BES2600_WSM_DUMPS) +- if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { +- u16 msgid, ifid; +- u16 *p = (u16 *)data; +- msgid = (*(p + 1)) & 0x3F; +- ifid = (*(p + 1)) >> 6; +- ifid &= 0xF; +- if (msgid == 0x0006) +- bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d MIB 0x%.4X\n", msgid, ifid, *p, *(p + 2)); +- else +- bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p); +- print_hex_dump(KERN_DEBUG, "--> ", DUMP_PREFIX_NONE, data, min(__le32_to_cpu(wsm->len), wsm_dump_max)); +- } +-#endif /* CONFIG_BES2600_WSM_DUMPS */ +- +- wsm_txed(hw_priv, data); +- hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) +- & WSM_TX_SEQ_MAX; +- +- if (tx_burst > 1) { +- bes2600_debug_tx_burst(hw_priv); +- ++rx_burst; +- goto tx; +- } +- } +- } +- +- if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) +- goto rx; +- } +- +- if (skb_rx) { +- bes2600_put_skb(hw_priv, skb_rx); +- skb_rx = NULL; +- } +- +- +- if (!term) { +- bes_devel("[BH] Fatal error, exitting.\n"); +-#if defined(CONFIG_BES2600_DUMP_ON_ERROR) +- BUG_ON(1); +-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */ +- hw_priv->bh_error = 1; +-#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS) +- spin_lock(&hw_priv->vif_list_lock); +- bes2600_for_each_vif(hw_priv, priv, i) { +- if (!priv) +- continue; +- ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); +- } +- spin_unlock(&hw_priv->vif_list_lock); +- bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ); +-#endif +- /* TODO: schedule_work(recovery) */ +-#ifndef HAS_PUT_TASK_STRUCT +- /* The only reason of having this stupid code here is +- * that __put_task_struct is not exported by kernel. */ +- for (;;) { +- int status = wait_event_interruptible(hw_priv->bh_wq, ({ +- term = atomic_xchg(&hw_priv->bh_term, 0); +- (term); +- })); +- +- if (status || term) +- break; +- } +-#endif +- } +- return 0; +-} +-#else + + extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg); + +@@ -1599,7 +1053,15 @@ static int bes2600_bh(void *arg) + + tx = 0; + +- BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); ++ /* ++ * Patch H: BUG_ON -> WARN_ON_ONCE in the steady-state ++ * hot path. The original BUG_ON ran every bh-loop ++ * iteration; tripping it on a bookkeeping bug locks ++ * the kernel up during normal operation, which is ++ * the wrong response. WARN_ON_ONCE surfaces the ++ * issue without taking the system down. ++ */ ++ WARN_ON_ONCE(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); + 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) + goto tx; + + done: +- /* Re-enable device interrupts */ +- //hw_priv->sbus_ops->lock(hw_priv->sbus_priv); +- //__bes2600_irq_enable(1); +- //hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); +- asm volatile ("nop"); ++ /* ++ * Patch H: dropped the dead `__bes2600_irq_enable(1)` / ++ * `asm volatile("nop")` placeholder that used to sit here. ++ * `__bes2600_irq_enable()` is a stub that returns 0 on ++ * bes2600 silicon — the IRQ is managed by sdio_claim_irq ++ * and chip-side firmware, not by a driver-side enable bit. ++ * (cw1200 inherited the function from a different chip ++ * shape; bes2600 kept the stub but the call sites are ++ * meaningless.) ++ */ ++ ; + } + +- /* Explicitly disable device interrupts */ +- hw_priv->sbus_ops->lock(hw_priv->sbus_priv); +- __bes2600_irq_enable(0); +- hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); +- + 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) + } + return 0; + } +-#endif +diff --git a/drivers/staging/bes2600/hwio.c b/drivers/staging/bes2600/hwio.c +index 0934a13..1a63e4f 100644 +--- a/drivers/staging/bes2600/hwio.c ++++ b/drivers/staging/bes2600/hwio.c +@@ -324,7 +324,10 @@ out: + } + #endif + +-int __bes2600_irq_enable(int enable) +-{ +- return 0; +-} ++/* ++ * Patch H: __bes2600_irq_enable stub removed. It was a no-op ++ * (always returned 0) inherited from cw1200 where the analogous ++ * function manipulates the chip's IRQ-enable register. bes2600 ++ * 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/sbus.h b/drivers/staging/bes2600/sbus.h +index 43c2dae..4193084 100644 +--- a/drivers/staging/bes2600/sbus.h ++++ b/drivers/staging/bes2600/sbus.h +@@ -95,7 +95,6 @@ struct sbus_ops { + + void bes2600_irq_handler(struct bes2600_common *priv); + +-/* This MUST be wrapped with hwbus_ops->lock/unlock! */ +-int __bes2600_irq_enable(int enable); ++/* Patch H: __bes2600_irq_enable removed (was a stub). */ + + #endif /* BES2600_SBUS_H */ +-- +2.54.0 + diff --git a/danctnix-besser-pkgbuild/kernel/0019-bes2600-take-pending_record_lock-with-_bh-to-fix-SOF.patch b/danctnix-besser-pkgbuild/kernel/0019-bes2600-take-pending_record_lock-with-_bh-to-fix-SOF.patch new file mode 100644 index 000000000..f18d8a786 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0019-bes2600-take-pending_record_lock-with-_bh-to-fix-SOF.patch @@ -0,0 +1,121 @@ +From f469448c605e41bb90440c6d48047830c6febe33 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Mon, 18 May 2026 16:58:49 +0200 +Subject: [PATCH 19/20] =?UTF-8?q?bes2600:=20take=20pending=5Frecord=5Flock?= + =?UTF-8?q?=20with=20=5Fbh()=20to=20fix=20SOFTIRQ-safe=20=E2=86=92=20-unsa?= + =?UTF-8?q?fe=20inversion=20(besser#18)?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PROVE_LOCKING reports: + + WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected + kworker/u16:1 is trying to acquire: + &hw_priv->tx_loop.pending_record_lock at bes2600_queue_clear+0x80 + and this task is already holding: + &queue->lock at bes2600_queue_clear+0x60 + + which would create a new lock dependency: + (&queue->lock){+.-.} -> (&hw_priv->tx_loop.pending_record_lock){+.+.} + + but this new dependency connects a SOFTIRQ-irq-safe lock: + (&queue->lock){+.-.} + ... which became SOFTIRQ-irq-safe at: + bes2600_tx -> ieee80211_handle_wake_tx_queue -> tasklet_action + to a SOFTIRQ-irq-unsafe lock: + (&hw_priv->tx_loop.pending_record_lock){+.+.} + ... which became SOFTIRQ-irq-unsafe at: + bes2600_queue_get_skb -> bes2600_join_work -> process_one_work + +queue->lock is taken consistently with spin_lock_bh() at 22 sites; +the nested acquisition of pending_record_lock at queue.c:289 (inside +the outer queue->lock_bh held at line 285) had it implicitly BH-safe +via the outer scope. But pending_record_lock is ALSO taken from +non-BH-disabled contexts: + + bes2600_queue_get_skb (queue.c:832) — process context via + bes2600_join_work (workqueue), no outer queue->lock held + bes2600_tx_loop_item_pending_check (tx_loop.c:112) + — TX-loop context, no outer + queue->lock held + +When CPU0 holds pending_record_lock from one of those non-BH paths +and a softirq fires that wants queue->lock, and CPU1 in softirq has +queue->lock and is about to acquire pending_record_lock — classic AB-BA +SOFTIRQ deadlock. + +The fix is the conservative one: take pending_record_lock with _bh() +at every site that's not already inside a queue->lock_bh-held scope. +That makes the lock consistently SOFTIRQ-safe, eliminating the +inversion. queue.c:289/295 stays as plain spin_lock because BH is +already disabled by the outer queue->lock_bh acquired at queue.c:285. + +Five sites converted: + bes2600/queue.c:832 -- spin_lock -> spin_lock_bh + bes2600/queue.c:839 -- spin_unlock -> spin_unlock_bh + bes2600/queue.c:844 -- spin_unlock -> spin_unlock_bh + bes2600/tx_loop.c:112 -- spin_lock -> spin_lock_bh + bes2600/tx_loop.c:114 -- spin_unlock -> spin_unlock_bh + +Contract: + - Documentation/locking/locktypes.rst spelling: spin_lock_bh() is + the canonical way to make a non-IRQ spinlock safe against + softirq preemption that might re-enter the same lock. + - Same shape as queue->lock in this driver and as is_drv->lock + in the cw1200 ancestor. + +Closes: besser#18 +Fixes: +Signed-off-by: Markus Fritsche +--- + bes2600/queue.c | 6 +++--- + bes2600/tx_loop.c | 4 ++-- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c +index b56ca43..1e8390f 100644 +--- a/drivers/staging/bes2600/queue.c ++++ b/drivers/staging/bes2600/queue.c +@@ -827,19 +827,19 @@ int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID, + bes2600_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + +- spin_lock(&queue->stats->hw_priv->tx_loop.pending_record_lock); ++ spin_lock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock); + if (!list_empty(&queue->stats->hw_priv->tx_loop.pending_record_list)) { + list_for_each_entry_safe(record_item, temp_record_item, &queue->stats->hw_priv->tx_loop.pending_record_list, head) { + if (record_item->packetID == packetID) { + list_del(&record_item->head); + dev_kfree_skb(record_item->skb); + kfree(record_item); +- spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock); ++ spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock); + return -EINVAL; + } + } + } +- spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock); ++ spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock); + + item = &queue->pool[item_id]; + +diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c +index e6cf072..0cf7ce1 100644 +--- a/drivers/staging/bes2600/tx_loop.c ++++ b/drivers/staging/bes2600/tx_loop.c +@@ -109,9 +109,9 @@ void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv, bool need_warn) + bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i], + bes2600_tx_loop_item_pending_item); + } +- spin_lock(&hw_priv->tx_loop.pending_record_lock); ++ spin_lock_bh(&hw_priv->tx_loop.pending_record_lock); + bes2600_queue_iterate_record_pending_packet(hw_priv, bes2600_tx_loop_item_pending_item); +- spin_unlock(&hw_priv->tx_loop.pending_record_lock); ++ spin_unlock_bh(&hw_priv->tx_loop.pending_record_lock); + + if (atomic_read(&hw_priv->bh_rx) > 0) + wake_up(&hw_priv->bh_wq); +-- +2.54.0 + diff --git a/danctnix-besser-pkgbuild/kernel/0020-bes2600-export-bus_reset-helpers-for-danctnix-bes260.patch b/danctnix-besser-pkgbuild/kernel/0020-bes2600-export-bus_reset-helpers-for-danctnix-bes260.patch new file mode 100644 index 000000000..bb55e6ee4 --- /dev/null +++ b/danctnix-besser-pkgbuild/kernel/0020-bes2600-export-bus_reset-helpers-for-danctnix-bes260.patch @@ -0,0 +1,47 @@ +From 0792ba44bb2f60e6f83e031364ee20739be71d01 Mon Sep 17 00:00:00 2001 +From: Markus Fritsche +Date: Wed, 20 May 2026 20:29:43 +0200 +Subject: [PATCH 20/20] bes2600: export bus_reset helpers for danctnix + bes2600_btuart (danctnix-flavor) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +bes2600_chrdev_do_bus_reset() and bes2600_chrdev_trigger_bus_reset() are +already present (added by the connection-loss bus_reset commit) but not +exported. danctnix's bes2600_btuart.c uses these symbols for BT power +switching and bus-error recovery; without EXPORT_SYMBOL_GPL the btuart +module cannot be built as a separate object in the intree staging tree. + +The userspace /dev/bes2600 chardev remains intact for danctnix — btuart +depends on the internal chardev state machine. This commit is +danctnix-specific; the Mobian DKMS flavor does not need the exports. + +Signed-off-by: Claude (noether) +--- + bes2600/bes_chardev.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c +index 801e4bf..35696af 100644 +--- a/drivers/staging/bes2600/bes_chardev.c ++++ b/drivers/staging/bes2600/bes_chardev.c +@@ -1116,6 +1116,7 @@ int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_pri + + return 0; + } ++EXPORT_SYMBOL_GPL(bes2600_chrdev_do_bus_reset); + + /* + * Trigger bes2600_chrdev_do_bus_reset() against the file-global +@@ -1128,6 +1129,7 @@ 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) + { +-- +2.54.0 + diff --git a/danctnix-besser-pkgbuild/kernel/PKGBUILD b/danctnix-besser-pkgbuild/kernel/PKGBUILD index 1c69c58d5..03ce70bbf 100644 --- a/danctnix-besser-pkgbuild/kernel/PKGBUILD +++ b/danctnix-besser-pkgbuild/kernel/PKGBUILD @@ -11,7 +11,7 @@ pkgbase=linux-pinetab2-danctnix-besser pkgver=7.0.danctnix1 -pkgrel=3 +pkgrel=4 pkgdesc='PineTab2 (BESser bes2600 driver patchset)' _srcname=linux-pinetab2 _srctag=v${pkgver%.*}-${pkgver##*.} @@ -38,10 +38,28 @@ options=( source=( https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/linux-${pkgver%.*}.tar.{xz,sign} ${_url_git}/releases/download/${_srctag}/${_srctag}.patch.zst{,.sig} - 0001-bes2600-besser-cumulative-series.patch + 0001-bes2600-defer-scan-and-soften-WARN-on-firmware-rejec.patch + 0002-bes2600-widen-scan-defer-backoff-to-30s-and-decay-co.patch + 0003-bes2600-recover-wedged-firmware-via-mmc_hw_reset-on-.patch + 0004-bes2600-gate-PM-indication-completion-on-pending-req.patch + 0005-bes2600-short-circuit-wake-handshake-when-chip-is-co.patch + 0006-bes2600-self-detect-when-firmware-does-not-honor-PSM.patch + 0007-bes2600-handle-multi-function-SDIO-cards-in-mmc_hw_r.patch + 0008-bes2600-pre-empt-AP-deauth-6-with-mac80211-reassoc-o.patch + 0009-bes2600-bus_reset-on-connection-loss-storm-to-dodge-.patch + 0010-bes2600-replace-a-set-of-atomic_add.patch + 0011-bes2600-fix-missing-destroy_workqueue-on-error-in-in.patch + 0012-bes2600-fix-concurrency-UAF-in-bes2600_hw_scan-and-s.patch + 0013-bes2600-drop-sdio_rx_work-relay-IRQ-bh-direct-no-rel.patch + 0014-bes2600-Patch-G-restore-SPDX-identifiers-ST-Ericsson.patch + 0015-bes2600-Patch-D-atomicize-ba_lock-counters-drop-the-.patch + 0016-bes2600-Patch-E-skip-ps_state_lock-when-PSM-known-di.patch + 0017-bes2600-Patch-C2-replace-ieee80211_rx_irqsafe-with-i.patch + 0018-bes2600-Patch-H-bh.c-hygiene-cleanup-drop-fossil-blo.patch + 0019-bes2600-take-pending_record_lock-with-_bh-to-fix-SOF.patch + 0020-bes2600-export-bus_reset-helpers-for-danctnix-bes260.patch 0002-bes2600-filter-5ghz-scan.patch 0003-arm64-xor-neon-ffixed-x18-build-fix.patch - 0003-arm64-xor-neon-ffixed-x18-build-fix.patch config # the main kernel config file ) validpgpkeys=( @@ -53,10 +71,28 @@ b2sums=('3d9795083c8938f80f480de0d10bfd9c525640e59d5c7f22983de3f12ee42c84c31be90 'SKIP' '71fe98221e802b315e54b4b10d3e8c8f376695a36bae3541d876e5776a37f3fa33c8f8dfa6e51fcbd6f5396add02e5166634165f2351836a0ea0453c172fe56c' 'SKIP' - 'fca0a5badf762d5dbc085261cccc07ddeef96384d2ae0a426fb0412acd7a180e068cabd59f01342b7575d41889afc0f47dfbc9256801ab809f746278e6dab510' + '5268f55c132441e1ef2e0042e48940a51556286c2e2813c99e983bf89606c2aa05df56e42ebd8bcfd201ceaf63493ca3f2639a39f926e8419b3bc27a4ac4aced' + 'ebf786a401b5883431068b7a88ff1890ff4f2936cfceb6560828ba202a548c0c6f1f89d721837f1b67e85165d4dd1a2973cbe97e396e1b258efe5288a17d1a81' + 'ddf0f8c052f7d40f324791353b3831827cdb80da4726fb5596a0e61d6f194e84cbd0ceb036e22cb89a1af2baccb15ef7850621799d90e96a4049f9b11fc61565' + 'c811e415a549100da927e2caa4ef46ccdd6b2b834b0a781db6ca232a12d90278744133e19916de6421be2f95780b2978ec10eb620fe81a9697df3f2539b5747a' + '4c28c0ee7443445986a4631d61e9c9f82944c4fd8380d6ba28a14dc85c8e641e88407f25c8abeb47000db25e267946a0d401d0bae4ad1c0b91e4f13953ad0081' + '597b648ef625aff58fab7ca2067c303c1b7abdf03b78296c7b656260982eaded1938f294975abda75e864499f2bad4801941ff7acc5713d2628ae6550c9ecea3' + '0f6e20acb800f55c853307a4fe9129280fd440a2b5214c068d91d3dbe5e7e207466ca5019d1792800ac9e4f072f006a5bbcb9b4004700426fc8f2eac6cbef5b2' + 'b793908df0483e64d98e91c7cae1496668f2597d5b6669e2f313abd3a648ba4a685562338e649cbe12a33ab142c90a129f9d642309ee38ad188cbc92fe99ae84' + '3a41ced2ebbc6773fc4f2803ac835b7e839d81bae529c84191355ad2768065c2ef5e67a165af6bed29c0775c608869425bd1d20c8e2632faceac5bfa8ecb18d5' + '2aa236f4a72712b974f3d4870ff6557892df8e05c748bc89a195284a3ab7330e0859a52815ee1c4447fd64365283117301fced72b590ea1d16cfc450cfd07018' + '8c0de659c5dcb70cd6d993c9c8b7607476491440fa62a26a9aea4ee075e20016fe05ce8023c43125bd82b7f8879b20537a0d74e5de2d1b7211b5b37e787b48aa' + '6e343e15b14ccc980e5ff21641051db57c8c8cb0705426403c0d0e2f7d1adf3efb79f331c34a5e1714ac5103b28e073404229588d8042ab5b8bb95c9ef8421a2' + '54c9529e1d4fe55d028341fd761e24630f4f0a1c43b287db67bc878aa84ceca8e64283560399980bdcd10987ad3222c30e173e33ac1d341190d1237d6cf4f806' + '0839ab95b408483774aaff978ece3a1e54ba8ec4bd8146cb2c649ee044224f3ad9c024bd534df09e6883e1d6d4b92593f7e168b6bd51bb32d9b3ee11f7b52716' + '7b11001ba0638c24e36926a934448203c94240261742df999429954b9a5253e7e72ddda93d47c39b44e61b99491b83b7a46be2d098d3054bf92f73c226048715' + '154a1a564a6d6ac316869456d271024c0af4cf7175c31579e6ac7293bdb20f413dcf5fd4684e63376627545c13c231ba2cbd28026684e33daec14e3751c25a1e' + '4611b825d9a79589c427569f2d9521cc3c8d21603d7aae980b763414bbfd96c8d2ef04917805c0af4a8abf397228a866ff5f2c0540ae035662eb1f376bae5312' + 'e318299e4cb828220ac7d5142dc41969f22f83f1f791bd46f7f4ce19dbd1d7074b0faa9ac6a4daac4f70e6c7852b38a6482de62111bb7e653cd870d2968fce70' + '5c71b88f2ae8a7ebd0932db9a4da72a3ba8c636f31a1bed953a81359588bcb0309f62aa9dee98db62bdc988a9b669341910da2b133d9fb92d14c27d64b54efe9' + 'e09273ddcdc44f4d40fe8a69e0fd70b963681ec4434ce63cf6114ea38954891e709ced877e0be914054854e2d295a2991e8c3d8dc0deb244bfc8b0568c681687' '396acbdcf570eada62533c0b8f505ed18077e8432249bab5b8ac8d1107cabc9489bdb91a5780446237ec4fd9ba5fc57a49dff34c16ddab60dc30513fc535f00f' '2714e3c0cd8ec978ce9431418f44f578220886fcabb738c9a0c43fc3c043753960b7c47ae96e1780154d8b266a2add6098407de4ffa7aee40d77ce17e8c70df9' - '2714e3c0cd8ec978ce9431418f44f578220886fcabb738c9a0c43fc3c043753960b7c47ae96e1780154d8b266a2add6098407de4ffa7aee40d77ce17e8c70df9' '656a998ab40cb85ee4c00f087b071a91632a6c091da2c84b0f74236b51d2dea6e9db6886625f80ad81dc249d8494ec47cd79d6dd9ea4f5e44f3cde857f861e10') export KBUILD_BUILD_HOST=archlinux