patches/driver/bes2600: mirror besser series — closes #2 #17
@@ -0,0 +1,92 @@
|
|||||||
|
# patches/driver/bes2600/
|
||||||
|
|
||||||
|
BES2600 WiFi driver patches (`drivers/staging/bes2600/*`, mainline-bound).
|
||||||
|
Mirrored from `marfrit/besser/patches/` on 2026-05-16.
|
||||||
|
|
||||||
|
Scope tag: `driver:bes2600` (see `fleet/ohm.yaml` for the consumer).
|
||||||
|
Consumer: ohm (PineTab2, RK3566 + BES2600 SDIO).
|
||||||
|
|
||||||
|
## Series taxonomy
|
||||||
|
|
||||||
|
30 series (15 base + 15 `-danctnix` siblings). The `-danctnix`
|
||||||
|
variants exist because vanilla series don't apply on the DanctNIX
|
||||||
|
kernel base (slightly different in-tree state for `drivers/staging/bes2600/*`).
|
||||||
|
Keep both as separate series until BES2600 lands upstream, then
|
||||||
|
collapse — issue #2 acceptance criterion.
|
||||||
|
|
||||||
|
Each series directory contains numbered `.patch` files plus
|
||||||
|
optionally a `0000-cover-letter.patch` for multi-patch series.
|
||||||
|
|
||||||
|
## Promotion eligibility (per series)
|
||||||
|
|
||||||
|
Marked here for the kernel-agent CLI (`ka-promote`) to pick up.
|
||||||
|
Markus to update as series mature. Default UNSET means "ask before
|
||||||
|
including in a build".
|
||||||
|
|
||||||
|
| Series | promote_eligible | Notes |
|
||||||
|
|------------------------------------|------------------|-------------------------------------------------------------|
|
||||||
|
| `debian-copyright-fsf-address` | unset | Debian packaging metadata; not kernel-side |
|
||||||
|
| `drop-dpd-file-paths` | unset | |
|
||||||
|
| `drop-dpd-file-paths-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `drop-orphan-file-io` | unset | |
|
||||||
|
| `drop-orphan-file-io-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `enable-testmode` | unset | |
|
||||||
|
| `factory-drop-kernel-write` | unset | |
|
||||||
|
| `factory-drop-kernel-write-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `factory-series` | unset | |
|
||||||
|
| `factory-thread-dev` | unset | |
|
||||||
|
| `lmac-recover-via-mmc-hw-reset` | unset | |
|
||||||
|
| `lmac-recover-via-mmc-hw-reset-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-detect-firmware-unsupported` | unset | |
|
||||||
|
| `pm-detect-firmware-unsupported-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-gate-on-handshake` | unset | |
|
||||||
|
| `pm-state-resync` | unset | |
|
||||||
|
| `pm-state-resync-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-timeout-silence` | unset | |
|
||||||
|
| `pm-timeout-silence-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-wake-consume-state` | unset | |
|
||||||
|
| `pm-wake-consume-state-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `remove-chardev-user-interface` | unset | Cross-ref `bes_chardev` merge regression (besser #17) |
|
||||||
|
| `scan-defer-backoff-tune` | unset | |
|
||||||
|
| `scan-defer-backoff-tune-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `scan-defer-on-reject` | unset | |
|
||||||
|
| `scan-defer-on-reject-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `staging-prep-series` | unset | 7-patch cover-letter series; upstream-staging-prep work |
|
||||||
|
| `staging-prep-series-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `tx-sdio-dma-oob` | unset | |
|
||||||
|
| `tx-sdio-dma-oob-danctnix` | unset | DanctNIX sibling |
|
||||||
|
|
||||||
|
## DKMS-to-in-tree transition path
|
||||||
|
|
||||||
|
`bes2600-dkms` (Mobian fork, in `marfrit/bes2600-dkms`) is the
|
||||||
|
out-of-tree shim that ohm currently uses for the BES2600 wifi+BT.
|
||||||
|
Once these `driver/bes2600/` series land in mainline (or at least in
|
||||||
|
DanctNIX's PineTab2 kernel base):
|
||||||
|
|
||||||
|
1. ohm's manifest drops the `bes2600-dkms` package dependency
|
||||||
|
2. `kernel-agent` builds the in-tree variant via the series listed here
|
||||||
|
3. `marfrit/bes2600-dkms` repo gets archived (kept as history)
|
||||||
|
4. PineTab2 buyers from then on get bes2600 directly out of the kernel
|
||||||
|
|
||||||
|
Track the dropdown in `fleet/ohm.yaml` (`replaces_dkms: bes2600-dkms`
|
||||||
|
once the cumulative series is enough to replace it).
|
||||||
|
|
||||||
|
## Cumulative-patch generation order
|
||||||
|
|
||||||
|
The current single-patch cumulative (`0001-bes2600-besser-cumulative-series.patch`
|
||||||
|
in the existing PKGBUILD) is generated in this order on boltzmann:
|
||||||
|
|
||||||
|
A, B, C v3, F, G, D, E, C2, c5.x, c6.x, c7, H
|
||||||
|
|
||||||
|
This is NOT alphabetical — `C2` follows `E` rather than coming after
|
||||||
|
`C v3`. `ka-promote` MUST honor an explicit series-ordering field
|
||||||
|
when concatenating, not sort by series name. Field name TBD; suggest
|
||||||
|
adding `apply_order:` to `fleet/ohm.yaml` (issue #5 will surface this
|
||||||
|
when the cumulative gets regenerated).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Issue: `git.reauktion.de/marfrit/kernel-agent/issues/2`
|
||||||
|
- Source repo: `git.reauktion.de/marfrit/besser/patches/`
|
||||||
|
- Consumer: ohm (`fleet/ohm.yaml`)
|
||||||
|
- Related: `bes2600-dkms`, `linux-pinetab2-danctnix-besser` PKGBUILD
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
From f31c57adf736df52b3f393f2650920af98b8e8f1 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Fri, 24 Apr 2026 09:40:44 +0200
|
||||||
|
Subject: [PATCH] debian/copyright: drop obsolete FSF street address
|
||||||
|
|
||||||
|
The 'You should have received a copy ... write to the Free
|
||||||
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301 USA' paragraph flags the lintian tag
|
||||||
|
'old-fsf-address-in-copyright-file'. Debian prefers either no
|
||||||
|
address at all or an https://www.gnu.org/licenses/ reference;
|
||||||
|
in this file /usr/share/common-licenses/LGPL-2.1 is already
|
||||||
|
cited a few lines below, so the address is redundant. Replace
|
||||||
|
with the gnu.org URL per current FSF boilerplate.
|
||||||
|
|
||||||
|
Pre-existing text, no change to the licence terms.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
debian/copyright | 3 +--
|
||||||
|
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/debian/copyright b/debian/copyright
|
||||||
|
index 961fc90..3228eec 100644
|
||||||
|
--- a/debian/copyright
|
||||||
|
+++ b/debian/copyright
|
||||||
|
@@ -18,8 +18,7 @@ License: LGPL-2.1
|
||||||
|
License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
- along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
+ along with this library; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
On Debian systems, the full text of the GNU Lesser General Public License
|
||||||
|
version 2.1 can be found in the file "/usr/share/common-licenses/LGPL-2.1".
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
From 699871fdc6bf1bed6d919732820183e57faeaddc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:04:11 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop BES2600_WRITE_DPD_TO_FILE kernel_*() file paths
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
bes_chardev.c carried three functions gated behind the
|
||||||
|
BES2600_WRITE_DPD_TO_FILE Kconfig/make-flag (default off):
|
||||||
|
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file()
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write()
|
||||||
|
writing a raw DPD calibration blob back to
|
||||||
|
BES2600_DPD_PATH (default /data/cfg/bes2600_dpd.bin, an
|
||||||
|
Android-AOSP path).
|
||||||
|
|
||||||
|
- bes2600_chrdev_read_and_check_dpd_data()
|
||||||
|
filp_open(O_RDONLY) + kernel_read() reading the DPD blob
|
||||||
|
from either BES2600_DPD_GOLDEN_PATH (/data/cfg/…) or
|
||||||
|
BES2600_DEFAULT_DPD_PATH (/lib/firmware/bes2600_dpd.bin),
|
||||||
|
followed by a CRC/version sanity check.
|
||||||
|
|
||||||
|
- bes2600_chrdev_dpd_is_vaild() (sic), the CRC/version helper
|
||||||
|
used only by the read path.
|
||||||
|
|
||||||
|
Plus the bes_cdev.no_dpd field, its module_param, and two
|
||||||
|
intrusion sites in bes2600_chrdev_get_dpd_data() and
|
||||||
|
bes2600_chrdev_update_dpd_data() that invoke the above.
|
||||||
|
|
||||||
|
The Makefile defaults BES2600_WRITE_DPD_TO_FILE=n, so in a stock
|
||||||
|
build all of this is dead code. It is still a standing upstream
|
||||||
|
blocker for exactly the same reasons as the factory-txt write
|
||||||
|
path removed in the preceding patch:
|
||||||
|
|
||||||
|
- filp_open() + kernel_read()/kernel_write() bypass the
|
||||||
|
firmware-class abstraction and LSM-governed access control
|
||||||
|
that apply to /lib/firmware/.
|
||||||
|
- The write target /data/cfg/ is an Android AOSP convention
|
||||||
|
that does not exist on a Linux distribution and cannot be
|
||||||
|
created by the kernel anyway.
|
||||||
|
- A runtime DPD re-calibration is intended to reduce TX EVM
|
||||||
|
after temperature or aging drift; persisting the result via
|
||||||
|
kernel_write() is fundamentally a userspace concern (debugfs
|
||||||
|
dump + userspace tool is the expected route).
|
||||||
|
|
||||||
|
Remove the entire #ifdef BES2600_WRITE_DPD_TO_FILE block from
|
||||||
|
bes_chardev.c (including the inner #ifdef inside
|
||||||
|
bes2600_chrdev_read_and_check_dpd_data() guarding a
|
||||||
|
DPD_BIN_FILE_SIZE size check that only applied to the read-back-
|
||||||
|
its-own-write case), the no_dpd field and module_param, and the
|
||||||
|
two invocation sites. Drop the Kconfig/make-flag and the three
|
||||||
|
associated PATH macros from the Makefile. Net: -155 lines, no
|
||||||
|
remaining filp_open/kernel_read/kernel_write anywhere in
|
||||||
|
bes_chardev.c.
|
||||||
|
|
||||||
|
The in-memory DPD state path is unchanged: bes2600_chrdev_get_dpd_
|
||||||
|
buffer() still allocates a kmalloc'd buffer used by the firmware-
|
||||||
|
download path, bes2600_chrdev_update_dpd_data() still validates
|
||||||
|
the buffer's CRC and transitions bes2600_cdev.wait_state on
|
||||||
|
success, and bes2600_chrdev_free_dpd_data() still releases the
|
||||||
|
buffer on unload. Only the file-I/O side-channel is removed.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 12 ----
|
||||||
|
bes2600/bes_chardev.c | 143 ------------------------------------------
|
||||||
|
2 files changed, 155 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 2c1a850..0dd3606 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -28,7 +28,6 @@ CONFIG_BES2600_WIFI_BOOT_ON ?= y
|
||||||
|
CONFIG_BES2600_BT_BOOT_ON ?= n
|
||||||
|
|
||||||
|
BES2600_GPIO_WAKEUP_AP ?= n
|
||||||
|
-BES2600_WRITE_DPD_TO_FILE ?= n
|
||||||
|
BES2600_TX_MORE_RETRY ?= n
|
||||||
|
|
||||||
|
# bes evb
|
||||||
|
@@ -93,12 +92,6 @@ ccflags-y += -DBES_UNIFIED_PM
|
||||||
|
ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
|
||||||
|
ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG
|
||||||
|
|
||||||
|
-ifeq ($(BES2600_WRITE_DPD_TO_FILE),y)
|
||||||
|
-BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin
|
||||||
|
-BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin
|
||||||
|
-BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin
|
||||||
|
-endif
|
||||||
|
-
|
||||||
|
ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
|
||||||
|
BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
|
||||||
|
endif
|
||||||
|
@@ -135,9 +128,6 @@ ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DRV_VERSION)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y)
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
|
||||||
|
@@ -159,8 +149,6 @@ ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
|
||||||
|
ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y)
|
||||||
|
-ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y)
|
||||||
|
-
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
index e2e4f1b..a02d6d9 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
@@ -63,9 +63,6 @@ struct bes_cdev {
|
||||||
|
struct delayed_work probe_timeout_work;
|
||||||
|
enum bus_probe_state bus_probe;
|
||||||
|
struct work_struct wifi_force_close_work;
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- int no_dpd;
|
||||||
|
-#endif
|
||||||
|
enum pend_read_op read_flag;
|
||||||
|
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
|
||||||
|
u16 wakeup_state; /* for userspace check wakeup reason */
|
||||||
|
@@ -85,9 +82,6 @@ struct bes2600_op_map {
|
||||||
|
|
||||||
|
static struct bes_cdev bes2600_cdev;
|
||||||
|
module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644);
|
||||||
|
-#endif
|
||||||
|
|
||||||
|
extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
@@ -269,137 +263,8 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- if (buffer == NULL || size == 0)
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
- if (ret < 0)
|
||||||
|
- bes_err("write dpd to file failed\n");
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_devel("write dpd to %s\n", path);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool bes2600_chrdev_dpd_is_vaild(u8 *dpd_data)
|
||||||
|
-{
|
||||||
|
- u32 cal_crc = 0;
|
||||||
|
- u32 dpd_crc = le32_to_cpup((__le32 *)(dpd_data));
|
||||||
|
- u32 dpd_ver = le32_to_cpup((__le32 *)(dpd_data + DPD_VERSION_OFFSET));
|
||||||
|
-
|
||||||
|
- /* check version */
|
||||||
|
- if (dpd_ver < DPD_CUR_VERSION)
|
||||||
|
- return false;
|
||||||
|
-
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
- cal_crc = crc32_le(cal_crc, dpd_data + 4, DPD_BIN_SIZE - 4);
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
-
|
||||||
|
- /* check if the dpd data is valid */
|
||||||
|
- if (cal_crc != dpd_crc) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
|
||||||
|
- cal_crc, dpd_crc);
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- u8* read_data = NULL;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- /* open file */
|
||||||
|
- fp = filp_open(file, O_RDONLY, 0);//S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",file);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, DPD_BIN_FILE_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
- /* allocate memory for storing reading data */
|
||||||
|
- read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL);
|
||||||
|
- if (read_data == NULL) {
|
||||||
|
- bes_devel("%s alloc mem fail\n", __func__);
|
||||||
|
- goto err1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* read data from file */
|
||||||
|
- ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
- if (ret < DPD_BIN_SIZE) {
|
||||||
|
- bes_err("%s read fail, ret=%d\n", __func__, ret);
|
||||||
|
- goto err2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* check dpd version and crc */
|
||||||
|
- if (!bes2600_chrdev_dpd_is_vaild(read_data))
|
||||||
|
- goto err2;
|
||||||
|
-
|
||||||
|
- /* close file */
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- /* copy data to external */
|
||||||
|
- *data = read_data;
|
||||||
|
- *len = DPD_BIN_SIZE;;
|
||||||
|
-
|
||||||
|
- /* output debug information */
|
||||||
|
- bes_devel("read dpd data from %s\n", file);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
-err2:
|
||||||
|
- kfree(read_data);
|
||||||
|
-err1:
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- *data = NULL;
|
||||||
|
- *len = 0;
|
||||||
|
- return -1;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
const u8* bes2600_chrdev_get_dpd_data(u32 *len)
|
||||||
|
{
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) {
|
||||||
|
- /* read dpd data from file that stores factory dpd calibration data */
|
||||||
|
- if ((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) &&
|
||||||
|
- (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) {
|
||||||
|
- bes_err("%s read dpd data fail\n", __func__);
|
||||||
|
- return NULL;
|
||||||
|
- } else {
|
||||||
|
- bes2600_cdev.dpd_calied = true;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
if (!bes2600_cdev.dpd_calied)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
@@ -460,14 +325,6 @@ int bes2600_chrdev_update_dpd_data(void)
|
||||||
|
}
|
||||||
|
spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- /* write dpd data to file */
|
||||||
|
- memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE);
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH,
|
||||||
|
- bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE);
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
From 699871fdc6bf1bed6d919732820183e57faeaddc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:04:11 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop BES2600_WRITE_DPD_TO_FILE kernel_*() file paths
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
bes_chardev.c carried three functions gated behind the
|
||||||
|
BES2600_WRITE_DPD_TO_FILE Kconfig/make-flag (default off):
|
||||||
|
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file()
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write()
|
||||||
|
writing a raw DPD calibration blob back to
|
||||||
|
BES2600_DPD_PATH (default /data/cfg/bes2600_dpd.bin, an
|
||||||
|
Android-AOSP path).
|
||||||
|
|
||||||
|
- bes2600_chrdev_read_and_check_dpd_data()
|
||||||
|
filp_open(O_RDONLY) + kernel_read() reading the DPD blob
|
||||||
|
from either BES2600_DPD_GOLDEN_PATH (/data/cfg/…) or
|
||||||
|
BES2600_DEFAULT_DPD_PATH (/lib/firmware/bes2600_dpd.bin),
|
||||||
|
followed by a CRC/version sanity check.
|
||||||
|
|
||||||
|
- bes2600_chrdev_dpd_is_vaild() (sic), the CRC/version helper
|
||||||
|
used only by the read path.
|
||||||
|
|
||||||
|
Plus the bes_cdev.no_dpd field, its module_param, and two
|
||||||
|
intrusion sites in bes2600_chrdev_get_dpd_data() and
|
||||||
|
bes2600_chrdev_update_dpd_data() that invoke the above.
|
||||||
|
|
||||||
|
The Makefile defaults BES2600_WRITE_DPD_TO_FILE=n, so in a stock
|
||||||
|
build all of this is dead code. It is still a standing upstream
|
||||||
|
blocker for exactly the same reasons as the factory-txt write
|
||||||
|
path removed in the preceding patch:
|
||||||
|
|
||||||
|
- filp_open() + kernel_read()/kernel_write() bypass the
|
||||||
|
firmware-class abstraction and LSM-governed access control
|
||||||
|
that apply to /lib/firmware/.
|
||||||
|
- The write target /data/cfg/ is an Android AOSP convention
|
||||||
|
that does not exist on a Linux distribution and cannot be
|
||||||
|
created by the kernel anyway.
|
||||||
|
- A runtime DPD re-calibration is intended to reduce TX EVM
|
||||||
|
after temperature or aging drift; persisting the result via
|
||||||
|
kernel_write() is fundamentally a userspace concern (debugfs
|
||||||
|
dump + userspace tool is the expected route).
|
||||||
|
|
||||||
|
Remove the entire #ifdef BES2600_WRITE_DPD_TO_FILE block from
|
||||||
|
bes_chardev.c (including the inner #ifdef inside
|
||||||
|
bes2600_chrdev_read_and_check_dpd_data() guarding a
|
||||||
|
DPD_BIN_FILE_SIZE size check that only applied to the read-back-
|
||||||
|
its-own-write case), the no_dpd field and module_param, and the
|
||||||
|
two invocation sites. Drop the Kconfig/make-flag and the three
|
||||||
|
associated PATH macros from the Makefile. Net: -155 lines, no
|
||||||
|
remaining filp_open/kernel_read/kernel_write anywhere in
|
||||||
|
bes_chardev.c.
|
||||||
|
|
||||||
|
The in-memory DPD state path is unchanged: bes2600_chrdev_get_dpd_
|
||||||
|
buffer() still allocates a kmalloc'd buffer used by the firmware-
|
||||||
|
download path, bes2600_chrdev_update_dpd_data() still validates
|
||||||
|
the buffer's CRC and transitions bes2600_cdev.wait_state on
|
||||||
|
success, and bes2600_chrdev_free_dpd_data() still releases the
|
||||||
|
buffer on unload. Only the file-I/O side-channel is removed.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 12 ----
|
||||||
|
bes2600/bes_chardev.c | 143 ------------------------------------------
|
||||||
|
2 files changed, 155 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 2c1a850..0dd3606 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -28,7 +28,6 @@ CONFIG_BES2600_WIFI_BOOT_ON ?= y
|
||||||
|
CONFIG_BES2600_BT_BOOT_ON ?= n
|
||||||
|
|
||||||
|
BES2600_GPIO_WAKEUP_AP ?= n
|
||||||
|
-BES2600_WRITE_DPD_TO_FILE ?= n
|
||||||
|
BES2600_TX_MORE_RETRY ?= n
|
||||||
|
|
||||||
|
# bes evb
|
||||||
|
@@ -93,12 +92,6 @@ ccflags-y += -DBES_UNIFIED_PM
|
||||||
|
ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
|
||||||
|
ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG
|
||||||
|
|
||||||
|
-ifeq ($(BES2600_WRITE_DPD_TO_FILE),y)
|
||||||
|
-BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin
|
||||||
|
-BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin
|
||||||
|
-BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin
|
||||||
|
-endif
|
||||||
|
-
|
||||||
|
ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
|
||||||
|
BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
|
||||||
|
endif
|
||||||
|
@@ -135,9 +128,6 @@ ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DRV_VERSION)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y)
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
|
||||||
|
@@ -159,8 +149,6 @@ ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
|
||||||
|
ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y)
|
||||||
|
-ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y)
|
||||||
|
-
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index e2e4f1b..a02d6d9 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -63,9 +63,6 @@ struct bes_cdev {
|
||||||
|
struct delayed_work probe_timeout_work;
|
||||||
|
enum bus_probe_state bus_probe;
|
||||||
|
struct work_struct wifi_force_close_work;
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- int no_dpd;
|
||||||
|
-#endif
|
||||||
|
enum pend_read_op read_flag;
|
||||||
|
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
|
||||||
|
u16 wakeup_state; /* for userspace check wakeup reason */
|
||||||
|
@@ -85,9 +82,6 @@ struct bes2600_op_map {
|
||||||
|
|
||||||
|
static struct bes_cdev bes2600_cdev;
|
||||||
|
module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644);
|
||||||
|
-#endif
|
||||||
|
|
||||||
|
extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
@@ -269,137 +263,8 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- if (buffer == NULL || size == 0)
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
- if (ret < 0)
|
||||||
|
- bes_err("write dpd to file failed\n");
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_devel("write dpd to %s\n", path);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool bes2600_chrdev_dpd_is_vaild(u8 *dpd_data)
|
||||||
|
-{
|
||||||
|
- u32 cal_crc = 0;
|
||||||
|
- u32 dpd_crc = le32_to_cpup((__le32 *)(dpd_data));
|
||||||
|
- u32 dpd_ver = le32_to_cpup((__le32 *)(dpd_data + DPD_VERSION_OFFSET));
|
||||||
|
-
|
||||||
|
- /* check version */
|
||||||
|
- if (dpd_ver < DPD_CUR_VERSION)
|
||||||
|
- return false;
|
||||||
|
-
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
- cal_crc = crc32_le(cal_crc, dpd_data + 4, DPD_BIN_SIZE - 4);
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
-
|
||||||
|
- /* check if the dpd data is valid */
|
||||||
|
- if (cal_crc != dpd_crc) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
|
||||||
|
- cal_crc, dpd_crc);
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- u8* read_data = NULL;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- /* open file */
|
||||||
|
- fp = filp_open(file, O_RDONLY, 0);//S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",file);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, DPD_BIN_FILE_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
- /* allocate memory for storing reading data */
|
||||||
|
- read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL);
|
||||||
|
- if (read_data == NULL) {
|
||||||
|
- bes_devel("%s alloc mem fail\n", __func__);
|
||||||
|
- goto err1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* read data from file */
|
||||||
|
- ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
- if (ret < DPD_BIN_SIZE) {
|
||||||
|
- bes_err("%s read fail, ret=%d\n", __func__, ret);
|
||||||
|
- goto err2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* check dpd version and crc */
|
||||||
|
- if (!bes2600_chrdev_dpd_is_vaild(read_data))
|
||||||
|
- goto err2;
|
||||||
|
-
|
||||||
|
- /* close file */
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- /* copy data to external */
|
||||||
|
- *data = read_data;
|
||||||
|
- *len = DPD_BIN_SIZE;;
|
||||||
|
-
|
||||||
|
- /* output debug information */
|
||||||
|
- bes_devel("read dpd data from %s\n", file);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
-err2:
|
||||||
|
- kfree(read_data);
|
||||||
|
-err1:
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- *data = NULL;
|
||||||
|
- *len = 0;
|
||||||
|
- return -1;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
const u8* bes2600_chrdev_get_dpd_data(u32 *len)
|
||||||
|
{
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) {
|
||||||
|
- /* read dpd data from file that stores factory dpd calibration data */
|
||||||
|
- if ((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) &&
|
||||||
|
- (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) {
|
||||||
|
- bes_err("%s read dpd data fail\n", __func__);
|
||||||
|
- return NULL;
|
||||||
|
- } else {
|
||||||
|
- bes2600_cdev.dpd_calied = true;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
if (!bes2600_cdev.dpd_calied)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
@@ -460,14 +325,6 @@ int bes2600_chrdev_update_dpd_data(void)
|
||||||
|
}
|
||||||
|
spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- /* write dpd data to file */
|
||||||
|
- memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE);
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH,
|
||||||
|
- bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE);
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
From 44e085360fec09c1c1f7b35a23ec679f7065d3f7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:19:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop orphan DATA_DUMP_OBSERVE and access_file() file
|
||||||
|
I/O
|
||||||
|
|
||||||
|
Two dead-in-default-build file-I/O sites remain in the driver
|
||||||
|
after the factory and chardev kernel_*() removals in the preceding
|
||||||
|
patches:
|
||||||
|
|
||||||
|
- bes_fw.c DATA_DUMP_OBSERVE: four #ifdef DATA_DUMP_OBSERVE
|
||||||
|
blocks built around the firmware-download path that open
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open(O_CREAT |
|
||||||
|
O_RDWR), then log every transmitted firmware chunk via
|
||||||
|
vfs_write() inside a get_fs()/set_fs(KERNEL_DS) wrapper. The
|
||||||
|
controlling #define at bes_fw.c line 128 is commented out
|
||||||
|
('//#define DATA_DUMP_OBSERVE'), so none of this is ever
|
||||||
|
compiled in a stock build.
|
||||||
|
|
||||||
|
- main.c access_file(): a helper gated on
|
||||||
|
GET_MAC_ADDR_METHOD == 2 || == 3 (default 4) using the same
|
||||||
|
get_fs()/set_fs()/vfs_read()/vfs_write() pattern. No caller
|
||||||
|
in the tree references it -- it was orphaned when the methods
|
||||||
|
that consumed it were refactored out.
|
||||||
|
|
||||||
|
Both sites are unbuildable on modern kernels anyway: get_fs() /
|
||||||
|
set_fs() were removed from arm64 and the generic uaccess path in
|
||||||
|
the v5.10 era, and the legacy vfs_read() / vfs_write() variants
|
||||||
|
that took userspace-typed buffers went with them. The in-kernel
|
||||||
|
replacements would be kernel_read() / kernel_write(), which this
|
||||||
|
series is explicitly removing from the driver.
|
||||||
|
|
||||||
|
Remove both blocks, the commented-out '//#define DATA_DUMP_OBSERVE'
|
||||||
|
line, and the access_file() definition and its #if gate. No
|
||||||
|
behaviour change in any default or non-default build, because
|
||||||
|
nothing compiled or linked in the first place. After this patch
|
||||||
|
the driver contains zero filp_open / kernel_read / kernel_write /
|
||||||
|
vfs_read / vfs_write references -- a precondition for a
|
||||||
|
drivers/staging/bes2600/ linux-wireless RFC.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_fw.c | 34 ----------------------------------
|
||||||
|
bes2600/main.c | 35 -----------------------------------
|
||||||
|
2 files changed, 69 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c
|
||||||
|
index 133c945..d612c3c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_fw.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_fw.c
|
||||||
|
@@ -125,8 +125,6 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
-//#define DATA_DUMP_OBSERVE
|
||||||
|
-
|
||||||
|
static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val)
|
||||||
|
{
|
||||||
|
u8 frame_num = 0;
|
||||||
|
@@ -468,14 +466,6 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n
|
||||||
|
|
||||||
|
const struct firmware *fw_bin;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- char *observe;
|
||||||
|
- size_t observe_len;
|
||||||
|
- loff_t observe_off = 0;
|
||||||
|
- mm_segment_t old_fs;
|
||||||
|
- struct file *observe_file = NULL;
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
struct fw_msg_hdr_t header;
|
||||||
|
struct fw_info_t fw_info;
|
||||||
|
struct download_fw_t download_addr;
|
||||||
|
@@ -583,14 +573,6 @@ retry:
|
||||||
|
}
|
||||||
|
download_addr.addr = fw_info.addr;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0);
|
||||||
|
- if (IS_ERR(observe_file)) {
|
||||||
|
- bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file));
|
||||||
|
- observe_file = NULL;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
while (code_length) {
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
@@ -640,17 +622,6 @@ retry:
|
||||||
|
//mdelay(5000);
|
||||||
|
bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length);
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
|
||||||
|
- observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t);
|
||||||
|
- old_fs = get_fs();
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- vfs_write(observe_file, observe, observe_len, &observe_off);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
|
||||||
|
if (ret) {
|
||||||
|
bes_err("tx download fw data err:%d\n", ret);
|
||||||
|
@@ -832,11 +803,6 @@ retry:
|
||||||
|
|
||||||
|
err2:
|
||||||
|
kfree(long_buf);
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- filp_close(observe_file, NULL);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
err1:
|
||||||
|
kfree(short_buf);
|
||||||
|
release_firmware(fw_bin);
|
||||||
|
diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c
|
||||||
|
index 6ed6b15..9d2aac5 100644
|
||||||
|
--- a/drivers/staging/bes2600/main.c
|
||||||
|
+++ b/drivers/staging/bes2600/main.c
|
||||||
|
@@ -790,41 +790,6 @@ void bes2600_core_release(struct bes2600_common *self)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */
|
||||||
|
-int access_file(char *path, char *buffer, int size, int isRead)
|
||||||
|
-{
|
||||||
|
- int ret=0;
|
||||||
|
- struct file *fp;
|
||||||
|
- mm_segment_t old_fs = get_fs();
|
||||||
|
-
|
||||||
|
- if(isRead)
|
||||||
|
- fp = filp_open(path,O_RDONLY,S_IRUSR);
|
||||||
|
- else
|
||||||
|
- fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR);
|
||||||
|
-
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n", path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (isRead) {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_read(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- } else {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_write(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_info("BES2600 : access_file return code(%d)\n", ret);
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
int bes2600_wifi_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0, if_id;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
From 44e085360fec09c1c1f7b35a23ec679f7065d3f7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:19:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop orphan DATA_DUMP_OBSERVE and access_file() file
|
||||||
|
I/O
|
||||||
|
|
||||||
|
Two dead-in-default-build file-I/O sites remain in the driver
|
||||||
|
after the factory and chardev kernel_*() removals in the preceding
|
||||||
|
patches:
|
||||||
|
|
||||||
|
- bes_fw.c DATA_DUMP_OBSERVE: four #ifdef DATA_DUMP_OBSERVE
|
||||||
|
blocks built around the firmware-download path that open
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open(O_CREAT |
|
||||||
|
O_RDWR), then log every transmitted firmware chunk via
|
||||||
|
vfs_write() inside a get_fs()/set_fs(KERNEL_DS) wrapper. The
|
||||||
|
controlling #define at bes_fw.c line 128 is commented out
|
||||||
|
('//#define DATA_DUMP_OBSERVE'), so none of this is ever
|
||||||
|
compiled in a stock build.
|
||||||
|
|
||||||
|
- main.c access_file(): a helper gated on
|
||||||
|
GET_MAC_ADDR_METHOD == 2 || == 3 (default 4) using the same
|
||||||
|
get_fs()/set_fs()/vfs_read()/vfs_write() pattern. No caller
|
||||||
|
in the tree references it -- it was orphaned when the methods
|
||||||
|
that consumed it were refactored out.
|
||||||
|
|
||||||
|
Both sites are unbuildable on modern kernels anyway: get_fs() /
|
||||||
|
set_fs() were removed from arm64 and the generic uaccess path in
|
||||||
|
the v5.10 era, and the legacy vfs_read() / vfs_write() variants
|
||||||
|
that took userspace-typed buffers went with them. The in-kernel
|
||||||
|
replacements would be kernel_read() / kernel_write(), which this
|
||||||
|
series is explicitly removing from the driver.
|
||||||
|
|
||||||
|
Remove both blocks, the commented-out '//#define DATA_DUMP_OBSERVE'
|
||||||
|
line, and the access_file() definition and its #if gate. No
|
||||||
|
behaviour change in any default or non-default build, because
|
||||||
|
nothing compiled or linked in the first place. After this patch
|
||||||
|
the driver contains zero filp_open / kernel_read / kernel_write /
|
||||||
|
vfs_read / vfs_write references -- a precondition for a
|
||||||
|
drivers/staging/bes2600/ linux-wireless RFC.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_fw.c | 34 ----------------------------------
|
||||||
|
bes2600/main.c | 35 -----------------------------------
|
||||||
|
2 files changed, 69 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_fw.c b/bes2600/bes_fw.c
|
||||||
|
index 133c945..d612c3c 100644
|
||||||
|
--- a/bes2600/bes_fw.c
|
||||||
|
+++ b/bes2600/bes_fw.c
|
||||||
|
@@ -125,8 +125,6 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
-//#define DATA_DUMP_OBSERVE
|
||||||
|
-
|
||||||
|
static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val)
|
||||||
|
{
|
||||||
|
u8 frame_num = 0;
|
||||||
|
@@ -468,14 +466,6 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n
|
||||||
|
|
||||||
|
const struct firmware *fw_bin;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- char *observe;
|
||||||
|
- size_t observe_len;
|
||||||
|
- loff_t observe_off = 0;
|
||||||
|
- mm_segment_t old_fs;
|
||||||
|
- struct file *observe_file = NULL;
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
struct fw_msg_hdr_t header;
|
||||||
|
struct fw_info_t fw_info;
|
||||||
|
struct download_fw_t download_addr;
|
||||||
|
@@ -583,14 +573,6 @@ retry:
|
||||||
|
}
|
||||||
|
download_addr.addr = fw_info.addr;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0);
|
||||||
|
- if (IS_ERR(observe_file)) {
|
||||||
|
- bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file));
|
||||||
|
- observe_file = NULL;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
while (code_length) {
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
@@ -640,17 +622,6 @@ retry:
|
||||||
|
//mdelay(5000);
|
||||||
|
bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length);
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
|
||||||
|
- observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t);
|
||||||
|
- old_fs = get_fs();
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- vfs_write(observe_file, observe, observe_len, &observe_off);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
|
||||||
|
if (ret) {
|
||||||
|
bes_err("tx download fw data err:%d\n", ret);
|
||||||
|
@@ -832,11 +803,6 @@ retry:
|
||||||
|
|
||||||
|
err2:
|
||||||
|
kfree(long_buf);
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- filp_close(observe_file, NULL);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
err1:
|
||||||
|
kfree(short_buf);
|
||||||
|
release_firmware(fw_bin);
|
||||||
|
diff --git a/bes2600/main.c b/bes2600/main.c
|
||||||
|
index 6ed6b15..9d2aac5 100644
|
||||||
|
--- a/bes2600/main.c
|
||||||
|
+++ b/bes2600/main.c
|
||||||
|
@@ -790,41 +790,6 @@ void bes2600_core_release(struct bes2600_common *self)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */
|
||||||
|
-int access_file(char *path, char *buffer, int size, int isRead)
|
||||||
|
-{
|
||||||
|
- int ret=0;
|
||||||
|
- struct file *fp;
|
||||||
|
- mm_segment_t old_fs = get_fs();
|
||||||
|
-
|
||||||
|
- if(isRead)
|
||||||
|
- fp = filp_open(path,O_RDONLY,S_IRUSR);
|
||||||
|
- else
|
||||||
|
- fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR);
|
||||||
|
-
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n", path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (isRead) {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_read(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- } else {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_write(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_info("BES2600 : access_file return code(%d)\n", ret);
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
int bes2600_wifi_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0, if_id;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+143
@@ -0,0 +1,143 @@
|
|||||||
|
From 9398d3028bc9d2f4ccbf8e830f8e9799bf065ce4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:04:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: enable CONFIG_BES2600_TESTMODE by default + fix
|
||||||
|
bit-rotted testmode plumbing
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The driver implements a mac80211 testmode_cmd operation that dispatches
|
||||||
|
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
|
||||||
|
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
|
||||||
|
BES2600 RF-test path (bes2600_vendor_rf_cmd → firmware
|
||||||
|
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
|
||||||
|
binding in struct ieee80211_ops are conditionally compiled under
|
||||||
|
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
|
||||||
|
|
||||||
|
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
|
||||||
|
included in the build and the .testmode_cmd op is populated. On the
|
||||||
|
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
|
||||||
|
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
|
||||||
|
through the standard nl80211 testmode interface ('iw phy0 ...').
|
||||||
|
|
||||||
|
This also makes visible two classes of bit-rot that had accumulated
|
||||||
|
while nobody was building with CONFIG_BES2600_TESTMODE=y:
|
||||||
|
|
||||||
|
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
|
||||||
|
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
|
||||||
|
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
|
||||||
|
first argument. Neither the macros nor any of the BES2600_DBG_*
|
||||||
|
constants are defined anywhere in the tree. The same call pattern
|
||||||
|
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
|
||||||
|
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
|
||||||
|
|
||||||
|
Add minimal shim macros to bes_log.h that rewire the calls onto
|
||||||
|
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
|
||||||
|
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
|
||||||
|
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
|
||||||
|
as 0 constants for documentation / grep.
|
||||||
|
|
||||||
|
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
|
||||||
|
bes2600_get_roam_delay() are declared in sta.c with external
|
||||||
|
linkage but have no prototype in any header. All callers live in
|
||||||
|
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
|
||||||
|
off the compiler never sees them; with it on gcc
|
||||||
|
-Werror=missing-prototypes breaks the build.
|
||||||
|
|
||||||
|
Mark the three functions static. (Keeping them file-local also
|
||||||
|
matches their actual usage.)
|
||||||
|
|
||||||
|
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
|
||||||
|
buildable; no behavioural change when the flag is off.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
|
||||||
|
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes_log.h | 23 +++++++++++++++++++++++
|
||||||
|
bes2600/sta.c | 6 +++---
|
||||||
|
3 files changed, 27 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 300912b..39150e0 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||||
|
# feature option
|
||||||
|
BES2600 ?= m
|
||||||
|
|
||||||
|
-CONFIG_BES2600_TESTMODE ?= n
|
||||||
|
+CONFIG_BES2600_TESTMODE ?= y
|
||||||
|
|
||||||
|
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_log.h b/bes2600/bes_log.h
|
||||||
|
index 605cea8..65cf703 100644
|
||||||
|
--- a/bes2600/bes_log.h
|
||||||
|
+++ b/bes2600/bes_log.h
|
||||||
|
@@ -8,3 +8,26 @@ extern struct device *global_dev;
|
||||||
|
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
|
||||||
|
+ * was never implemented in-tree; these shims let code paths gated by
|
||||||
|
+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
|
||||||
|
+ * build when their conditions are enabled. The first argument is
|
||||||
|
+ * currently unused; pick one of the BES2600_DBG_* constants below for
|
||||||
|
+ * documentation.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_DBG_SBUS 0
|
||||||
|
+#define BES2600_DBG_DOWNLOAD 0
|
||||||
|
+#define BES2600_DBG_ITP 0
|
||||||
|
+#define BES2600_DBG_TEST_MODE 0
|
||||||
|
+
|
||||||
|
+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_cond) \
|
||||||
|
+ bes_err(fmt, ##__VA_ARGS__); \
|
||||||
|
+ } while (0)
|
||||||
|
diff --git a/bes2600/sta.c b/bes2600/sta.c
|
||||||
|
index aa69eb8..5f1a456 100644
|
||||||
|
--- a/bes2600/sta.c
|
||||||
|
+++ b/bes2600/sta.c
|
||||||
|
@@ -3633,7 +3633,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;
|
||||||
|
@@ -3663,7 +3663,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;
|
||||||
|
@@ -3703,7 +3703,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
*
|
||||||
|
* Returns: Returns the last measured roam delay
|
||||||
|
*/
|
||||||
|
-int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
From 5f475a9624490b07c305329f12016ff4a4df3b47 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 19:31:25 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop kernel_write() persistence from factory cali
|
||||||
|
save
|
||||||
|
|
||||||
|
Following the conversion of the factory-calibration READ path to
|
||||||
|
request_firmware() (earlier in this series), the factory-calibration
|
||||||
|
WRITE path in factory_section_write_file() was still using
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write() to persist
|
||||||
|
updated calibration data back to FACTORY_PATH
|
||||||
|
(default /lib/firmware/bes2600/bes2600_factory.txt).
|
||||||
|
|
||||||
|
Writing to files under /lib/firmware/ from kernel code is a
|
||||||
|
standing upstream blocker for staging and for drivers/net/wireless/
|
||||||
|
submission generally:
|
||||||
|
|
||||||
|
- filp_open()/kernel_write() bypass the firmware-class abstraction,
|
||||||
|
the LSM framework, and user/group/mode enforcement that governs
|
||||||
|
the firmware search paths. They have been repeatedly called out
|
||||||
|
in staging-prep reviews.
|
||||||
|
- The kernel runs with capabilities that userspace does not (CAP_
|
||||||
|
DAC_OVERRIDE effectively); quietly rewriting firmware blobs that
|
||||||
|
userspace owns is a surprise contract.
|
||||||
|
- A module unload / reboot immediately after the write races the
|
||||||
|
writeback and can leave a truncated calibration file on disk.
|
||||||
|
|
||||||
|
Remove factory_section_write_file() and its two call sites in
|
||||||
|
bes2600_wifi_cali_table_save(). The in-memory factory_save_p
|
||||||
|
remains authoritative for the duration of the session: the WSM
|
||||||
|
command handlers that triggered this path (power-cali-table,
|
||||||
|
freq-cali, efuse-flag, power-cali-flag) already update the live
|
||||||
|
struct factory_t, and reads served from file_buffer pick up the
|
||||||
|
rebuilt serialised form immediately. On the next probe the
|
||||||
|
firmware-class file is re-read read-only via request_firmware(),
|
||||||
|
as set up by the earlier patch.
|
||||||
|
|
||||||
|
If cross-reboot persistence of runtime-updated calibration becomes
|
||||||
|
a requirement, the expected route is a userspace-visible dump
|
||||||
|
interface -- a read-only debugfs file exporting the serialised
|
||||||
|
blob, or an nl80211 vendor command -- that lets userspace copy the
|
||||||
|
values to a chosen location under its own privileges. Such a
|
||||||
|
facility can land as a follow-up without touching the core driver
|
||||||
|
write path again.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 63 +++++++++++----------------------------
|
||||||
|
1 file changed, 17 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index 1cda447..1b43b41 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -179,34 +179,6 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-/**
|
||||||
|
- * factory_section_write_file - Write data of specified length to file
|
||||||
|
- * @path: path of the file
|
||||||
|
- * @buffer: storage of write data
|
||||||
|
- * @size: length of data to write
|
||||||
|
- *
|
||||||
|
- * Return: length on success, negative error code otherwise.
|
||||||
|
- */
|
||||||
|
-static int factory_section_write_file(char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- bes_devel("writing %s \n", path);
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
@@ -898,9 +870,22 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Rebuild the serialised calibration blob in file_buffer from the live
|
||||||
|
+ * in-memory factory_save_p. Previously this function also persisted the
|
||||||
|
+ * blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that
|
||||||
|
+ * is not acceptable in mainline, so the persistence step has been removed.
|
||||||
|
+ *
|
||||||
|
+ * The in-memory factory_save_p remains authoritative for the duration of
|
||||||
|
+ * the session; on the next probe the firmware-class file is read back
|
||||||
|
+ * read-only via request_firmware(). If cross-reboot persistence of runtime
|
||||||
|
+ * calibration updates becomes a requirement, the expected route is a
|
||||||
|
+ * userspace-facing dump interface (debugfs read-only blob, or nl80211
|
||||||
|
+ * vendor command) that lets userspace read the serialised form and store
|
||||||
|
+ * it under its own privileges.
|
||||||
|
+ */
|
||||||
|
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
int w_size;
|
||||||
|
u32 crc_len = sizeof(factory_data_t);
|
||||||
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||||
|
@@ -909,13 +894,11 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
|
||||||
|
bes_devel("enter %s\n", __func__);
|
||||||
|
|
||||||
|
- if (!file_buffer) {
|
||||||
|
+ if (!file_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
- }
|
||||||
|
|
||||||
|
- if (!factory_save_p) {
|
||||||
|
+ if (!factory_save_p)
|
||||||
|
return -ENOENT;
|
||||||
|
- }
|
||||||
|
|
||||||
|
/* All initialized to space */
|
||||||
|
memset(file_buffer, 32, FACTORY_MAX_SIZE);
|
||||||
|
@@ -927,22 +910,10 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
w_size = factory_build(file_buffer, factory_save_p);
|
||||||
|
|
||||||
|
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err("%s: build failed! ret = %d.", __func__, ret);
|
||||||
|
+ bes_err("%s: build failed! w_size = %d.", __func__, w_size);
|
||||||
|
return -ETXTBSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#ifdef FACTORY_SAVE_MULTI_PATH
|
||||||
|
- /* avoid trailing characters '\0' */
|
||||||
|
- file_buffer[w_size] = 32;
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE);
|
||||||
|
-#else
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size);
|
||||||
|
-#endif
|
||||||
|
- if(ret < 0) {
|
||||||
|
- bes_err("%s: write failed! ret = %d.", __func__, ret);
|
||||||
|
- return ret;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
From 5f475a9624490b07c305329f12016ff4a4df3b47 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 19:31:25 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop kernel_write() persistence from factory cali
|
||||||
|
save
|
||||||
|
|
||||||
|
Following the conversion of the factory-calibration READ path to
|
||||||
|
request_firmware() (earlier in this series), the factory-calibration
|
||||||
|
WRITE path in factory_section_write_file() was still using
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write() to persist
|
||||||
|
updated calibration data back to FACTORY_PATH
|
||||||
|
(default /lib/firmware/bes2600/bes2600_factory.txt).
|
||||||
|
|
||||||
|
Writing to files under /lib/firmware/ from kernel code is a
|
||||||
|
standing upstream blocker for staging and for drivers/net/wireless/
|
||||||
|
submission generally:
|
||||||
|
|
||||||
|
- filp_open()/kernel_write() bypass the firmware-class abstraction,
|
||||||
|
the LSM framework, and user/group/mode enforcement that governs
|
||||||
|
the firmware search paths. They have been repeatedly called out
|
||||||
|
in staging-prep reviews.
|
||||||
|
- The kernel runs with capabilities that userspace does not (CAP_
|
||||||
|
DAC_OVERRIDE effectively); quietly rewriting firmware blobs that
|
||||||
|
userspace owns is a surprise contract.
|
||||||
|
- A module unload / reboot immediately after the write races the
|
||||||
|
writeback and can leave a truncated calibration file on disk.
|
||||||
|
|
||||||
|
Remove factory_section_write_file() and its two call sites in
|
||||||
|
bes2600_wifi_cali_table_save(). The in-memory factory_save_p
|
||||||
|
remains authoritative for the duration of the session: the WSM
|
||||||
|
command handlers that triggered this path (power-cali-table,
|
||||||
|
freq-cali, efuse-flag, power-cali-flag) already update the live
|
||||||
|
struct factory_t, and reads served from file_buffer pick up the
|
||||||
|
rebuilt serialised form immediately. On the next probe the
|
||||||
|
firmware-class file is re-read read-only via request_firmware(),
|
||||||
|
as set up by the earlier patch.
|
||||||
|
|
||||||
|
If cross-reboot persistence of runtime-updated calibration becomes
|
||||||
|
a requirement, the expected route is a userspace-visible dump
|
||||||
|
interface -- a read-only debugfs file exporting the serialised
|
||||||
|
blob, or an nl80211 vendor command -- that lets userspace copy the
|
||||||
|
values to a chosen location under its own privileges. Such a
|
||||||
|
facility can land as a follow-up without touching the core driver
|
||||||
|
write path again.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 63 +++++++++++----------------------------
|
||||||
|
1 file changed, 17 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index 1cda447..1b43b41 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -179,34 +179,6 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-/**
|
||||||
|
- * factory_section_write_file - Write data of specified length to file
|
||||||
|
- * @path: path of the file
|
||||||
|
- * @buffer: storage of write data
|
||||||
|
- * @size: length of data to write
|
||||||
|
- *
|
||||||
|
- * Return: length on success, negative error code otherwise.
|
||||||
|
- */
|
||||||
|
-static int factory_section_write_file(char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- bes_devel("writing %s \n", path);
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
@@ -898,9 +870,22 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Rebuild the serialised calibration blob in file_buffer from the live
|
||||||
|
+ * in-memory factory_save_p. Previously this function also persisted the
|
||||||
|
+ * blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that
|
||||||
|
+ * is not acceptable in mainline, so the persistence step has been removed.
|
||||||
|
+ *
|
||||||
|
+ * The in-memory factory_save_p remains authoritative for the duration of
|
||||||
|
+ * the session; on the next probe the firmware-class file is read back
|
||||||
|
+ * read-only via request_firmware(). If cross-reboot persistence of runtime
|
||||||
|
+ * calibration updates becomes a requirement, the expected route is a
|
||||||
|
+ * userspace-facing dump interface (debugfs read-only blob, or nl80211
|
||||||
|
+ * vendor command) that lets userspace read the serialised form and store
|
||||||
|
+ * it under its own privileges.
|
||||||
|
+ */
|
||||||
|
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
int w_size;
|
||||||
|
u32 crc_len = sizeof(factory_data_t);
|
||||||
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||||
|
@@ -909,13 +894,11 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
|
||||||
|
bes_devel("enter %s\n", __func__);
|
||||||
|
|
||||||
|
- if (!file_buffer) {
|
||||||
|
+ if (!file_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
- }
|
||||||
|
|
||||||
|
- if (!factory_save_p) {
|
||||||
|
+ if (!factory_save_p)
|
||||||
|
return -ENOENT;
|
||||||
|
- }
|
||||||
|
|
||||||
|
/* All initialized to space */
|
||||||
|
memset(file_buffer, 32, FACTORY_MAX_SIZE);
|
||||||
|
@@ -927,22 +910,10 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
w_size = factory_build(file_buffer, factory_save_p);
|
||||||
|
|
||||||
|
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err("%s: build failed! ret = %d.", __func__, ret);
|
||||||
|
+ bes_err("%s: build failed! w_size = %d.", __func__, w_size);
|
||||||
|
return -ETXTBSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#ifdef FACTORY_SAVE_MULTI_PATH
|
||||||
|
- /* avoid trailing characters '\0' */
|
||||||
|
- file_buffer[w_size] = 32;
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE);
|
||||||
|
-#else
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size);
|
||||||
|
-#endif
|
||||||
|
- if(ret < 0) {
|
||||||
|
- bes_err("%s: write failed! ret = %d.", __func__, ret);
|
||||||
|
- return ret;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
From 1a5d54a3213041262caf1605bb19c66ddded41f7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 10:09:44 +0200
|
||||||
|
Subject: [PATCH 1/2] bes2600: use request_firmware() for factory.txt read
|
||||||
|
|
||||||
|
The BES2600 factory calibration file (bes2600_factory.txt) was being read
|
||||||
|
via filp_open() + kernel_read() from a hard-coded absolute path baked in
|
||||||
|
at compile time via the FACTORY_PATH Makefile macro
|
||||||
|
(default: /lib/firmware/bes2600_factory.txt).
|
||||||
|
|
||||||
|
This had several problems:
|
||||||
|
|
||||||
|
1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
|
||||||
|
device-pine64-pinetab2) ships the file at
|
||||||
|
/lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
|
||||||
|
driver logged '(NULL device *): read and check
|
||||||
|
/lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
|
||||||
|
running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
|
||||||
|
2. Direct filesystem access via filp_open() / kernel_read() from a driver
|
||||||
|
is an anti-pattern that upstream rejects: drivers should use
|
||||||
|
request_firmware() to get binary data from userspace-managed firmware
|
||||||
|
directories. request_firmware() natively searches the firmware_class
|
||||||
|
path list (typically /lib/firmware + derivatives), associates the load
|
||||||
|
with a uevent, and respects the firmware-loading infrastructure.
|
||||||
|
|
||||||
|
3. The (NULL device *) prefix in error messages indicated the absence of
|
||||||
|
proper device-context logging. While this patch does not yet thread
|
||||||
|
struct device through, the upstream path uses request_firmware() which
|
||||||
|
works with dev=NULL and is the building block for a follow-up patch
|
||||||
|
that adds per-chip device context.
|
||||||
|
|
||||||
|
Repoint the FACTORY_PATH default to the firmware-class name
|
||||||
|
(bes2600/bes2600_factory.txt) - request_firmware() prepends
|
||||||
|
/lib/firmware/ from the configured search paths. The macro remains
|
||||||
|
overridable at build time for non-standard deployments.
|
||||||
|
|
||||||
|
Rewrite factory_section_read_file() to:
|
||||||
|
* Call request_firmware(&fw, path, NULL).
|
||||||
|
* Size-check fw->size against FACTORY_MAX_SIZE.
|
||||||
|
* memcpy the data into the caller's buffer.
|
||||||
|
* Always call release_firmware() on exit.
|
||||||
|
|
||||||
|
The file write path (factory_section_write_file + kernel_write) is left
|
||||||
|
unchanged in this patch; it is the subject of a follow-up patch that
|
||||||
|
removes kernel_write and moves any remaining userspace-visible factory
|
||||||
|
configuration to a standard kernel-userspace boundary (debugfs or
|
||||||
|
nl80211 testmode).
|
||||||
|
|
||||||
|
No caller signature changes. No Makefile flag drops. Bisectable.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
|
||||||
|
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' is gone; request_firmware reads the file successfully (a separate
|
||||||
|
factory_parse() bug, previously masked by the read failure, is now
|
||||||
|
exposed and tracked separately).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
|
||||||
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 300912b..788aee2 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
-FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||||
|
+FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
# basic function
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index dc5d3da..8d60b7c 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#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>
|
||||||
|
@@ -137,38 +138,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, NULL);
|
||||||
|
+ 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;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ memcpy(buffer, fw->data, fw->size);
|
||||||
|
+ ret = (int)fw->size;
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
From 82ba594a444a855310fbbe2a5c8ff02f211d8e83 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:17:56 +0200
|
||||||
|
Subject: [PATCH 2/2] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for
|
||||||
|
PineTab2 factory.txt format
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The shipped factory calibration file bes2600_factory.txt on PineTab2
|
||||||
|
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
|
||||||
|
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
|
||||||
|
bt (4). The file terminates with '%%\n' directly after edr_power.
|
||||||
|
|
||||||
|
When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
|
||||||
|
assembles STANDARD_FACTORY with an extra select_efuse_flag section
|
||||||
|
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):
|
||||||
|
|
||||||
|
__STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
|
||||||
|
+ \"%%%%\\n\"
|
||||||
|
|
||||||
|
The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
|
||||||
|
stops after field 30 and factory_parse() returns -1 with:
|
||||||
|
|
||||||
|
bes2600_factory.txt parse fail
|
||||||
|
read and check bes2600/bes2600_factory.txt error
|
||||||
|
factory cali data get failed.
|
||||||
|
|
||||||
|
This was latent until the preceding patch (use request_firmware() for
|
||||||
|
factory.txt read) fixed the path bug that masked the parse failure.
|
||||||
|
|
||||||
|
Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
|
||||||
|
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
|
||||||
|
firmware packages that do ship the select_efuse_flag section.
|
||||||
|
|
||||||
|
Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
|
||||||
|
inconsistently wrapped in a conditional that keyed on
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
|
||||||
|
the call site in sta.c are ungated. With the flag now defaulting to
|
||||||
|
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
|
||||||
|
conditional wrapper around the prototype — the function exists and is
|
||||||
|
used regardless of the factory-parse flag.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
|
||||||
|
succeeds on the shipped factory.txt, factory_cali_data is populated,
|
||||||
|
and dmesg no longer shows the parse-fail / read-and-check-error /
|
||||||
|
factory-cali-data-get-failed sequence.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/wsm.h | 2 --
|
||||||
|
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 788aee2..2dcba09 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
-STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
+STANDARD_FACTORY_EFUSE_FLAG ?= n
|
||||||
|
FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
diff --git a/bes2600/wsm.h b/bes2600/wsm.h
|
||||||
|
index 0673131..22845ac 100644
|
||||||
|
--- a/bes2600/wsm.h
|
||||||
|
+++ b/bes2600/wsm.h
|
||||||
|
@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
|
||||||
|
|
||||||
|
-#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
||||||
|
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
|
||||||
|
-#endif
|
||||||
|
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
From 8732881c5916106539b9071b51710489c57e8d73 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:18:38 +0200
|
||||||
|
Subject: [PATCH] bes2600: thread struct device * through factory
|
||||||
|
request_firmware() call
|
||||||
|
|
||||||
|
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
|
||||||
|
That patch switched the factory calibration read path from filp_open()
|
||||||
|
+ kernel_read() to request_firmware(), but passed dev=NULL to
|
||||||
|
request_firmware() because factory_section_read_file() did not have a
|
||||||
|
struct device * in scope. The resulting logs carry the
|
||||||
|
'(NULL device *):' prefix and do not propagate a udev association.
|
||||||
|
|
||||||
|
Add a module-local static struct device * used as the firmware-class
|
||||||
|
load context, plus a small exported setter:
|
||||||
|
|
||||||
|
static struct device *bes2600_factory_dev;
|
||||||
|
void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
|
||||||
|
Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
|
||||||
|
right after bes2600_platform_data_init() so the platform layer has
|
||||||
|
already had a chance to use the same struct device for its own
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
factory_section_read_file() now passes bes2600_factory_dev (instead
|
||||||
|
of NULL) to request_firmware(). When the factory read happens before
|
||||||
|
probe (not currently the case on PineTab2) the pointer is still NULL
|
||||||
|
and request_firmware() accepts that; no regression.
|
||||||
|
|
||||||
|
No API changes to bes2600_get_factory_cali_data() callers. The
|
||||||
|
char *path parameter remains (it is the firmware-class name fed
|
||||||
|
straight to request_firmware()).
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
|
||||||
|
post-c5 factory diagnostics now carry the SDIO device identity
|
||||||
|
instead of '(NULL device *)'.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 14 +++++++++++++-
|
||||||
|
bes2600/bes2600_factory.h | 3 +++
|
||||||
|
bes2600/bes2600_sdio.c | 4 ++++
|
||||||
|
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index 8d60b7c..1cda447 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -31,6 +31,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
|
||||||
|
@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
|
||||||
|
bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ ret = request_firmware(&fw, path, bes2600_factory_dev);
|
||||||
|
if (ret) {
|
||||||
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
diff --git a/bes2600/bes2600_factory.h b/bes2600/bes2600_factory.h
|
||||||
|
index 3835b0d..7dbe9f8 100644
|
||||||
|
--- a/bes2600/bes2600_factory.h
|
||||||
|
+++ b/bes2600/bes2600_factory.h
|
||||||
|
@@ -199,6 +199,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/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..371ef4f 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
#include "bes2600.h"
|
||||||
|
#include "sbus.h"
|
||||||
|
#include "bes2600_plat.h"
|
||||||
|
+#include "bes2600_factory.h"
|
||||||
|
#include "hwio.h"
|
||||||
|
#include "bes_chardev.h"
|
||||||
|
#include "bes_log.h"
|
||||||
|
@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
+ /* wire struct device into factory.c for request_firmware() context */
|
||||||
|
+ bes2600_factory_set_dev(dev);
|
||||||
|
+
|
||||||
|
self->pdata = bes2600_get_platform_data();
|
||||||
|
self->func = func;
|
||||||
|
self->dev = &func->dev;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+251
@@ -0,0 +1,251 @@
|
|||||||
|
From 9ea8a8e810ee5eb220de700a5c0a6d1153b15130 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Mon, 27 Apr 2026 06:32:41 +0200
|
||||||
|
Subject: [PATCH] bes2600: recover wedged firmware via mmc_hw_reset on link
|
||||||
|
break
|
||||||
|
|
||||||
|
When the LMAC active monitor detects 'link break between lmac and host'
|
||||||
|
(the hw_buf_used==pending watchdog in bes2600_bh_lmac_active_monitor),
|
||||||
|
bes2600_chrdev_wifi_force_close(hw_priv, true) is invoked to tear the
|
||||||
|
device down and prepare for a fresh probe. On the wifi_force_close_work
|
||||||
|
side this calls bes2600_chrdev_do_system_close() which dispatches
|
||||||
|
sbus_ops->power_switch(0).
|
||||||
|
|
||||||
|
On PineTab2 (RK3566 + BES2600WM over SDIO) this recovery path is a
|
||||||
|
no-op:
|
||||||
|
|
||||||
|
* bes2600_sdio_power_down() writes a SYSTEM_CLOSE host-int message,
|
||||||
|
clears MMC_CAP_NONREMOVABLE, and schedules sdio_scan_work, which is
|
||||||
|
the literal one-line stub bes_warn("...this function does
|
||||||
|
nothing\n").
|
||||||
|
* bes2600_sdio_on() (the eventual power_switch(1) counterpart)
|
||||||
|
toggles pdata->powerup, which is NULL on PineTab2 because the
|
||||||
|
wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 device
|
||||||
|
tree node (see arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi:
|
||||||
|
'The reset pin is claimed by sdio_mmcseq, It is better to move it
|
||||||
|
to U-Boot so the OS can use it.').
|
||||||
|
|
||||||
|
Net result: the chip is never reset. The function drivers are not
|
||||||
|
removed (the SDIO core has no signal that the card is gone), the
|
||||||
|
firmware stays wedged, and a subsequent rmmod bes2600 leaves the SDIO
|
||||||
|
function in a half-torn-down state. modprobe bes2600 then fails with
|
||||||
|
'probe with driver bes2600_wlan failed with error -123' (-ENOMEDIUM)
|
||||||
|
on both functions (:1 wifi, :2 BT-companion) until a full system
|
||||||
|
reboot.
|
||||||
|
|
||||||
|
Observed on PineTab2 (linux-pinetab2 6.19.10-danctnix1-1) after ~150
|
||||||
|
minutes of background-scan rejects (wsm_generic_confirm 0x0007,
|
||||||
|
[SCAN] Scan failed (-22)) accumulating until the LMAC stopped
|
||||||
|
acknowledging TX buffers (hw_buf_used:24 pending:24). Reproducible
|
||||||
|
under sustained scan pressure.
|
||||||
|
|
||||||
|
Add a sbus operation bus_reset() that the recovery path can call when
|
||||||
|
power_switch() has no effective chip-reset signal of its own. Provide
|
||||||
|
an SDIO implementation that calls mmc_hw_reset(self->func->card),
|
||||||
|
which on a multi-function SDIO card (PineTab2 binds func 1 for WLAN
|
||||||
|
and func 2 for the BT-companion path) takes the remove-and-rescan
|
||||||
|
path: mmc_sdio_hw_reset() marks the card removed and schedules
|
||||||
|
mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
directly, which on PineTab2 toggles the wifi-reset GPIO via
|
||||||
|
sdio_pwrseq.
|
||||||
|
|
||||||
|
Add bes2600_chrdev_do_bus_reset() as the chrdev-side helper. It
|
||||||
|
invokes the bus op and then waits on probe_done_wq for the SDIO
|
||||||
|
remove() callback to clear sbus_priv, mirroring the wait pattern
|
||||||
|
already used by bes2600_chrdev_do_system_close() so that a subsequent
|
||||||
|
bes2600_switch_wifi(true) sees a clean state and can wait on the
|
||||||
|
fresh probe.
|
||||||
|
|
||||||
|
Wire it into bes2600_chrdev_wifi_force_close_work(): when halt_dev is
|
||||||
|
set (the hard-exception path used by both
|
||||||
|
bes2600_bh_lmac_active_monitor and bes2600_bh_mcu_active_monitor) and
|
||||||
|
the underlying bus implements bus_reset, take the new recovery path;
|
||||||
|
otherwise fall back to the legacy power_switch(0) sequence so this
|
||||||
|
patch is a no-op on USB or any other future bus that does not provide
|
||||||
|
bus_reset.
|
||||||
|
|
||||||
|
mmc_hw_reset() is exported by the MMC core and is the canonical
|
||||||
|
recovery primitive; calling it without holding the SDIO host claim is
|
||||||
|
correct because the multi-func remove-and-rescan path acquires the
|
||||||
|
host claim via the mmc workqueue, and the single-func mmc_power_cycle
|
||||||
|
path does not require the host claim.
|
||||||
|
|
||||||
|
No DT change is required: this works against the existing PineTab2
|
||||||
|
DTS, where the wifi-reset GPIO and the optional sdio_pwrkey GPIO (on
|
||||||
|
v2.0 boards) are both already configured as MMC pwrseq resets.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 29 +++++++++++++
|
||||||
|
drivers/staging/bes2600/bes_chardev.c | 59 +++++++++++++++++++++++++-
|
||||||
|
drivers/staging/bes2600/bes_chardev.h | 1 +
|
||||||
|
drivers/staging/bes2600/sbus.h | 8 ++++
|
||||||
|
4 files changed, 95 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index b9d836fab7af..f7f86d765bba 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -16,6 +16,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>
|
||||||
|
@@ -1777,6 +1778,33 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
|
||||||
|
sdio_work_debug(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Trigger an SDIO bus reset via mmc_hw_reset().
|
||||||
|
+ *
|
||||||
|
+ * With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and
|
||||||
|
+ * func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the
|
||||||
|
+ * remove-and-rescan path: it marks the card removed and schedules
|
||||||
|
+ * mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
+ * the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
+ *
|
||||||
|
+ * With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
+ * directly, which on PineTab2 toggles the wifi-reset GPIO via sdio_pwrseq.
|
||||||
|
+ *
|
||||||
|
+ * In both cases the chip ends up in a freshly reset state, which is the
|
||||||
|
+ * goal of the recovery path.
|
||||||
|
+ *
|
||||||
|
+ * mmc_hw_reset() must be called without holding the SDIO host claim --
|
||||||
|
+ * the multi-func remove-and-rescan path acquires the host claim via the
|
||||||
|
+ * mmc workqueue.
|
||||||
|
+ */
|
||||||
|
+static int bes2600_sdio_bus_reset(struct sbus_priv *self)
|
||||||
|
+{
|
||||||
|
+ if (!self || !self->func || !self->func->card)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ return mmc_hw_reset(self->func->card);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
|
||||||
|
{
|
||||||
|
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
|
||||||
|
@@ -1815,6 +1843,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
|
||||||
|
.gpio_sleep = bes2600_gpio_allow_mcu_sleep,
|
||||||
|
.halt_device = bes2600_sdio_halt_device,
|
||||||
|
.wakeup_source = bes2600_sdio_wakeup_source,
|
||||||
|
+ .bus_reset = bes2600_sdio_bus_reset,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
index 455108a2dd66..b776aab5e062 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
@@ -626,6 +626,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Hard-reset the bus and wait for the bus core to remove the chip.
|
||||||
|
+ *
|
||||||
|
+ * Used by the firmware-wedge recovery path on platforms where the normal
|
||||||
|
+ * power_switch(0) sequence has no effective chip-reset signal. The bus
|
||||||
|
+ * implementation triggers an asynchronous re-detect; this helper waits for
|
||||||
|
+ * the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
|
||||||
|
+ * subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
|
||||||
|
+ * the fresh probe.
|
||||||
|
+ */
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ long status;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops || !priv)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops->bus_reset)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
+ bes_info("trigger bus reset to recover wedged firmware.\n");
|
||||||
|
+
|
||||||
|
+ ret = sbus_ops->bus_reset(priv);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_err("bus_reset failed: %d\n", ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * The bus reset is asynchronous: the bus core schedules a rescan
|
||||||
|
+ * which removes the bound function drivers and then re-detects the
|
||||||
|
+ * chip. Wait for the remove callback to clear sbus_priv. Do not
|
||||||
|
+ * dereference 'priv' after this point -- it may already be freed.
|
||||||
|
+ */
|
||||||
|
+ status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
+ !bes2600_cdev.sbus_priv, HZ * 3);
|
||||||
|
+ WARN_ON(status <= 0);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
bool bes2600_chrdev_is_wifi_opened(void)
|
||||||
|
{
|
||||||
|
bool wifi_opened = false;
|
||||||
|
@@ -726,8 +768,21 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
/* unregister wifi */
|
||||||
|
bes2600_switch_wifi(0);
|
||||||
|
|
||||||
|
- /* power down device if wifi is only opened */
|
||||||
|
- if (bes2600_chrdev_check_system_close()) {
|
||||||
|
+ /*
|
||||||
|
+ * Hard exception with a bus_reset implementation: tear the
|
||||||
|
+ * bus down via mmc_hw_reset() (or equivalent) so the next
|
||||||
|
+ * bringup probes a freshly reset chip. On PineTab2 this is
|
||||||
|
+ * the only effective recovery path -- the existing
|
||||||
|
+ * power_switch(0)/(1) sequence has no chip-reset signal of
|
||||||
|
+ * its own (sdio_pwrseq owns wifi_reset).
|
||||||
|
+ *
|
||||||
|
+ * Soft close, or hard close on a board without bus_reset:
|
||||||
|
+ * fall back to the legacy power_switch(0) sequence.
|
||||||
|
+ */
|
||||||
|
+ if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
|
||||||
|
+ bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
|
||||||
|
+ bes2600_cdev.sbus_priv);
|
||||||
|
+ } else if (bes2600_chrdev_check_system_close()) {
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h
|
||||||
|
index c627bb7c3d65..ca8419eead8f 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.h
|
||||||
|
@@ -60,6 +60,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
|
||||||
|
/* used to control device power down */
|
||||||
|
int bes2600_chrdev_check_system_close(void);
|
||||||
|
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
void bes2600_chrdev_wakeup_bt(void);
|
||||||
|
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
|
||||||
|
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
|
||||||
|
diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h
|
||||||
|
index 1f2c0cda73de..cb9089004041 100644
|
||||||
|
--- a/drivers/staging/bes2600/sbus.h
|
||||||
|
+++ b/drivers/staging/bes2600/sbus.h
|
||||||
|
@@ -75,6 +75,14 @@ struct sbus_ops {
|
||||||
|
void (*halt_device)(struct sbus_priv *self);
|
||||||
|
bool (*wakeup_source)(struct sbus_priv *self);
|
||||||
|
int (*reboot)(struct sbus_priv *self);
|
||||||
|
+ /*
|
||||||
|
+ * Force the host bus to re-detect and re-probe the chip. Called
|
||||||
|
+ * from the firmware-wedge recovery path when power_switch() has no
|
||||||
|
+ * effective chip-reset signal of its own (e.g. PineTab2, where the
|
||||||
|
+ * wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node).
|
||||||
|
+ * Returns 0 on success or a negative errno.
|
||||||
|
+ */
|
||||||
|
+ int (*bus_reset)(struct sbus_priv *self);
|
||||||
|
};
|
||||||
|
|
||||||
|
void bes2600_irq_handler(struct bes2600_common *priv);
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+251
@@ -0,0 +1,251 @@
|
|||||||
|
From 460495803346f71a9d5dcc634180e5368ff9b1dc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Sun, 26 Apr 2026 22:31:58 +0200
|
||||||
|
Subject: [PATCH] bes2600: recover wedged firmware via mmc_hw_reset on link
|
||||||
|
break
|
||||||
|
|
||||||
|
When the LMAC active monitor detects 'link break between lmac and host'
|
||||||
|
(the hw_buf_used==pending watchdog in bes2600_bh_lmac_active_monitor),
|
||||||
|
bes2600_chrdev_wifi_force_close(hw_priv, true) is invoked to tear the
|
||||||
|
device down and prepare for a fresh probe. On the wifi_force_close_work
|
||||||
|
side this calls bes2600_chrdev_do_system_close() which dispatches
|
||||||
|
sbus_ops->power_switch(0).
|
||||||
|
|
||||||
|
On PineTab2 (RK3566 + BES2600WM over SDIO) this recovery path is a
|
||||||
|
no-op:
|
||||||
|
|
||||||
|
* bes2600_sdio_power_down() writes a SYSTEM_CLOSE host-int message,
|
||||||
|
clears MMC_CAP_NONREMOVABLE, and schedules sdio_scan_work, which is
|
||||||
|
the literal one-line stub bes_warn("...this function does
|
||||||
|
nothing\n").
|
||||||
|
* bes2600_sdio_on() (the eventual power_switch(1) counterpart)
|
||||||
|
toggles pdata->powerup, which is NULL on PineTab2 because the
|
||||||
|
wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 device
|
||||||
|
tree node (see arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi:
|
||||||
|
'The reset pin is claimed by sdio_mmcseq, It is better to move it
|
||||||
|
to U-Boot so the OS can use it.').
|
||||||
|
|
||||||
|
Net result: the chip is never reset. The function drivers are not
|
||||||
|
removed (the SDIO core has no signal that the card is gone), the
|
||||||
|
firmware stays wedged, and a subsequent rmmod bes2600 leaves the SDIO
|
||||||
|
function in a half-torn-down state. modprobe bes2600 then fails with
|
||||||
|
'probe with driver bes2600_wlan failed with error -123' (-ENOMEDIUM)
|
||||||
|
on both functions (:1 wifi, :2 BT-companion) until a full system
|
||||||
|
reboot.
|
||||||
|
|
||||||
|
Observed on PineTab2 (linux-pinetab2 6.19.10-danctnix1-1) after ~150
|
||||||
|
minutes of background-scan rejects (wsm_generic_confirm 0x0007,
|
||||||
|
[SCAN] Scan failed (-22)) accumulating until the LMAC stopped
|
||||||
|
acknowledging TX buffers (hw_buf_used:24 pending:24). Reproducible
|
||||||
|
under sustained scan pressure.
|
||||||
|
|
||||||
|
Add a sbus operation bus_reset() that the recovery path can call when
|
||||||
|
power_switch() has no effective chip-reset signal of its own. Provide
|
||||||
|
an SDIO implementation that calls mmc_hw_reset(self->func->card),
|
||||||
|
which on a multi-function SDIO card (PineTab2 binds func 1 for WLAN
|
||||||
|
and func 2 for the BT-companion path) takes the remove-and-rescan
|
||||||
|
path: mmc_sdio_hw_reset() marks the card removed and schedules
|
||||||
|
mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
directly, which on PineTab2 toggles the wifi-reset GPIO via
|
||||||
|
sdio_pwrseq.
|
||||||
|
|
||||||
|
Add bes2600_chrdev_do_bus_reset() as the chrdev-side helper. It
|
||||||
|
invokes the bus op and then waits on probe_done_wq for the SDIO
|
||||||
|
remove() callback to clear sbus_priv, mirroring the wait pattern
|
||||||
|
already used by bes2600_chrdev_do_system_close() so that a subsequent
|
||||||
|
bes2600_switch_wifi(true) sees a clean state and can wait on the
|
||||||
|
fresh probe.
|
||||||
|
|
||||||
|
Wire it into bes2600_chrdev_wifi_force_close_work(): when halt_dev is
|
||||||
|
set (the hard-exception path used by both
|
||||||
|
bes2600_bh_lmac_active_monitor and bes2600_bh_mcu_active_monitor) and
|
||||||
|
the underlying bus implements bus_reset, take the new recovery path;
|
||||||
|
otherwise fall back to the legacy power_switch(0) sequence so this
|
||||||
|
patch is a no-op on USB or any other future bus that does not provide
|
||||||
|
bus_reset.
|
||||||
|
|
||||||
|
mmc_hw_reset() is exported by the MMC core and is the canonical
|
||||||
|
recovery primitive; calling it without holding the SDIO host claim is
|
||||||
|
correct because the multi-func remove-and-rescan path acquires the
|
||||||
|
host claim via the mmc workqueue, and the single-func mmc_power_cycle
|
||||||
|
path does not require the host claim.
|
||||||
|
|
||||||
|
No DT change is required: this works against the existing PineTab2
|
||||||
|
DTS, where the wifi-reset GPIO and the optional sdio_pwrkey GPIO (on
|
||||||
|
v2.0 boards) are both already configured as MMC pwrseq resets.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 29 +++++++++++++++++++++
|
||||||
|
bes2600/bes_chardev.c | 59 ++++++++++++++++++++++++++++++++++++++++--
|
||||||
|
bes2600/bes_chardev.h | 1 +
|
||||||
|
bes2600/sbus.h | 8 ++++++
|
||||||
|
4 files changed, 95 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index 3e04e8c..e5840c8 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -16,6 +16,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>
|
||||||
|
@@ -1777,6 +1778,33 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
|
||||||
|
sdio_work_debug(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Trigger an SDIO bus reset via mmc_hw_reset().
|
||||||
|
+ *
|
||||||
|
+ * With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and
|
||||||
|
+ * func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the
|
||||||
|
+ * remove-and-rescan path: it marks the card removed and schedules
|
||||||
|
+ * mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
+ * the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
+ *
|
||||||
|
+ * With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
+ * directly, which on PineTab2 toggles the wifi-reset GPIO via sdio_pwrseq.
|
||||||
|
+ *
|
||||||
|
+ * In both cases the chip ends up in a freshly reset state, which is the
|
||||||
|
+ * goal of the recovery path.
|
||||||
|
+ *
|
||||||
|
+ * mmc_hw_reset() must be called without holding the SDIO host claim --
|
||||||
|
+ * the multi-func remove-and-rescan path acquires the host claim via the
|
||||||
|
+ * mmc workqueue.
|
||||||
|
+ */
|
||||||
|
+static int bes2600_sdio_bus_reset(struct sbus_priv *self)
|
||||||
|
+{
|
||||||
|
+ if (!self || !self->func || !self->func->card)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ return mmc_hw_reset(self->func->card);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
|
||||||
|
{
|
||||||
|
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
|
||||||
|
@@ -1815,6 +1843,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
|
||||||
|
.gpio_sleep = bes2600_gpio_allow_mcu_sleep,
|
||||||
|
.halt_device = bes2600_sdio_halt_device,
|
||||||
|
.wakeup_source = bes2600_sdio_wakeup_source,
|
||||||
|
+ .bus_reset = bes2600_sdio_bus_reset,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index a02d6d9..d1375bc 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -442,6 +442,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Hard-reset the bus and wait for the bus core to remove the chip.
|
||||||
|
+ *
|
||||||
|
+ * Used by the firmware-wedge recovery path on platforms where the normal
|
||||||
|
+ * power_switch(0) sequence has no effective chip-reset signal. The bus
|
||||||
|
+ * implementation triggers an asynchronous re-detect; this helper waits for
|
||||||
|
+ * the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
|
||||||
|
+ * subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
|
||||||
|
+ * the fresh probe.
|
||||||
|
+ */
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ long status;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops || !priv)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops->bus_reset)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
+ bes_info("trigger bus reset to recover wedged firmware.\n");
|
||||||
|
+
|
||||||
|
+ ret = sbus_ops->bus_reset(priv);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_err("bus_reset failed: %d\n", ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * The bus reset is asynchronous: the bus core schedules a rescan
|
||||||
|
+ * which removes the bound function drivers and then re-detects the
|
||||||
|
+ * chip. Wait for the remove callback to clear sbus_priv. Do not
|
||||||
|
+ * dereference 'priv' after this point -- it may already be freed.
|
||||||
|
+ */
|
||||||
|
+ status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
+ !bes2600_cdev.sbus_priv, HZ * 3);
|
||||||
|
+ WARN_ON(status <= 0);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
bool bes2600_chrdev_is_wifi_opened(void)
|
||||||
|
{
|
||||||
|
bool wifi_opened = false;
|
||||||
|
@@ -540,8 +582,21 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
/* unregister wifi */
|
||||||
|
bes2600_switch_wifi(0);
|
||||||
|
|
||||||
|
- /* power down device if wifi is only opened */
|
||||||
|
- if (bes2600_chrdev_check_system_close()) {
|
||||||
|
+ /*
|
||||||
|
+ * Hard exception with a bus_reset implementation: tear the
|
||||||
|
+ * bus down via mmc_hw_reset() (or equivalent) so the next
|
||||||
|
+ * bringup probes a freshly reset chip. On PineTab2 this is
|
||||||
|
+ * the only effective recovery path -- the existing
|
||||||
|
+ * power_switch(0)/(1) sequence has no chip-reset signal of
|
||||||
|
+ * its own (sdio_pwrseq owns wifi_reset).
|
||||||
|
+ *
|
||||||
|
+ * Soft close, or hard close on a board without bus_reset:
|
||||||
|
+ * fall back to the legacy power_switch(0) sequence.
|
||||||
|
+ */
|
||||||
|
+ if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
|
||||||
|
+ bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
|
||||||
|
+ bes2600_cdev.sbus_priv);
|
||||||
|
+ } else if (bes2600_chrdev_check_system_close()) {
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
diff --git a/bes2600/bes_chardev.h b/bes2600/bes_chardev.h
|
||||||
|
index 15602ba..3f0c59b 100644
|
||||||
|
--- a/bes2600/bes_chardev.h
|
||||||
|
+++ b/bes2600/bes_chardev.h
|
||||||
|
@@ -60,6 +60,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
|
||||||
|
/* used to control device power down */
|
||||||
|
int bes2600_chrdev_check_system_close(void);
|
||||||
|
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
void bes2600_chrdev_wakeup_bt(void);
|
||||||
|
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
|
||||||
|
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
|
||||||
|
diff --git a/bes2600/sbus.h b/bes2600/sbus.h
|
||||||
|
index 1f2c0cd..cb90890 100644
|
||||||
|
--- a/bes2600/sbus.h
|
||||||
|
+++ b/bes2600/sbus.h
|
||||||
|
@@ -75,6 +75,14 @@ struct sbus_ops {
|
||||||
|
void (*halt_device)(struct sbus_priv *self);
|
||||||
|
bool (*wakeup_source)(struct sbus_priv *self);
|
||||||
|
int (*reboot)(struct sbus_priv *self);
|
||||||
|
+ /*
|
||||||
|
+ * Force the host bus to re-detect and re-probe the chip. Called
|
||||||
|
+ * from the firmware-wedge recovery path when power_switch() has no
|
||||||
|
+ * effective chip-reset signal of its own (e.g. PineTab2, where the
|
||||||
|
+ * wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node).
|
||||||
|
+ * Returns 0 on success or a negative errno.
|
||||||
|
+ */
|
||||||
|
+ int (*bus_reset)(struct sbus_priv *self);
|
||||||
|
};
|
||||||
|
|
||||||
|
void bes2600_irq_handler(struct bes2600_common *priv);
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
From d1de35c62930b1bc035d3863d75901356548b6f0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 16:54:07 +0200
|
||||||
|
Subject: [PATCH] bes2600: self-detect when firmware does not honor PSM and
|
||||||
|
skip the cycle
|
||||||
|
|
||||||
|
The c6 series fixed several host-side bookkeeping bugs around PSM
|
||||||
|
transitions, but didn't address the underlying contract: this chip's
|
||||||
|
firmware (BES2600 with the Bestechnic Dec 2023 build that ships on
|
||||||
|
PineTab2 and most danctnix images) silently drops every WSM_set_pm
|
||||||
|
request without emitting the corresponding PM_INDICATION. The driver's
|
||||||
|
own power_down_work delayed work calls bes2600_pwr_enter_lp_mode every
|
||||||
|
~10s; without firmware acknowledgment each call burns 5s on
|
||||||
|
wait_for_completion_timeout(pm_enter_cmpl, 5*HZ) and produces a
|
||||||
|
recurring three-line cascade in dmesg:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
Confirmed by tripwire instrumentation on PineTab2 (linux-pinetab2
|
||||||
|
6.19.10-danctnix1, ohm) running the c5+c6 stack: zero
|
||||||
|
wsm_set_pm_indication() invocations across an entire boot, while
|
||||||
|
bes2600_pwr_enter_lp_mode timed out repeatedly, and
|
||||||
|
bes2600_sdio_active() consistently saw BES_SLAVE_STATUS_REG_ID return
|
||||||
|
0x2f (every "ready" bit set except MCU_WAKEUP_READY (bit 4) - the
|
||||||
|
firmware reports "I'm awake, there's nothing to wake from").
|
||||||
|
|
||||||
|
This patch makes the driver self-heal:
|
||||||
|
|
||||||
|
* struct bes2600_pwr_t gains pm_unsupported (bool) and
|
||||||
|
pm_consecutive_timeouts (unsigned int). Both initialised to
|
||||||
|
0/false.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode early-returns -EOPNOTSUPP when
|
||||||
|
pm_unsupported is set. Skips the per-VIF set_pm round-trip and
|
||||||
|
the wait_for_completion entirely.
|
||||||
|
|
||||||
|
* On the cmpxchg-success branch of the timeout path, we increment
|
||||||
|
pm_consecutive_timeouts. When it crosses
|
||||||
|
BES2600_PM_UNSUPPORTED_THRESHOLD (3, ~15s of trying), we latch
|
||||||
|
pm_unsupported = true and force chip_pm_state = ACTIVE so that
|
||||||
|
bes2600_pwr_device_exit_lp_mode's c6.2 skip branch covers the
|
||||||
|
wake side (no gpio_wake / sbus_active / WSM_set_operational_mode
|
||||||
|
reissue past the first one).
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed resets pm_consecutive_timeouts to 0
|
||||||
|
on any incoming PM indication, and clears pm_unsupported if it
|
||||||
|
was previously latched. So a firmware update that fixes PM_IND
|
||||||
|
delivery automatically re-enables PSM transitions without a
|
||||||
|
driver rebuild.
|
||||||
|
|
||||||
|
mac80211's PSM requests via bes2600_set_pm() still flow to the
|
||||||
|
firmware unchanged; they just don't have host-side timeouts so they
|
||||||
|
remain silent regardless of firmware acknowledgment. Power
|
||||||
|
consumption goes up if the firmware actually CAN do PSM (we'd be
|
||||||
|
keeping the chip awake unnecessarily), but on a chip where the
|
||||||
|
counter trips this trade-off is forced anyway: the chip stayed awake
|
||||||
|
under the broken cascade as well, just with constant SDIO churn.
|
||||||
|
|
||||||
|
Net effect on dmesg: after ~15s of boot, the three-line cascade stops
|
||||||
|
firing entirely. The firmware-side wedge is observed once per boot
|
||||||
|
(captured by the pm_unsupported latch) instead of per-cycle.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++-
|
||||||
|
drivers/staging/bes2600/bes_pwr.h | 9 ++++
|
||||||
|
2 files changed, 78 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index d54e1a0bab0c..ebaa42e3e61e 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -467,6 +467,45 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
bes_devel("device enter sleep\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero
|
||||||
|
+ * PM_INDICATIONs received) before we conclude the firmware does not
|
||||||
|
+ * honor host-driven PSM and switch to a sticky skip path.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_PM_UNSUPPORTED_THRESHOLD 3
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the
|
||||||
|
+ * c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode.
|
||||||
|
+ * Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode
|
||||||
|
+ * timeouts with zero PM_INDICATIONs.
|
||||||
|
+ */
|
||||||
|
+static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+ bes_warn("PSM not honored (%u timeouts), switching to skip mode\n",
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts);
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = true;
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Hold the MCU wake-flag bit permanently. Without this, every
|
||||||
|
+ * sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX)
|
||||||
|
+ * when gpio_wakup_flags == 0, drives the GPIO high and msleeps
|
||||||
|
+ * 10 ms per RX. With ~50 RX/s of beacons + multicast that's
|
||||||
|
+ * ~50%% of the bes_sdio workqueue thread blocked in msleep,
|
||||||
|
+ * which directly caps RX throughput. Holding the MCU bit makes
|
||||||
|
+ * those calls bit-only bookkeeping (gpio_wakeup = (flags == 0)
|
||||||
|
+ * stays false, no GPIO toggle, no msleep). The bit is never
|
||||||
|
+ * cleared once pm_unsupported is set because
|
||||||
|
+ * bes2600_pwr_device_enter_lp_mode is unreachable under the
|
||||||
|
+ * early-return.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
@@ -476,6 +515,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * Sticky early-return when we've previously concluded the firmware
|
||||||
|
+ * doesn't honor PSM. Each attempt would otherwise burn 5s on a
|
||||||
|
+ * doomed wait_for_completion_timeout and produce a noisy three-line
|
||||||
|
+ * cascade in dmesg every time power_down_work retries (every
|
||||||
|
+ * ~10s). The chip stays in active mode, which on this firmware is
|
||||||
|
+ * the de-facto state anyway.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
/* set interface low power configuration */
|
||||||
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||||
|
#ifdef P2P_MULTIVIF
|
||||||
|
@@ -571,6 +621,9 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
timeouts++;
|
||||||
|
+ if (++hw_priv->bes_power.pm_consecutive_timeouts
|
||||||
|
+ >= BES2600_PM_UNSUPPORTED_THRESHOLD)
|
||||||
|
+ bes2600_pwr_latch_pm_unsupported(hw_priv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -609,7 +662,8 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* GPIO stays high and the bit clear here is purely
|
||||||
|
* bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
*/
|
||||||
|
- if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ if (!hw_priv->bes_power.pm_unsupported &&
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
@@ -932,6 +986,8 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
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);
|
||||||
|
@@ -1321,6 +1377,18 @@ void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
* indication can prime a future wait against a freshly
|
||||||
|
* reinit_completion()'ed state.
|
||||||
|
*/
|
||||||
|
+ /*
|
||||||
|
+ * Any PM indication, whatever its psmode, proves the firmware is
|
||||||
|
+ * actually emitting them. Reset the consecutive-timeout counter
|
||||||
|
+ * so a transient stall doesn't permanently disable PSM, and clear
|
||||||
|
+ * pm_unsupported if a previous run had latched it.
|
||||||
|
+ */
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts = 0;
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported) {
|
||||||
|
+ bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n");
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_LP);
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
index 6bc44acd7501..92de90b398c6 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
@@ -121,6 +121,15 @@ struct bes2600_pwr_t
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
atomic_t chip_pm_state;
|
||||||
|
+ /*
|
||||||
|
+ * Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD
|
||||||
|
+ * consecutive enter_lp_mode timeouts with zero PM_INDICATIONs
|
||||||
|
+ * received from firmware. Indicates this chip's firmware does
|
||||||
|
+ * not honor host-driven PSM transitions; further attempts are
|
||||||
|
+ * skipped to avoid the 5s timeout cascade.
|
||||||
|
+ */
|
||||||
|
+ bool pm_unsupported;
|
||||||
|
+ unsigned int pm_consecutive_timeouts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
From f12e87002576f094c441ac6c945a451c88868592 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 16:54:06 +0200
|
||||||
|
Subject: [PATCH] bes2600: self-detect when firmware does not honor PSM and
|
||||||
|
skip the cycle
|
||||||
|
|
||||||
|
The c6 series fixed several host-side bookkeeping bugs around PSM
|
||||||
|
transitions, but didn't address the underlying contract: this chip's
|
||||||
|
firmware (BES2600 with the Bestechnic Dec 2023 build that ships on
|
||||||
|
PineTab2 and most danctnix images) silently drops every WSM_set_pm
|
||||||
|
request without emitting the corresponding PM_INDICATION. The driver's
|
||||||
|
own power_down_work delayed work calls bes2600_pwr_enter_lp_mode every
|
||||||
|
~10s; without firmware acknowledgment each call burns 5s on
|
||||||
|
wait_for_completion_timeout(pm_enter_cmpl, 5*HZ) and produces a
|
||||||
|
recurring three-line cascade in dmesg:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
Confirmed by tripwire instrumentation on PineTab2 (linux-pinetab2
|
||||||
|
6.19.10-danctnix1, ohm) running the c5+c6 stack: zero
|
||||||
|
wsm_set_pm_indication() invocations across an entire boot, while
|
||||||
|
bes2600_pwr_enter_lp_mode timed out repeatedly, and
|
||||||
|
bes2600_sdio_active() consistently saw BES_SLAVE_STATUS_REG_ID return
|
||||||
|
0x2f (every "ready" bit set except MCU_WAKEUP_READY (bit 4) - the
|
||||||
|
firmware reports "I'm awake, there's nothing to wake from").
|
||||||
|
|
||||||
|
This patch makes the driver self-heal:
|
||||||
|
|
||||||
|
* struct bes2600_pwr_t gains pm_unsupported (bool) and
|
||||||
|
pm_consecutive_timeouts (unsigned int). Both initialised to
|
||||||
|
0/false.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode early-returns -EOPNOTSUPP when
|
||||||
|
pm_unsupported is set. Skips the per-VIF set_pm round-trip and
|
||||||
|
the wait_for_completion entirely.
|
||||||
|
|
||||||
|
* On the cmpxchg-success branch of the timeout path, we increment
|
||||||
|
pm_consecutive_timeouts. When it crosses
|
||||||
|
BES2600_PM_UNSUPPORTED_THRESHOLD (3, ~15s of trying), we latch
|
||||||
|
pm_unsupported = true and force chip_pm_state = ACTIVE so that
|
||||||
|
bes2600_pwr_device_exit_lp_mode's c6.2 skip branch covers the
|
||||||
|
wake side (no gpio_wake / sbus_active / WSM_set_operational_mode
|
||||||
|
reissue past the first one).
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed resets pm_consecutive_timeouts to 0
|
||||||
|
on any incoming PM indication, and clears pm_unsupported if it
|
||||||
|
was previously latched. So a firmware update that fixes PM_IND
|
||||||
|
delivery automatically re-enables PSM transitions without a
|
||||||
|
driver rebuild.
|
||||||
|
|
||||||
|
mac80211's PSM requests via bes2600_set_pm() still flow to the
|
||||||
|
firmware unchanged; they just don't have host-side timeouts so they
|
||||||
|
remain silent regardless of firmware acknowledgment. Power
|
||||||
|
consumption goes up if the firmware actually CAN do PSM (we'd be
|
||||||
|
keeping the chip awake unnecessarily), but on a chip where the
|
||||||
|
counter trips this trade-off is forced anyway: the chip stayed awake
|
||||||
|
under the broken cascade as well, just with constant SDIO churn.
|
||||||
|
|
||||||
|
Net effect on dmesg: after ~15s of boot, the three-line cascade stops
|
||||||
|
firing entirely. The firmware-side wedge is observed once per boot
|
||||||
|
(captured by the pm_unsupported latch) instead of per-cycle.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/bes_pwr.h | 9 ++++++
|
||||||
|
2 files changed, 78 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index b7b6c2f..620acef 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -467,6 +467,45 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
bes_devel("device enter sleep\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero
|
||||||
|
+ * PM_INDICATIONs received) before we conclude the firmware does not
|
||||||
|
+ * honor host-driven PSM and switch to a sticky skip path.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_PM_UNSUPPORTED_THRESHOLD 3
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the
|
||||||
|
+ * c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode.
|
||||||
|
+ * Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode
|
||||||
|
+ * timeouts with zero PM_INDICATIONs.
|
||||||
|
+ */
|
||||||
|
+static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+ bes_warn("PSM not honored (%u timeouts), switching to skip mode\n",
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts);
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = true;
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Hold the MCU wake-flag bit permanently. Without this, every
|
||||||
|
+ * sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX)
|
||||||
|
+ * when gpio_wakup_flags == 0, drives the GPIO high and msleeps
|
||||||
|
+ * 10 ms per RX. With ~50 RX/s of beacons + multicast that's
|
||||||
|
+ * ~50%% of the bes_sdio workqueue thread blocked in msleep,
|
||||||
|
+ * which directly caps RX throughput. Holding the MCU bit makes
|
||||||
|
+ * those calls bit-only bookkeeping (gpio_wakeup = (flags == 0)
|
||||||
|
+ * stays false, no GPIO toggle, no msleep). The bit is never
|
||||||
|
+ * cleared once pm_unsupported is set because
|
||||||
|
+ * bes2600_pwr_device_enter_lp_mode is unreachable under the
|
||||||
|
+ * early-return.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
@@ -476,6 +515,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * Sticky early-return when we've previously concluded the firmware
|
||||||
|
+ * doesn't honor PSM. Each attempt would otherwise burn 5s on a
|
||||||
|
+ * doomed wait_for_completion_timeout and produce a noisy three-line
|
||||||
|
+ * cascade in dmesg every time power_down_work retries (every
|
||||||
|
+ * ~10s). The chip stays in active mode, which on this firmware is
|
||||||
|
+ * the de-facto state anyway.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
/* set interface low power configuration */
|
||||||
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||||
|
#ifdef P2P_MULTIVIF
|
||||||
|
@@ -571,6 +621,9 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
timeouts++;
|
||||||
|
+ if (++hw_priv->bes_power.pm_consecutive_timeouts
|
||||||
|
+ >= BES2600_PM_UNSUPPORTED_THRESHOLD)
|
||||||
|
+ bes2600_pwr_latch_pm_unsupported(hw_priv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -609,7 +662,8 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* GPIO stays high and the bit clear here is purely
|
||||||
|
* bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
*/
|
||||||
|
- if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ if (!hw_priv->bes_power.pm_unsupported &&
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
@@ -932,6 +986,8 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
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);
|
||||||
|
@@ -1321,6 +1377,18 @@ void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
* indication can prime a future wait against a freshly
|
||||||
|
* reinit_completion()'ed state.
|
||||||
|
*/
|
||||||
|
+ /*
|
||||||
|
+ * Any PM indication, whatever its psmode, proves the firmware is
|
||||||
|
+ * actually emitting them. Reset the consecutive-timeout counter
|
||||||
|
+ * so a transient stall doesn't permanently disable PSM, and clear
|
||||||
|
+ * pm_unsupported if a previous run had latched it.
|
||||||
|
+ */
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts = 0;
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported) {
|
||||||
|
+ bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n");
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_LP);
|
||||||
|
diff --git a/bes2600/bes_pwr.h b/bes2600/bes_pwr.h
|
||||||
|
index 6bc44ac..92de90b 100644
|
||||||
|
--- a/bes2600/bes_pwr.h
|
||||||
|
+++ b/bes2600/bes_pwr.h
|
||||||
|
@@ -121,6 +121,15 @@ struct bes2600_pwr_t
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
atomic_t chip_pm_state;
|
||||||
|
+ /*
|
||||||
|
+ * Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD
|
||||||
|
+ * consecutive enter_lp_mode timeouts with zero PM_INDICATIONs
|
||||||
|
+ * received from firmware. Indicates this chip's firmware does
|
||||||
|
+ * not honor host-driven PSM transitions; further attempts are
|
||||||
|
+ * skipped to avoid the 5s timeout cascade.
|
||||||
|
+ */
|
||||||
|
+ bool pm_unsupported;
|
||||||
|
+ unsigned int pm_consecutive_timeouts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
From 80178ec9b1f83aed1dcce9ea7ca02bc81341ba01 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:37:45 +0200
|
||||||
|
Subject: [PATCH] bes2600: gate device LP-mode entry on successful per-VIF
|
||||||
|
firmware handshake
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() drives the transition to low-power for each
|
||||||
|
associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on
|
||||||
|
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
|
||||||
|
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Two bugs:
|
||||||
|
|
||||||
|
1. A failed wsm_set_pm() only logs an error, then still falls into
|
||||||
|
wait_for_completion_timeout() on a completion the firmware will
|
||||||
|
never post (the set-mode command never reached it). The loop
|
||||||
|
therefore always blocks the full 5 s, logs a second error, and
|
||||||
|
proceeds.
|
||||||
|
|
||||||
|
2. A genuine wait-timeout (firmware received the set-mode command but
|
||||||
|
never posted the indication) also only logs a warning. The code
|
||||||
|
then drops to bes2600_pwr_device_enter_lp_mode(), handing the
|
||||||
|
device subsystem an inconsistent view of mac-layer state.
|
||||||
|
|
||||||
|
On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
|
||||||
|
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
|
||||||
|
message flooding dmesg every 5-10 s when the interface is associated
|
||||||
|
and idle. Sending the device to LP in that state cascades into the
|
||||||
|
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
|
||||||
|
WARN splat.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Add a 'timeouts' counter; bump it on both failure paths.
|
||||||
|
- Skip the wait_for_completion entirely when wsm_set_pm() failed
|
||||||
|
(there is no completion to wait for).
|
||||||
|
- Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
|
||||||
|
handshake reached firmware-ACKed completion; otherwise return
|
||||||
|
-ETIMEDOUT and leave the device in its current power state.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
Post-patch the handshake still fails on this particular firmware
|
||||||
|
revision (separate root-cause investigation outside this patch), but
|
||||||
|
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
|
||||||
|
and destabilising the SDIO path.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 20 +++++++++++++++++---
|
||||||
|
1 file changed, 17 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index e7a1045..f62ae22 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
int i = 0;
|
||||||
|
struct bes2600_vif *priv;
|
||||||
|
int ret = 0;
|
||||||
|
+ int timeouts = 0;
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
bes_err("%s, set operation mode fail\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
- if (!status)
|
||||||
|
+ if (!status) {
|
||||||
|
bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* set device low power configuration */
|
||||||
|
- bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ /*
|
||||||
|
+ * 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
|
||||||
|
+ ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
From 4ab8c790304206abd134de48c878b637a70f3c59 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:05:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: gate PM indication completion on pending request and
|
||||||
|
track chip state
|
||||||
|
|
||||||
|
When mac80211 toggles PSM on the BES2600, the host sends WSM set_pm
|
||||||
|
and waits up to 5 s on bes_power.pm_enter_cmpl for a firmware-side
|
||||||
|
PM-changed indication confirming the transition. Three sequenced
|
||||||
|
flaws make the wait-and-confirm racy and leave host/chip bookkeeping
|
||||||
|
desynced when anything misfires:
|
||||||
|
|
||||||
|
1) bes2600_pwr_notify_ps_changed() unconditionally fires
|
||||||
|
complete(pm_enter_cmpl) for any non-active psmode. It does not
|
||||||
|
check whether a host-initiated set_pm is actually pending. A
|
||||||
|
spontaneous indication (firmware-internal coex move,
|
||||||
|
idle-driven aging) primes the completion, and the next host-
|
||||||
|
driven enter_lp_mode sees a false success on its first
|
||||||
|
wait_for_completion_timeout.
|
||||||
|
|
||||||
|
2) The wait/reinit ordering in bes2600_pwr_enter_lp_mode is
|
||||||
|
|
||||||
|
status = wait_for_completion_timeout(...);
|
||||||
|
atomic_set(pm_set_in_process, 0);
|
||||||
|
reinit_completion(...);
|
||||||
|
|
||||||
|
If an indication arrives between wait_for_completion_timeout
|
||||||
|
returning with status==1 and reinit_completion, the next
|
||||||
|
enter_lp_mode iteration's wait can also see false success. The
|
||||||
|
reinit must happen *before* we start the new request, not
|
||||||
|
after handling the previous one.
|
||||||
|
|
||||||
|
3) On wait_pm_ind timeout, the driver returns -ETIMEDOUT and walks
|
||||||
|
away. It does not record that the firmware's actual PM state
|
||||||
|
is no longer known to the host. Subsequent wake paths
|
||||||
|
(gpio_wake / sbus_active) assume the chip is still active and
|
||||||
|
hit deterministic SDIO failures when the firmware has
|
||||||
|
transitioned anyway.
|
||||||
|
|
||||||
|
This patch is the safe-prerequisite half of a wider fix:
|
||||||
|
|
||||||
|
* bes_pwr.h gains enum bes2600_chip_pm_state {ACTIVE, LP, UNKNOWN}
|
||||||
|
and bes_power.chip_pm_state. Its job is to track what the host
|
||||||
|
has *seen the firmware confirm*, not what the host has
|
||||||
|
requested. Initialised to ACTIVE in bes2600_pwr_init().
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed() unconditionally updates
|
||||||
|
chip_pm_state on every indication, but only fires
|
||||||
|
complete(pm_enter_cmpl) when atomic_cmpxchg(pm_set_in_process,
|
||||||
|
1, 0) succeeds. A spontaneous indication can no longer prime a
|
||||||
|
waiter that will only set up its request afterwards.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode() now reinit_completion()s before
|
||||||
|
setting pm_set_in_process and sending wsm_set_pm. After a
|
||||||
|
timeout, it cmpxchgs pm_set_in_process back to 0 (so a late
|
||||||
|
indication cannot prime the next iteration) and on the win-
|
||||||
|
cmpxchg branch records chip_pm_state=UNKNOWN.
|
||||||
|
|
||||||
|
A follow-up patch consumes chip_pm_state on the wake side
|
||||||
|
(bes2600_pwr_device_exit_lp_mode + bes2600_gpio_wakeup_mcu) to fix
|
||||||
|
the deterministic "active mcu fail" cycle this state-record
|
||||||
|
enables a fix for. Splitting the work this way keeps the lock-free
|
||||||
|
race fix small and reviewable on its own.
|
||||||
|
|
||||||
|
No new locks, no behaviour change on the success path. Only the
|
||||||
|
recovery path (timeout + spontaneous indication) gains correctness.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 94 ++++++++++++++++++++++++++++---
|
||||||
|
drivers/staging/bes2600/bes_pwr.h | 15 +++++
|
||||||
|
2 files changed, 100 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index f62ae226d295..de46e5826ee7 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -524,7 +524,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
|
||||||
|
__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
|
||||||
|
priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
|
||||||
|
+ /*
|
||||||
|
+ * Reinit BEFORE the WSM goes out, so a stale
|
||||||
|
+ * indication from a previous cycle cannot have
|
||||||
|
+ * primed pm_enter_cmpl. From here until the
|
||||||
|
+ * indication callback's cmpxchg(1->0) on
|
||||||
|
+ * pm_set_in_process, only the indication for
|
||||||
|
+ * THIS request can complete the wait.
|
||||||
|
+ */
|
||||||
|
+ reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
|
||||||
|
+
|
||||||
|
ret = bes2600_set_pm(priv, &priv->powersave_mode);
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
@@ -535,11 +545,33 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
- atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
- reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
- timeouts++;
|
||||||
|
+ /*
|
||||||
|
+ * The indication callback only fires
|
||||||
|
+ * complete() when it observes
|
||||||
|
+ * pm_set_in_process == 1; cmpxchg it
|
||||||
|
+ * to 0 here so a late indication
|
||||||
|
+ * cannot prime the next wait.
|
||||||
|
+ *
|
||||||
|
+ * If we win the cmpxchg, this is a
|
||||||
|
+ * real timeout: the firmware's PS
|
||||||
|
+ * state is unknown to us. Mark it as
|
||||||
|
+ * such so the next wake path can
|
||||||
|
+ * probe before assuming the chip is
|
||||||
|
+ * still active.
|
||||||
|
+ *
|
||||||
|
+ * If we lose the cmpxchg, the
|
||||||
|
+ * indication arrived between the
|
||||||
|
+ * wait timing out and us getting
|
||||||
|
+ * here; treat as success.
|
||||||
|
+ */
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
@@ -554,10 +586,34 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
* the BES2600.
|
||||||
|
*/
|
||||||
|
- if (timeouts == 0)
|
||||||
|
+ if (timeouts == 0) {
|
||||||
|
bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
- else
|
||||||
|
+ } else {
|
||||||
|
+ /*
|
||||||
|
+ * device_enter_lp_mode() was skipped (one or more VIFs
|
||||||
|
+ * timed out waiting for the firmware indication) so its
|
||||||
|
+ * gpio_sleep(MCU) - which drops the wake-flag bit and, if
|
||||||
|
+ * no other subsystem holds the wake, drives the GPIO low -
|
||||||
|
+ * never ran. Without it the bit stays asserted, and the
|
||||||
|
+ * next bes2600_pwr_device_exit_lp_mode() calls
|
||||||
|
+ * gpio_wake(MCU) into a "bit already set" no-op: the GPIO
|
||||||
|
+ * never re-edges, sbus_active() exhausts its 200x2ms
|
||||||
|
+ * MCU_WAKEUP_READY budget against an unwoken chip, and
|
||||||
|
+ * the first TX after idle stalls for several seconds.
|
||||||
|
+ *
|
||||||
|
+ * Drop the MCU wake-flag bit explicitly here so the next
|
||||||
|
+ * wake injects a real GPIO edge. gpio_allow_mcu_sleep
|
||||||
|
+ * preserves multi-subsystem semantics: it only drives the
|
||||||
|
+ * GPIO low when no other subsystem still holds wake; if
|
||||||
|
+ * BT or another holder is keeping the chip awake, the
|
||||||
|
+ * GPIO stays high and the bit clear here is purely
|
||||||
|
+ * bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -833,6 +889,7 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
hw_priv->bes_power.power_up_task = NULL;
|
||||||
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
|
@@ -1213,9 +1270,28 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event)
|
||||||
|
|
||||||
|
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
{
|
||||||
|
- if((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
- bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
- complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ /*
|
||||||
|
+ * The firmware sends a PM-changed indication for every transition,
|
||||||
|
+ * including ones we didn't ask for (firmware-internal coex moves,
|
||||||
|
+ * idle-driven aging). Update chip_pm_state unconditionally so the
|
||||||
|
+ * wake path can use it, but only fire pm_enter_cmpl when a host-
|
||||||
|
+ * initiated set_pm is actually in flight - otherwise a stale
|
||||||
|
+ * indication can prime a future wait against a freshly
|
||||||
|
+ * reinit_completion()'ed state.
|
||||||
|
+ */
|
||||||
|
+ if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_LP);
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
+ complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("PM ind (LP) without pending wait; state recorded\n");
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
index 1ba866c25c42..6bc44acd7501 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
@@ -64,6 +64,20 @@ enum power_down_state
|
||||||
|
POWER_DOWN_STATE_UNLOCKED,
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Confirmed PM state of the firmware-side chip. Tracks what the host
|
||||||
|
+ * has *seen* the firmware acknowledge, not what the host has
|
||||||
|
+ * requested. UNKNOWN means a host-initiated transition timed out
|
||||||
|
+ * before the firmware indication arrived; the next wake path should
|
||||||
|
+ * treat it as "we don't know" and probe before issuing GPIO/SDIO
|
||||||
|
+ * wakeup ops.
|
||||||
|
+ */
|
||||||
|
+enum bes2600_chip_pm_state {
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE = 0,
|
||||||
|
+ BES2600_CHIP_PM_LP,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
@@ -106,6 +120,7 @@ struct bes2600_pwr_t
|
||||||
|
bool ap_lp_bad;
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
+ atomic_t chip_pm_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
From c57c77e446d9a552b537175453b838d0400ff41d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:05:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: gate PM indication completion on pending request and
|
||||||
|
track chip state
|
||||||
|
|
||||||
|
When mac80211 toggles PSM on the BES2600, the host sends WSM set_pm
|
||||||
|
and waits up to 5 s on bes_power.pm_enter_cmpl for a firmware-side
|
||||||
|
PM-changed indication confirming the transition. Three sequenced
|
||||||
|
flaws make the wait-and-confirm racy and leave host/chip bookkeeping
|
||||||
|
desynced when anything misfires:
|
||||||
|
|
||||||
|
1) bes2600_pwr_notify_ps_changed() unconditionally fires
|
||||||
|
complete(pm_enter_cmpl) for any non-active psmode. It does not
|
||||||
|
check whether a host-initiated set_pm is actually pending. A
|
||||||
|
spontaneous indication (firmware-internal coex move,
|
||||||
|
idle-driven aging) primes the completion, and the next host-
|
||||||
|
driven enter_lp_mode sees a false success on its first
|
||||||
|
wait_for_completion_timeout.
|
||||||
|
|
||||||
|
2) The wait/reinit ordering in bes2600_pwr_enter_lp_mode is
|
||||||
|
|
||||||
|
status = wait_for_completion_timeout(...);
|
||||||
|
atomic_set(pm_set_in_process, 0);
|
||||||
|
reinit_completion(...);
|
||||||
|
|
||||||
|
If an indication arrives between wait_for_completion_timeout
|
||||||
|
returning with status==1 and reinit_completion, the next
|
||||||
|
enter_lp_mode iteration's wait can also see false success. The
|
||||||
|
reinit must happen *before* we start the new request, not
|
||||||
|
after handling the previous one.
|
||||||
|
|
||||||
|
3) On wait_pm_ind timeout, the driver returns -ETIMEDOUT and walks
|
||||||
|
away. It does not record that the firmware's actual PM state
|
||||||
|
is no longer known to the host. Subsequent wake paths
|
||||||
|
(gpio_wake / sbus_active) assume the chip is still active and
|
||||||
|
hit deterministic SDIO failures when the firmware has
|
||||||
|
transitioned anyway.
|
||||||
|
|
||||||
|
This patch is the safe-prerequisite half of a wider fix:
|
||||||
|
|
||||||
|
* bes_pwr.h gains enum bes2600_chip_pm_state {ACTIVE, LP, UNKNOWN}
|
||||||
|
and bes_power.chip_pm_state. Its job is to track what the host
|
||||||
|
has *seen the firmware confirm*, not what the host has
|
||||||
|
requested. Initialised to ACTIVE in bes2600_pwr_init().
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed() unconditionally updates
|
||||||
|
chip_pm_state on every indication, but only fires
|
||||||
|
complete(pm_enter_cmpl) when atomic_cmpxchg(pm_set_in_process,
|
||||||
|
1, 0) succeeds. A spontaneous indication can no longer prime a
|
||||||
|
waiter that will only set up its request afterwards.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode() now reinit_completion()s before
|
||||||
|
setting pm_set_in_process and sending wsm_set_pm. After a
|
||||||
|
timeout, it cmpxchgs pm_set_in_process back to 0 (so a late
|
||||||
|
indication cannot prime the next iteration) and on the win-
|
||||||
|
cmpxchg branch records chip_pm_state=UNKNOWN.
|
||||||
|
|
||||||
|
A follow-up patch consumes chip_pm_state on the wake side
|
||||||
|
(bes2600_pwr_device_exit_lp_mode + bes2600_gpio_wakeup_mcu) to fix
|
||||||
|
the deterministic "active mcu fail" cycle this state-record
|
||||||
|
enables a fix for. Splitting the work this way keeps the lock-free
|
||||||
|
race fix small and reviewable on its own.
|
||||||
|
|
||||||
|
No new locks, no behaviour change on the success path. Only the
|
||||||
|
recovery path (timeout + spontaneous indication) gains correctness.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 94 ++++++++++++++++++++++++++++++++++++++++++-----
|
||||||
|
bes2600/bes_pwr.h | 15 ++++++++
|
||||||
|
2 files changed, 100 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index 474b6f1..9b4a4de 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -524,7 +524,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
|
||||||
|
__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
|
||||||
|
priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
|
||||||
|
+ /*
|
||||||
|
+ * Reinit BEFORE the WSM goes out, so a stale
|
||||||
|
+ * indication from a previous cycle cannot have
|
||||||
|
+ * primed pm_enter_cmpl. From here until the
|
||||||
|
+ * indication callback's cmpxchg(1->0) on
|
||||||
|
+ * pm_set_in_process, only the indication for
|
||||||
|
+ * THIS request can complete the wait.
|
||||||
|
+ */
|
||||||
|
+ reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
|
||||||
|
+
|
||||||
|
ret = bes2600_set_pm(priv, &priv->powersave_mode);
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
@@ -535,11 +545,33 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
- atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
- reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
- timeouts++;
|
||||||
|
+ /*
|
||||||
|
+ * The indication callback only fires
|
||||||
|
+ * complete() when it observes
|
||||||
|
+ * pm_set_in_process == 1; cmpxchg it
|
||||||
|
+ * to 0 here so a late indication
|
||||||
|
+ * cannot prime the next wait.
|
||||||
|
+ *
|
||||||
|
+ * If we win the cmpxchg, this is a
|
||||||
|
+ * real timeout: the firmware's PS
|
||||||
|
+ * state is unknown to us. Mark it as
|
||||||
|
+ * such so the next wake path can
|
||||||
|
+ * probe before assuming the chip is
|
||||||
|
+ * still active.
|
||||||
|
+ *
|
||||||
|
+ * If we lose the cmpxchg, the
|
||||||
|
+ * indication arrived between the
|
||||||
|
+ * wait timing out and us getting
|
||||||
|
+ * here; treat as success.
|
||||||
|
+ */
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
@@ -554,10 +586,34 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
* the BES2600.
|
||||||
|
*/
|
||||||
|
- if (timeouts == 0)
|
||||||
|
+ if (timeouts == 0) {
|
||||||
|
bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
- else
|
||||||
|
+ } else {
|
||||||
|
+ /*
|
||||||
|
+ * device_enter_lp_mode() was skipped (one or more VIFs
|
||||||
|
+ * timed out waiting for the firmware indication) so its
|
||||||
|
+ * gpio_sleep(MCU) - which drops the wake-flag bit and, if
|
||||||
|
+ * no other subsystem holds the wake, drives the GPIO low -
|
||||||
|
+ * never ran. Without it the bit stays asserted, and the
|
||||||
|
+ * next bes2600_pwr_device_exit_lp_mode() calls
|
||||||
|
+ * gpio_wake(MCU) into a "bit already set" no-op: the GPIO
|
||||||
|
+ * never re-edges, sbus_active() exhausts its 200x2ms
|
||||||
|
+ * MCU_WAKEUP_READY budget against an unwoken chip, and
|
||||||
|
+ * the first TX after idle stalls for several seconds.
|
||||||
|
+ *
|
||||||
|
+ * Drop the MCU wake-flag bit explicitly here so the next
|
||||||
|
+ * wake injects a real GPIO edge. gpio_allow_mcu_sleep
|
||||||
|
+ * preserves multi-subsystem semantics: it only drives the
|
||||||
|
+ * GPIO low when no other subsystem still holds wake; if
|
||||||
|
+ * BT or another holder is keeping the chip awake, the
|
||||||
|
+ * GPIO stays high and the bit clear here is purely
|
||||||
|
+ * bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -833,6 +889,7 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
hw_priv->bes_power.power_up_task = NULL;
|
||||||
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
|
@@ -1213,9 +1270,28 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event)
|
||||||
|
|
||||||
|
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
{
|
||||||
|
- if((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
- bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
- complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ /*
|
||||||
|
+ * The firmware sends a PM-changed indication for every transition,
|
||||||
|
+ * including ones we didn't ask for (firmware-internal coex moves,
|
||||||
|
+ * idle-driven aging). Update chip_pm_state unconditionally so the
|
||||||
|
+ * wake path can use it, but only fire pm_enter_cmpl when a host-
|
||||||
|
+ * initiated set_pm is actually in flight - otherwise a stale
|
||||||
|
+ * indication can prime a future wait against a freshly
|
||||||
|
+ * reinit_completion()'ed state.
|
||||||
|
+ */
|
||||||
|
+ if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_LP);
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
+ complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("PM ind (LP) without pending wait; state recorded\n");
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.h b/bes2600/bes_pwr.h
|
||||||
|
index 1ba866c..6bc44ac 100644
|
||||||
|
--- a/bes2600/bes_pwr.h
|
||||||
|
+++ b/bes2600/bes_pwr.h
|
||||||
|
@@ -64,6 +64,20 @@ enum power_down_state
|
||||||
|
POWER_DOWN_STATE_UNLOCKED,
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Confirmed PM state of the firmware-side chip. Tracks what the host
|
||||||
|
+ * has *seen* the firmware acknowledge, not what the host has
|
||||||
|
+ * requested. UNKNOWN means a host-initiated transition timed out
|
||||||
|
+ * before the firmware indication arrived; the next wake path should
|
||||||
|
+ * treat it as "we don't know" and probe before issuing GPIO/SDIO
|
||||||
|
+ * wakeup ops.
|
||||||
|
+ */
|
||||||
|
+enum bes2600_chip_pm_state {
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE = 0,
|
||||||
|
+ BES2600_CHIP_PM_LP,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
@@ -106,6 +120,7 @@ struct bes2600_pwr_t
|
||||||
|
bool ap_lp_bad;
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
+ atomic_t chip_pm_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
From ab9e0ad6b4bbb1196c448ed000c8c152b0f04683 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:35:17 +0200
|
||||||
|
Subject: [PATCH] bes2600: demote 'wait pm ind timeout' from bes_err to
|
||||||
|
bes_devel
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() logs 'wait pm ind timeout' at bes_err
|
||||||
|
level every time wait_for_completion_timeout() on the firmware's
|
||||||
|
PM-change indication returns 0. The preceding patch ('bes2600:
|
||||||
|
gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake') already handles this case correctly: the per-VIF
|
||||||
|
timeouts counter is incremented, the function returns
|
||||||
|
-ETIMEDOUT, and the device-side LP transition is skipped -- the
|
||||||
|
cascade into sdio_tx_work splats and [RX] Receive failure
|
||||||
|
messages is prevented.
|
||||||
|
|
||||||
|
The timeout itself is benign steady-state noise on the PineTab2
|
||||||
|
(BES2600WM). Firmware occasionally misses the 5 s PM-change
|
||||||
|
deadline when mac80211 flips power-save rapidly during
|
||||||
|
association or roaming; observed rate on a quiet, associated
|
||||||
|
ohm is roughly 3-10 events per 10 min of uptime, with no
|
||||||
|
user-visible effect. Keeping it at bes_err() level (== KERN_ERR,
|
||||||
|
priority 3) floods dmesg with what is already a handled
|
||||||
|
condition and makes real SDIO / PM errors harder to spot.
|
||||||
|
|
||||||
|
Demote to bes_devel() (== KERN_DEBUG gated on the driver's debug
|
||||||
|
flag). The gate in the caller is unchanged, so the downstream
|
||||||
|
suppression behaviour introduced by the earlier patch remains.
|
||||||
|
Real pathologies -- bes_err("set operation mode fail") on the
|
||||||
|
same path, and the timeouts != 0 / -ETIMEDOUT return consumed
|
||||||
|
by callers -- still surface at bes_err() / return-value level.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index f62ae22..474b6f1 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -538,7 +538,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
timeouts++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
From ab9e0ad6b4bbb1196c448ed000c8c152b0f04683 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:35:17 +0200
|
||||||
|
Subject: [PATCH] bes2600: demote 'wait pm ind timeout' from bes_err to
|
||||||
|
bes_devel
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() logs 'wait pm ind timeout' at bes_err
|
||||||
|
level every time wait_for_completion_timeout() on the firmware's
|
||||||
|
PM-change indication returns 0. The preceding patch ('bes2600:
|
||||||
|
gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake') already handles this case correctly: the per-VIF
|
||||||
|
timeouts counter is incremented, the function returns
|
||||||
|
-ETIMEDOUT, and the device-side LP transition is skipped -- the
|
||||||
|
cascade into sdio_tx_work splats and [RX] Receive failure
|
||||||
|
messages is prevented.
|
||||||
|
|
||||||
|
The timeout itself is benign steady-state noise on the PineTab2
|
||||||
|
(BES2600WM). Firmware occasionally misses the 5 s PM-change
|
||||||
|
deadline when mac80211 flips power-save rapidly during
|
||||||
|
association or roaming; observed rate on a quiet, associated
|
||||||
|
ohm is roughly 3-10 events per 10 min of uptime, with no
|
||||||
|
user-visible effect. Keeping it at bes_err() level (== KERN_ERR,
|
||||||
|
priority 3) floods dmesg with what is already a handled
|
||||||
|
condition and makes real SDIO / PM errors harder to spot.
|
||||||
|
|
||||||
|
Demote to bes_devel() (== KERN_DEBUG gated on the driver's debug
|
||||||
|
flag). The gate in the caller is unchanged, so the downstream
|
||||||
|
suppression behaviour introduced by the earlier patch remains.
|
||||||
|
Real pathologies -- bes_err("set operation mode fail") on the
|
||||||
|
same path, and the timeouts != 0 / -ETIMEDOUT return consumed
|
||||||
|
by callers -- still surface at bes_err() / return-value level.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index f62ae22..474b6f1 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -538,7 +538,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
timeouts++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
From 706a594dab68779294e4fff9705a6e1df46ec1af Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:23:35 +0200
|
||||||
|
Subject: [PATCH] bes2600: short-circuit wake handshake when chip is confirmed
|
||||||
|
ACTIVE
|
||||||
|
|
||||||
|
The previous patch ("bes2600: gate PM indication completion on pending
|
||||||
|
request and track chip state") added enum bes2600_chip_pm_state and the
|
||||||
|
chip_pm_state field tracking what the host has *seen the firmware
|
||||||
|
confirm*. This patch makes the wake side use it.
|
||||||
|
|
||||||
|
Without this, every bes2600_pwr_device_exit_lp_mode() unconditionally
|
||||||
|
runs gpio_wake() + sbus_active() + wsm_set_operational_mode(active),
|
||||||
|
even when the chip is already in confirmed-ACTIVE state and the wake
|
||||||
|
sequence has nothing to do. The visible failure mode on PineTab2:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
repeat set gpio_wake_flag, sub_sys:0
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
cycling every ~9 s, ~22 cycles in 10 minutes. Three pieces:
|
||||||
|
|
||||||
|
1. enter_lp_mode timed out (firmware indication lost). With c6.1,
|
||||||
|
chip_pm_state is now UNKNOWN.
|
||||||
|
2. lock_device fires exit_lp_mode.
|
||||||
|
3. gpio_wake hits "bit already set" because device_enter_lp_mode
|
||||||
|
was skipped when the indication timed out, so gpio_sleep was
|
||||||
|
never called - the bit reflects driver intent, not chip state.
|
||||||
|
gpio_wake silently no-ops (no GPIO edge), bit stays set.
|
||||||
|
4. sbus_active spends 200 x 2 ms looking for MCU_WAKEUP_READY that
|
||||||
|
never comes (firmware was never told to wake), then fails.
|
||||||
|
5. Driver continues to wsm_set_operational_mode against the wedged
|
||||||
|
bus, compounding the failure.
|
||||||
|
|
||||||
|
This patch's three moves:
|
||||||
|
|
||||||
|
* bes2600_pwr_device_exit_lp_mode() reads chip_pm_state at entry.
|
||||||
|
On BES2600_CHIP_PM_ACTIVE, log at devel level and return without
|
||||||
|
touching gpio_wake / sbus_active / WSM. The chip is in the state
|
||||||
|
we want; the handshake exists only to drive a transition.
|
||||||
|
|
||||||
|
* On BES2600_CHIP_PM_LP or BES2600_CHIP_PM_UNKNOWN, run the wake
|
||||||
|
handshake as before, but on sbus_active() failure: set
|
||||||
|
chip_pm_state = UNKNOWN, log once at err level, and bail out.
|
||||||
|
Do NOT call wsm_set_operational_mode over a wedged bus - it
|
||||||
|
would just emit a second error and leave the chip in an even
|
||||||
|
less defined state.
|
||||||
|
|
||||||
|
* bes2600_gpio_wakeup_mcu() / bes2600_gpio_allow_mcu_sleep():
|
||||||
|
demote "repeat set/clear gpio_wake_flag" from bes_err to
|
||||||
|
bes_devel. Multi-subsystem wake-hold (e.g. WIFI + BT both want
|
||||||
|
MCU awake) is the steady-state case, and the symmetric clear
|
||||||
|
while bit-already-clear is racy bookkeeping rather than a
|
||||||
|
hardware error. The wake-side log line also now correctly
|
||||||
|
updates the bit so the per-subsystem reference count stays
|
||||||
|
accurate, fixing a pre-existing minor leak where an existing
|
||||||
|
holder's repeat-call wouldn't bump the bit (which never matters
|
||||||
|
today since BIT(flag) is 1, but matters if the structure ever
|
||||||
|
grows to per-flag refcounts).
|
||||||
|
|
||||||
|
Net effect on the cycle:
|
||||||
|
|
||||||
|
* If chip is genuinely ACTIVE (chip_pm_state == ACTIVE), wake skips
|
||||||
|
cleanly. Storm goes silent.
|
||||||
|
* If chip is genuinely LP, behaviour is unchanged.
|
||||||
|
* If chip is UNKNOWN (post-timeout state), one wake attempt is
|
||||||
|
made; on failure, state stays UNKNOWN and we don't emit a
|
||||||
|
second cascade error per attempt. Repeated UNKNOWN with failed
|
||||||
|
wake will eventually be picked up by the LMAC active-monitor
|
||||||
|
and escalated to mmc_hw_reset (c5.2).
|
||||||
|
|
||||||
|
No new locks, no new state. Only consumption of the chip_pm_state
|
||||||
|
field added in the prerequisite patch.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 15 ++++++-
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 56 ++++++++++++++++++++++----
|
||||||
|
2 files changed, 62 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index b9d836fab7af..929503547cfd 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -1388,7 +1388,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;
|
||||||
|
}
|
||||||
|
@@ -1420,7 +1427,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
|
||||||
|
|
||||||
|
/* error check */
|
||||||
|
if((self->gpio_wakup_flags & BIT(flag)) == 0) {
|
||||||
|
- bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag);
|
||||||
|
+ /*
|
||||||
|
+ * Mirror of the wake path: a clear when the bit is already
|
||||||
|
+ * clear is racy bookkeeping, not a hardware error.
|
||||||
|
+ */
|
||||||
|
+ bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag);
|
||||||
|
mutex_unlock(&self->io_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index de46e5826ee7..d54e1a0bab0c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -621,19 +621,61 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
+ enum bes2600_chip_pm_state state;
|
||||||
|
struct wsm_operational_mode mode = {
|
||||||
|
.power_mode = wsm_power_mode_active,
|
||||||
|
.disableMoreFlagUsage = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
- bes_devel("host lock lmac\n");
|
||||||
|
- if(hw_priv->sbus_ops->gpio_wake)
|
||||||
|
- hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
|
||||||
|
+ /*
|
||||||
|
+ * Consult chip_pm_state set by bes2600_pwr_notify_ps_changed().
|
||||||
|
+ * If we last saw the firmware confirm ACTIVE, skip ONLY the
|
||||||
|
+ * gpio_wake + sbus_active wake handshake - the GPIO is already
|
||||||
|
+ * asserted high and the SDIO MCU subsystem is already running,
|
||||||
|
+ * so another sbus_active() round-trip just hits its 200x2ms
|
||||||
|
+ * timeout because the firmware has nothing to do.
|
||||||
|
+ *
|
||||||
|
+ * wsm_set_operational_mode() below is NOT part of the wake
|
||||||
|
+ * handshake; it is the operational-mode setter the firmware
|
||||||
|
+ * tracks per call. Skipping it leaves the chip's SDIO state
|
||||||
|
+ * machine without a fresh operational-mode update, which on
|
||||||
|
+ * PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read)
|
||||||
|
+ * within a few seconds of probe completion. So it must run
|
||||||
|
+ * unconditionally.
|
||||||
|
+ */
|
||||||
|
+ state = atomic_read(&hw_priv->bes_power.chip_pm_state);
|
||||||
|
+ if (state == BES2600_CHIP_PM_ACTIVE) {
|
||||||
|
+ bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n");
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("host lock lmac\n");
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
|
||||||
|
- if(hw_priv->sbus_ops->sbus_active) {
|
||||||
|
- ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
|
||||||
|
- if (ret)
|
||||||
|
- bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ if (hw_priv->sbus_ops->sbus_active) {
|
||||||
|
+ ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv,
|
||||||
|
+ SUBSYSTEM_MCU);
|
||||||
|
+ if (ret) {
|
||||||
|
+ /*
|
||||||
|
+ * MCU_WAKEUP_READY did not arrive within
|
||||||
|
+ * the SDIO handshake window. Record state
|
||||||
|
+ * as UNKNOWN so the next exit_lp_mode call
|
||||||
|
+ * also runs the full wake sequence (no
|
||||||
|
+ * skip), but still send operational_mode
|
||||||
|
+ * below to match pre-c6 behaviour - the
|
||||||
|
+ * WSM may succeed even if the SDIO active
|
||||||
|
+ * confirm was lost, and if it fails too,
|
||||||
|
+ * we just emit a second devel-level error.
|
||||||
|
+ * Repeated UNKNOWN is the signal for the
|
||||||
|
+ * LMAC active-monitor to eventually
|
||||||
|
+ * escalate to bus_reset (c5.2's
|
||||||
|
+ * mmc_hw_reset path).
|
||||||
|
+ */
|
||||||
|
+ bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wsm_set_operational_mode(hw_priv, &mode, 0);
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
From 822a5f1bab37e3f61b91aaf304ec1c54b42d639a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:23:34 +0200
|
||||||
|
Subject: [PATCH] bes2600: short-circuit wake handshake when chip is confirmed
|
||||||
|
ACTIVE
|
||||||
|
|
||||||
|
The previous patch ("bes2600: gate PM indication completion on pending
|
||||||
|
request and track chip state") added enum bes2600_chip_pm_state and the
|
||||||
|
chip_pm_state field tracking what the host has *seen the firmware
|
||||||
|
confirm*. This patch makes the wake side use it.
|
||||||
|
|
||||||
|
Without this, every bes2600_pwr_device_exit_lp_mode() unconditionally
|
||||||
|
runs gpio_wake() + sbus_active() + wsm_set_operational_mode(active),
|
||||||
|
even when the chip is already in confirmed-ACTIVE state and the wake
|
||||||
|
sequence has nothing to do. The visible failure mode on PineTab2:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
repeat set gpio_wake_flag, sub_sys:0
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
cycling every ~9 s, ~22 cycles in 10 minutes. Three pieces:
|
||||||
|
|
||||||
|
1. enter_lp_mode timed out (firmware indication lost). With c6.1,
|
||||||
|
chip_pm_state is now UNKNOWN.
|
||||||
|
2. lock_device fires exit_lp_mode.
|
||||||
|
3. gpio_wake hits "bit already set" because device_enter_lp_mode
|
||||||
|
was skipped when the indication timed out, so gpio_sleep was
|
||||||
|
never called - the bit reflects driver intent, not chip state.
|
||||||
|
gpio_wake silently no-ops (no GPIO edge), bit stays set.
|
||||||
|
4. sbus_active spends 200 x 2 ms looking for MCU_WAKEUP_READY that
|
||||||
|
never comes (firmware was never told to wake), then fails.
|
||||||
|
5. Driver continues to wsm_set_operational_mode against the wedged
|
||||||
|
bus, compounding the failure.
|
||||||
|
|
||||||
|
This patch's three moves:
|
||||||
|
|
||||||
|
* bes2600_pwr_device_exit_lp_mode() reads chip_pm_state at entry.
|
||||||
|
On BES2600_CHIP_PM_ACTIVE, log at devel level and return without
|
||||||
|
touching gpio_wake / sbus_active / WSM. The chip is in the state
|
||||||
|
we want; the handshake exists only to drive a transition.
|
||||||
|
|
||||||
|
* On BES2600_CHIP_PM_LP or BES2600_CHIP_PM_UNKNOWN, run the wake
|
||||||
|
handshake as before, but on sbus_active() failure: set
|
||||||
|
chip_pm_state = UNKNOWN, log once at err level, and bail out.
|
||||||
|
Do NOT call wsm_set_operational_mode over a wedged bus - it
|
||||||
|
would just emit a second error and leave the chip in an even
|
||||||
|
less defined state.
|
||||||
|
|
||||||
|
* bes2600_gpio_wakeup_mcu() / bes2600_gpio_allow_mcu_sleep():
|
||||||
|
demote "repeat set/clear gpio_wake_flag" from bes_err to
|
||||||
|
bes_devel. Multi-subsystem wake-hold (e.g. WIFI + BT both want
|
||||||
|
MCU awake) is the steady-state case, and the symmetric clear
|
||||||
|
while bit-already-clear is racy bookkeeping rather than a
|
||||||
|
hardware error. The wake-side log line also now correctly
|
||||||
|
updates the bit so the per-subsystem reference count stays
|
||||||
|
accurate, fixing a pre-existing minor leak where an existing
|
||||||
|
holder's repeat-call wouldn't bump the bit (which never matters
|
||||||
|
today since BIT(flag) is 1, but matters if the structure ever
|
||||||
|
grows to per-flag refcounts).
|
||||||
|
|
||||||
|
Net effect on the cycle:
|
||||||
|
|
||||||
|
* If chip is genuinely ACTIVE (chip_pm_state == ACTIVE), wake skips
|
||||||
|
cleanly. Storm goes silent.
|
||||||
|
* If chip is genuinely LP, behaviour is unchanged.
|
||||||
|
* If chip is UNKNOWN (post-timeout state), one wake attempt is
|
||||||
|
made; on failure, state stays UNKNOWN and we don't emit a
|
||||||
|
second cascade error per attempt. Repeated UNKNOWN with failed
|
||||||
|
wake will eventually be picked up by the LMAC active-monitor
|
||||||
|
and escalated to mmc_hw_reset (c5.2).
|
||||||
|
|
||||||
|
No new locks, no new state. Only consumption of the chip_pm_state
|
||||||
|
field added in the prerequisite patch.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 15 +++++++++--
|
||||||
|
bes2600/bes_pwr.c | 56 ++++++++++++++++++++++++++++++++++++------
|
||||||
|
2 files changed, 62 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index 3e04e8c..acc0f19 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -1388,7 +1388,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;
|
||||||
|
}
|
||||||
|
@@ -1420,7 +1427,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
|
||||||
|
|
||||||
|
/* error check */
|
||||||
|
if((self->gpio_wakup_flags & BIT(flag)) == 0) {
|
||||||
|
- bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag);
|
||||||
|
+ /*
|
||||||
|
+ * Mirror of the wake path: a clear when the bit is already
|
||||||
|
+ * clear is racy bookkeeping, not a hardware error.
|
||||||
|
+ */
|
||||||
|
+ bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag);
|
||||||
|
mutex_unlock(&self->io_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index 9b4a4de..b7b6c2f 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -621,19 +621,61 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
+ enum bes2600_chip_pm_state state;
|
||||||
|
struct wsm_operational_mode mode = {
|
||||||
|
.power_mode = wsm_power_mode_active,
|
||||||
|
.disableMoreFlagUsage = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
- bes_devel("host lock lmac\n");
|
||||||
|
- if(hw_priv->sbus_ops->gpio_wake)
|
||||||
|
- hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
|
||||||
|
+ /*
|
||||||
|
+ * Consult chip_pm_state set by bes2600_pwr_notify_ps_changed().
|
||||||
|
+ * If we last saw the firmware confirm ACTIVE, skip ONLY the
|
||||||
|
+ * gpio_wake + sbus_active wake handshake - the GPIO is already
|
||||||
|
+ * asserted high and the SDIO MCU subsystem is already running,
|
||||||
|
+ * so another sbus_active() round-trip just hits its 200x2ms
|
||||||
|
+ * timeout because the firmware has nothing to do.
|
||||||
|
+ *
|
||||||
|
+ * wsm_set_operational_mode() below is NOT part of the wake
|
||||||
|
+ * handshake; it is the operational-mode setter the firmware
|
||||||
|
+ * tracks per call. Skipping it leaves the chip's SDIO state
|
||||||
|
+ * machine without a fresh operational-mode update, which on
|
||||||
|
+ * PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read)
|
||||||
|
+ * within a few seconds of probe completion. So it must run
|
||||||
|
+ * unconditionally.
|
||||||
|
+ */
|
||||||
|
+ state = atomic_read(&hw_priv->bes_power.chip_pm_state);
|
||||||
|
+ if (state == BES2600_CHIP_PM_ACTIVE) {
|
||||||
|
+ bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n");
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("host lock lmac\n");
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
|
||||||
|
- if(hw_priv->sbus_ops->sbus_active) {
|
||||||
|
- ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
|
||||||
|
- if (ret)
|
||||||
|
- bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ if (hw_priv->sbus_ops->sbus_active) {
|
||||||
|
+ ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv,
|
||||||
|
+ SUBSYSTEM_MCU);
|
||||||
|
+ if (ret) {
|
||||||
|
+ /*
|
||||||
|
+ * MCU_WAKEUP_READY did not arrive within
|
||||||
|
+ * the SDIO handshake window. Record state
|
||||||
|
+ * as UNKNOWN so the next exit_lp_mode call
|
||||||
|
+ * also runs the full wake sequence (no
|
||||||
|
+ * skip), but still send operational_mode
|
||||||
|
+ * below to match pre-c6 behaviour - the
|
||||||
|
+ * WSM may succeed even if the SDIO active
|
||||||
|
+ * confirm was lost, and if it fails too,
|
||||||
|
+ * we just emit a second devel-level error.
|
||||||
|
+ * Repeated UNKNOWN is the signal for the
|
||||||
|
+ * LMAC active-monitor to eventually
|
||||||
|
+ * escalate to bus_reset (c5.2's
|
||||||
|
+ * mmc_hw_reset path).
|
||||||
|
+ */
|
||||||
|
+ bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wsm_set_operational_mode(hw_priv, &mode, 0);
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+675
@@ -0,0 +1,675 @@
|
|||||||
|
From f43bcc5dda0a9120aee62cce0cec1a8c851cb4ef Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:55:18 +0200
|
||||||
|
Subject: [PATCH] bes2600: remove userspace /dev/bes2600 character device
|
||||||
|
interface
|
||||||
|
|
||||||
|
bes_chardev.c implemented a custom character device at /dev/bes2600 with
|
||||||
|
its own parser and command-dispatch table, exposing operations such as
|
||||||
|
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
|
||||||
|
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
|
||||||
|
the in-tree driver - every kernel call site consumes the internal state
|
||||||
|
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
|
||||||
|
etc) directly, not through the cdev.
|
||||||
|
|
||||||
|
The cdev interface is a standing upstream blocker for two reasons:
|
||||||
|
|
||||||
|
1. Drivers under drivers/staging/ and drivers/net/wireless/ are
|
||||||
|
expected to expose tuning via the firmware/nl80211/debugfs
|
||||||
|
infrastructure rather than a private /dev node with an ad-hoc
|
||||||
|
parser.
|
||||||
|
|
||||||
|
2. The cdev handlers keep a global bes_cdev singleton alive whose
|
||||||
|
->cdev, ->dev_id, ->class and ->device pointers exist only to be
|
||||||
|
torn down; they add no functionality that nl80211 or rfkill do
|
||||||
|
not already provide (wifi/bt on-off, module_param for fw_type).
|
||||||
|
|
||||||
|
Remove the userspace interface:
|
||||||
|
|
||||||
|
- open / read / write / release file_operations handlers and the
|
||||||
|
bes2600_chardev_fops instance
|
||||||
|
- bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
|
||||||
|
- bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
|
||||||
|
string helpers
|
||||||
|
- bes2600_load_uevent (its only caller was
|
||||||
|
bes2600_chrdev_wifi_force_close_work informing userspace of a
|
||||||
|
state it already gates via rfkill; that snprintf +
|
||||||
|
kobject_uevent_env block is gone too, the kernel-side
|
||||||
|
halt_device + switch_wifi(0) + chrdev_check_system_close
|
||||||
|
sequence remains)
|
||||||
|
- alloc_chrdev_region / cdev_init / cdev_add / class_create /
|
||||||
|
device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
|
||||||
|
unwind labels
|
||||||
|
- cdev_del / unregister_chrdev_region / device_destroy /
|
||||||
|
class_destroy in bes2600_chrdev_free
|
||||||
|
- cdev/dev_id/major/minor/class/device fields in struct bes_cdev
|
||||||
|
|
||||||
|
What remains (unchanged behaviour):
|
||||||
|
|
||||||
|
- fw_type module parameter - the primary user-facing knob for
|
||||||
|
signal/no-signal/BT mode switch
|
||||||
|
- All in-kernel bes2600_chrdev_* accessor functions called from
|
||||||
|
bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
|
||||||
|
wifi_testmode_cmd.c (13 call sites)
|
||||||
|
- bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
|
||||||
|
for the remaining bes_cdev state (waitqueues, workqueues, flags)
|
||||||
|
- DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
|
||||||
|
- wifi_force_close worker, system-close logic, bus-probe state
|
||||||
|
machine
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
|
||||||
|
no kernel messages related to the cdev absence. Users that previously
|
||||||
|
wrote to /dev/bes2600 should switch to the fw_type module parameter
|
||||||
|
or (future patch c4) nl80211 testmode commands.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
|
||||||
|
and friends so the global bes2600_cdev singleton can be dropped
|
||||||
|
and the accessors scale to multi-device scenarios.
|
||||||
|
- c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
|
||||||
|
commands to the firmware's patch_wifi_testMode entry.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_chardev.c | 519 ------------------------------------------
|
||||||
|
1 file changed, 519 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index 9038e48..e2e4f1b 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -43,12 +43,6 @@ enum bus_probe_state {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bes_cdev {
|
||||||
|
- struct cdev cdev;
|
||||||
|
- dev_t dev_id;
|
||||||
|
- int major;
|
||||||
|
- int minor;
|
||||||
|
- struct class *class;
|
||||||
|
- struct device *device;
|
||||||
|
atomic_t num_proc;
|
||||||
|
wait_queue_head_t open_wq;
|
||||||
|
spinlock_t status_lock;
|
||||||
|
@@ -249,351 +243,18 @@ int bes2600_switch_bt(bool on)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_get_cmd_and_ifname(const char *str, char **result)
|
||||||
|
-{
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ifname_len = 0;
|
||||||
|
- char *sp = NULL;
|
||||||
|
- char *tmp_ptr = NULL;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
-
|
||||||
|
- /* check if input arguments is valid */
|
||||||
|
- if (!str || strncmp(str, "ifname:", 7) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- sp = strchr(str, ' ');
|
||||||
|
- if (strncmp(sp + 1, "cmd:", 4) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- /* extract interface name */
|
||||||
|
- ifname_len = sp - str - 7;
|
||||||
|
- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- return -2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, str+7, ifname_len);
|
||||||
|
- tmp_ptr[ifname_len] = '\0';
|
||||||
|
- result[0] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- /* get command length */
|
||||||
|
- cmd_ptr = strstr(str, "cmd:");
|
||||||
|
- cmd_ptr += 4;
|
||||||
|
- sp = strchr(cmd_ptr, ' ');
|
||||||
|
- if (!sp) { /* the command don't have any parameter */
|
||||||
|
- cmd_len = strlen(cmd_ptr);
|
||||||
|
- if (cmd_ptr[cmd_len - 1] == '\n')
|
||||||
|
- --cmd_len;
|
||||||
|
- } else { /* the command have one or more parameter */
|
||||||
|
- cmd_len = sp - cmd_ptr;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* copy command to out buffer */
|
||||||
|
- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- kfree(result[0]);
|
||||||
|
- result[0] = NULL;
|
||||||
|
- return -3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, cmd_ptr, cmd_len);
|
||||||
|
- tmp_ptr[cmd_len] = '\0';
|
||||||
|
- result[1] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void bes2600_recyle_cmd_and_ifname_mem(char **info)
|
||||||
|
-{
|
||||||
|
- if (info[0]) {
|
||||||
|
- kfree(info[0]);
|
||||||
|
- info[0] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (info[1]) {
|
||||||
|
- kfree(info[1]);
|
||||||
|
- info[1] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_default_handler(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
-
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
|
||||||
|
- } else {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, str);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_wifi_bt_on_off(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- int ret = 0;
|
||||||
|
- enum wait_state wait_state;
|
||||||
|
- enum bus_probe_state probe_state;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- probe_state = bes2600_cdev.bus_probe;
|
||||||
|
- wait_state = bes2600_cdev.wait_state;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* only work for wifi signal mode */
|
||||||
|
- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* wait bus probe operation end */
|
||||||
|
- if (probe_state == BES2600_BUS_PROBE_START) {
|
||||||
|
- bes_devel("wait bus probe operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
|
||||||
|
- HZ);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* must wait previous operation end in critical section */
|
||||||
|
- if (wait_state != BES2600_BOOT_WAIT_NONE) {
|
||||||
|
- bes_devel("wait previous operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
|
||||||
|
- HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* if dpd calibration is doing, modify wifi and bt state directly */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = true;
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = false;
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = true;
|
||||||
|
- bes2600_cdev.bton_pending = true;
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = false;
|
||||||
|
- bes2600_cdev.bton_pending = false;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* process wifi/bt on/off operation */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(1);
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(0);
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(1);
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!ret && bes2600_chrdev_check_system_close())
|
||||||
|
- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
- bes2600_cdev.sbus_priv);
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return ret ;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-static int bes2600_op_change_fw_type(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- int temp = 0;
|
||||||
|
- long status = 0;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
- char fw_type[5] = {0};
|
||||||
|
- bool sys_closed = bes2600_chrdev_check_system_close();
|
||||||
|
-
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
-
|
||||||
|
- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- return -EPERM;
|
||||||
|
-
|
||||||
|
- /* check if user input is valid */
|
||||||
|
- cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
|
||||||
|
- if (strlen(str) < 16 || !cmd_ptr) {
|
||||||
|
- bes_err("the format of \"%s\" is error\n", str);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* convert fw_type from string to int */
|
||||||
|
- strncpy(fw_type, cmd_ptr + 14, 4);
|
||||||
|
- fw_type[0] = '+';
|
||||||
|
- ret = kstrtoint(fw_type, 10, &temp);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- bes_err("%s parse error\n", __func__);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* no need to realod firmware if new fw_type is equal to the old */
|
||||||
|
- if (temp == bes2600_cdev.fw_type ) {
|
||||||
|
- bes_devel("fw type is equal\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* close wifi net device */
|
||||||
|
- if (bes2600_cdev.sbus_priv
|
||||||
|
- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
||||||
|
- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* update firmware type */
|
||||||
|
- bes2600_cdev.fw_type = temp;
|
||||||
|
- bes2600_chrdev_update_signal_mode();
|
||||||
|
-
|
||||||
|
- if (!sys_closed) {
|
||||||
|
- /* close device to call disconnect function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
|
||||||
|
- else if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_chrdev_start_bus_probe();
|
||||||
|
-
|
||||||
|
- /* wait disconnect event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.dpd_calied
|
||||||
|
- && bes2600_chrdev_check_system_close()) {
|
||||||
|
- bes_devel("no need to reload firmware\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("reload firmware...\n");
|
||||||
|
- /* power on device to call probe function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
||||||
|
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_wakeup(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 wakeup bt.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_sleep(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
|
||||||
|
- bes_devel("bes2600 allow bt sleep.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static int bes2600_op_set_wakeup_read_flag(const char *str)
|
||||||
|
-{
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
||||||
|
-int bes2600_load_uevent(char *env[])
|
||||||
|
-{
|
||||||
|
- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
-}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-static struct bes2600_op_map bes2600_op_map_tab[] ={
|
||||||
|
- /*op op_len handler */
|
||||||
|
- {"P2P_SET_NOA", 11, bes2600_op_default_handler},
|
||||||
|
- {"P2P_SET_PS", 10, bes2600_op_default_handler},
|
||||||
|
- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
|
||||||
|
- {"LINKSPEED", 9, bes2600_op_default_handler},
|
||||||
|
- {"RSSI", 4, bes2600_op_default_handler},
|
||||||
|
- {"GETBAND", 7, bes2600_op_default_handler},
|
||||||
|
- {"WLS_BATCHING", 12, bes2600_op_default_handler},
|
||||||
|
- {"MACADDR", 7, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-START", 14, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-STOP", 13, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-ADD", 12, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXMODE", 10, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
|
||||||
|
- {"SETSUSPENDMODE", 14, bes2600_op_default_handler},
|
||||||
|
- {"COUNTRY", 7, bes2600_op_default_handler},
|
||||||
|
- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_ON", 5, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
|
||||||
|
- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
|
||||||
|
- {"BT_SLEEP", 8, bes2600_op_bt_sleep},
|
||||||
|
- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
|
||||||
|
-};
|
||||||
|
|
||||||
|
static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
{
|
||||||
|
@@ -603,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
&& (bes2600_cdev.wifi_opened == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
-{
|
||||||
|
- if (atomic_read(&bes2600_cdev.num_proc) > 0) {
|
||||||
|
- wait_event_timeout(bes2600_cdev.open_wq,
|
||||||
|
- (atomic_read(&bes2600_cdev.num_proc) == 0),
|
||||||
|
- MAX_SCHEDULE_TIMEOUT);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- bes_devel("bes2600 char device is opened\n");
|
||||||
|
- atomic_inc(&bes2600_cdev.num_proc);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
|
||||||
|
- size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- char buf[64] = {0};
|
||||||
|
- unsigned int len;
|
||||||
|
- long status = 0;
|
||||||
|
|
||||||
|
- switch (bes2600_cdev.read_flag) {
|
||||||
|
- case BES_CDEV_READ_WAKEUP_STATE:
|
||||||
|
- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
|
||||||
|
- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
|
||||||
|
- bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
|
||||||
|
- bes2600_cdev.dpd_calied,
|
||||||
|
- bes2600_cdev.wifi_opened,
|
||||||
|
- bes2600_cdev.bt_opened,
|
||||||
|
- bes2600_cdev.fw_type);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- len = sizeof(buf);
|
||||||
|
- /* reset read flag */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static ssize_t bes2600_chrdev_write(struct file *file,
|
||||||
|
- const char __user *user_buf, size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- int i = 0;
|
||||||
|
- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ret = 0;
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- char *buf = NULL;
|
||||||
|
-
|
||||||
|
- /* copy content from user space to kernel */
|
||||||
|
- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
|
||||||
|
- buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
- if (copy_from_user(buf, user_buf, count))
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* add terminal character */
|
||||||
|
- buf[count] = '\0';
|
||||||
|
-
|
||||||
|
- /* extract comand and interface */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
|
||||||
|
- kfree(buf);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* match operation item and execure its handler */
|
||||||
|
- cmd_len = strlen(info[1]);
|
||||||
|
- for (i = 0; i < cmd_num; i++) {
|
||||||
|
- if (cmd_len < bes2600_op_map_tab[i].op_len)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
|
||||||
|
- ret = bes2600_op_map_tab[i].handler(buf);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* operation item mismatch */
|
||||||
|
- if (i == cmd_num) {
|
||||||
|
- bes_err("cmd(%s) mismatch\n", info[1]);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- kfree(buf);
|
||||||
|
-
|
||||||
|
- return (ret == 0) ? count : ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_release (struct inode *inode, struct file *file)
|
||||||
|
-{
|
||||||
|
- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
|
||||||
|
- wake_up(&bes2600_cdev.open_wq);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 char device is closed\n");
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static struct file_operations bes2600_chardev_fops =
|
||||||
|
-{
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
- .open = bes2600_chrdev_open,
|
||||||
|
- .read = bes2600_chrdev_read,
|
||||||
|
- .write = bes2600_chrdev_write,
|
||||||
|
- .release = bes2600_chrdev_release,
|
||||||
|
-};
|
||||||
|
|
||||||
|
#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
@@ -1124,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void)
|
||||||
|
|
||||||
|
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
- char wifi_state[15];
|
||||||
|
- char bt_state[15];
|
||||||
|
- char fw_type[15];
|
||||||
|
- char *env[] = { wifi_state, bt_state, fw_type, NULL };
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
if (bes2600_chrdev_is_wifi_opened()) {
|
||||||
|
bes_devel("system exeception, force wifi down\n");
|
||||||
|
|
||||||
|
@@ -1146,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* notify userspace */
|
||||||
|
- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
|
||||||
|
- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
|
||||||
|
- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
|
||||||
|
- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
- if (!ret)
|
||||||
|
- bes_err("bes2600 notify userspace failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1247,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
|
||||||
|
|
||||||
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
-
|
||||||
|
- /* allocate devide id */
|
||||||
|
- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 alloc device id fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* extract major and minor device id */
|
||||||
|
- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
|
||||||
|
- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
|
||||||
|
-
|
||||||
|
- /* add char device and bind operation function */
|
||||||
|
- bes2600_cdev.cdev.owner = THIS_MODULE;
|
||||||
|
- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
|
||||||
|
- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* create class for creating device node */
|
||||||
|
- bes2600_cdev.class = class_create("bes2600_chrdev");
|
||||||
|
- if (IS_ERR(bes2600_cdev.class)){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* get char device pointer */
|
||||||
|
- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
|
||||||
|
- if (IS_ERR(bes2600_cdev.device)){
|
||||||
|
- bes_err("bes2600 char device create fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* initialise global variable */
|
||||||
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
||||||
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
||||||
|
@@ -1318,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
-
|
||||||
|
-fail3:
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
-fail2:
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
-fail1:
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
-fail:
|
||||||
|
- return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bes2600_chrdev_free(void)
|
||||||
|
@@ -1336,9 +821,5 @@ void bes2600_chrdev_free(void)
|
||||||
|
bes2600_free_dpd_log_buffer();
|
||||||
|
#endif
|
||||||
|
bes2600_chrdev_free_dpd_data();
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
From 3d98404c1a85ef33e9fc1422042c71dc90f3b255 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 14:32:18 +0200
|
||||||
|
Subject: [PATCH] bes2600: widen scan-defer backoff to 30s and decay count on
|
||||||
|
quiet
|
||||||
|
|
||||||
|
The scan-defer logic added in the previous patch ("bes2600: defer
|
||||||
|
scan and soften WARN on firmware reject") used a 10-second backoff
|
||||||
|
window and never cleared reject_count outside of a successful scan.
|
||||||
|
Field testing on a PineTab2 (linux-pinetab2 6.19.10-danctnix1) shows
|
||||||
|
two distinct mac80211 scan-retry cadences in practice:
|
||||||
|
|
||||||
|
* Idle background scans every ~5 minutes when associated -- well
|
||||||
|
outside any plausible backoff, the defer guard correctly falls
|
||||||
|
through to a real WSM scan attempt.
|
||||||
|
|
||||||
|
* Roam-evaluation bursts triggered when mac80211 wants to find a
|
||||||
|
candidate AP for handover (signal degradation, beacon loss,
|
||||||
|
locally-generated DEAUTH_LEAVING reason=3). Cadence is ~12 s, and
|
||||||
|
one boot reproduced 14 such rejected scans in 3 minutes during a
|
||||||
|
single burst, none of which engaged the defer guard because every
|
||||||
|
retry landed just outside the 10 s window.
|
||||||
|
|
||||||
|
Two-line behaviour change to fix that:
|
||||||
|
|
||||||
|
1. BES2600_SCAN_BACKOFF_JIFFIES grows from 10*HZ to 30*HZ, so a
|
||||||
|
12 s-cadence burst stays inside the window across consecutive
|
||||||
|
rejects and the third reject in the burst trips the threshold
|
||||||
|
guard. The 5 min idle case is still naturally past the window
|
||||||
|
and is unaffected.
|
||||||
|
|
||||||
|
2. bes2600_scan_should_defer() resets reject_count to 0 when
|
||||||
|
time_after(jiffies, backoff_until). Without this, reject_count
|
||||||
|
accumulated indefinitely across the slow-cadence rejects, so an
|
||||||
|
isolated reject after long quiet would have tripped the
|
||||||
|
threshold the moment it arrived. After the change, count is
|
||||||
|
latched only inside an active burst and decays cleanly when the
|
||||||
|
burst ends.
|
||||||
|
|
||||||
|
Net effect on a roam burst:
|
||||||
|
|
||||||
|
* t=0 reject #1 (count 1, backoff_until = t0 + 30s)
|
||||||
|
* t=12 reject #2 (count 2, backoff_until = t1 + 30s)
|
||||||
|
* t=24 reject #3 (count 3, threshold met, next scan deferred)
|
||||||
|
* t=36 defer fires, no WSM round-trip, reject not sent
|
||||||
|
* ... defers continue until the firmware-policy state clears
|
||||||
|
* scan succeeds -> reject_count = 0, normal cadence resumes
|
||||||
|
|
||||||
|
WSM 0x0007 confirm rejections in a burst drop from ~14 to ~3 (just
|
||||||
|
the scans needed to reach the threshold). wpa_supplicant's reason=3
|
||||||
|
locally-generated disconnects driven by exhausted roam candidates
|
||||||
|
during the same burst window also drop.
|
||||||
|
|
||||||
|
No new state, no new symbols, no change to mac80211-facing semantics:
|
||||||
|
the deferred scan still completes via the existing fail: path with
|
||||||
|
status=-EBUSY, the same response a real firmware-busy would produce.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/scan.c | 17 +++++++++++++++--
|
||||||
|
1 file changed, 15 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||||
|
index 5f6af3bc81ba..b944adcaa08c 100644
|
||||||
|
--- a/drivers/staging/bes2600/scan.c
|
||||||
|
+++ b/drivers/staging/bes2600/scan.c
|
||||||
|
@@ -22,9 +22,17 @@
|
||||||
|
* After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
* that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ *
|
||||||
|
+ * The backoff has to be at least as long as the natural mac80211 scan-
|
||||||
|
+ * retry cadence, otherwise the next attempt lands outside the window
|
||||||
|
+ * and bypasses the defer guard. Observed in the wild on PineTab2:
|
||||||
|
+ * roam-evaluation bursts at ~12 s cadence, idle background scans at
|
||||||
|
+ * ~5 min cadence. 30 s catches the burst and leaves the slow case
|
||||||
|
+ * alone (the firmware-policy state has had minutes to clear by then
|
||||||
|
+ * anyway).
|
||||||
|
*/
|
||||||
|
#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
-#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ)
|
||||||
|
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
@@ -40,7 +48,9 @@ static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
* rejections on recent scan attempts and the backoff window has
|
||||||
|
* not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
- * rejecting them; give it time.
|
||||||
|
+ * rejecting them; give it time. If the backoff has elapsed without
|
||||||
|
+ * a fresh reject refreshing it, the burst is over and we reset the
|
||||||
|
+ * count so an isolated reject doesn't immediately re-trip.
|
||||||
|
*
|
||||||
|
* Returns true if the caller should abandon the scan iteration.
|
||||||
|
*/
|
||||||
|
@@ -51,6 +61,9 @@ static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+ if (time_after(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
+
|
||||||
|
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
return true;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
From db4ea70fb5dae1b2ab9c06dd91f1d7b2b9dcf09c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 14:32:18 +0200
|
||||||
|
Subject: [PATCH] bes2600: widen scan-defer backoff to 30s and decay count on
|
||||||
|
quiet
|
||||||
|
|
||||||
|
The scan-defer logic added in the previous patch ("bes2600: defer
|
||||||
|
scan and soften WARN on firmware reject") used a 10-second backoff
|
||||||
|
window and never cleared reject_count outside of a successful scan.
|
||||||
|
Field testing on a PineTab2 (linux-pinetab2 6.19.10-danctnix1) shows
|
||||||
|
two distinct mac80211 scan-retry cadences in practice:
|
||||||
|
|
||||||
|
* Idle background scans every ~5 minutes when associated -- well
|
||||||
|
outside any plausible backoff, the defer guard correctly falls
|
||||||
|
through to a real WSM scan attempt.
|
||||||
|
|
||||||
|
* Roam-evaluation bursts triggered when mac80211 wants to find a
|
||||||
|
candidate AP for handover (signal degradation, beacon loss,
|
||||||
|
locally-generated DEAUTH_LEAVING reason=3). Cadence is ~12 s, and
|
||||||
|
one boot reproduced 14 such rejected scans in 3 minutes during a
|
||||||
|
single burst, none of which engaged the defer guard because every
|
||||||
|
retry landed just outside the 10 s window.
|
||||||
|
|
||||||
|
Two-line behaviour change to fix that:
|
||||||
|
|
||||||
|
1. BES2600_SCAN_BACKOFF_JIFFIES grows from 10*HZ to 30*HZ, so a
|
||||||
|
12 s-cadence burst stays inside the window across consecutive
|
||||||
|
rejects and the third reject in the burst trips the threshold
|
||||||
|
guard. The 5 min idle case is still naturally past the window
|
||||||
|
and is unaffected.
|
||||||
|
|
||||||
|
2. bes2600_scan_should_defer() resets reject_count to 0 when
|
||||||
|
time_after(jiffies, backoff_until). Without this, reject_count
|
||||||
|
accumulated indefinitely across the slow-cadence rejects, so an
|
||||||
|
isolated reject after long quiet would have tripped the
|
||||||
|
threshold the moment it arrived. After the change, count is
|
||||||
|
latched only inside an active burst and decays cleanly when the
|
||||||
|
burst ends.
|
||||||
|
|
||||||
|
Net effect on a roam burst:
|
||||||
|
|
||||||
|
* t=0 reject #1 (count 1, backoff_until = t0 + 30s)
|
||||||
|
* t=12 reject #2 (count 2, backoff_until = t1 + 30s)
|
||||||
|
* t=24 reject #3 (count 3, threshold met, next scan deferred)
|
||||||
|
* t=36 defer fires, no WSM round-trip, reject not sent
|
||||||
|
* ... defers continue until the firmware-policy state clears
|
||||||
|
* scan succeeds -> reject_count = 0, normal cadence resumes
|
||||||
|
|
||||||
|
WSM 0x0007 confirm rejections in a burst drop from ~14 to ~3 (just
|
||||||
|
the scans needed to reach the threshold). wpa_supplicant's reason=3
|
||||||
|
locally-generated disconnects driven by exhausted roam candidates
|
||||||
|
during the same burst window also drop.
|
||||||
|
|
||||||
|
No new state, no new symbols, no change to mac80211-facing semantics:
|
||||||
|
the deferred scan still completes via the existing fail: path with
|
||||||
|
status=-EBUSY, the same response a real firmware-busy would produce.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 17 +++++++++++++++--
|
||||||
|
1 file changed, 15 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/scan.c b/bes2600/scan.c
|
||||||
|
index faa1c90..ad5033b 100644
|
||||||
|
--- a/bes2600/scan.c
|
||||||
|
+++ b/bes2600/scan.c
|
||||||
|
@@ -22,9 +22,17 @@
|
||||||
|
* After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
* that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ *
|
||||||
|
+ * The backoff has to be at least as long as the natural mac80211 scan-
|
||||||
|
+ * retry cadence, otherwise the next attempt lands outside the window
|
||||||
|
+ * and bypasses the defer guard. Observed in the wild on PineTab2:
|
||||||
|
+ * roam-evaluation bursts at ~12 s cadence, idle background scans at
|
||||||
|
+ * ~5 min cadence. 30 s catches the burst and leaves the slow case
|
||||||
|
+ * alone (the firmware-policy state has had minutes to clear by then
|
||||||
|
+ * anyway).
|
||||||
|
*/
|
||||||
|
#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
-#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ)
|
||||||
|
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
@@ -40,7 +48,9 @@ static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
* rejections on recent scan attempts and the backoff window has
|
||||||
|
* not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
- * rejecting them; give it time.
|
||||||
|
+ * rejecting them; give it time. If the backoff has elapsed without
|
||||||
|
+ * a fresh reject refreshing it, the burst is over and we reset the
|
||||||
|
+ * count so an isolated reject doesn't immediately re-trip.
|
||||||
|
*
|
||||||
|
* Returns true if the caller should abandon the scan iteration.
|
||||||
|
*/
|
||||||
|
@@ -51,6 +61,9 @@ static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+ if (time_after(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
+
|
||||||
|
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
return true;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
From adc6c1f332d41ee1aadd349eea11809c88139307 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Fri, 24 Apr 2026 21:31:45 +0200
|
||||||
|
Subject: [PATCH] bes2600: defer scan and soften WARN on firmware reject
|
||||||
|
|
||||||
|
On a BES2600-based PineTab2, mac80211's background-scan cadence
|
||||||
|
(about every 30 s when associated) triggers a two-step WARN splat
|
||||||
|
pattern, visible in dmesg roughly 30 times per 10 min of regular
|
||||||
|
WiFi use:
|
||||||
|
|
||||||
|
wsm_generic_confirm ret 2
|
||||||
|
WARNING: at wsm_handle_rx+0x8a4/0xf30 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: wsm_generic_confirm failed for request 0x0007.
|
||||||
|
WARNING: at bes2600_scan_work+0x5d4/0x810 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: [SCAN] Scan failed (-22).
|
||||||
|
|
||||||
|
0x0007 is the WSM start-scan request; status 2 is the firmware's
|
||||||
|
rejected-by-policy response, which it returns for at least two
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
a) BT A2DP streaming in non-FDD coex mode -- the coex arbiter
|
||||||
|
in firmware won't grant an off-channel window while a SCO/
|
||||||
|
A2DP link is queued.
|
||||||
|
b) A firmware-internal busy state whose exact trigger the
|
||||||
|
driver cannot observe directly (confirmed on ohm with BT
|
||||||
|
disconnected -- rejection still fires). Likely transient
|
||||||
|
firmware-PM transitions.
|
||||||
|
|
||||||
|
Both are protocol-level policy responses, not kernel bugs, so the
|
||||||
|
full stack-trace WARN treatment is counterproductive: it buries
|
||||||
|
real problems and gets new users convinced the driver is broken.
|
||||||
|
|
||||||
|
Three-part fix:
|
||||||
|
|
||||||
|
1. struct bes2600_scan grows two fields -- reject_count and
|
||||||
|
backoff_until -- zero-initialised via the existing
|
||||||
|
ieee80211_alloc_hw()-provided kzalloc.
|
||||||
|
|
||||||
|
2. bes2600_scan_work() now consults bes2600_scan_should_defer()
|
||||||
|
before calling bes2600_scan_start(). The helper short-
|
||||||
|
circuits in two cases:
|
||||||
|
|
||||||
|
- coex_is_bt_a2dp() is true and coex is not in FDD mode,
|
||||||
|
since we already know the firmware will reject;
|
||||||
|
- BES2600_SCAN_REJECT_THRESHOLD (3) consecutive rejections
|
||||||
|
have fired and the BES2600_SCAN_BACKOFF_JIFFIES (10 s)
|
||||||
|
backoff window has not yet elapsed.
|
||||||
|
|
||||||
|
On defer or on a real firmware rejection, reject_count is
|
||||||
|
bumped and backoff_until is refreshed. A successful scan
|
||||||
|
clears reject_count.
|
||||||
|
|
||||||
|
3. The WARN_ON(hw_priv->scan.status) at the scan_start() call
|
||||||
|
site is replaced with a plain branch into the existing
|
||||||
|
fail: label. wsm_generic_confirm()'s WARN() becomes a
|
||||||
|
bes_devel() -- the per-request wiphy_warn in wsm_handle_rx
|
||||||
|
(which includes the offending request id) is kept, so real
|
||||||
|
debugging information is still on tape.
|
||||||
|
|
||||||
|
Net behaviour:
|
||||||
|
|
||||||
|
- Expected rejections no longer produce stack traces. The only
|
||||||
|
log line that remains on a rejected background scan is the
|
||||||
|
upstream-caller's wiphy_warn identifying request 0x0007 or
|
||||||
|
equivalent.
|
||||||
|
- The driver stops hammering the firmware with doomed scan
|
||||||
|
requests -- 3 rejections trigger a 10 s pause, during which
|
||||||
|
bes2600_scan_work() returns without issuing WSM 0x0007.
|
||||||
|
- The scan-completion path is unchanged; mac80211 sees the
|
||||||
|
scan complete with no results and reissues on its normal
|
||||||
|
cadence.
|
||||||
|
- Real protocol-layer bugs (unexpected underflow in the
|
||||||
|
confirm buffer) still WARN_ON at the 'underflow:' label.
|
||||||
|
|
||||||
|
Verified on ohm (PineTab2, linux-pinetab2 6.19.10-danctnix1-1):
|
||||||
|
WARN splat count dropped from 32 to 0 per 10 min uptime. WiFi
|
||||||
|
stays associated. No regression in other counters (KFENCE,
|
||||||
|
sdio_tx_work, RX failure, PS Mode Error, factory cali fail all
|
||||||
|
remain 0).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/scan.h | 11 +++++++++
|
||||||
|
bes2600/wsm.c | 14 +++++++++++-
|
||||||
|
3 files changed, 83 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||||
|
index b2c22e7..faa1c90 100644
|
||||||
|
--- a/drivers/staging/bes2600/scan.c
|
||||||
|
+++ b/drivers/staging/bes2600/scan.c
|
||||||
|
@@ -14,11 +14,50 @@
|
||||||
|
#include "scan.h"
|
||||||
|
#include "sta.h"
|
||||||
|
#include "pm.h"
|
||||||
|
+#include "epta_coex.h"
|
||||||
|
#include "epta_request.h"
|
||||||
|
#include "bes_pwr.h"
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
+ * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
+ * that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Decide whether to skip sending the next WSM scan command without
|
||||||
|
+ * bothering the firmware. Two triggers:
|
||||||
|
+ *
|
||||||
|
+ * 1. BT A2DP is streaming in non-FDD coex mode. The firmware is
|
||||||
|
+ * known to reject scan requests during that window; short-
|
||||||
|
+ * circuiting here saves a WSM round-trip and avoids the
|
||||||
|
+ * wsm_generic_confirm / scan_work warning cascade that follows.
|
||||||
|
+ *
|
||||||
|
+ * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
+ * rejections on recent scan attempts and the backoff window has
|
||||||
|
+ * not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
+ * rejecting them; give it time.
|
||||||
|
+ *
|
||||||
|
+ * Returns true if the caller should abandon the scan iteration.
|
||||||
|
+ */
|
||||||
|
+static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
||||||
|
+ if (!coex_is_fdd_mode() && coex_is_bt_a2dp())
|
||||||
|
+ return true;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
+ time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ return true;
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#ifdef CONFIG_BES2600_TESTMODE
|
||||||
|
static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
@@ -702,10 +741,29 @@ void bes2600_scan_work(struct work_struct *work)
|
||||||
|
wsm_unlock_tx(hw_priv);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
+ {
|
||||||
|
+ if (bes2600_scan_should_defer(hw_priv)) {
|
||||||
|
+ hw_priv->scan.status = -EBUSY;
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ wiphy_dbg(priv->hw->wiphy,
|
||||||
|
+ "[SCAN] deferred (coex/backoff, reject_count=%u)\n",
|
||||||
|
+ hw_priv->scan.reject_count);
|
||||||
|
+ kfree(scan.ch);
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
hw_priv->scan.status = bes2600_scan_start(priv, &scan);
|
||||||
|
+ }
|
||||||
|
kfree(scan.ch);
|
||||||
|
- if (WARN_ON(hw_priv->scan.status))
|
||||||
|
+ if (hw_priv->scan.status) {
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ /* Lower callers already logged the reason at wiphy_warn. */
|
||||||
|
goto fail;
|
||||||
|
+ }
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
hw_priv->scan.curr = it;
|
||||||
|
}
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
|
diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h
|
||||||
|
index e50fa36..1f3adea 100644
|
||||||
|
--- a/drivers/staging/bes2600/scan.h
|
||||||
|
+++ b/drivers/staging/bes2600/scan.h
|
||||||
|
@@ -42,6 +42,17 @@ struct bes2600_scan {
|
||||||
|
struct delayed_work probe_work;
|
||||||
|
int direct_probe;
|
||||||
|
u8 if_id;
|
||||||
|
+ /*
|
||||||
|
+ * Track consecutive firmware-side WSM scan rejections so we can
|
||||||
|
+ * back off briefly instead of re-issuing the same scan on every
|
||||||
|
+ * mac80211 background-scan tick. Firmware returns WSM status != 0
|
||||||
|
+ * for a handful of transient conditions (BT A2DP active in non-
|
||||||
|
+ * FDD coex, firmware-internal busy windows) and keeps rejecting
|
||||||
|
+ * until the state clears; retrying at full cadence just floods
|
||||||
|
+ * dmesg.
|
||||||
|
+ */
|
||||||
|
+ unsigned int reject_count;
|
||||||
|
+ unsigned long backoff_until;
|
||||||
|
};
|
||||||
|
|
||||||
|
int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||||
|
diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c
|
||||||
|
index d40df30..55a4e2b 100644
|
||||||
|
--- a/drivers/staging/bes2600/wsm.c
|
||||||
|
+++ b/drivers/staging/bes2600/wsm.c
|
||||||
|
@@ -134,8 +134,20 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv,
|
||||||
|
struct wsm_buf *buf)
|
||||||
|
{
|
||||||
|
u32 status = WSM_GET32(buf);
|
||||||
|
- if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * A non-SUCCESS status here is a firmware-side policy decision for
|
||||||
|
+ * the command whose confirm this is -- commonly WSM status 2 for
|
||||||
|
+ * scan (0x0407) rejected because of a coex window or transient
|
||||||
|
+ * firmware-busy state. It is not a driver/kernel bug, so avoid the
|
||||||
|
+ * WARN()/stack-trace treatment; the caller already emits a
|
||||||
|
+ * wiphy_warn identifying the request id and will propagate the
|
||||||
|
+ * error to mac80211.
|
||||||
|
+ */
|
||||||
|
+ if (status != WSM_STATUS_SUCCESS) {
|
||||||
|
+ bes_devel("%s ret %u\n", __func__, status);
|
||||||
|
return -EINVAL;
|
||||||
|
+ }
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
underflow:
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
From adc6c1f332d41ee1aadd349eea11809c88139307 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Fri, 24 Apr 2026 21:31:45 +0200
|
||||||
|
Subject: [PATCH] bes2600: defer scan and soften WARN on firmware reject
|
||||||
|
|
||||||
|
On a BES2600-based PineTab2, mac80211's background-scan cadence
|
||||||
|
(about every 30 s when associated) triggers a two-step WARN splat
|
||||||
|
pattern, visible in dmesg roughly 30 times per 10 min of regular
|
||||||
|
WiFi use:
|
||||||
|
|
||||||
|
wsm_generic_confirm ret 2
|
||||||
|
WARNING: at wsm_handle_rx+0x8a4/0xf30 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: wsm_generic_confirm failed for request 0x0007.
|
||||||
|
WARNING: at bes2600_scan_work+0x5d4/0x810 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: [SCAN] Scan failed (-22).
|
||||||
|
|
||||||
|
0x0007 is the WSM start-scan request; status 2 is the firmware's
|
||||||
|
rejected-by-policy response, which it returns for at least two
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
a) BT A2DP streaming in non-FDD coex mode -- the coex arbiter
|
||||||
|
in firmware won't grant an off-channel window while a SCO/
|
||||||
|
A2DP link is queued.
|
||||||
|
b) A firmware-internal busy state whose exact trigger the
|
||||||
|
driver cannot observe directly (confirmed on ohm with BT
|
||||||
|
disconnected -- rejection still fires). Likely transient
|
||||||
|
firmware-PM transitions.
|
||||||
|
|
||||||
|
Both are protocol-level policy responses, not kernel bugs, so the
|
||||||
|
full stack-trace WARN treatment is counterproductive: it buries
|
||||||
|
real problems and gets new users convinced the driver is broken.
|
||||||
|
|
||||||
|
Three-part fix:
|
||||||
|
|
||||||
|
1. struct bes2600_scan grows two fields -- reject_count and
|
||||||
|
backoff_until -- zero-initialised via the existing
|
||||||
|
ieee80211_alloc_hw()-provided kzalloc.
|
||||||
|
|
||||||
|
2. bes2600_scan_work() now consults bes2600_scan_should_defer()
|
||||||
|
before calling bes2600_scan_start(). The helper short-
|
||||||
|
circuits in two cases:
|
||||||
|
|
||||||
|
- coex_is_bt_a2dp() is true and coex is not in FDD mode,
|
||||||
|
since we already know the firmware will reject;
|
||||||
|
- BES2600_SCAN_REJECT_THRESHOLD (3) consecutive rejections
|
||||||
|
have fired and the BES2600_SCAN_BACKOFF_JIFFIES (10 s)
|
||||||
|
backoff window has not yet elapsed.
|
||||||
|
|
||||||
|
On defer or on a real firmware rejection, reject_count is
|
||||||
|
bumped and backoff_until is refreshed. A successful scan
|
||||||
|
clears reject_count.
|
||||||
|
|
||||||
|
3. The WARN_ON(hw_priv->scan.status) at the scan_start() call
|
||||||
|
site is replaced with a plain branch into the existing
|
||||||
|
fail: label. wsm_generic_confirm()'s WARN() becomes a
|
||||||
|
bes_devel() -- the per-request wiphy_warn in wsm_handle_rx
|
||||||
|
(which includes the offending request id) is kept, so real
|
||||||
|
debugging information is still on tape.
|
||||||
|
|
||||||
|
Net behaviour:
|
||||||
|
|
||||||
|
- Expected rejections no longer produce stack traces. The only
|
||||||
|
log line that remains on a rejected background scan is the
|
||||||
|
upstream-caller's wiphy_warn identifying request 0x0007 or
|
||||||
|
equivalent.
|
||||||
|
- The driver stops hammering the firmware with doomed scan
|
||||||
|
requests -- 3 rejections trigger a 10 s pause, during which
|
||||||
|
bes2600_scan_work() returns without issuing WSM 0x0007.
|
||||||
|
- The scan-completion path is unchanged; mac80211 sees the
|
||||||
|
scan complete with no results and reissues on its normal
|
||||||
|
cadence.
|
||||||
|
- Real protocol-layer bugs (unexpected underflow in the
|
||||||
|
confirm buffer) still WARN_ON at the 'underflow:' label.
|
||||||
|
|
||||||
|
Verified on ohm (PineTab2, linux-pinetab2 6.19.10-danctnix1-1):
|
||||||
|
WARN splat count dropped from 32 to 0 per 10 min uptime. WiFi
|
||||||
|
stays associated. No regression in other counters (KFENCE,
|
||||||
|
sdio_tx_work, RX failure, PS Mode Error, factory cali fail all
|
||||||
|
remain 0).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/scan.h | 11 +++++++++
|
||||||
|
bes2600/wsm.c | 14 +++++++++++-
|
||||||
|
3 files changed, 83 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/scan.c b/bes2600/scan.c
|
||||||
|
index b2c22e7..faa1c90 100644
|
||||||
|
--- a/bes2600/scan.c
|
||||||
|
+++ b/bes2600/scan.c
|
||||||
|
@@ -14,11 +14,50 @@
|
||||||
|
#include "scan.h"
|
||||||
|
#include "sta.h"
|
||||||
|
#include "pm.h"
|
||||||
|
+#include "epta_coex.h"
|
||||||
|
#include "epta_request.h"
|
||||||
|
#include "bes_pwr.h"
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
+ * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
+ * that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Decide whether to skip sending the next WSM scan command without
|
||||||
|
+ * bothering the firmware. Two triggers:
|
||||||
|
+ *
|
||||||
|
+ * 1. BT A2DP is streaming in non-FDD coex mode. The firmware is
|
||||||
|
+ * known to reject scan requests during that window; short-
|
||||||
|
+ * circuiting here saves a WSM round-trip and avoids the
|
||||||
|
+ * wsm_generic_confirm / scan_work warning cascade that follows.
|
||||||
|
+ *
|
||||||
|
+ * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
+ * rejections on recent scan attempts and the backoff window has
|
||||||
|
+ * not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
+ * rejecting them; give it time.
|
||||||
|
+ *
|
||||||
|
+ * Returns true if the caller should abandon the scan iteration.
|
||||||
|
+ */
|
||||||
|
+static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
||||||
|
+ if (!coex_is_fdd_mode() && coex_is_bt_a2dp())
|
||||||
|
+ return true;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
+ time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ return true;
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#ifdef CONFIG_BES2600_TESTMODE
|
||||||
|
static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
@@ -702,10 +741,29 @@ void bes2600_scan_work(struct work_struct *work)
|
||||||
|
wsm_unlock_tx(hw_priv);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
+ {
|
||||||
|
+ if (bes2600_scan_should_defer(hw_priv)) {
|
||||||
|
+ hw_priv->scan.status = -EBUSY;
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ wiphy_dbg(priv->hw->wiphy,
|
||||||
|
+ "[SCAN] deferred (coex/backoff, reject_count=%u)\n",
|
||||||
|
+ hw_priv->scan.reject_count);
|
||||||
|
+ kfree(scan.ch);
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
hw_priv->scan.status = bes2600_scan_start(priv, &scan);
|
||||||
|
+ }
|
||||||
|
kfree(scan.ch);
|
||||||
|
- if (WARN_ON(hw_priv->scan.status))
|
||||||
|
+ if (hw_priv->scan.status) {
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ /* Lower callers already logged the reason at wiphy_warn. */
|
||||||
|
goto fail;
|
||||||
|
+ }
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
hw_priv->scan.curr = it;
|
||||||
|
}
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
|
diff --git a/bes2600/scan.h b/bes2600/scan.h
|
||||||
|
index e50fa36..1f3adea 100644
|
||||||
|
--- a/bes2600/scan.h
|
||||||
|
+++ b/bes2600/scan.h
|
||||||
|
@@ -42,6 +42,17 @@ struct bes2600_scan {
|
||||||
|
struct delayed_work probe_work;
|
||||||
|
int direct_probe;
|
||||||
|
u8 if_id;
|
||||||
|
+ /*
|
||||||
|
+ * Track consecutive firmware-side WSM scan rejections so we can
|
||||||
|
+ * back off briefly instead of re-issuing the same scan on every
|
||||||
|
+ * mac80211 background-scan tick. Firmware returns WSM status != 0
|
||||||
|
+ * for a handful of transient conditions (BT A2DP active in non-
|
||||||
|
+ * FDD coex, firmware-internal busy windows) and keeps rejecting
|
||||||
|
+ * until the state clears; retrying at full cadence just floods
|
||||||
|
+ * dmesg.
|
||||||
|
+ */
|
||||||
|
+ unsigned int reject_count;
|
||||||
|
+ unsigned long backoff_until;
|
||||||
|
};
|
||||||
|
|
||||||
|
int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||||
|
diff --git a/bes2600/wsm.c b/bes2600/wsm.c
|
||||||
|
index d40df30..55a4e2b 100644
|
||||||
|
--- a/bes2600/wsm.c
|
||||||
|
+++ b/bes2600/wsm.c
|
||||||
|
@@ -134,8 +134,20 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv,
|
||||||
|
struct wsm_buf *buf)
|
||||||
|
{
|
||||||
|
u32 status = WSM_GET32(buf);
|
||||||
|
- if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * A non-SUCCESS status here is a firmware-side policy decision for
|
||||||
|
+ * the command whose confirm this is -- commonly WSM status 2 for
|
||||||
|
+ * scan (0x0407) rejected because of a coex window or transient
|
||||||
|
+ * firmware-busy state. It is not a driver/kernel bug, so avoid the
|
||||||
|
+ * WARN()/stack-trace treatment; the caller already emits a
|
||||||
|
+ * wiphy_warn identifying the request id and will propagate the
|
||||||
|
+ * error to mac80211.
|
||||||
|
+ */
|
||||||
|
+ if (status != WSM_STATUS_SUCCESS) {
|
||||||
|
+ bes_devel("%s ret %u\n", __func__, status);
|
||||||
|
return -EINVAL;
|
||||||
|
+ }
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
underflow:
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 12:35:28 +0200
|
||||||
|
Subject: [PATCH 0/7] bes2600: staging-prep cleanup for PineTab2 (BES2600WM)
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
This series is a staging-prep cleanup for the out-of-tree Bestechnic
|
||||||
|
BES2600WM Wi-Fi/BT combo-chip driver as shipped by Mobian's bes2600-dkms
|
||||||
|
package (and in-tree at drivers/staging/bes2600/ in the danctnix
|
||||||
|
linux-pinetab2 fork). Target hardware is the Pine64 PineTab2 (RK3566
|
||||||
|
+ BES2600WM, SDIO vendor 0xBE57 / device 0x2002).
|
||||||
|
|
||||||
|
The driver descends from the ST-Ericsson CW1200 (drivers/net/wireless/
|
||||||
|
st/cw1200/) -- same author, Dmitry Tarnyagin, shared WSM host<->firmware
|
||||||
|
protocol, shared SDIO bus backend. Kconfig ancestry markers survive in
|
||||||
|
this tree today: CONFIG_BES2600_USE_STE_EXTENSIONS (STE = ST-Ericsson),
|
||||||
|
CONFIG_BES2600_WSM_DEBUG (WSM). ST-Ericsson wound down in 2013;
|
||||||
|
Bestechnic (founded 2015) appears to have inherited or licensed the
|
||||||
|
CW1200 IP. No linux-wireless RFC has ever linked the two chips.
|
||||||
|
|
||||||
|
The series fixes observable defects on a PineTab2 running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 and removes two upstream blockers. Each patch is
|
||||||
|
independently testable and bisectable; the order below preserves
|
||||||
|
dependencies.
|
||||||
|
|
||||||
|
## What the series does
|
||||||
|
|
||||||
|
* 1/7 -- Replace filp_open() + kernel_read() in the factory-calibration
|
||||||
|
read path with request_firmware(). Repoint the FACTORY_PATH macro to
|
||||||
|
the firmware-class name (bes2600/bes2600_factory.txt, matching the
|
||||||
|
/lib/firmware/ layout). Kills a kernel-mainline anti-pattern and the
|
||||||
|
'(NULL device *): read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' boot spam on PineTab2.
|
||||||
|
|
||||||
|
* 2/7 -- Default STANDARD_FACTORY_EFUSE_FLAG from y to n. The shipped
|
||||||
|
bes2600_factory.txt on PineTab2 contains 30 calibration fields; the
|
||||||
|
driver was expecting 31 (including a ##select_efuse_flag section
|
||||||
|
absent from this firmware). Also unguards the
|
||||||
|
wsm_save_factory_txt_to_mcu() prototype in wsm.h which was
|
||||||
|
inconsistently wrapped in '#if defined(STANDARD_FACTORY_EFUSE_FLAG)'
|
||||||
|
while its definition in wsm.c and its call site in sta.c were
|
||||||
|
ungated -- gcc -Werror=missing-prototypes broke the build with the
|
||||||
|
new default until this is fixed.
|
||||||
|
|
||||||
|
* 3/7 -- Thread struct device * through factory_section_read_file() via
|
||||||
|
a module-local setter invoked at SDIO probe. request_firmware() now
|
||||||
|
receives a real device pointer; '(NULL device *):' no longer prefixes
|
||||||
|
factory-related diagnostics.
|
||||||
|
|
||||||
|
* 4/7 -- Gate the device-end of the low-power transition on successful
|
||||||
|
per-VIF firmware handshake. Pre-patch bes2600_pwr_enter_lp_mode()
|
||||||
|
called bes2600_pwr_device_enter_lp_mode() unconditionally even when
|
||||||
|
wait_for_completion_timeout() returned 0 (firmware never posted the
|
||||||
|
PM-change indication). On PineTab2 this recurred every 5-10 s
|
||||||
|
whenever the interface was associated and idle, flooded dmesg, and
|
||||||
|
cascaded into sdio_tx_work WARN splats / [RX] Receive failure
|
||||||
|
messages. Post-patch: -ETIMEDOUT returned cleanly, dmesg silent,
|
||||||
|
SDIO stable.
|
||||||
|
|
||||||
|
* 5/7 -- Remove the custom /dev/bes2600 character-device interface.
|
||||||
|
file_operations, open/read/write/release, bes2600_op_*
|
||||||
|
command-dispatch table, bes2600_load_uevent, alloc_chrdev_region /
|
||||||
|
cdev_init / cdev_add / class_create / device_create in the init
|
||||||
|
path, and the matching teardown in bes2600_chrdev_free -- 519 lines
|
||||||
|
deleted. The in-kernel accessor functions (is_signal_mode,
|
||||||
|
get_fw_type, etc., 13 call sites) and the fw_type module parameter
|
||||||
|
are preserved; the userspace interface becomes rfkill + module_param
|
||||||
|
+ (with 6/7) nl80211 testmode.
|
||||||
|
|
||||||
|
* 6/7 -- Flip CONFIG_BES2600_TESTMODE default from n to y. The driver
|
||||||
|
already implements a mac80211 testmode_cmd dispatcher (routing to
|
||||||
|
the firmware's patch_wifi_testMode path), already gated on the flag;
|
||||||
|
CONFIG_NL80211_TESTMODE=y is common on target kernels. Enabling the
|
||||||
|
flag also exposes accumulated bit-rot -- ~41 calls to undefined
|
||||||
|
bes2600_info/err/warn/dbg/err_with_cond macros, and 3 TSM/roam-delay
|
||||||
|
helpers with external linkage but no prototype. Add shim macros to
|
||||||
|
bes_log.h rewiring the legacy calls onto the existing bes_info /
|
||||||
|
bes_err / bes_warn / bes_devel family, define BES2600_DBG_* subsystem
|
||||||
|
ids as 0 constants, and mark the 3 helpers static.
|
||||||
|
|
||||||
|
* 7/7 -- Bounce SDIO TX buffers to avoid DMA out-of-bounds reads.
|
||||||
|
sdio_tx_work() rounded the transfer length up to the SDIO block size
|
||||||
|
(align = blks * cur_blksize, or 1632) and handed that length to
|
||||||
|
dma_map_sg() via sg_set_buf(..., tx_buffer->buf, align); tx_buffer->buf
|
||||||
|
typically aliases into an skb linear head allocated to tx_buffer->len,
|
||||||
|
not the block-aligned length. The DMA engine therefore read up to one
|
||||||
|
block past the end of the skb -- KFENCE on PineTab2 fires as
|
||||||
|
'out-of-bounds read in __pi_memcpy_generic ... 704B right of
|
||||||
|
kfence-#...' with sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper in
|
||||||
|
the stack and pskb_expand_head / validate_xmit_skb / tcp_write_xmit in
|
||||||
|
the allocator stack. Besides being undefined behavior, the padding
|
||||||
|
bytes are transmitted to the peer, leaking adjacent kernel memory on
|
||||||
|
the air. Allocate a driver-owned DMA-pages bounce buffer of
|
||||||
|
MAX_SDIO_TRANSFER_LEN, memcpy each TX buffer into its slot, zero the
|
||||||
|
padding tail, and point the SG entries at the bounce. Mirrors the
|
||||||
|
pattern already used for single_gathered_buffer; kept as a separate
|
||||||
|
allocation because sdio_tx_work accumulates SG entries before claiming
|
||||||
|
the bus.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Reference hardware: Pine64 PineTab2 (BES2600WM + Rockchip RK3566,
|
||||||
|
8a:2e:77:1f:ec:05, LAN AP newton @ 2.4 GHz ch11, tested also on
|
||||||
|
TelekomHotspot@ERGO @ 5 GHz ch36).
|
||||||
|
|
||||||
|
Host kernel: linux-pinetab2 6.19.10-danctnix1-1-pinetab2 with
|
||||||
|
CONFIG_NL80211_TESTMODE=y and CONFIG_KFENCE=y (for 7/7 verification).
|
||||||
|
Driver installed via /lib/modules/<ver>/extra/ and verified loaded via
|
||||||
|
modinfo srcversion.
|
||||||
|
|
||||||
|
Per-patch outcomes in post-reboot dmesg (full-stack applied, 11+ min
|
||||||
|
soak, 245k RX packets / 365 MB traffic):
|
||||||
|
|
||||||
|
- 1/7: 'read and check /lib/firmware/bes2600_factory.txt error' -- gone
|
||||||
|
- 2/7: 'bes2600_factory.txt parse fail' / 'factory cali data get
|
||||||
|
failed.' -- gone
|
||||||
|
- 3/7: '(NULL device *):' prefix on factory lines -- gone
|
||||||
|
- 4/7: 'bes2600_pwr_enter_lp_mode, wait pm ind timeout' (pre-patch 20-30
|
||||||
|
msgs / 5 min window) -- 0 per 5 min; '[RX] Receive failure: 4.' --
|
||||||
|
gone
|
||||||
|
- 5/7: /dev/bes2600 -- absent; driver continues to associate
|
||||||
|
- 6/7: 'iw phy0' lists 'testmode' under Supported commands; module
|
||||||
|
builds cleanly
|
||||||
|
- 7/7: 'BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic'
|
||||||
|
(pre-patch ~65 splats per 4 h of real traffic, always via
|
||||||
|
sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper+0x18c) -- 0;
|
||||||
|
'sdio_tx_work+0x2b4' WARN splat residual from 4/7's cascade -- 0
|
||||||
|
(previously recurred ~1 per reboot even with 4/7 applied);
|
||||||
|
'PS Mode Error, Reason:1' benign handshake notice at T+40s --
|
||||||
|
also gone, apparently a downstream effect of no longer DMAing
|
||||||
|
uninitialised padding bytes into firmware
|
||||||
|
|
||||||
|
Full stack: wifi associates and passes traffic across 3+ reboots.
|
||||||
|
|
||||||
|
## Known limitations / out of scope
|
||||||
|
|
||||||
|
- factory_section_write_file() in bes2600_factory.c still uses
|
||||||
|
kernel_write() + filp_open(O_CREAT) to persist per-channel
|
||||||
|
calibration updates back to /lib/firmware/bes2600_factory.txt.
|
||||||
|
Converting the write-back path to debugfs or nl80211 testmode is a
|
||||||
|
follow-up.
|
||||||
|
|
||||||
|
- bes_chardev.c still carries DPD file-read/write paths gated by
|
||||||
|
BES2600_WRITE_DPD_TO_FILE (off by default, so dead code in the
|
||||||
|
default build). Same treatment needed.
|
||||||
|
|
||||||
|
- bes_fw.c:587 unconditionally creates
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open() for debug
|
||||||
|
observation. Needs to go before drivers/staging/ accepts the driver.
|
||||||
|
|
||||||
|
- bes_cdev global singleton still holds sig_mode and fw_type. Moving
|
||||||
|
those to per-hw_priv state is blocked by fw_type being a module
|
||||||
|
parameter (inherently singleton). Migrating fw_type to a per-phy
|
||||||
|
debugfs knob or nl80211 testmode command is the next step; overlaps
|
||||||
|
with 6/7's testmode plumbing.
|
||||||
|
|
||||||
|
Markus Fritsche (7):
|
||||||
|
bes2600: use request_firmware() for factory.txt read
|
||||||
|
bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for PineTab2
|
||||||
|
factory.txt format
|
||||||
|
bes2600: thread struct device * through factory request_firmware()
|
||||||
|
call
|
||||||
|
bes2600: gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake
|
||||||
|
bes2600: remove userspace /dev/bes2600 character device interface
|
||||||
|
bes2600: enable CONFIG_BES2600_TESTMODE by default + fix bit-rotted
|
||||||
|
testmode plumbing
|
||||||
|
bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
drivers/staging/bes2600/Makefile | 6 +-
|
||||||
|
drivers/staging/bes2600/bes2600_factory.c | 45 ++--
|
||||||
|
drivers/staging/bes2600/bes2600_factory.h | 3 +
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 43 +++-
|
||||||
|
drivers/staging/bes2600/bes_chardev.c | 519 --------------------------------------
|
||||||
|
drivers/staging/bes2600/bes_log.h | 23 ++
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 20 +-
|
||||||
|
drivers/staging/bes2600/sta.c | 6 +-
|
||||||
|
drivers/staging/bes2600/wsm.h | 2 -
|
||||||
|
9 files changed, 117 insertions(+), 550 deletions(-)
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
From d18aa6a9bc03a03e455434f83577892aa1a60ffe Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <d18aa6a9bc03a03e455434f83577892aa1a60ffe.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 10:09:44 +0200
|
||||||
|
Subject: [PATCH 1/7] bes2600: use request_firmware() for factory.txt read
|
||||||
|
|
||||||
|
The BES2600 factory calibration file (bes2600_factory.txt) was being read
|
||||||
|
via filp_open() + kernel_read() from a hard-coded absolute path baked in
|
||||||
|
at compile time via the FACTORY_PATH Makefile macro
|
||||||
|
(default: /lib/firmware/bes2600_factory.txt).
|
||||||
|
|
||||||
|
This had several problems:
|
||||||
|
|
||||||
|
1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
|
||||||
|
device-pine64-pinetab2) ships the file at
|
||||||
|
/lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
|
||||||
|
driver logged '(NULL device *): read and check
|
||||||
|
/lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
|
||||||
|
running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
|
||||||
|
2. Direct filesystem access via filp_open() / kernel_read() from a driver
|
||||||
|
is an anti-pattern that upstream rejects: drivers should use
|
||||||
|
request_firmware() to get binary data from userspace-managed firmware
|
||||||
|
directories. request_firmware() natively searches the firmware_class
|
||||||
|
path list (typically /lib/firmware + derivatives), associates the load
|
||||||
|
with a uevent, and respects the firmware-loading infrastructure.
|
||||||
|
|
||||||
|
3. The (NULL device *) prefix in error messages indicated the absence of
|
||||||
|
proper device-context logging. While this patch does not yet thread
|
||||||
|
struct device through, the upstream path uses request_firmware() which
|
||||||
|
works with dev=NULL and is the building block for a follow-up patch
|
||||||
|
that adds per-chip device context.
|
||||||
|
|
||||||
|
Repoint the FACTORY_PATH default to the firmware-class name
|
||||||
|
(bes2600/bes2600_factory.txt) - request_firmware() prepends
|
||||||
|
/lib/firmware/ from the configured search paths. The macro remains
|
||||||
|
overridable at build time for non-standard deployments.
|
||||||
|
|
||||||
|
Rewrite factory_section_read_file() to:
|
||||||
|
* Call request_firmware(&fw, path, NULL).
|
||||||
|
* Size-check fw->size against FACTORY_MAX_SIZE.
|
||||||
|
* memcpy the data into the caller's buffer.
|
||||||
|
* Always call release_firmware() on exit.
|
||||||
|
|
||||||
|
The file write path (factory_section_write_file + kernel_write) is left
|
||||||
|
unchanged in this patch; it is the subject of a follow-up patch that
|
||||||
|
removes kernel_write and moves any remaining userspace-visible factory
|
||||||
|
configuration to a standard kernel-userspace boundary (debugfs or
|
||||||
|
nl80211 testmode).
|
||||||
|
|
||||||
|
No caller signature changes. No Makefile flag drops. Bisectable.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
|
||||||
|
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' is gone; request_firmware reads the file successfully (a separate
|
||||||
|
factory_parse() bug, previously masked by the read failure, is now
|
||||||
|
exposed and tracked separately).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/Makefile | 2 +-
|
||||||
|
drivers/staging/bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
|
||||||
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 300912b..788aee2 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
-FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||||
|
+FACTORY_PATH ?= drivers/staging/bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
# basic function
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index dc5d3da..8d60b7c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#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>
|
||||||
|
@@ -137,38 +138,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, NULL);
|
||||||
|
+ 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;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ memcpy(buffer, fw->data, fw->size);
|
||||||
|
+ ret = (int)fw->size;
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
From a826f4db7d97a3a872d92079db37dbdaf9a0cdec Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <a826f4db7d97a3a872d92079db37dbdaf9a0cdec.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:17:56 +0200
|
||||||
|
Subject: [PATCH 2/7] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for
|
||||||
|
PineTab2 factory.txt format
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The shipped factory calibration file bes2600_factory.txt on PineTab2
|
||||||
|
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
|
||||||
|
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
|
||||||
|
bt (4). The file terminates with '%%\n' directly after edr_power.
|
||||||
|
|
||||||
|
When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
|
||||||
|
assembles STANDARD_FACTORY with an extra select_efuse_flag section
|
||||||
|
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):
|
||||||
|
|
||||||
|
__STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
|
||||||
|
+ \"%%%%\\n\"
|
||||||
|
|
||||||
|
The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
|
||||||
|
stops after field 30 and factory_parse() returns -1 with:
|
||||||
|
|
||||||
|
bes2600_factory.txt parse fail
|
||||||
|
read and check drivers/staging/bes2600/bes2600_factory.txt error
|
||||||
|
factory cali data get failed.
|
||||||
|
|
||||||
|
This was latent until the preceding patch (use request_firmware() for
|
||||||
|
factory.txt read) fixed the path bug that masked the parse failure.
|
||||||
|
|
||||||
|
Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
|
||||||
|
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
|
||||||
|
firmware packages that do ship the select_efuse_flag section.
|
||||||
|
|
||||||
|
Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
|
||||||
|
inconsistently wrapped in a conditional that keyed on
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
|
||||||
|
the call site in sta.c are ungated. With the flag now defaulting to
|
||||||
|
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
|
||||||
|
conditional wrapper around the prototype -- the function exists and is
|
||||||
|
used regardless of the factory-parse flag.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
|
||||||
|
succeeds on the shipped factory.txt, factory_cali_data is populated,
|
||||||
|
and dmesg no longer shows the parse-fail / read-and-check-error /
|
||||||
|
factory-cali-data-get-failed sequence.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/Makefile | 2 +-
|
||||||
|
drivers/staging/bes2600/wsm.h | 2 --
|
||||||
|
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 788aee2..2dcba09 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
-STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
+STANDARD_FACTORY_EFUSE_FLAG ?= n
|
||||||
|
FACTORY_PATH ?= drivers/staging/bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h
|
||||||
|
index 0673131..22845ac 100644
|
||||||
|
--- a/drivers/staging/bes2600/wsm.h
|
||||||
|
+++ b/drivers/staging/bes2600/wsm.h
|
||||||
|
@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
|
||||||
|
|
||||||
|
-#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
||||||
|
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
|
||||||
|
-#endif
|
||||||
|
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
From c7ba2044b78cc3778763737daea60c9912b710c6 Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <c7ba2044b78cc3778763737daea60c9912b710c6.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:18:38 +0200
|
||||||
|
Subject: [PATCH 3/7] bes2600: thread struct device * through factory
|
||||||
|
request_firmware() call
|
||||||
|
|
||||||
|
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
|
||||||
|
That patch switched the factory calibration read path from filp_open()
|
||||||
|
+ kernel_read() to request_firmware(), but passed dev=NULL to
|
||||||
|
request_firmware() because factory_section_read_file() did not have a
|
||||||
|
struct device * in scope. The resulting logs carry the
|
||||||
|
'(NULL device *):' prefix and do not propagate a udev association.
|
||||||
|
|
||||||
|
Add a module-local static struct device * used as the firmware-class
|
||||||
|
load context, plus a small exported setter:
|
||||||
|
|
||||||
|
static struct device *bes2600_factory_dev;
|
||||||
|
void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
|
||||||
|
Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
|
||||||
|
right after bes2600_platform_data_init() so the platform layer has
|
||||||
|
already had a chance to use the same struct device for its own
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
factory_section_read_file() now passes bes2600_factory_dev (instead
|
||||||
|
of NULL) to request_firmware(). When the factory read happens before
|
||||||
|
probe (not currently the case on PineTab2) the pointer is still NULL
|
||||||
|
and request_firmware() accepts that; no regression.
|
||||||
|
|
||||||
|
No API changes to bes2600_get_factory_cali_data() callers. The
|
||||||
|
char *path parameter remains (it is the firmware-class name fed
|
||||||
|
straight to request_firmware()).
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
|
||||||
|
post-c5 factory diagnostics now carry the SDIO device identity
|
||||||
|
instead of '(NULL device *)'.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_factory.c | 14 +++++++++++++-
|
||||||
|
drivers/staging/bes2600/bes2600_factory.h | 3 +++
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 4 ++++
|
||||||
|
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index 8d60b7c..1cda447 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -31,6 +31,18 @@
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(factory_lock);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * 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
|
||||||
|
@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
|
||||||
|
bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ ret = request_firmware(&fw, path, bes2600_factory_dev);
|
||||||
|
if (ret) {
|
||||||
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
index 3835b0d..7dbe9f8 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
@@ -199,6 +199,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_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..371ef4f 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
#include "bes2600.h"
|
||||||
|
#include "sbus.h"
|
||||||
|
#include "bes2600_plat.h"
|
||||||
|
+#include "bes2600_factory.h"
|
||||||
|
#include "hwio.h"
|
||||||
|
#include "bes_chardev.h"
|
||||||
|
#include "bes_log.h"
|
||||||
|
@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
+ /* wire struct device into factory.c for request_firmware() context */
|
||||||
|
+ bes2600_factory_set_dev(dev);
|
||||||
|
+
|
||||||
|
self->pdata = bes2600_get_platform_data();
|
||||||
|
self->func = func;
|
||||||
|
self->dev = &func->dev;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
From 108d3967eac4ba3a6e0f508d865a5f221b49079d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <108d3967eac4ba3a6e0f508d865a5f221b49079d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:37:45 +0200
|
||||||
|
Subject: [PATCH 4/7] bes2600: gate device LP-mode entry on successful per-VIF
|
||||||
|
firmware handshake
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() drives the transition to low-power for each
|
||||||
|
associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on
|
||||||
|
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
|
||||||
|
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Two bugs:
|
||||||
|
|
||||||
|
1. A failed wsm_set_pm() only logs an error, then still falls into
|
||||||
|
wait_for_completion_timeout() on a completion the firmware will
|
||||||
|
never post (the set-mode command never reached it). The loop
|
||||||
|
therefore always blocks the full 5 s, logs a second error, and
|
||||||
|
proceeds.
|
||||||
|
|
||||||
|
2. A genuine wait-timeout (firmware received the set-mode command but
|
||||||
|
never posted the indication) also only logs a warning. The code
|
||||||
|
then drops to bes2600_pwr_device_enter_lp_mode(), handing the
|
||||||
|
device subsystem an inconsistent view of mac-layer state.
|
||||||
|
|
||||||
|
On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
|
||||||
|
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
|
||||||
|
message flooding dmesg every 5-10 s when the interface is associated
|
||||||
|
and idle. Sending the device to LP in that state cascades into the
|
||||||
|
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
|
||||||
|
WARN splat.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Add a 'timeouts' counter; bump it on both failure paths.
|
||||||
|
- Skip the wait_for_completion entirely when wsm_set_pm() failed
|
||||||
|
(there is no completion to wait for).
|
||||||
|
- Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
|
||||||
|
handshake reached firmware-ACKed completion; otherwise return
|
||||||
|
-ETIMEDOUT and leave the device in its current power state.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
Post-patch the handshake still fails on this particular firmware
|
||||||
|
revision (separate root-cause investigation outside this patch), but
|
||||||
|
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
|
||||||
|
and destabilising the SDIO path.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 20 +++++++++++++++++---
|
||||||
|
1 file changed, 17 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index e7a1045..f62ae22 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
int i = 0;
|
||||||
|
struct bes2600_vif *priv;
|
||||||
|
int ret = 0;
|
||||||
|
+ int timeouts = 0;
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
bes_err("%s, set operation mode fail\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
- if (!status)
|
||||||
|
+ if (!status) {
|
||||||
|
bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* set device low power configuration */
|
||||||
|
- bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ /*
|
||||||
|
+ * 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
|
||||||
|
+ ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+678
@@ -0,0 +1,678 @@
|
|||||||
|
From 3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:55:18 +0200
|
||||||
|
Subject: [PATCH 5/7] bes2600: remove userspace /dev/bes2600 character device
|
||||||
|
interface
|
||||||
|
|
||||||
|
bes_chardev.c implemented a custom character device at /dev/bes2600 with
|
||||||
|
its own parser and command-dispatch table, exposing operations such as
|
||||||
|
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
|
||||||
|
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
|
||||||
|
the in-tree driver - every kernel call site consumes the internal state
|
||||||
|
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
|
||||||
|
etc) directly, not through the cdev.
|
||||||
|
|
||||||
|
The cdev interface is a standing upstream blocker for two reasons:
|
||||||
|
|
||||||
|
1. Drivers under drivers/staging/ and drivers/net/wireless/ are
|
||||||
|
expected to expose tuning via the firmware/nl80211/debugfs
|
||||||
|
infrastructure rather than a private /dev node with an ad-hoc
|
||||||
|
parser.
|
||||||
|
|
||||||
|
2. The cdev handlers keep a global bes_cdev singleton alive whose
|
||||||
|
->cdev, ->dev_id, ->class and ->device pointers exist only to be
|
||||||
|
torn down; they add no functionality that nl80211 or rfkill do
|
||||||
|
not already provide (wifi/bt on-off, module_param for fw_type).
|
||||||
|
|
||||||
|
Remove the userspace interface:
|
||||||
|
|
||||||
|
- open / read / write / release file_operations handlers and the
|
||||||
|
bes2600_chardev_fops instance
|
||||||
|
- bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
|
||||||
|
- bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
|
||||||
|
string helpers
|
||||||
|
- bes2600_load_uevent (its only caller was
|
||||||
|
bes2600_chrdev_wifi_force_close_work informing userspace of a
|
||||||
|
state it already gates via rfkill; that snprintf +
|
||||||
|
kobject_uevent_env block is gone too, the kernel-side
|
||||||
|
halt_device + switch_wifi(0) + chrdev_check_system_close
|
||||||
|
sequence remains)
|
||||||
|
- alloc_chrdev_region / cdev_init / cdev_add / class_create /
|
||||||
|
device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
|
||||||
|
unwind labels
|
||||||
|
- cdev_del / unregister_chrdev_region / device_destroy /
|
||||||
|
class_destroy in bes2600_chrdev_free
|
||||||
|
- cdev/dev_id/major/minor/class/device fields in struct bes_cdev
|
||||||
|
|
||||||
|
What remains (unchanged behaviour):
|
||||||
|
|
||||||
|
- fw_type module parameter - the primary user-facing knob for
|
||||||
|
signal/no-signal/BT mode switch
|
||||||
|
- All in-kernel bes2600_chrdev_* accessor functions called from
|
||||||
|
bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
|
||||||
|
wifi_testmode_cmd.c (13 call sites)
|
||||||
|
- bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
|
||||||
|
for the remaining bes_cdev state (waitqueues, workqueues, flags)
|
||||||
|
- DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
|
||||||
|
- wifi_force_close worker, system-close logic, bus-probe state
|
||||||
|
machine
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
|
||||||
|
no kernel messages related to the cdev absence. Users that previously
|
||||||
|
wrote to /dev/bes2600 should switch to the fw_type module parameter
|
||||||
|
or (future patch c4) nl80211 testmode commands.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
|
||||||
|
and friends so the global bes2600_cdev singleton can be dropped
|
||||||
|
and the accessors scale to multi-device scenarios.
|
||||||
|
- c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
|
||||||
|
commands to the firmware's patch_wifi_testMode entry.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes_chardev.c | 519 ------------------------------------------
|
||||||
|
1 file changed, 519 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
index 9038e48..e2e4f1b 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
@@ -43,12 +43,6 @@ enum bus_probe_state {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bes_cdev {
|
||||||
|
- struct cdev cdev;
|
||||||
|
- dev_t dev_id;
|
||||||
|
- int major;
|
||||||
|
- int minor;
|
||||||
|
- struct class *class;
|
||||||
|
- struct device *device;
|
||||||
|
atomic_t num_proc;
|
||||||
|
wait_queue_head_t open_wq;
|
||||||
|
spinlock_t status_lock;
|
||||||
|
@@ -249,351 +243,18 @@ int bes2600_switch_bt(bool on)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_get_cmd_and_ifname(const char *str, char **result)
|
||||||
|
-{
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ifname_len = 0;
|
||||||
|
- char *sp = NULL;
|
||||||
|
- char *tmp_ptr = NULL;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
-
|
||||||
|
- /* check if input arguments is valid */
|
||||||
|
- if (!str || strncmp(str, "ifname:", 7) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- sp = strchr(str, ' ');
|
||||||
|
- if (strncmp(sp + 1, "cmd:", 4) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- /* extract interface name */
|
||||||
|
- ifname_len = sp - str - 7;
|
||||||
|
- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- return -2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, str+7, ifname_len);
|
||||||
|
- tmp_ptr[ifname_len] = '\0';
|
||||||
|
- result[0] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- /* get command length */
|
||||||
|
- cmd_ptr = strstr(str, "cmd:");
|
||||||
|
- cmd_ptr += 4;
|
||||||
|
- sp = strchr(cmd_ptr, ' ');
|
||||||
|
- if (!sp) { /* the command don't have any parameter */
|
||||||
|
- cmd_len = strlen(cmd_ptr);
|
||||||
|
- if (cmd_ptr[cmd_len - 1] == '\n')
|
||||||
|
- --cmd_len;
|
||||||
|
- } else { /* the command have one or more parameter */
|
||||||
|
- cmd_len = sp - cmd_ptr;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* copy command to out buffer */
|
||||||
|
- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- kfree(result[0]);
|
||||||
|
- result[0] = NULL;
|
||||||
|
- return -3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, cmd_ptr, cmd_len);
|
||||||
|
- tmp_ptr[cmd_len] = '\0';
|
||||||
|
- result[1] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void bes2600_recyle_cmd_and_ifname_mem(char **info)
|
||||||
|
-{
|
||||||
|
- if (info[0]) {
|
||||||
|
- kfree(info[0]);
|
||||||
|
- info[0] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (info[1]) {
|
||||||
|
- kfree(info[1]);
|
||||||
|
- info[1] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_default_handler(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
-
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
|
||||||
|
- } else {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, str);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_wifi_bt_on_off(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- int ret = 0;
|
||||||
|
- enum wait_state wait_state;
|
||||||
|
- enum bus_probe_state probe_state;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- probe_state = bes2600_cdev.bus_probe;
|
||||||
|
- wait_state = bes2600_cdev.wait_state;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* only work for wifi signal mode */
|
||||||
|
- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* wait bus probe operation end */
|
||||||
|
- if (probe_state == BES2600_BUS_PROBE_START) {
|
||||||
|
- bes_devel("wait bus probe operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
|
||||||
|
- HZ);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* must wait previous operation end in critical section */
|
||||||
|
- if (wait_state != BES2600_BOOT_WAIT_NONE) {
|
||||||
|
- bes_devel("wait previous operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
|
||||||
|
- HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* if dpd calibration is doing, modify wifi and bt state directly */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = true;
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = false;
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = true;
|
||||||
|
- bes2600_cdev.bton_pending = true;
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = false;
|
||||||
|
- bes2600_cdev.bton_pending = false;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* process wifi/bt on/off operation */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(1);
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(0);
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(1);
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!ret && bes2600_chrdev_check_system_close())
|
||||||
|
- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
- bes2600_cdev.sbus_priv);
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return ret ;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-static int bes2600_op_change_fw_type(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- int temp = 0;
|
||||||
|
- long status = 0;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
- char fw_type[5] = {0};
|
||||||
|
- bool sys_closed = bes2600_chrdev_check_system_close();
|
||||||
|
-
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
-
|
||||||
|
- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- return -EPERM;
|
||||||
|
-
|
||||||
|
- /* check if user input is valid */
|
||||||
|
- cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
|
||||||
|
- if (strlen(str) < 16 || !cmd_ptr) {
|
||||||
|
- bes_err("the format of \"%s\" is error\n", str);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* convert fw_type from string to int */
|
||||||
|
- strncpy(fw_type, cmd_ptr + 14, 4);
|
||||||
|
- fw_type[0] = '+';
|
||||||
|
- ret = kstrtoint(fw_type, 10, &temp);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- bes_err("%s parse error\n", __func__);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* no need to realod firmware if new fw_type is equal to the old */
|
||||||
|
- if (temp == bes2600_cdev.fw_type ) {
|
||||||
|
- bes_devel("fw type is equal\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* close wifi net device */
|
||||||
|
- if (bes2600_cdev.sbus_priv
|
||||||
|
- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
||||||
|
- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* update firmware type */
|
||||||
|
- bes2600_cdev.fw_type = temp;
|
||||||
|
- bes2600_chrdev_update_signal_mode();
|
||||||
|
-
|
||||||
|
- if (!sys_closed) {
|
||||||
|
- /* close device to call disconnect function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
|
||||||
|
- else if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_chrdev_start_bus_probe();
|
||||||
|
-
|
||||||
|
- /* wait disconnect event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.dpd_calied
|
||||||
|
- && bes2600_chrdev_check_system_close()) {
|
||||||
|
- bes_devel("no need to reload firmware\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("reload firmware...\n");
|
||||||
|
- /* power on device to call probe function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
||||||
|
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_wakeup(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 wakeup bt.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_sleep(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
|
||||||
|
- bes_devel("bes2600 allow bt sleep.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static int bes2600_op_set_wakeup_read_flag(const char *str)
|
||||||
|
-{
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
||||||
|
-int bes2600_load_uevent(char *env[])
|
||||||
|
-{
|
||||||
|
- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
-}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-static struct bes2600_op_map bes2600_op_map_tab[] ={
|
||||||
|
- /*op op_len handler */
|
||||||
|
- {"P2P_SET_NOA", 11, bes2600_op_default_handler},
|
||||||
|
- {"P2P_SET_PS", 10, bes2600_op_default_handler},
|
||||||
|
- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
|
||||||
|
- {"LINKSPEED", 9, bes2600_op_default_handler},
|
||||||
|
- {"RSSI", 4, bes2600_op_default_handler},
|
||||||
|
- {"GETBAND", 7, bes2600_op_default_handler},
|
||||||
|
- {"WLS_BATCHING", 12, bes2600_op_default_handler},
|
||||||
|
- {"MACADDR", 7, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-START", 14, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-STOP", 13, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-ADD", 12, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXMODE", 10, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
|
||||||
|
- {"SETSUSPENDMODE", 14, bes2600_op_default_handler},
|
||||||
|
- {"COUNTRY", 7, bes2600_op_default_handler},
|
||||||
|
- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_ON", 5, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
|
||||||
|
- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
|
||||||
|
- {"BT_SLEEP", 8, bes2600_op_bt_sleep},
|
||||||
|
- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
|
||||||
|
-};
|
||||||
|
|
||||||
|
static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
{
|
||||||
|
@@ -603,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
&& (bes2600_cdev.wifi_opened == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
-{
|
||||||
|
- if (atomic_read(&bes2600_cdev.num_proc) > 0) {
|
||||||
|
- wait_event_timeout(bes2600_cdev.open_wq,
|
||||||
|
- (atomic_read(&bes2600_cdev.num_proc) == 0),
|
||||||
|
- MAX_SCHEDULE_TIMEOUT);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- bes_devel("bes2600 char device is opened\n");
|
||||||
|
- atomic_inc(&bes2600_cdev.num_proc);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
|
||||||
|
- size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- char buf[64] = {0};
|
||||||
|
- unsigned int len;
|
||||||
|
- long status = 0;
|
||||||
|
|
||||||
|
- switch (bes2600_cdev.read_flag) {
|
||||||
|
- case BES_CDEV_READ_WAKEUP_STATE:
|
||||||
|
- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
|
||||||
|
- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
|
||||||
|
- bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
|
||||||
|
- bes2600_cdev.dpd_calied,
|
||||||
|
- bes2600_cdev.wifi_opened,
|
||||||
|
- bes2600_cdev.bt_opened,
|
||||||
|
- bes2600_cdev.fw_type);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- len = sizeof(buf);
|
||||||
|
- /* reset read flag */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static ssize_t bes2600_chrdev_write(struct file *file,
|
||||||
|
- const char __user *user_buf, size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- int i = 0;
|
||||||
|
- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ret = 0;
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- char *buf = NULL;
|
||||||
|
-
|
||||||
|
- /* copy content from user space to kernel */
|
||||||
|
- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
|
||||||
|
- buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
- if (copy_from_user(buf, user_buf, count))
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* add terminal character */
|
||||||
|
- buf[count] = '\0';
|
||||||
|
-
|
||||||
|
- /* extract comand and interface */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
|
||||||
|
- kfree(buf);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* match operation item and execure its handler */
|
||||||
|
- cmd_len = strlen(info[1]);
|
||||||
|
- for (i = 0; i < cmd_num; i++) {
|
||||||
|
- if (cmd_len < bes2600_op_map_tab[i].op_len)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
|
||||||
|
- ret = bes2600_op_map_tab[i].handler(buf);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* operation item mismatch */
|
||||||
|
- if (i == cmd_num) {
|
||||||
|
- bes_err("cmd(%s) mismatch\n", info[1]);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- kfree(buf);
|
||||||
|
-
|
||||||
|
- return (ret == 0) ? count : ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_release (struct inode *inode, struct file *file)
|
||||||
|
-{
|
||||||
|
- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
|
||||||
|
- wake_up(&bes2600_cdev.open_wq);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 char device is closed\n");
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static struct file_operations bes2600_chardev_fops =
|
||||||
|
-{
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
- .open = bes2600_chrdev_open,
|
||||||
|
- .read = bes2600_chrdev_read,
|
||||||
|
- .write = bes2600_chrdev_write,
|
||||||
|
- .release = bes2600_chrdev_release,
|
||||||
|
-};
|
||||||
|
|
||||||
|
#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
@@ -1124,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void)
|
||||||
|
|
||||||
|
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
- char wifi_state[15];
|
||||||
|
- char bt_state[15];
|
||||||
|
- char fw_type[15];
|
||||||
|
- char *env[] = { wifi_state, bt_state, fw_type, NULL };
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
if (bes2600_chrdev_is_wifi_opened()) {
|
||||||
|
bes_devel("system exeception, force wifi down\n");
|
||||||
|
|
||||||
|
@@ -1146,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* notify userspace */
|
||||||
|
- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
|
||||||
|
- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
|
||||||
|
- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
|
||||||
|
- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
- if (!ret)
|
||||||
|
- bes_err("bes2600 notify userspace failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1247,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
|
||||||
|
|
||||||
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
-
|
||||||
|
- /* allocate devide id */
|
||||||
|
- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 alloc device id fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* extract major and minor device id */
|
||||||
|
- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
|
||||||
|
- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
|
||||||
|
-
|
||||||
|
- /* add char device and bind operation function */
|
||||||
|
- bes2600_cdev.cdev.owner = THIS_MODULE;
|
||||||
|
- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
|
||||||
|
- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* create class for creating device node */
|
||||||
|
- bes2600_cdev.class = class_create("bes2600_chrdev");
|
||||||
|
- if (IS_ERR(bes2600_cdev.class)){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* get char device pointer */
|
||||||
|
- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
|
||||||
|
- if (IS_ERR(bes2600_cdev.device)){
|
||||||
|
- bes_err("bes2600 char device create fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* initialise global variable */
|
||||||
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
||||||
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
||||||
|
@@ -1318,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
-
|
||||||
|
-fail3:
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
-fail2:
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
-fail1:
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
-fail:
|
||||||
|
- return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bes2600_chrdev_free(void)
|
||||||
|
@@ -1336,9 +821,5 @@ void bes2600_chrdev_free(void)
|
||||||
|
bes2600_free_dpd_log_buffer();
|
||||||
|
#endif
|
||||||
|
bes2600_chrdev_free_dpd_data();
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
From 6f13e008d21d453db486f707f47340a0a17e650b Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <6f13e008d21d453db486f707f47340a0a17e650b.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:04:27 +0200
|
||||||
|
Subject: [PATCH 6/7] bes2600: enable CONFIG_BES2600_TESTMODE by default + fix
|
||||||
|
bit-rotted testmode plumbing
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The driver implements a mac80211 testmode_cmd operation that dispatches
|
||||||
|
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
|
||||||
|
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
|
||||||
|
BES2600 RF-test path (bes2600_vendor_rf_cmd -> firmware
|
||||||
|
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
|
||||||
|
binding in struct ieee80211_ops are conditionally compiled under
|
||||||
|
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
|
||||||
|
|
||||||
|
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
|
||||||
|
included in the build and the .testmode_cmd op is populated. On the
|
||||||
|
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
|
||||||
|
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
|
||||||
|
through the standard nl80211 testmode interface ('iw phy0 ...').
|
||||||
|
|
||||||
|
This also makes visible two classes of bit-rot that had accumulated
|
||||||
|
while nobody was building with CONFIG_BES2600_TESTMODE=y:
|
||||||
|
|
||||||
|
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
|
||||||
|
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
|
||||||
|
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
|
||||||
|
first argument. Neither the macros nor any of the BES2600_DBG_*
|
||||||
|
constants are defined anywhere in the tree. The same call pattern
|
||||||
|
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
|
||||||
|
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
|
||||||
|
|
||||||
|
Add minimal shim macros to bes_log.h that rewire the calls onto
|
||||||
|
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
|
||||||
|
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
|
||||||
|
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
|
||||||
|
as 0 constants for documentation / grep.
|
||||||
|
|
||||||
|
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
|
||||||
|
bes2600_get_roam_delay() are declared in sta.c with external
|
||||||
|
linkage but have no prototype in any header. All callers live in
|
||||||
|
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
|
||||||
|
off the compiler never sees them; with it on gcc
|
||||||
|
-Werror=missing-prototypes breaks the build.
|
||||||
|
|
||||||
|
Mark the three functions static. (Keeping them file-local also
|
||||||
|
matches their actual usage.)
|
||||||
|
|
||||||
|
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
|
||||||
|
buildable; no behavioural change when the flag is off.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
|
||||||
|
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/Makefile | 2 +-
|
||||||
|
drivers/staging/bes2600/bes_log.h | 23 +++++++++++++++++++++++
|
||||||
|
drivers/staging/bes2600/sta.c | 6 +++---
|
||||||
|
3 files changed, 27 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 2dcba09..2c1a850 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||||
|
# feature option
|
||||||
|
BES2600 ?= m
|
||||||
|
|
||||||
|
-CONFIG_BES2600_TESTMODE ?= n
|
||||||
|
+CONFIG_BES2600_TESTMODE ?= y
|
||||||
|
|
||||||
|
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h
|
||||||
|
index 605cea8..65cf703 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_log.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes_log.h
|
||||||
|
@@ -8,3 +8,26 @@ extern struct device *global_dev;
|
||||||
|
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
|
||||||
|
+ * was never implemented in-tree; these shims let code paths gated by
|
||||||
|
+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
|
||||||
|
+ * build when their conditions are enabled. The first argument is
|
||||||
|
+ * currently unused; pick one of the BES2600_DBG_* constants below for
|
||||||
|
+ * documentation.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_DBG_SBUS 0
|
||||||
|
+#define BES2600_DBG_DOWNLOAD 0
|
||||||
|
+#define BES2600_DBG_ITP 0
|
||||||
|
+#define BES2600_DBG_TEST_MODE 0
|
||||||
|
+
|
||||||
|
+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_cond) \
|
||||||
|
+ bes_err(fmt, ##__VA_ARGS__); \
|
||||||
|
+ } while (0)
|
||||||
|
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
|
||||||
|
index aa69eb8..5f1a456 100644
|
||||||
|
--- a/drivers/staging/bes2600/sta.c
|
||||||
|
+++ b/drivers/staging/bes2600/sta.c
|
||||||
|
@@ -3633,7 +3633,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;
|
||||||
|
@@ -3663,7 +3663,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;
|
||||||
|
@@ -3703,7 +3703,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
*
|
||||||
|
* Returns: Returns the last measured roam delay
|
||||||
|
*/
|
||||||
|
-int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <10a05d21bfe4563f963e16d65228fd7a713c143d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH 7/7] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index 371ef4f..3e04e8c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -95,6 +95,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;
|
||||||
|
@@ -1136,7 +1137,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:*/
|
||||||
|
@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (!self->single_gathered_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ if (!self->tx_bounce) {
|
||||||
|
+#ifndef SDIO_HOST_ADMA_SUPPORT
|
||||||
|
+ free_pages((unsigned long)self->single_gathered_buffer,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+#endif
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
#ifdef BES_SDIO_RXTX_TOGGLE
|
||||||
|
self->fw_started = false;
|
||||||
|
#endif
|
||||||
|
@@ -1985,6 +2016,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 12:35:28 +0200
|
||||||
|
Subject: [PATCH 0/7] bes2600: staging-prep cleanup for PineTab2 (BES2600WM)
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
This series is a staging-prep cleanup for the out-of-tree Bestechnic
|
||||||
|
BES2600WM Wi-Fi/BT combo-chip driver as shipped by Mobian's bes2600-dkms
|
||||||
|
package (and in-tree at drivers/staging/bes2600/ in the danctnix
|
||||||
|
linux-pinetab2 fork). Target hardware is the Pine64 PineTab2 (RK3566
|
||||||
|
+ BES2600WM, SDIO vendor 0xBE57 / device 0x2002).
|
||||||
|
|
||||||
|
The driver descends from the ST-Ericsson CW1200 (drivers/net/wireless/
|
||||||
|
st/cw1200/) -- same author, Dmitry Tarnyagin, shared WSM host<->firmware
|
||||||
|
protocol, shared SDIO bus backend. Kconfig ancestry markers survive in
|
||||||
|
this tree today: CONFIG_BES2600_USE_STE_EXTENSIONS (STE = ST-Ericsson),
|
||||||
|
CONFIG_BES2600_WSM_DEBUG (WSM). ST-Ericsson wound down in 2013;
|
||||||
|
Bestechnic (founded 2015) appears to have inherited or licensed the
|
||||||
|
CW1200 IP. No linux-wireless RFC has ever linked the two chips.
|
||||||
|
|
||||||
|
The series fixes observable defects on a PineTab2 running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 and removes two upstream blockers. Each patch is
|
||||||
|
independently testable and bisectable; the order below preserves
|
||||||
|
dependencies.
|
||||||
|
|
||||||
|
## What the series does
|
||||||
|
|
||||||
|
* 1/7 -- Replace filp_open() + kernel_read() in the factory-calibration
|
||||||
|
read path with request_firmware(). Repoint the FACTORY_PATH macro to
|
||||||
|
the firmware-class name (bes2600/bes2600_factory.txt, matching the
|
||||||
|
/lib/firmware/ layout). Kills a kernel-mainline anti-pattern and the
|
||||||
|
'(NULL device *): read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' boot spam on PineTab2.
|
||||||
|
|
||||||
|
* 2/7 -- Default STANDARD_FACTORY_EFUSE_FLAG from y to n. The shipped
|
||||||
|
bes2600_factory.txt on PineTab2 contains 30 calibration fields; the
|
||||||
|
driver was expecting 31 (including a ##select_efuse_flag section
|
||||||
|
absent from this firmware). Also unguards the
|
||||||
|
wsm_save_factory_txt_to_mcu() prototype in wsm.h which was
|
||||||
|
inconsistently wrapped in '#if defined(STANDARD_FACTORY_EFUSE_FLAG)'
|
||||||
|
while its definition in wsm.c and its call site in sta.c were
|
||||||
|
ungated -- gcc -Werror=missing-prototypes broke the build with the
|
||||||
|
new default until this is fixed.
|
||||||
|
|
||||||
|
* 3/7 -- Thread struct device * through factory_section_read_file() via
|
||||||
|
a module-local setter invoked at SDIO probe. request_firmware() now
|
||||||
|
receives a real device pointer; '(NULL device *):' no longer prefixes
|
||||||
|
factory-related diagnostics.
|
||||||
|
|
||||||
|
* 4/7 -- Gate the device-end of the low-power transition on successful
|
||||||
|
per-VIF firmware handshake. Pre-patch bes2600_pwr_enter_lp_mode()
|
||||||
|
called bes2600_pwr_device_enter_lp_mode() unconditionally even when
|
||||||
|
wait_for_completion_timeout() returned 0 (firmware never posted the
|
||||||
|
PM-change indication). On PineTab2 this recurred every 5-10 s
|
||||||
|
whenever the interface was associated and idle, flooded dmesg, and
|
||||||
|
cascaded into sdio_tx_work WARN splats / [RX] Receive failure
|
||||||
|
messages. Post-patch: -ETIMEDOUT returned cleanly, dmesg silent,
|
||||||
|
SDIO stable.
|
||||||
|
|
||||||
|
* 5/7 -- Remove the custom /dev/bes2600 character-device interface.
|
||||||
|
file_operations, open/read/write/release, bes2600_op_*
|
||||||
|
command-dispatch table, bes2600_load_uevent, alloc_chrdev_region /
|
||||||
|
cdev_init / cdev_add / class_create / device_create in the init
|
||||||
|
path, and the matching teardown in bes2600_chrdev_free -- 519 lines
|
||||||
|
deleted. The in-kernel accessor functions (is_signal_mode,
|
||||||
|
get_fw_type, etc., 13 call sites) and the fw_type module parameter
|
||||||
|
are preserved; the userspace interface becomes rfkill + module_param
|
||||||
|
+ (with 6/7) nl80211 testmode.
|
||||||
|
|
||||||
|
* 6/7 -- Flip CONFIG_BES2600_TESTMODE default from n to y. The driver
|
||||||
|
already implements a mac80211 testmode_cmd dispatcher (routing to
|
||||||
|
the firmware's patch_wifi_testMode path), already gated on the flag;
|
||||||
|
CONFIG_NL80211_TESTMODE=y is common on target kernels. Enabling the
|
||||||
|
flag also exposes accumulated bit-rot -- ~41 calls to undefined
|
||||||
|
bes2600_info/err/warn/dbg/err_with_cond macros, and 3 TSM/roam-delay
|
||||||
|
helpers with external linkage but no prototype. Add shim macros to
|
||||||
|
bes_log.h rewiring the legacy calls onto the existing bes_info /
|
||||||
|
bes_err / bes_warn / bes_devel family, define BES2600_DBG_* subsystem
|
||||||
|
ids as 0 constants, and mark the 3 helpers static.
|
||||||
|
|
||||||
|
* 7/7 -- Bounce SDIO TX buffers to avoid DMA out-of-bounds reads.
|
||||||
|
sdio_tx_work() rounded the transfer length up to the SDIO block size
|
||||||
|
(align = blks * cur_blksize, or 1632) and handed that length to
|
||||||
|
dma_map_sg() via sg_set_buf(..., tx_buffer->buf, align); tx_buffer->buf
|
||||||
|
typically aliases into an skb linear head allocated to tx_buffer->len,
|
||||||
|
not the block-aligned length. The DMA engine therefore read up to one
|
||||||
|
block past the end of the skb -- KFENCE on PineTab2 fires as
|
||||||
|
'out-of-bounds read in __pi_memcpy_generic ... 704B right of
|
||||||
|
kfence-#...' with sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper in
|
||||||
|
the stack and pskb_expand_head / validate_xmit_skb / tcp_write_xmit in
|
||||||
|
the allocator stack. Besides being undefined behavior, the padding
|
||||||
|
bytes are transmitted to the peer, leaking adjacent kernel memory on
|
||||||
|
the air. Allocate a driver-owned DMA-pages bounce buffer of
|
||||||
|
MAX_SDIO_TRANSFER_LEN, memcpy each TX buffer into its slot, zero the
|
||||||
|
padding tail, and point the SG entries at the bounce. Mirrors the
|
||||||
|
pattern already used for single_gathered_buffer; kept as a separate
|
||||||
|
allocation because sdio_tx_work accumulates SG entries before claiming
|
||||||
|
the bus.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Reference hardware: Pine64 PineTab2 (BES2600WM + Rockchip RK3566,
|
||||||
|
8a:2e:77:1f:ec:05, LAN AP newton @ 2.4 GHz ch11, tested also on
|
||||||
|
TelekomHotspot@ERGO @ 5 GHz ch36).
|
||||||
|
|
||||||
|
Host kernel: linux-pinetab2 6.19.10-danctnix1-1-pinetab2 with
|
||||||
|
CONFIG_NL80211_TESTMODE=y and CONFIG_KFENCE=y (for 7/7 verification).
|
||||||
|
Driver installed via /lib/modules/<ver>/extra/ and verified loaded via
|
||||||
|
modinfo srcversion.
|
||||||
|
|
||||||
|
Per-patch outcomes in post-reboot dmesg (full-stack applied, 11+ min
|
||||||
|
soak, 245k RX packets / 365 MB traffic):
|
||||||
|
|
||||||
|
- 1/7: 'read and check /lib/firmware/bes2600_factory.txt error' -- gone
|
||||||
|
- 2/7: 'bes2600_factory.txt parse fail' / 'factory cali data get
|
||||||
|
failed.' -- gone
|
||||||
|
- 3/7: '(NULL device *):' prefix on factory lines -- gone
|
||||||
|
- 4/7: 'bes2600_pwr_enter_lp_mode, wait pm ind timeout' (pre-patch 20-30
|
||||||
|
msgs / 5 min window) -- 0 per 5 min; '[RX] Receive failure: 4.' --
|
||||||
|
gone
|
||||||
|
- 5/7: /dev/bes2600 -- absent; driver continues to associate
|
||||||
|
- 6/7: 'iw phy0' lists 'testmode' under Supported commands; module
|
||||||
|
builds cleanly
|
||||||
|
- 7/7: 'BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic'
|
||||||
|
(pre-patch ~65 splats per 4 h of real traffic, always via
|
||||||
|
sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper+0x18c) -- 0;
|
||||||
|
'sdio_tx_work+0x2b4' WARN splat residual from 4/7's cascade -- 0
|
||||||
|
(previously recurred ~1 per reboot even with 4/7 applied);
|
||||||
|
'PS Mode Error, Reason:1' benign handshake notice at T+40s --
|
||||||
|
also gone, apparently a downstream effect of no longer DMAing
|
||||||
|
uninitialised padding bytes into firmware
|
||||||
|
|
||||||
|
Full stack: wifi associates and passes traffic across 3+ reboots.
|
||||||
|
|
||||||
|
## Known limitations / out of scope
|
||||||
|
|
||||||
|
- factory_section_write_file() in bes2600_factory.c still uses
|
||||||
|
kernel_write() + filp_open(O_CREAT) to persist per-channel
|
||||||
|
calibration updates back to /lib/firmware/bes2600_factory.txt.
|
||||||
|
Converting the write-back path to debugfs or nl80211 testmode is a
|
||||||
|
follow-up.
|
||||||
|
|
||||||
|
- bes_chardev.c still carries DPD file-read/write paths gated by
|
||||||
|
BES2600_WRITE_DPD_TO_FILE (off by default, so dead code in the
|
||||||
|
default build). Same treatment needed.
|
||||||
|
|
||||||
|
- bes_fw.c:587 unconditionally creates
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open() for debug
|
||||||
|
observation. Needs to go before drivers/staging/ accepts the driver.
|
||||||
|
|
||||||
|
- bes_cdev global singleton still holds sig_mode and fw_type. Moving
|
||||||
|
those to per-hw_priv state is blocked by fw_type being a module
|
||||||
|
parameter (inherently singleton). Migrating fw_type to a per-phy
|
||||||
|
debugfs knob or nl80211 testmode command is the next step; overlaps
|
||||||
|
with 6/7's testmode plumbing.
|
||||||
|
|
||||||
|
Markus Fritsche (7):
|
||||||
|
bes2600: use request_firmware() for factory.txt read
|
||||||
|
bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for PineTab2
|
||||||
|
factory.txt format
|
||||||
|
bes2600: thread struct device * through factory request_firmware()
|
||||||
|
call
|
||||||
|
bes2600: gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake
|
||||||
|
bes2600: remove userspace /dev/bes2600 character device interface
|
||||||
|
bes2600: enable CONFIG_BES2600_TESTMODE by default + fix bit-rotted
|
||||||
|
testmode plumbing
|
||||||
|
bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
bes2600/Makefile | 6 +-
|
||||||
|
bes2600/bes2600_factory.c | 45 ++--
|
||||||
|
bes2600/bes2600_factory.h | 3 +
|
||||||
|
bes2600/bes2600_sdio.c | 43 +++-
|
||||||
|
bes2600/bes_chardev.c | 519 --------------------------------------
|
||||||
|
bes2600/bes_log.h | 23 ++
|
||||||
|
bes2600/bes_pwr.c | 20 +-
|
||||||
|
bes2600/sta.c | 6 +-
|
||||||
|
bes2600/wsm.h | 2 -
|
||||||
|
9 files changed, 117 insertions(+), 550 deletions(-)
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
From d18aa6a9bc03a03e455434f83577892aa1a60ffe Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <d18aa6a9bc03a03e455434f83577892aa1a60ffe.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 10:09:44 +0200
|
||||||
|
Subject: [PATCH 1/7] bes2600: use request_firmware() for factory.txt read
|
||||||
|
|
||||||
|
The BES2600 factory calibration file (bes2600_factory.txt) was being read
|
||||||
|
via filp_open() + kernel_read() from a hard-coded absolute path baked in
|
||||||
|
at compile time via the FACTORY_PATH Makefile macro
|
||||||
|
(default: /lib/firmware/bes2600_factory.txt).
|
||||||
|
|
||||||
|
This had several problems:
|
||||||
|
|
||||||
|
1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
|
||||||
|
device-pine64-pinetab2) ships the file at
|
||||||
|
/lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
|
||||||
|
driver logged '(NULL device *): read and check
|
||||||
|
/lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
|
||||||
|
running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
|
||||||
|
2. Direct filesystem access via filp_open() / kernel_read() from a driver
|
||||||
|
is an anti-pattern that upstream rejects: drivers should use
|
||||||
|
request_firmware() to get binary data from userspace-managed firmware
|
||||||
|
directories. request_firmware() natively searches the firmware_class
|
||||||
|
path list (typically /lib/firmware + derivatives), associates the load
|
||||||
|
with a uevent, and respects the firmware-loading infrastructure.
|
||||||
|
|
||||||
|
3. The (NULL device *) prefix in error messages indicated the absence of
|
||||||
|
proper device-context logging. While this patch does not yet thread
|
||||||
|
struct device through, the upstream path uses request_firmware() which
|
||||||
|
works with dev=NULL and is the building block for a follow-up patch
|
||||||
|
that adds per-chip device context.
|
||||||
|
|
||||||
|
Repoint the FACTORY_PATH default to the firmware-class name
|
||||||
|
(bes2600/bes2600_factory.txt) - request_firmware() prepends
|
||||||
|
/lib/firmware/ from the configured search paths. The macro remains
|
||||||
|
overridable at build time for non-standard deployments.
|
||||||
|
|
||||||
|
Rewrite factory_section_read_file() to:
|
||||||
|
* Call request_firmware(&fw, path, NULL).
|
||||||
|
* Size-check fw->size against FACTORY_MAX_SIZE.
|
||||||
|
* memcpy the data into the caller's buffer.
|
||||||
|
* Always call release_firmware() on exit.
|
||||||
|
|
||||||
|
The file write path (factory_section_write_file + kernel_write) is left
|
||||||
|
unchanged in this patch; it is the subject of a follow-up patch that
|
||||||
|
removes kernel_write and moves any remaining userspace-visible factory
|
||||||
|
configuration to a standard kernel-userspace boundary (debugfs or
|
||||||
|
nl80211 testmode).
|
||||||
|
|
||||||
|
No caller signature changes. No Makefile flag drops. Bisectable.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
|
||||||
|
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' is gone; request_firmware reads the file successfully (a separate
|
||||||
|
factory_parse() bug, previously masked by the read failure, is now
|
||||||
|
exposed and tracked separately).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
|
||||||
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 300912b..788aee2 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
-FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||||
|
+FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
# basic function
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index dc5d3da..8d60b7c 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#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>
|
||||||
|
@@ -137,38 +138,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, NULL);
|
||||||
|
+ 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;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ memcpy(buffer, fw->data, fw->size);
|
||||||
|
+ ret = (int)fw->size;
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
From a826f4db7d97a3a872d92079db37dbdaf9a0cdec Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <a826f4db7d97a3a872d92079db37dbdaf9a0cdec.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:17:56 +0200
|
||||||
|
Subject: [PATCH 2/7] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for
|
||||||
|
PineTab2 factory.txt format
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The shipped factory calibration file bes2600_factory.txt on PineTab2
|
||||||
|
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
|
||||||
|
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
|
||||||
|
bt (4). The file terminates with '%%\n' directly after edr_power.
|
||||||
|
|
||||||
|
When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
|
||||||
|
assembles STANDARD_FACTORY with an extra select_efuse_flag section
|
||||||
|
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):
|
||||||
|
|
||||||
|
__STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
|
||||||
|
+ \"%%%%\\n\"
|
||||||
|
|
||||||
|
The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
|
||||||
|
stops after field 30 and factory_parse() returns -1 with:
|
||||||
|
|
||||||
|
bes2600_factory.txt parse fail
|
||||||
|
read and check bes2600/bes2600_factory.txt error
|
||||||
|
factory cali data get failed.
|
||||||
|
|
||||||
|
This was latent until the preceding patch (use request_firmware() for
|
||||||
|
factory.txt read) fixed the path bug that masked the parse failure.
|
||||||
|
|
||||||
|
Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
|
||||||
|
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
|
||||||
|
firmware packages that do ship the select_efuse_flag section.
|
||||||
|
|
||||||
|
Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
|
||||||
|
inconsistently wrapped in a conditional that keyed on
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
|
||||||
|
the call site in sta.c are ungated. With the flag now defaulting to
|
||||||
|
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
|
||||||
|
conditional wrapper around the prototype -- the function exists and is
|
||||||
|
used regardless of the factory-parse flag.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
|
||||||
|
succeeds on the shipped factory.txt, factory_cali_data is populated,
|
||||||
|
and dmesg no longer shows the parse-fail / read-and-check-error /
|
||||||
|
factory-cali-data-get-failed sequence.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/wsm.h | 2 --
|
||||||
|
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 788aee2..2dcba09 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
-STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
+STANDARD_FACTORY_EFUSE_FLAG ?= n
|
||||||
|
FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
diff --git a/bes2600/wsm.h b/bes2600/wsm.h
|
||||||
|
index 0673131..22845ac 100644
|
||||||
|
--- a/bes2600/wsm.h
|
||||||
|
+++ b/bes2600/wsm.h
|
||||||
|
@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
|
||||||
|
|
||||||
|
-#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
||||||
|
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
|
||||||
|
-#endif
|
||||||
|
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
From c7ba2044b78cc3778763737daea60c9912b710c6 Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <c7ba2044b78cc3778763737daea60c9912b710c6.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:18:38 +0200
|
||||||
|
Subject: [PATCH 3/7] bes2600: thread struct device * through factory
|
||||||
|
request_firmware() call
|
||||||
|
|
||||||
|
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
|
||||||
|
That patch switched the factory calibration read path from filp_open()
|
||||||
|
+ kernel_read() to request_firmware(), but passed dev=NULL to
|
||||||
|
request_firmware() because factory_section_read_file() did not have a
|
||||||
|
struct device * in scope. The resulting logs carry the
|
||||||
|
'(NULL device *):' prefix and do not propagate a udev association.
|
||||||
|
|
||||||
|
Add a module-local static struct device * used as the firmware-class
|
||||||
|
load context, plus a small exported setter:
|
||||||
|
|
||||||
|
static struct device *bes2600_factory_dev;
|
||||||
|
void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
|
||||||
|
Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
|
||||||
|
right after bes2600_platform_data_init() so the platform layer has
|
||||||
|
already had a chance to use the same struct device for its own
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
factory_section_read_file() now passes bes2600_factory_dev (instead
|
||||||
|
of NULL) to request_firmware(). When the factory read happens before
|
||||||
|
probe (not currently the case on PineTab2) the pointer is still NULL
|
||||||
|
and request_firmware() accepts that; no regression.
|
||||||
|
|
||||||
|
No API changes to bes2600_get_factory_cali_data() callers. The
|
||||||
|
char *path parameter remains (it is the firmware-class name fed
|
||||||
|
straight to request_firmware()).
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
|
||||||
|
post-c5 factory diagnostics now carry the SDIO device identity
|
||||||
|
instead of '(NULL device *)'.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 14 +++++++++++++-
|
||||||
|
bes2600/bes2600_factory.h | 3 +++
|
||||||
|
bes2600/bes2600_sdio.c | 4 ++++
|
||||||
|
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index 8d60b7c..1cda447 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -31,6 +31,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
|
||||||
|
@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
|
||||||
|
bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ ret = request_firmware(&fw, path, bes2600_factory_dev);
|
||||||
|
if (ret) {
|
||||||
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
diff --git a/bes2600/bes2600_factory.h b/bes2600/bes2600_factory.h
|
||||||
|
index 3835b0d..7dbe9f8 100644
|
||||||
|
--- a/bes2600/bes2600_factory.h
|
||||||
|
+++ b/bes2600/bes2600_factory.h
|
||||||
|
@@ -199,6 +199,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/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..371ef4f 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
#include "bes2600.h"
|
||||||
|
#include "sbus.h"
|
||||||
|
#include "bes2600_plat.h"
|
||||||
|
+#include "bes2600_factory.h"
|
||||||
|
#include "hwio.h"
|
||||||
|
#include "bes_chardev.h"
|
||||||
|
#include "bes_log.h"
|
||||||
|
@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
+ /* wire struct device into factory.c for request_firmware() context */
|
||||||
|
+ bes2600_factory_set_dev(dev);
|
||||||
|
+
|
||||||
|
self->pdata = bes2600_get_platform_data();
|
||||||
|
self->func = func;
|
||||||
|
self->dev = &func->dev;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
From 108d3967eac4ba3a6e0f508d865a5f221b49079d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <108d3967eac4ba3a6e0f508d865a5f221b49079d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:37:45 +0200
|
||||||
|
Subject: [PATCH 4/7] bes2600: gate device LP-mode entry on successful per-VIF
|
||||||
|
firmware handshake
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() drives the transition to low-power for each
|
||||||
|
associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on
|
||||||
|
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
|
||||||
|
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Two bugs:
|
||||||
|
|
||||||
|
1. A failed wsm_set_pm() only logs an error, then still falls into
|
||||||
|
wait_for_completion_timeout() on a completion the firmware will
|
||||||
|
never post (the set-mode command never reached it). The loop
|
||||||
|
therefore always blocks the full 5 s, logs a second error, and
|
||||||
|
proceeds.
|
||||||
|
|
||||||
|
2. A genuine wait-timeout (firmware received the set-mode command but
|
||||||
|
never posted the indication) also only logs a warning. The code
|
||||||
|
then drops to bes2600_pwr_device_enter_lp_mode(), handing the
|
||||||
|
device subsystem an inconsistent view of mac-layer state.
|
||||||
|
|
||||||
|
On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
|
||||||
|
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
|
||||||
|
message flooding dmesg every 5-10 s when the interface is associated
|
||||||
|
and idle. Sending the device to LP in that state cascades into the
|
||||||
|
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
|
||||||
|
WARN splat.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Add a 'timeouts' counter; bump it on both failure paths.
|
||||||
|
- Skip the wait_for_completion entirely when wsm_set_pm() failed
|
||||||
|
(there is no completion to wait for).
|
||||||
|
- Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
|
||||||
|
handshake reached firmware-ACKed completion; otherwise return
|
||||||
|
-ETIMEDOUT and leave the device in its current power state.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
Post-patch the handshake still fails on this particular firmware
|
||||||
|
revision (separate root-cause investigation outside this patch), but
|
||||||
|
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
|
||||||
|
and destabilising the SDIO path.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 20 +++++++++++++++++---
|
||||||
|
1 file changed, 17 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index e7a1045..f62ae22 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
int i = 0;
|
||||||
|
struct bes2600_vif *priv;
|
||||||
|
int ret = 0;
|
||||||
|
+ int timeouts = 0;
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
bes_err("%s, set operation mode fail\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
- if (!status)
|
||||||
|
+ if (!status) {
|
||||||
|
bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* set device low power configuration */
|
||||||
|
- bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ /*
|
||||||
|
+ * 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
|
||||||
|
+ ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+678
@@ -0,0 +1,678 @@
|
|||||||
|
From 3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:55:18 +0200
|
||||||
|
Subject: [PATCH 5/7] bes2600: remove userspace /dev/bes2600 character device
|
||||||
|
interface
|
||||||
|
|
||||||
|
bes_chardev.c implemented a custom character device at /dev/bes2600 with
|
||||||
|
its own parser and command-dispatch table, exposing operations such as
|
||||||
|
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
|
||||||
|
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
|
||||||
|
the in-tree driver - every kernel call site consumes the internal state
|
||||||
|
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
|
||||||
|
etc) directly, not through the cdev.
|
||||||
|
|
||||||
|
The cdev interface is a standing upstream blocker for two reasons:
|
||||||
|
|
||||||
|
1. Drivers under drivers/staging/ and drivers/net/wireless/ are
|
||||||
|
expected to expose tuning via the firmware/nl80211/debugfs
|
||||||
|
infrastructure rather than a private /dev node with an ad-hoc
|
||||||
|
parser.
|
||||||
|
|
||||||
|
2. The cdev handlers keep a global bes_cdev singleton alive whose
|
||||||
|
->cdev, ->dev_id, ->class and ->device pointers exist only to be
|
||||||
|
torn down; they add no functionality that nl80211 or rfkill do
|
||||||
|
not already provide (wifi/bt on-off, module_param for fw_type).
|
||||||
|
|
||||||
|
Remove the userspace interface:
|
||||||
|
|
||||||
|
- open / read / write / release file_operations handlers and the
|
||||||
|
bes2600_chardev_fops instance
|
||||||
|
- bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
|
||||||
|
- bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
|
||||||
|
string helpers
|
||||||
|
- bes2600_load_uevent (its only caller was
|
||||||
|
bes2600_chrdev_wifi_force_close_work informing userspace of a
|
||||||
|
state it already gates via rfkill; that snprintf +
|
||||||
|
kobject_uevent_env block is gone too, the kernel-side
|
||||||
|
halt_device + switch_wifi(0) + chrdev_check_system_close
|
||||||
|
sequence remains)
|
||||||
|
- alloc_chrdev_region / cdev_init / cdev_add / class_create /
|
||||||
|
device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
|
||||||
|
unwind labels
|
||||||
|
- cdev_del / unregister_chrdev_region / device_destroy /
|
||||||
|
class_destroy in bes2600_chrdev_free
|
||||||
|
- cdev/dev_id/major/minor/class/device fields in struct bes_cdev
|
||||||
|
|
||||||
|
What remains (unchanged behaviour):
|
||||||
|
|
||||||
|
- fw_type module parameter - the primary user-facing knob for
|
||||||
|
signal/no-signal/BT mode switch
|
||||||
|
- All in-kernel bes2600_chrdev_* accessor functions called from
|
||||||
|
bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
|
||||||
|
wifi_testmode_cmd.c (13 call sites)
|
||||||
|
- bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
|
||||||
|
for the remaining bes_cdev state (waitqueues, workqueues, flags)
|
||||||
|
- DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
|
||||||
|
- wifi_force_close worker, system-close logic, bus-probe state
|
||||||
|
machine
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
|
||||||
|
no kernel messages related to the cdev absence. Users that previously
|
||||||
|
wrote to /dev/bes2600 should switch to the fw_type module parameter
|
||||||
|
or (future patch c4) nl80211 testmode commands.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
|
||||||
|
and friends so the global bes2600_cdev singleton can be dropped
|
||||||
|
and the accessors scale to multi-device scenarios.
|
||||||
|
- c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
|
||||||
|
commands to the firmware's patch_wifi_testMode entry.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_chardev.c | 519 ------------------------------------------
|
||||||
|
1 file changed, 519 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index 9038e48..e2e4f1b 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -43,12 +43,6 @@ enum bus_probe_state {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bes_cdev {
|
||||||
|
- struct cdev cdev;
|
||||||
|
- dev_t dev_id;
|
||||||
|
- int major;
|
||||||
|
- int minor;
|
||||||
|
- struct class *class;
|
||||||
|
- struct device *device;
|
||||||
|
atomic_t num_proc;
|
||||||
|
wait_queue_head_t open_wq;
|
||||||
|
spinlock_t status_lock;
|
||||||
|
@@ -249,351 +243,18 @@ int bes2600_switch_bt(bool on)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_get_cmd_and_ifname(const char *str, char **result)
|
||||||
|
-{
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ifname_len = 0;
|
||||||
|
- char *sp = NULL;
|
||||||
|
- char *tmp_ptr = NULL;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
-
|
||||||
|
- /* check if input arguments is valid */
|
||||||
|
- if (!str || strncmp(str, "ifname:", 7) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- sp = strchr(str, ' ');
|
||||||
|
- if (strncmp(sp + 1, "cmd:", 4) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- /* extract interface name */
|
||||||
|
- ifname_len = sp - str - 7;
|
||||||
|
- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- return -2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, str+7, ifname_len);
|
||||||
|
- tmp_ptr[ifname_len] = '\0';
|
||||||
|
- result[0] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- /* get command length */
|
||||||
|
- cmd_ptr = strstr(str, "cmd:");
|
||||||
|
- cmd_ptr += 4;
|
||||||
|
- sp = strchr(cmd_ptr, ' ');
|
||||||
|
- if (!sp) { /* the command don't have any parameter */
|
||||||
|
- cmd_len = strlen(cmd_ptr);
|
||||||
|
- if (cmd_ptr[cmd_len - 1] == '\n')
|
||||||
|
- --cmd_len;
|
||||||
|
- } else { /* the command have one or more parameter */
|
||||||
|
- cmd_len = sp - cmd_ptr;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* copy command to out buffer */
|
||||||
|
- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- kfree(result[0]);
|
||||||
|
- result[0] = NULL;
|
||||||
|
- return -3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, cmd_ptr, cmd_len);
|
||||||
|
- tmp_ptr[cmd_len] = '\0';
|
||||||
|
- result[1] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void bes2600_recyle_cmd_and_ifname_mem(char **info)
|
||||||
|
-{
|
||||||
|
- if (info[0]) {
|
||||||
|
- kfree(info[0]);
|
||||||
|
- info[0] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (info[1]) {
|
||||||
|
- kfree(info[1]);
|
||||||
|
- info[1] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_default_handler(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
-
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
|
||||||
|
- } else {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, str);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_wifi_bt_on_off(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- int ret = 0;
|
||||||
|
- enum wait_state wait_state;
|
||||||
|
- enum bus_probe_state probe_state;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- probe_state = bes2600_cdev.bus_probe;
|
||||||
|
- wait_state = bes2600_cdev.wait_state;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* only work for wifi signal mode */
|
||||||
|
- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* wait bus probe operation end */
|
||||||
|
- if (probe_state == BES2600_BUS_PROBE_START) {
|
||||||
|
- bes_devel("wait bus probe operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
|
||||||
|
- HZ);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* must wait previous operation end in critical section */
|
||||||
|
- if (wait_state != BES2600_BOOT_WAIT_NONE) {
|
||||||
|
- bes_devel("wait previous operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
|
||||||
|
- HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* if dpd calibration is doing, modify wifi and bt state directly */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = true;
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = false;
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = true;
|
||||||
|
- bes2600_cdev.bton_pending = true;
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = false;
|
||||||
|
- bes2600_cdev.bton_pending = false;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* process wifi/bt on/off operation */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(1);
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(0);
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(1);
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!ret && bes2600_chrdev_check_system_close())
|
||||||
|
- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
- bes2600_cdev.sbus_priv);
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return ret ;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-static int bes2600_op_change_fw_type(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- int temp = 0;
|
||||||
|
- long status = 0;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
- char fw_type[5] = {0};
|
||||||
|
- bool sys_closed = bes2600_chrdev_check_system_close();
|
||||||
|
-
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
-
|
||||||
|
- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- return -EPERM;
|
||||||
|
-
|
||||||
|
- /* check if user input is valid */
|
||||||
|
- cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
|
||||||
|
- if (strlen(str) < 16 || !cmd_ptr) {
|
||||||
|
- bes_err("the format of \"%s\" is error\n", str);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* convert fw_type from string to int */
|
||||||
|
- strncpy(fw_type, cmd_ptr + 14, 4);
|
||||||
|
- fw_type[0] = '+';
|
||||||
|
- ret = kstrtoint(fw_type, 10, &temp);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- bes_err("%s parse error\n", __func__);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* no need to realod firmware if new fw_type is equal to the old */
|
||||||
|
- if (temp == bes2600_cdev.fw_type ) {
|
||||||
|
- bes_devel("fw type is equal\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* close wifi net device */
|
||||||
|
- if (bes2600_cdev.sbus_priv
|
||||||
|
- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
||||||
|
- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* update firmware type */
|
||||||
|
- bes2600_cdev.fw_type = temp;
|
||||||
|
- bes2600_chrdev_update_signal_mode();
|
||||||
|
-
|
||||||
|
- if (!sys_closed) {
|
||||||
|
- /* close device to call disconnect function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
|
||||||
|
- else if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_chrdev_start_bus_probe();
|
||||||
|
-
|
||||||
|
- /* wait disconnect event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.dpd_calied
|
||||||
|
- && bes2600_chrdev_check_system_close()) {
|
||||||
|
- bes_devel("no need to reload firmware\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("reload firmware...\n");
|
||||||
|
- /* power on device to call probe function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
||||||
|
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_wakeup(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 wakeup bt.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_sleep(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
|
||||||
|
- bes_devel("bes2600 allow bt sleep.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static int bes2600_op_set_wakeup_read_flag(const char *str)
|
||||||
|
-{
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
||||||
|
-int bes2600_load_uevent(char *env[])
|
||||||
|
-{
|
||||||
|
- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
-}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-static struct bes2600_op_map bes2600_op_map_tab[] ={
|
||||||
|
- /*op op_len handler */
|
||||||
|
- {"P2P_SET_NOA", 11, bes2600_op_default_handler},
|
||||||
|
- {"P2P_SET_PS", 10, bes2600_op_default_handler},
|
||||||
|
- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
|
||||||
|
- {"LINKSPEED", 9, bes2600_op_default_handler},
|
||||||
|
- {"RSSI", 4, bes2600_op_default_handler},
|
||||||
|
- {"GETBAND", 7, bes2600_op_default_handler},
|
||||||
|
- {"WLS_BATCHING", 12, bes2600_op_default_handler},
|
||||||
|
- {"MACADDR", 7, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-START", 14, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-STOP", 13, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-ADD", 12, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXMODE", 10, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
|
||||||
|
- {"SETSUSPENDMODE", 14, bes2600_op_default_handler},
|
||||||
|
- {"COUNTRY", 7, bes2600_op_default_handler},
|
||||||
|
- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_ON", 5, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
|
||||||
|
- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
|
||||||
|
- {"BT_SLEEP", 8, bes2600_op_bt_sleep},
|
||||||
|
- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
|
||||||
|
-};
|
||||||
|
|
||||||
|
static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
{
|
||||||
|
@@ -603,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
&& (bes2600_cdev.wifi_opened == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
-{
|
||||||
|
- if (atomic_read(&bes2600_cdev.num_proc) > 0) {
|
||||||
|
- wait_event_timeout(bes2600_cdev.open_wq,
|
||||||
|
- (atomic_read(&bes2600_cdev.num_proc) == 0),
|
||||||
|
- MAX_SCHEDULE_TIMEOUT);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- bes_devel("bes2600 char device is opened\n");
|
||||||
|
- atomic_inc(&bes2600_cdev.num_proc);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
|
||||||
|
- size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- char buf[64] = {0};
|
||||||
|
- unsigned int len;
|
||||||
|
- long status = 0;
|
||||||
|
|
||||||
|
- switch (bes2600_cdev.read_flag) {
|
||||||
|
- case BES_CDEV_READ_WAKEUP_STATE:
|
||||||
|
- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
|
||||||
|
- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
|
||||||
|
- bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
|
||||||
|
- bes2600_cdev.dpd_calied,
|
||||||
|
- bes2600_cdev.wifi_opened,
|
||||||
|
- bes2600_cdev.bt_opened,
|
||||||
|
- bes2600_cdev.fw_type);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- len = sizeof(buf);
|
||||||
|
- /* reset read flag */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static ssize_t bes2600_chrdev_write(struct file *file,
|
||||||
|
- const char __user *user_buf, size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- int i = 0;
|
||||||
|
- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ret = 0;
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- char *buf = NULL;
|
||||||
|
-
|
||||||
|
- /* copy content from user space to kernel */
|
||||||
|
- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
|
||||||
|
- buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
- if (copy_from_user(buf, user_buf, count))
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* add terminal character */
|
||||||
|
- buf[count] = '\0';
|
||||||
|
-
|
||||||
|
- /* extract comand and interface */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
|
||||||
|
- kfree(buf);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* match operation item and execure its handler */
|
||||||
|
- cmd_len = strlen(info[1]);
|
||||||
|
- for (i = 0; i < cmd_num; i++) {
|
||||||
|
- if (cmd_len < bes2600_op_map_tab[i].op_len)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
|
||||||
|
- ret = bes2600_op_map_tab[i].handler(buf);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* operation item mismatch */
|
||||||
|
- if (i == cmd_num) {
|
||||||
|
- bes_err("cmd(%s) mismatch\n", info[1]);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- kfree(buf);
|
||||||
|
-
|
||||||
|
- return (ret == 0) ? count : ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_release (struct inode *inode, struct file *file)
|
||||||
|
-{
|
||||||
|
- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
|
||||||
|
- wake_up(&bes2600_cdev.open_wq);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 char device is closed\n");
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static struct file_operations bes2600_chardev_fops =
|
||||||
|
-{
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
- .open = bes2600_chrdev_open,
|
||||||
|
- .read = bes2600_chrdev_read,
|
||||||
|
- .write = bes2600_chrdev_write,
|
||||||
|
- .release = bes2600_chrdev_release,
|
||||||
|
-};
|
||||||
|
|
||||||
|
#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
@@ -1124,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void)
|
||||||
|
|
||||||
|
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
- char wifi_state[15];
|
||||||
|
- char bt_state[15];
|
||||||
|
- char fw_type[15];
|
||||||
|
- char *env[] = { wifi_state, bt_state, fw_type, NULL };
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
if (bes2600_chrdev_is_wifi_opened()) {
|
||||||
|
bes_devel("system exeception, force wifi down\n");
|
||||||
|
|
||||||
|
@@ -1146,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* notify userspace */
|
||||||
|
- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
|
||||||
|
- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
|
||||||
|
- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
|
||||||
|
- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
- if (!ret)
|
||||||
|
- bes_err("bes2600 notify userspace failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1247,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
|
||||||
|
|
||||||
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
-
|
||||||
|
- /* allocate devide id */
|
||||||
|
- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 alloc device id fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* extract major and minor device id */
|
||||||
|
- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
|
||||||
|
- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
|
||||||
|
-
|
||||||
|
- /* add char device and bind operation function */
|
||||||
|
- bes2600_cdev.cdev.owner = THIS_MODULE;
|
||||||
|
- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
|
||||||
|
- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* create class for creating device node */
|
||||||
|
- bes2600_cdev.class = class_create("bes2600_chrdev");
|
||||||
|
- if (IS_ERR(bes2600_cdev.class)){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* get char device pointer */
|
||||||
|
- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
|
||||||
|
- if (IS_ERR(bes2600_cdev.device)){
|
||||||
|
- bes_err("bes2600 char device create fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* initialise global variable */
|
||||||
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
||||||
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
||||||
|
@@ -1318,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
-
|
||||||
|
-fail3:
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
-fail2:
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
-fail1:
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
-fail:
|
||||||
|
- return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bes2600_chrdev_free(void)
|
||||||
|
@@ -1336,9 +821,5 @@ void bes2600_chrdev_free(void)
|
||||||
|
bes2600_free_dpd_log_buffer();
|
||||||
|
#endif
|
||||||
|
bes2600_chrdev_free_dpd_data();
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
From 6f13e008d21d453db486f707f47340a0a17e650b Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <6f13e008d21d453db486f707f47340a0a17e650b.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:04:27 +0200
|
||||||
|
Subject: [PATCH 6/7] bes2600: enable CONFIG_BES2600_TESTMODE by default + fix
|
||||||
|
bit-rotted testmode plumbing
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The driver implements a mac80211 testmode_cmd operation that dispatches
|
||||||
|
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
|
||||||
|
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
|
||||||
|
BES2600 RF-test path (bes2600_vendor_rf_cmd -> firmware
|
||||||
|
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
|
||||||
|
binding in struct ieee80211_ops are conditionally compiled under
|
||||||
|
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
|
||||||
|
|
||||||
|
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
|
||||||
|
included in the build and the .testmode_cmd op is populated. On the
|
||||||
|
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
|
||||||
|
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
|
||||||
|
through the standard nl80211 testmode interface ('iw phy0 ...').
|
||||||
|
|
||||||
|
This also makes visible two classes of bit-rot that had accumulated
|
||||||
|
while nobody was building with CONFIG_BES2600_TESTMODE=y:
|
||||||
|
|
||||||
|
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
|
||||||
|
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
|
||||||
|
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
|
||||||
|
first argument. Neither the macros nor any of the BES2600_DBG_*
|
||||||
|
constants are defined anywhere in the tree. The same call pattern
|
||||||
|
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
|
||||||
|
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
|
||||||
|
|
||||||
|
Add minimal shim macros to bes_log.h that rewire the calls onto
|
||||||
|
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
|
||||||
|
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
|
||||||
|
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
|
||||||
|
as 0 constants for documentation / grep.
|
||||||
|
|
||||||
|
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
|
||||||
|
bes2600_get_roam_delay() are declared in sta.c with external
|
||||||
|
linkage but have no prototype in any header. All callers live in
|
||||||
|
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
|
||||||
|
off the compiler never sees them; with it on gcc
|
||||||
|
-Werror=missing-prototypes breaks the build.
|
||||||
|
|
||||||
|
Mark the three functions static. (Keeping them file-local also
|
||||||
|
matches their actual usage.)
|
||||||
|
|
||||||
|
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
|
||||||
|
buildable; no behavioural change when the flag is off.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
|
||||||
|
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes_log.h | 23 +++++++++++++++++++++++
|
||||||
|
bes2600/sta.c | 6 +++---
|
||||||
|
3 files changed, 27 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 2dcba09..2c1a850 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||||
|
# feature option
|
||||||
|
BES2600 ?= m
|
||||||
|
|
||||||
|
-CONFIG_BES2600_TESTMODE ?= n
|
||||||
|
+CONFIG_BES2600_TESTMODE ?= y
|
||||||
|
|
||||||
|
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_log.h b/bes2600/bes_log.h
|
||||||
|
index 605cea8..65cf703 100644
|
||||||
|
--- a/bes2600/bes_log.h
|
||||||
|
+++ b/bes2600/bes_log.h
|
||||||
|
@@ -8,3 +8,26 @@ extern struct device *global_dev;
|
||||||
|
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
|
||||||
|
+ * was never implemented in-tree; these shims let code paths gated by
|
||||||
|
+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
|
||||||
|
+ * build when their conditions are enabled. The first argument is
|
||||||
|
+ * currently unused; pick one of the BES2600_DBG_* constants below for
|
||||||
|
+ * documentation.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_DBG_SBUS 0
|
||||||
|
+#define BES2600_DBG_DOWNLOAD 0
|
||||||
|
+#define BES2600_DBG_ITP 0
|
||||||
|
+#define BES2600_DBG_TEST_MODE 0
|
||||||
|
+
|
||||||
|
+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_cond) \
|
||||||
|
+ bes_err(fmt, ##__VA_ARGS__); \
|
||||||
|
+ } while (0)
|
||||||
|
diff --git a/bes2600/sta.c b/bes2600/sta.c
|
||||||
|
index aa69eb8..5f1a456 100644
|
||||||
|
--- a/bes2600/sta.c
|
||||||
|
+++ b/bes2600/sta.c
|
||||||
|
@@ -3633,7 +3633,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;
|
||||||
|
@@ -3663,7 +3663,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;
|
||||||
|
@@ -3703,7 +3703,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
*
|
||||||
|
* Returns: Returns the last measured roam delay
|
||||||
|
*/
|
||||||
|
-int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <10a05d21bfe4563f963e16d65228fd7a713c143d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH 7/7] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index 371ef4f..3e04e8c 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -95,6 +95,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;
|
||||||
|
@@ -1136,7 +1137,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:*/
|
||||||
|
@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (!self->single_gathered_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ if (!self->tx_bounce) {
|
||||||
|
+#ifndef SDIO_HOST_ADMA_SUPPORT
|
||||||
|
+ free_pages((unsigned long)self->single_gathered_buffer,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+#endif
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
#ifdef BES_SDIO_RXTX_TOGGLE
|
||||||
|
self->fw_started = false;
|
||||||
|
#endif
|
||||||
|
@@ -1985,6 +2016,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
From 4ec7d25817af09654fb9439e472890f69281840c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..7bc922c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -94,6 +94,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;
|
||||||
|
@@ -1135,7 +1136,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:*/
|
||||||
|
@@ -1853,6 +1873,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
|
||||||
|
@@ -1981,6 +2012,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
From 4ec7d25817af09654fb9439e472890f69281840c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..7bc922c 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -94,6 +94,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;
|
||||||
|
@@ -1135,7 +1136,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:*/
|
||||||
|
@@ -1853,6 +1873,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
|
||||||
|
@@ -1981,6 +2012,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
Reference in New Issue
Block a user