# BES2600 driver -- upstream submission prep ## Status Seven patches, all checkpatch `--strict` clean, all deployed and verified on a PineTab2 (BES2600WM + Rockchip RK3566) running linux-pinetab2 6.19.10-danctnix1. Ready for Mobian DKMS merge requests and as the basis for a future `drivers/staging/bes2600/` submission to linux-wireless. The 7th patch (`tx-sdio-dma-oob`) fixes a KFENCE-detected SDIO DMA out-of-bounds read on every TX burst -- a real memory-safety bug that also leaks adjacent kernel memory onto the air. This upgrades the series from "cleanup" to "cleanup + live bug fix" and should precede any linux-wireless RFC. ## The series Recommended submission order (dependency-clean, bisectable): | # | Branch | Commit | Intent | |---|--------|--------|--------| | 01 | `bes2600/factory-request-firmware` | `1a5d54a` | c1 -- Replace `filp_open` + `kernel_read` in the factory-read path with `request_firmware()`. Repoint `FACTORY_PATH` macro to the firmware-class name. Kills a kernel-mainline anti-pattern and the `(NULL device *)` `read and check /lib/firmware/bes2600_factory.txt error` boot spam on PineTab2. | | 02 | `bes2600/factory-no-efuse-flag` | `82ba594` | c5 -- Default `STANDARD_FACTORY_EFUSE_FLAG` from y to n. The shipped `bes2600_factory.txt` has 30 calibration fields; the driver was expecting 31 (incl. a `##select_efuse_flag` section this firmware doesn't ship). Also drops an inconsistent `#if defined(STANDARD_FACTORY_EFUSE_FLAG)` wrapper around the `wsm_save_factory_txt_to_mcu()` prototype -- the definition and call site are always ungated, so the gate broke the build once EFUSE_FLAG=n. | | 03 | `bes2600/factory-thread-dev` | `8732881` | c1.1 -- Thread `struct device *` through `factory_section_read_file()` via a module-local setter invoked at SDIO probe, so `request_firmware()` gets proper per-device context instead of NULL. Removes the `(NULL device *)` log prefix from factory-related diagnostics. | | 04 | `bes2600/pm-gate-on-handshake` | `80178ec` | c2 -- Gate the device-end of the LP transition on successful per-VIF firmware handshake. Before: `bes2600_pwr_device_enter_lp_mode()` was called unconditionally even when `wait_for_completion_timeout()` returned 0 (firmware never ack'd). On PineTab2 this produced `bes2600_pwr_enter_lp_mode, wait pm ind timeout` every 5-10s and cascaded into `sdio_tx_work` WARN splats. After: `-ETIMEDOUT` returned cleanly, dmesg silent, SDIO stable. | | 05 | `bes2600/remove-chardev-user-interface` | `f43bcc5` | c3 -- Remove the custom `/dev/bes2600` character-device interface (fops, command dispatcher, `bes2600_op_*` handlers, `bes2600_load_uevent`, `alloc_chrdev_region`/`class_create`/`device_create` registration, cdev fields in struct bes_cdev). 519-line deletion. Keeps all in-kernel accessor functions (13 callers) and the `fw_type` module parameter as the user-facing replacement. | | 06 | `bes2600/enable-testmode` | `9398d30` | c4 -- Flip `CONFIG_BES2600_TESTMODE` default from n to y, exposing the driver's mac80211 testmode_cmd surface (dispatches to the firmware's `patch_wifi_testMode` path) through the standard nl80211 testmode interface. Also fixes accumulated bit-rot revealed by enabling the flag: adds compat shim macros in `bes_log.h` for the legacy `bes2600_info()` / `bes2600_err()` / `bes2600_warn()` / `bes2600_dbg()` / `bes2600_err_with_cond()` family (which had no definition anywhere, despite ~41 call sites in testmode code), defines `BES2600_DBG_*` subsystem ids as 0 constants, and marks 3 TSM/roam-delay helpers in sta.c as `static` for `-Werror=missing-prototypes`. | | 07 | `bes2600/tx-sdio-dma-oob` | `10a05d2` | tx-bounce -- Fix a KFENCE OOB read in the SDIO TX path. `sdio_tx_work()` handed dma_map_sg a block-aligned length (`blks * cur_blksize`, or 1632) but `tx_buffer->buf` is only `tx_buffer->len` bytes long (aliases into an skb linear head sized by `pskb_expand_head` / TCP). DMA read up to one block past the skb -- KFENCE fires `out-of-bounds read in __pi_memcpy_generic` / `sdio_tx_work+0x2b4` / `bes_sdio_memcpy_to_io_helper+0x18c`, and the padding bytes are transmitted to the peer (kernel-memory leak to air). Allocate a driver-owned DMA-pages bounce buffer of `MAX_SDIO_TRANSFER_LEN`, memcpy each TX buffer into its slot, zero-pad, and point SG entries at the bounce. Separate from `single_gathered_buffer` (that one is bus-serialised via `sdio_claim_host` but `sdio_tx_work` accumulates SG entries before claiming). | Total: +117 / -550 lines excluding commit messages. ## Driver lineage (for the cover letter) The BES2600 Linux driver is a descendant of the ST-Ericsson CW1200 driver already in mainline at `drivers/net/wireless/st/cw1200/`. Evidence: - Same author on both: `Dmitry Tarnyagin ` (visible in the running `modinfo` on ohm and in the mainline cw1200 commit history). - Kconfig option names carry the ancestry: `CONFIG_BES2600_USE_STE_EXTENSIONS` (STE = ST-Ericsson), `CONFIG_BES2600_WSM_DEBUG` (WSM = CW1200's host <-> firmware Wireless Sensor Module protocol). - File-layout parallel: `bh.c`, `hwio.c`, `fwio.c`, `queue.c`, `scan.c`, `sta.c`, `txrx.c`, `wsm.c`, `pm.c`, `main.c` -- same spine as CW1200, plus BES-specific additions (ePTA coexistence, `bes2600_btuart`, 5 GHz support, `bes_pwr`, `bes2600_factory`, `itp`, `bes_chardev`). ST-Ericsson wound down in 2013; Bestechnic (founded 2015) appears to have licensed or acquired the CW1200 IP. No public LKML/linux-wireless RFC linking the two has been filed. Bestechnic has never contributed the BES2600 driver upstream. ## Background on which fork this series lands Upstream today for BES2600 is fragmented: - **`salsa.debian.org/Mobian-team/devices/bes2600-dkms`** -- Mobian DKMS package, actively maintained (last commit 2025-12). Primary PR target. - **`codeberg.org/DanctNIX/linux-pinetab2`** -- danctnix kernel fork; ships the driver in-tree at `drivers/staging/bes2600/` at tag `v6.19.10-danctnix1`. - **`gitlab.com/arjanvlek/bes2600`** -- original community fork; master was sanitized in 2024-10 (`e5055a3 "Removed sources from main branch"`). `gitlab.com/TuxThePenguin0/bes2600` preserves the pre-sanitization snapshot. The Mobian DKMS tree and the danctnix in-kernel tree have converged -- 0- hunk diff on `bes_pwr.c`, `bes2600_factory.c`, `wsm.c` between them. This series lands on Mobian first (branches already pushed to `git.reauktion.de/marfrit/bes2600-dkms`). ## Testing Reference hardware: Pine64 PineTab2 (BES2600WM + Rockchip RK3566, `8a:2e:77:1f:ec:05`, AP `newton` @ 2.4 GHz ch11 / `TelekomHotspot@ERGO` @ 5 GHz ch36). Host kernel: `linux-pinetab2 6.19.10-danctnix1-1-pinetab2` with `CONFIG_NL80211_TESTMODE=y`. Per-patch: - **c1**: post-reboot dmesg no longer shows `read and check /lib/firmware/ bes2600_factory.txt error`; `request_firmware()` reads the file cleanly. - **c5**: `factory_parse()` now succeeds on the shipped 30-field `bes2600_factory.txt`; no `parse fail` / `factory cali data get failed.` - **c1.1**: factory-related log lines no longer carry the `(NULL device *):` prefix. - **c2**: pre-patch dmesg carried ~20-30 `wait pm ind timeout` messages per 5 min and 2+ `sdio_tx_work` WARN splats; post-patch `wait pm ind timeout` is 0 in the same window and `[RX] Receive failure: 4.` disappears. A residual `sdio_tx_work+0x2b4` splat (~1 per reboot) still recurred with c1..c6 alone; see c7 below for its root cause and fix. - **c3**: `/dev/bes2600` character device node is absent post-reboot; `lsmod | grep bes2600` still shows both `bes2600` and `bes2600_btuart` loaded and associated; WiFi continues to function. - **c4**: `iw phy0` now lists `testmode` under *Supported commands*; nl80211 testmode interface reachable via `iw phy0 testmode cmd ` (not exercised end-to-end yet -- depends on userspace tooling). Full stack c1+c5+c1.1+c2+c3+c4: WiFi associates and passes traffic across 3+ reboots. ## Known limitations / not-in-this-series - `factory_section_write_file()` in `bes2600_factory.c` still uses `kernel_write()` + `filp_open(O_CREAT | O_TRUNC | O_RDWR)` to persist per-channel calibration updates back to `/lib/firmware/bes2600_factory.txt`. Converting the write-back path to a debugfs or nl80211 testmode interface is out of scope here; upstream submission for `drivers/staging/` will need to take that on. - `bes_chardev.c` still carries `bes2600_chrdev_write_dpd_data_to_file()` (gated `BES2600_WRITE_DPD_TO_FILE`, off by default so dead code in the default build) and `bes2600_chrdev_read_and_check_dpd_data()`. Both use `filp_open`/`kernel_read`/`kernel_write` and need the same treatment. - `bes_fw.c:587` unconditionally creates `/lib/firmware/bes2002_fw_write.bin` via `filp_open(O_CREAT | O_RDWR)` to capture firmware bytes as they are sent to the chip. Pure debug helper; should be deleted or gated behind a debugfs trigger. - After c3, the `bes2600_chrdev_is_signal_mode()` / `_update_signal_mode()` family still reads/writes a `bes_cdev` singleton. Threading `struct bes2600_common *` through those call sites (13 files) requires first replacing the `fw_type` module_param with a per-phy debugfs knob or nl80211 testmode command -- a migration that overlaps with c4's testmode plumbing. Tracked as c3.1 (deferred). - One residual dmesg notice after the full stack: `bes2600_wlan mmc2:0001:1: PS Mode Error, Reason:1` at T+40s on fresh boot. Distinct from the pre-c2 `wait pm ind timeout` cascade -- benign, driver recovers, WiFi stays up. Root cause not investigated; candidate for a future c2.1 or firmware-RE follow-up. - Firmware RE (task d) was pursued briefly. The shipped firmware keeps many format-string literals (e.g. `patch_HI_Set_Coex_Params, hw_epta_ enable:0x%x, new_run_flag:0x%x, gEptaBypass: %d`) as dead data -- the string is in the blob but no code references it in any encoding. The compiler kept the literal while dead-stripping the `TRACE()` / `patch_*()` callers that would have used it. Takeaway: strings-mining overstates the reachable feature surface. A proper firmware RE would need function-graph analysis from the reset vector; out of scope here. ## Submission routes ### Near-term: Mobian DKMS merge request All 7 branches are pushed to `git.reauktion.de/marfrit/bes2600-dkms` with upstream set to `salsa.debian.org/Mobian-team/devices/bes2600-dkms`. To open an MR against Mobian: 1. Create a salsa.debian.org account and add the Gitea-side SSH key. 2. Push the branches to a personal fork on salsa: ``` git remote add salsa git@salsa.debian.org:/bes2600-dkms.git for br in \ bes2600/factory-request-firmware \ bes2600/factory-no-efuse-flag \ bes2600/factory-thread-dev \ bes2600/pm-gate-on-handshake \ bes2600/remove-chardev-user-interface \ bes2600/enable-testmode \ bes2600/tx-sdio-dma-oob; do git push salsa $br done ``` 3. For each branch open an MR against `Mobian-team/devices/bes2600-dkms:mobian`. Use the commit message as the MR description (it already reads as a standalone rationale). 4. Consider bundling into a single MR with all 7 commits if the Mobian reviewer prefers series review. ### Near-term alt: danctnix linux-pinetab2 (codeberg) Danctnix is the de-facto upstream for PineTab2 Arch-ARM images (maintainer Danct12). The driver ships in-tree at `drivers/staging/bes2600/` in `codeberg.org/DanctNIX/linux-pinetab2`. A danctnix-layout variant of the series (same content, paths rooted at `drivers/staging/bes2600/` instead of Mobian's `bes2600/`) is at `marfrit/besser/patches/staging-prep-series-danctnix/`. Verified to apply cleanly against tag `v6.19.10-danctnix1` and checkpatch-clean. Submission route: 1. Create a codeberg.org account + add your SSH key. 2. Fork `DanctNIX/linux-pinetab2` on codeberg; push the 7 commits as a branch `bes2600/staging-prep-series`. 3. Open a codeberg PR against `DanctNIX/linux-pinetab2:master` with the cover-letter content as PR description. 4. Alt: ping Danct12 directly (Matrix / IRC `#pine64` / mastodon `@danctnix@social.treehouse.systems`). Their merge workflow may be more casual than formal PR review. Mobian and danctnix share the bes2600 source -- `bes_pwr.c`, `bes2600_factory.c`, `wsm.c` all 0-hunk diff at the patched files. Landing in either benefits users of both. ### Longer-term: linux-wireless RFC for drivers/staging/bes2600/ This series cleans up the Mobian DKMS tree. A proper mainline staging submission is bigger work -- new Kconfig + Makefile integration under `drivers/staging/`, a MAINTAINERS entry for Markus Fritsche + CC Dmitry Tarnyagin for CW1200 lineage ack, cover-letter framing the driver as a new staging submission with the CW1200 ancestry cited so reviewers can contextualise the structural parallels. Gate: the `kernel_write()` / `filp_open()` leftovers (factory write-back, DPD-to-file, observe_file debug path) must all go before staging will accept it -- drivers/staging still has a nominal filter against direct filesystem manipulation. Plan those as follow-up patches (c1.2, c1.3, c1.4) and include them in the staging series. Recommended linux-wireless CC list for a future RFC: - Johannes Berg `` (wireless maintainer) - Kalle Valo `` (wireless drivers maintainer) - Dmitry Tarnyagin (CW1200 author; email may be stale post-ST-Ericsson) - Dang Huynh `` (bes2600_btuart.c author) - Julian `` (Mobian DKMS maintainer) - Danct12 (dreemurrs-embedded, PineTab2 kernel maintainer) - `linux-wireless@vger.kernel.org` - `linux-arm-kernel@lists.infradead.org` (PineTab2 RK3566 context) - `linux-staging@lists.linux.dev` (if targeting drivers/staging/) Signed-off-by chain: every patch has one. Tested-on: PineTab2 (BES2600WM + RK3566) line on each patch.