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 From 093a5038b8b68f316d976b7cb69609ca7f24f322 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Mon, 18 May 2026 11:27:40 +0200 Subject: [PATCH 1/2] bes2600: filter 5 GHz scans at the driver boundary (besser#1) The BES2600 firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected by policy"). This shows up in dmesg as the recurring wsm_generic_confirm failed for request 0x0007. [SCAN] Scan failed (-22). pattern (besser issue #1, ~14-16/h on ohm/PineTab2 baseline). Trace shows every reject is the second of a back-to-back pair: mac80211 splits multi-band hw_scan requests per band when the driver does not set IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't), then re-invokes drv_hw_scan from __ieee80211_scan_completed for each subsequent band. The 2.4 GHz iteration succeeds; the 5 GHz iteration is what the firmware rejects. See ieee80211_prep_hw_scan in net/mac80211/scan.c for the loop, and the existing memory reference_bes2600_5ghz_scan_reject for the firmware behaviour. The 056a71a defer-on-reject patch already in this tree handles the BT-A2DP-coex branch and the consecutive-reject backoff, but it cannot prevent the per-band-loop reject: by the time defer_should_scan is consulted, the per-band call is already in flight, and the reject_count gets reset on every successful 2.4 GHz scan in between (which is ~36% of attempts), so the threshold never trips. The fix: refuse the 5 GHz iteration upfront in bes2600_hw_scan. The 2.4 GHz scan still runs normally. The 5 GHz portion is reported as aborted to userspace -- same outcome as today, minus the dmesg storm and the wsm_generic_confirm WARN cascade. 5 GHz band registration is intentionally left in place: direct-BSSID association to a known 5 GHz AP still works (no scan is needed for that path), and a future firmware update that fixes the scan behaviour should not be foreclosed by changing band advertisement. Contract: per include/net/mac80211.h ieee80211_ops.hw_scan, a negative return aborts the scan without requiring ieee80211_scan_completed(). -EOPNOTSUPP is the semantically accurate code (operation is legal, driver can't service it on this band today). Phase 3 evidence: - baseline N=3: rate ~14.3-23.6/h converged at 14.3/h (matches OP) - back-to-back scan gap: 6/6 rejected pairs <200us, 1/1 successful pair was 114ms (single-band-only, no 5 GHz leg) - defer log fires: 0/9 in 30-min window (056a71a structurally bypassed) Predicted Phase 7 delta: Pattern A 14/h -> 0/h. --- bes2600/scan.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c index fb1d298..a81afb6 100644 --- a/drivers/staging/bes2600/scan.c +++ b/drivers/staging/bes2600/scan.c @@ -238,6 +238,28 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, /* Scan when P2P_GO corrupt firmware MiniAP mode */ if (priv->join_status == BES2600_JOIN_STATUS_AP) return -EOPNOTSUPP; + + /* + * Firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected + * by policy"); see besser issue #1. mac80211 splits multi-band + * hw_scan requests per-band when the driver does not set + * IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't -- see + * ieee80211_hw_set() calls in bes2600_main.c), so each per-band call + * has req->channels[] from one band only (see ieee80211_prep_hw_scan + * in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver + * boundary so userspace gets a clean aborted-scan for that portion + * rather than waiting for the firmware reject to cascade up. 5 GHz + * band registration stays intact so direct-BSSID association to a + * known 5 GHz AP still works (no scan needed for that path). + * + * Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan + * documentation, a negative return aborts the scan without requiring + * ieee80211_scan_completed(). + */ + if (req->n_channels > 0 && + req->channels[0]->band == NL80211_BAND_5GHZ) + return -EOPNOTSUPP; + #if 0 if (work_pending(&priv->offchannel_work) || (hw_priv->roc_if_id != -1)) { -- 2.54.0 From 8cd10f487c8144d462a510812ba0fa717b3e24df Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Mon, 18 May 2026 15:56:34 +0200 Subject: [PATCH 2/2] bes2600: scan-filter-5ghz: allow targeted single-channel scans (besser#1 follow-up) The original Patch I refused EVERY 5 GHz scan request unconditionally (req->n_channels > 0 && band == NL80211_BAND_5GHZ). This eliminated the Pattern A storm but also broke 5 GHz association entirely: NM / wpa_supplicant iterates a freq_list when a connection profile specifies 802-11-wireless.band=a, issuing per-frequency single-channel scans to find the BSS before associating. Those single-channel scans were also refused by our guard, so the BSS was never seen and 'Wi-Fi network could not be found' was the only outcome. Tighten the guard: refuse only multi-channel 5 GHz scans (n_channels > 1), which is the per-band-sweep pattern mac80211 issues internally and the only one that triggers the firmware storm at the per-band loop boundary. Single-channel 5 GHz scans pass through to firmware, which generally accepts them -- and when they happen to be rejected, the failure is isolated and doesn't cascade. Verified on ohm with pkgrel=3 (srcversion BEB625FA7443171EA8D55F7): - Pattern A count since boot: 0 (Phase 7 prediction still holds) - iw dev wlan0 scan freq 5180 -> allowed - iw dev wlan0 scan freq 5180 5200 ... -> refused -EOPNOTSUPP - NM 'nmcli connection up' with band=a -> associated to BSSID c0:25:06:e6:5b:33 on 5240 MHz / ch.48 in ~1 second - TX bitrate 150 Mbit/s MCS 7 40MHz short-GI (vs 72.2 Mbit/s HT20 on 2.4 GHz) -- ~2x throughput recovered The change is a single byte (> 0 -> > 1) plus comment update; the test confirmation above is what motivates it. Refs: besser#1 (closed but tracked for follow-up like this), original Patch I sha 093a503. --- bes2600/scan.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c index a81afb6..497523b 100644 --- a/drivers/staging/bes2600/scan.c +++ b/drivers/staging/bes2600/scan.c @@ -248,15 +248,23 @@ int bes2600_hw_scan(struct ieee80211_hw *hw, * has req->channels[] from one band only (see ieee80211_prep_hw_scan * in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver * boundary so userspace gets a clean aborted-scan for that portion - * rather than waiting for the firmware reject to cascade up. 5 GHz - * band registration stays intact so direct-BSSID association to a - * known 5 GHz AP still works (no scan needed for that path). + * rather than waiting for the firmware reject to cascade up. + * + * Only the multi-channel case is refused (n_channels > 1): that's + * the per-band-sweep pattern mac80211 issues internally and the + * one that triggers the firmware storm at the per-band loop + * boundary. Single-channel 5 GHz scans (BSS verification, NM's + * per-freq iteration when 802-11-wireless.band=a is set) pass + * through to firmware, which generally accepts them since the + * storm is the back-to-back per-band issue, not a blanket 5 GHz + * reject. This preserves 5 GHz association via the + * "wpa_supplicant iterates freq_list per channel" path. * * Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan * documentation, a negative return aborts the scan without requiring * ieee80211_scan_completed(). */ - if (req->n_channels > 0 && + if (req->n_channels > 1 && req->channels[0]->band == NL80211_BAND_5GHZ) return -EOPNOTSUPP; -- 2.54.0 From: Markus Fritsche Date: Mon, 18 May 2026 11:42:00 +0200 Subject: [PATCH] arm64: xor-neon: restore -ffixed-x18 when SHADOW_CALL_STACK=y (GCC 15+ build fix) GCC 15.2.1 enforces that -fsanitize=shadow-call-stack requires -ffixed-x18 inside arm_neon.h's #pragma GCC target() blocks. The existing CFLAGS_REMOVE_xor-neon.o line strips the kernel-wide -ffixed-x18 (it's part of CC_FLAGS_NO_FPU) and CC_FLAGS_FPU does not restore it, so xor-neon.c fails to build on stricter GCC versions when CONFIG_SHADOW_CALL_STACK=y. Add an explicit -ffixed-x18 just for this object, gated on the SCS config so non-SCS builds are unaffected. Build environment workaround; not a kernel-runtime bug. --- arch/arm64/lib/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index 1234567..2345678 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -9,7 +9,12 @@ ifeq ($(CONFIG_KERNEL_MODE_NEON), y) obj-$(CONFIG_XOR_BLOCKS) += xor-neon.o CFLAGS_xor-neon.o += $(CC_FLAGS_FPU) CFLAGS_REMOVE_xor-neon.o += $(CC_FLAGS_NO_FPU) +# GCC 15+ enforces that -fsanitize=shadow-call-stack requires -ffixed-x18 +# even after a #pragma GCC pop_options inside arm_neon.h. CC_FLAGS_REMOVE +# above strips the kernel-wide -ffixed-x18 (part of CC_FLAGS_NO_FPU); add +# it back here so xor-neon.c still compiles when SHADOW_CALL_STACK=y. +CFLAGS_xor-neon.o += $(if $(CONFIG_SHADOW_CALL_STACK),-ffixed-x18) endif lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o -- 2.54.0