Files
marfrit-packages/arch/linux-pinetab2-danctnix-besser/0001-bes2600-besser-kernel-agent-cumulative.patch
T
claude-noether 7a5587c0c6 linux-pinetab2-danctnix-besser: bump cumulative b2sum to ad9e2cb5...
Re-promoted after kernel-agent fixed malformed @@ hunk counts in the
SCS xor-neon patch (kernel-agent commit ahead of this on
noether/migrate-pinetab2-pkg-and-patches).

b2sum: bd42cd39106298879eeb... -> ad9e2cb533957f218058...
size unchanged at 157 458.
2026-05-18 16:58:23 +02:00

4530 lines
154 KiB
Diff

From 4e176b8f930373bc02382c903e6d739ab2d5fd47 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
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 <fritsche.markus@gmail.com>
---
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 <linux/umh.h>
#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 <linux/version.h>
#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 <flamingice@sourmilk.net>
*
- * Based on the islsm (softmac prism54) driver, which is:
+ * Based on the islsm (softmac prism54) driver, which is
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, 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 <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
+#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/crc32.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
*
- * 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 <linux/version.h>
@@ -16,6 +17,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
#include <linux/mmc/sdio.h>
#include <linux/spinlock.h>
#include <net/mac80211.h>
@@ -28,8 +30,10 @@
#include <linux/of_gpio.h>
#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<linux/module.h>
#include <linux/init.h>
@@ -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 <linux/list.h>
#include <linux/pm.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
#include <linux/kthread.h>
@@ -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],
- &timestamp, -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/module.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/types.h>
#include <linux/version.h>
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 <linux/types.h>
#include <linux/kernel.h>
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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/init.h>
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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/types.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
*
- * 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 <linux/module.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
+ *
+ * Based on the mac80211 Prism54 code, which is
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, 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 <linux/module.h>
@@ -32,7 +38,6 @@
#include "pm.h"
#include "bes2600_factory.h"
#include "bes_chardev.h"
-#include "txrx_opt.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/platform_device.h>
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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/sched.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/vmalloc.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/version.h>
#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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/netlink.h>
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 <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <linux/skbuff.h>
@@ -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 <dmitry.tarnyagin@lockless.no>
*
- * Based on BES2600 UMAC WSM API, which is
- * Copyright (C) SA 2010
- * Author: Stewart Mathers <stewart.mathers@stericsson.com>
+ * 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 <fritsche.markus@gmail.com>
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 <fritsche.markus@gmail.com>
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 <fritsche.markus@gmail.com>
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