23 Commits

Author SHA1 Message Date
claude-noether b04c8cd501 patches/driver/bes2600/*-danctnix + arch/arm64/scs-...: rebased on danctnix baseline (#29 redo)
PR #33's per-series mirrors were generated against the bes2600-dkms
cleanups branch (rooted at fe73571) without rebasing onto the
v7.0-danctnix1 kernel baseline. Result: per-commit diffs carried
stale baseline context (e.g. from_timer rather than the new
timer_container_of API), so the cumulative no longer applied cleanly
to ohm's actual base. pkgrel=6 build #1 failed with 'Hunk #3 FAILED'
in Patch D's sta.c.

Fix: in marfrit/bes2600-dkms, create danctnix-sync branch
(fe73571 + drop-in replace bes2600/ with v7.0-danctnix1's
drivers/staging/bes2600/), rebase cleanups onto it as
cleanups-rebased-on-danctnix, manually resolve the resulting conflicts
keeping each commit's intent + the new baseline context, rebase
Patch H accordingly. Format-patch and re-route to the same series-dir
names as PR #33.

Conflict resolution notes:
- 'remove userspace /dev/bes2600 character device interface' commit:
  the chardev wrapper was removed but two utility funcs that danctnix's
  bes2600_btuart.c depends on (bes2600_chrdev_is_bus_error,
  bes2600_chrdev_switch_subsys_glb) were re-added with EXPORT_SYMBOL_GPL.
  bes2600_switch_bt re-added as static (file-local, called only from
  bes2600_chrdev_switch_subsys_glb).
- Patch D (atomicize ba_lock): re-resolved bes2600_ba_timer's
  timer_container_of() vs from_timer() to keep the new API.
- SCS Makefile @@ hunk counts corrected from -9,6 +9,10 to -9,6 +9,11
  (the original was actually wrong; build-via-fuzz was masking it).

Cumulative b2sum: ka-promote ohm now emits
  eb179c03f35a4dbaec2e40036f0033ef04985bb6b14ab22419d68e5caaa5874f...
  (279 554 bytes, 32 patches resolved).

pkgrel=6 built from this manifest + installed on ohm 2026-05-19 ~23:39.
Functional verification: bes2600 + bes2600_btuart both load, Pattern A
0 over fresh boot, wlan0 associates to newton. srcversion
1A919EED0E6DC2478559B17 differs from pkgrel=5's BEB625FA... — the
reconstruction is functionally equivalent (5 GHz working, no
firmware/driver race conditions) but NOT byte-equivalent (the chardev
utility re-add chose different formatting than the original danctnix
code). Byte-equivalence is not a goal; per-series traceability and
working hardware are.

Closes (proper this time): #29.
Refs: #28, #30, #33 (the half-working attempt), #31, #32.
2026-05-19 23:44:29 +02:00
marfrit 38fd672940 Merge pull request 'patches/driver/bes2600/*-danctnix: reconstruct from cleanups (closes #29)' (#33) from claude-noether/kernel-agent:noether/kernel-agent-29-per-series-reconstruct into main
Reviewed-on: marfrit/kernel-agent#33
2026-05-19 04:58:54 +00:00
claude-noether 8b356aa11f patches/driver/bes2600/*-danctnix: reconstruct from cleanups (closes #29)
Replaces the 13 broken DKMS-path -danctnix mirrors from PR #17 + adds
9 new series-dirs for the c-stack patches that were never split
(Patches A/B/C-v3/F/D/E/C2/G/H) + retires the cumulative-c5x-danctnix
single-file interim from fleet/ohm.yaml.

Mechanism:
  cd marfrit/bes2600-dkms-mobian
  git format-patch fe73571..cleanups --no-merges -o /tmp/cleanups/
  git format-patch cleanups..bes2600/bh-c-fossil-cleanup --no-merges -o /tmp/h/
  for each commit: route to series-dir, sed-rewrite
                   a/bes2600/foo.c -> a/drivers/staging/bes2600/foo.c

The 29 cleanups commits + 1 Patch H commit map to 25 series-dirs (a
few series-dirs get multiple commits: lmac-recover gets c5.2 + c5.2.1
as 0001+0002; cw1200-fix-backports gets F3+F2+F1 as 0001-0003;
factory-series gets request_firmware + STANDARD_FACTORY_EFUSE_FLAG
as 0001+0002).

fleet/ohm.yaml apply order matches cleanups commit chronology, which
is what produced the working c5x interim. cumulative.patch from
ka-promote ohm now has 32 resolved patches (29 cleanups + 1 Patch H
+ scan-filter-5ghz + xor-neon SCS + besser#18-fix), 276 079 bytes,
b2sum 7418db5ddf8fe938b130bc9d0e9f7dc9060f3a13703cd50757835ac43140a13...

Apply order in cleanups + bh-c-fossil-cleanup:
  1   factory-series                       (c1 + factory-no-efuse-flag)
  3   factory-thread-dev
  4   pm-gate-on-handshake
  5   remove-chardev-user-interface
  6   enable-testmode
  7   tx-sdio-dma-oob-danctnix             (was 'staging-prep-series')
  8   factory-drop-kernel-write-danctnix
  9   drop-dpd-file-paths-danctnix
  10  drop-orphan-file-io-danctnix
  11  pm-timeout-silence-danctnix
  12  scan-defer-on-reject-danctnix        (c5.1)
  13  scan-defer-backoff-tune-danctnix     (c5.1.1)
  14  lmac-recover-via-mmc-hw-reset-danctnix  (c5.2 + c5.2.1)
  16  pm-state-resync-danctnix             (c6.1)
  17  pm-wake-consume-state-danctnix       (c6.2)
  18  pm-detect-firmware-unsupported-danctnix (c7)
  19  decrypt-storm-fast-recover-danctnix  (Patch A)
  20  connection-loss-fast-recover-danctnix (Patch B)
  21  cw1200-fix-backports-danctnix        (Patches F3 + F2 + F1)
  24  sdio-rx-no-relay-danctnix            (Patch C v3)
  25  license-spdx-restore-attribution-danctnix (Patch G)
  26  ba-lock-atomic-danctnix              (Patch D)
  27  ps-state-lock-skip-pm-disabled-danctnix (Patch E)
  28  rx-list-batch-delivery-danctnix      (Patch C2)
  29  bh-c-fossil-cleanup-danctnix         (Patch H)
  30  scan-filter-5ghz-danctnix            (besser#1)
  31  arch/arm64/xor-neon-...              (GCC 15 SCS)
  32  queue-pending-record-lock-bh-danctnix (besser#18)

Verification: pkgrel=6 build from this manifest in progress; if
srcversion == 26B0003FE9F2B05DCE838C4 (pkgrel=5's), source-tree is
byte-equivalent to the c5x interim + scan-filter + besser#18 stack
that's currently running on ohm.

Refs: #17 (the broken mirror), #28 (the interim PR that landed
cumulative-c5x), #31 (ka-promote trailer normalisation followup).
2026-05-19 06:41:37 +02:00
marfrit 443f5e992e Merge pull request 'ka-promote: auto-normalise git format-patch trailers (closes #31)' (#32) from noether/ka-promote-normalise-trailers into main
Reviewed-on: marfrit/kernel-agent#32
2026-05-19 04:33:03 +00:00
test0r 2f119a3fb7 ka-promote: auto-normalise git format-patch trailers (closes #31)
write_cumulative() now strips any "-- \n<MAJOR>.<MINOR>(.<PATCH>)?\n" sentinel
from each input patch and emits a single canonical separator between, but not
after, concatenated patches. Source patches in patches/<scope>/ can therefore
keep their original git format-patch shape regardless of their position in
fleet/<host>.yaml — the brittle "trailer flip-flop on include reorder" mode
from PR #28 (commits 84734ba ↔ ceec602) is gone.

Tests:
- new unit covers strip_trailer + write_cumulative shape with mixed
  trailer states + asserts no orphan trailer leaks at EOF
- fresnel parity b2sum re-recorded after the shape change
  (4d9d93c6... -> 9c21751c...) — the cumulative is byte-identical
  modulo per-patch trailer normalisation; git apply --check on the
  v7.0 baseline still passes
- existing series-dir, bad-include, missing-patch, duplicate-include
  rejections unchanged
2026-05-19 06:30:38 +02:00
marfrit 7a86ebb587 Merge pull request 'fleet/ohm: switch bes2600 to cumulative-c5x interim + close besser#1 + GCC 15 SCS fix (closes #5 partial)' (#28) from claude-noether/kernel-agent:noether/migrate-pinetab2-pkg-and-patches into main
Reviewed-on: marfrit/kernel-agent#28
2026-05-18 20:56:41 +00:00
claude-noether 731e98e079 fleet/ohm.yaml: fix arch/arm64 include path after merge rename
The merge commit renamed arch/arm64/xor-neon-ffixed-x18-scs-build-fix-danctnix/
to arch/arm64/scs-arm-neon-build-fix/ (= main's canonical name) but the
include reference in ohm.yaml didn't get updated atomically.

Update the include path to match the renamed dir; ka-promote would have
exit-2'd on this manifest otherwise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:52:59 +00:00
claude-noether bae99da612 Merge upstream/main into noether/migrate-pinetab2-pkg-and-patches
Resolves the conflict-window between the PR's "switch bes2600 to
cumulative-c5x interim" intent and main's incremental per-patch
evolution.

Resolution per discussion:
- fleet/ohm.yaml: keep PR's cumulative-c5x layout (replaces per-patch
  list) but rename arch/arm64 include to main's canonical
  'scs-arm-neon-build-fix/' (branch's renamed dir dropped).
- patches/driver/bes2600/queue-pending-record-lock-bh-danctnix/
  0001-*.patch: take main's (= identical content + the git-format-patch
  trailer that the branch's earlier add omitted).
- patches/driver/bes2600/scan-filter-5ghz-danctnix/: drop branch's
  older '0001-...-allow-single-channel.patch' variant; keep main's
  newer '0001-...-filter-at-driver-boundary.patch' to avoid 0001-*
  collisions in ka-promote's series-dir resolver.
- patches/arch/arm64/xor-neon-ffixed-x18-scs-build-fix-danctnix/:
  dropped (= duplicate of main's scs-arm-neon-build-fix/).
- All other main additions (rkvdec vp9 patches, scan-filter-5ghz/,
  fleet/ampere.yaml updates) auto-merged cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 20:52:27 +00:00
marfrit 3d10a2c21a Merge pull request 'patches/driver/bes2600/queue-pending-record-lock-bh-danctnix: mirror besser#18 fix' (#30) from claude-noether/kernel-agent:noether/bes2600-pending-record-lock-bh into main
Reviewed-on: marfrit/kernel-agent#30
2026-05-18 19:18:51 +00:00
claude-noether 3ee0ef7d86 patches/arch/arm64/xor-neon-...: correct @@ hunk counts (overcorrected in a840f76)
a840f76 changed @@ from -9,6 +9,10 to -9,7 +9,12 but overshot by 1.
Actual hunk is 6 context + 5 add = -9,6 +9,11. Wrong counts were
silently masked in pkgrel=4 build #4 by the trailer-stripped EOF
letting patch fuzz recover. pkgrel=5 with besser#18 after SCS
exposes it as 'malformed patch at line N: 2.54.0'.

Cumulative b2sum: ceec602afa8574c74354... -> 50397711a6a3ba522283...
Size unchanged 162 716.
2026-05-18 19:17:22 +02:00
claude-noether 878e86f103 patches/arch/arm64/xor-neon-...: restore trailer (SCS is no longer last)
When the SCS patch was the LAST patch in ohm's cumulative, the
trailing '-- \n2.54.0\n' git-format-patch sentinel was an orphan that
patch(1) read as a malformed header — fixed in 84734ba by stripping
the trailer.

Now besser#18 (queue-pending-record-lock-bh-danctnix) is added at the
end of ohm.yaml's includes. SCS is no longer last. Without its
trailer to mark end-of-patch, patch(1) reads straight into besser#18's
'From d95453c... Mon Sep 17 00:00:00 2001' line and errors as
'malformed patch at line N: From ...'.

Restoring the trailer makes the separator unambiguous again.

Cumulative b2sum: 0eb091ddaba4a8f1c3c2a78... -> ceec602afa8574c74354...
Size: 162 704 -> 162 716 (+12 = the trailer bytes).

This rule — 'only the LAST patch must lack a trailer; all others must
keep theirs' — is sensitive to ohm.yaml include ordering, which is
brittle. Filed as a kernel-agent followup: ka-promote should rewrite
trailers automatically (always add to non-last, always strip from
last) so source patches don't need to be ordering-aware.
2026-05-18 18:15:45 +02:00
claude-noether 4d98a8169d fleet/ohm + patches/driver/bes2600/queue-pending-record-lock-bh-danctnix: bundle besser#18 fix into the migration
Pulls the besser#18 lockdep fix (originally on
noether/bes2600-pending-record-lock-bh / PR #30) into this PR so the
ohm migration ships a single self-consistent pkgrel that contains all
three goal components: kernel-agent flow + Patch I + besser#18 fix
(plus the GCC 15 SCS Makefile workaround, no-op while SCS=n).

ohm.yaml includes now resolve to 4 patches:
  1. driver/bes2600/cumulative-c5x-danctnix/             (148 149 B)
  2. driver/bes2600/scan-filter-5ghz-danctnix/           (  7 735 B)
  3. arch/arm64/xor-neon-ffixed-x18-scs-build-fix-danctnix/ (1 562 B)
  4. driver/bes2600/queue-pending-record-lock-bh-danctnix/  (5 258 B)
  ----
  cumulative.patch                                       (162 704 B)
  b2sum 0eb091ddaba4a8f1c3c2a78eb8c621cdc6e6dfed6c43f7dac03e508a05b...

Trailer-strip applied to the besser#18 patch source for the same
reason as the SCS patch — it's now the last in the concatenated
cumulative, and patch(1) errors on the orphan '-- \n2.54.0\n' EOF
sentinel. Same gotcha documented in 84734ba.

PR #30 (the standalone besser#18 mirror PR) becomes superfluous
once this lands; close it as 'bundled into #28'.
2026-05-18 18:01:41 +02:00
claude-noether 84734ba527 patches/arch/arm64/xor-neon-...: strip trailing git-format-patch sentinel
The '-- \n2.54.0\n\n' trailer added in 989b884 was wrong. The
underlying problem was the malformed @@ hunk counts (off by 1 in
both old and new), fixed in a840f76. With correct @@ counts, patch(1)
processes the hunk fully and then sees the orphan trailer at EOF —
which it tries to parse as the start of a new patch header
('malformed patch at line N: 2.54.0').

The original (no-trailer) shape works correctly in the concatenated
cumulative as long as the @@ counts are right. Removing the trailer
brings the file back to the original 1562-byte size and the
cumulative b2sum to 334c37b5d37067982bd9... (size unchanged 157 458 ->
157 446 since the 12 byte trailer is gone).

Lesson for ka-promote: when concatenating patches as a stream for
patch(1), the LAST patch must not carry a trailing '-- \n<version>\n'
sentinel — the previous patches' sentinels are fine because they are
followed by 'From <sha>' headers that patch(1) recognises as the next
patch boundary. Documented in series-dir README as a gotcha.
2026-05-18 17:00:11 +02:00
claude-noether c9e9ad973c patches/driver/bes2600/queue-pending-record-lock-bh-danctnix: mirror besser#18 fix from bes2600-dkms
Single-patch series-dir, mirror of the Markus-authored commit d95453c
on marfrit/bes2600-dkms branch bes2600/queue-pending-record-lock-bh-fix
(PR #11). Paths rewritten from DKMS-style (bes2600/foo.c) to in-tree
staging (drivers/staging/bes2600/foo.c) via sed -- this is the
in-tree variant.

Fix: convert plain spin_lock(&pending_record_lock) to spin_lock_bh()
at the 5 sites where it's taken in non-BH-disabled contexts
(queue.c:832/839/844, tx_loop.c:112/114). queue.c:289/295 stays as
plain spin_lock because BH is already disabled by the outer
queue->lock_bh acquired at queue.c:285.

Eliminates the SOFTIRQ-safe -> SOFTIRQ-unsafe lockdep warning
reported in besser#18 (PROVE_LOCKING-only -- non-fatal on production
builds where lockdep is off, but real AB-BA window between
bes2600_join_work workqueue context and bes2600_tx softirq context).

This commit does NOT add the include to fleet/ohm.yaml. The patch
will be wired into ohm's manifest in a follow-up commit (or this
branch's PR can extend with the ohm.yaml change once the migration
PR #28 lands and the bes2600-dkms PR #11 is reviewed).

Closes: besser#18
Refs: marfrit/bes2600-dkms #11 (source-of-truth PR)
2026-05-18 16:59:28 +02:00
claude-noether a840f76907 patches/arch/arm64/xor-neon-...: fix malformed @@ hunk counts
The hunk header @@ -9,6 +9,10 @@ understated both old (actual 7) and
new (actual 12) line counts by 1. patch(1) standalone tolerates this
via fuzz, but in the concatenated cumulative the wrong counts cause
patch to mis-judge the hunk boundary and read the trailing context
line ('lib-...uaccess_flushcache.o') as the start of a new patch
header — 'malformed patch at line 4526'.

Cumulative b2sum: bd42cd39106298879eeb... -> ad9e2cb533957f218058...
(size unchanged at 157 458; only the @@ counts in the SCS patch
differ)
2026-05-18 16:58:19 +02:00
claude-noether 989b8842fb patches/arch/arm64/xor-neon-...: append git-format-patch trailer
The SCS-build-fix patch was missing the standard '-- \n2.54.0\n'
trailer that git format-patch emits between patches. Without it,
BSD-flavour patch(1) in makepkg's prepare() reads the trailing context
line of the @@ hunk as the start of a new patch header and dies with
'malformed patch at line N'. Affects builds where ka-promote
concatenates this series with any others. Reproduced 2026-05-18 on
the first attempted ohm pkgrel=4 build.

Cumulative b2sum changes accordingly:
  a807297b25be... -> bd42cd39106298879eeb...
(size 157446 -> 157458; 12 bytes for the trailer)
2026-05-18 16:52:46 +02:00
claude-noether f203b70f4f fleet/ohm: switch bes2600 driver scope to cumulative-c5x-danctnix interim (closes #5 partial migration)
Audit during ohm pkgrel=4 migration found the per-series -danctnix
mirrors merged in #17 do NOT apply against the linux-pinetab2 baseline:
all 17 of them use DKMS-style root paths (bes2600/foo.c) rather than
in-tree staging paths (drivers/staging/bes2600/foo.c), and at least one
has a corrupted mixed-prefix header
(a/drivers/staging/bes2600/...  b/bes2600/...).

ka-promote ohm with those includes produced a 172 644-byte cumulative
touching 27 file paths, of which 11 are bogus. The hand-curated
0001-bes2600-besser-cumulative-series.patch from the working
danctnix-besser-pkgbuild flow on boltzmann (148 149 bytes, 48 in-tree
staging files) is what pkgrel=3 actually builds with.

Until the per-series mirrors are reconstructed (followup issue to be
opened separately), the bes2600 driver scope is satisfied here by
staging that hand-curated cumulative as a single-file series-dir
patches/driver/bes2600/cumulative-c5x-danctnix/. ohm.yaml drops the
broken per-series includes in favour of:

  - driver/bes2600/cumulative-c5x-danctnix/
  - driver/bes2600/scan-filter-5ghz-danctnix/      (closes besser#1)
  - arch/arm64/xor-neon-ffixed-x18-scs-build-fix-danctnix/

ka-promote ohm now produces a self-consistent 157 446-byte cumulative
(148 149 + 7 735 + 1 562 = exact byte arithmetic) with b2sum
a807297b25be... which is what the new
marfrit-packages/arch/linux-pinetab2-danctnix-besser PKGBUILD pkgrel=4
pins.

Also fixes fleet/ohm.yaml YAML parse error: bar5_burn_in had a scalar
value followed by a sub-list, which ka-promote (PyYAML) refused to
parse. The whole manifest had never parsed cleanly since #18 landed.

Refs: #5 (migrate PKGBUILD), #2 (mirror besser series — needs per-series
rewrite followup), besser#1 (Patch I).
2026-05-18 16:50:41 +02:00
marfrit a254b6f0bb Merge pull request 'bes2600/scan-filter-5ghz: refine to allow targeted single-channel scans' (#26) from noether/scan-filter-5ghz-refine-targeted-allowed into main
Reviewed-on: marfrit/kernel-agent#26
2026-05-18 14:06:53 +00:00
test0r 43c8f0cba8 patches/driver/bes2600: scan-filter-5ghz refinement — allow targeted single-channel scans
Updates both flavors with the n_channels > 1 refinement (was > 0).
The original guard refused ALL 5 GHz scans which broke 5 GHz
association via NM band=a profiles (NM iterates freq_list per
channel, single-channel scans were also refused).

Tightened: only multi-channel 5 GHz scans (the per-band-sweep
that triggers the firmware storm) are refused; single-channel
5 GHz scans pass through so NM/wpa_supplicant can find and
associate to 5 GHz BSSes.

Verified on ohm with locally-built pkgrel=3 (srcversion
BEB625FA7443171EA8D55F7): associated to 5 GHz BSSID
c0:25:06:e6:5b:33 on 5240 MHz / ch.48, 150 Mbit/s MCS 7
40MHz short-GI; Pattern A still 0 since boot.

Patch file is now a concatenation of two commits from
marfrit/bes2600-dkms bes2600/scan-filter-5ghz branch:
  093a503 (original Patch I)
  8cd10f4 (this refinement)
patch -Np1 applies them sequentially -> net effect = single squash.

Refs: besser#1 (closed), PKGBUILD update at marfrit/besser
claude-noether-14 commit 122582e (pkgrel=3 deployed to ohm
on 2026-05-18 same session).
2026-05-18 15:57:20 +02:00
marfrit 42b0c5042a Merge pull request 'fleet/ohm: import Patch I (5GHz scan filter, closes besser#1) + arm64 SCS build-fix' (#25) from noether/import-scan-filter-5ghz-and-scs-fix into main
Reviewed-on: marfrit/kernel-agent#25
2026-05-18 13:34:55 +00:00
test0r 4c80458d1f fleet/ohm: import Patch I (5GHz scan filter) + arm64 SCS build-fix
Patch I closes besser#1 — the wsm_generic_confirm 0x0007 dmesg storm.
One-line guard in bes2600_hw_scan() refuses the 5 GHz iteration of
mac80211's per-band hw_scan loop with -EOPNOTSUPP, so the firmware
never sees the scan request that would be rejected with status 2 →
-EINVAL cascade.  Phase 7 verified 2026-05-18 on ohm running pkgrel=2:
Pattern A 14.3/h → 0/h over 30-min window, no WARN/BUG, single-band
2.4 GHz scans still return BSSes cleanly.

Two flavors imported (scan-filter-5ghz and scan-filter-5ghz-danctnix)
matching the convention of other bes2600 series — the code path
doesn't touch timer APIs so the two are byte-identical for now;
flavor separation is kept to preserve consistency in ohm.yaml.

The arm64 scs-arm-neon-build-fix series is a build-environment
workaround: GCC 15.2.1 strictly validates that -fsanitize=shadow-
call-stack requires -ffixed-x18, and arm_neon.h's #pragma target/
push/pop blocks lose x18 fixing inside the wrapped section.  The
Makefile tweak re-adds -ffixed-x18 explicitly for xor-neon.o.  It's
a no-op when SCS is off (current pkgrel=2 ohm config) and unblocks
SCS=y once GCC upstream is fixed.

ohm.yaml gains a CONFIG_SHADOW_CALL_STACK=n config override with a
pointer to besser#20 (the re-enable tracking issue) so future
manifest-driven kconfig generation honors the workaround without
silently dropping it.

Source-of-truth commit for Patch I:
  marfrit/bes2600-dkms branch bes2600/scan-filter-5ghz sha 093a503
PKGBUILD-side (already deployed to ohm via pkgrel=2):
  marfrit/besser branch claude-noether-14 sha ae175f9

Refs: besser#1 (closed), besser#20, kernel-agent#5
2026-05-18 15:25:37 +02:00
marfrit 96af34d775 Merge PR #24: import Sarma VP9-VDPU381 series + enable in fleet/ampere.yaml 2026-05-18 13:15:19 +00:00
marfrit 95be39ef80 fleet/ampere: enable Sarma VP9-VDPU381 patches in baseline
Reference the 3 patches imported in the previous commit under the
scope-tagged patch list. Apply order is strict (0001 → 0002 → 0003).

Verified 2026-05-18 via the arch_vp9_test extlinux boot on ampere:
- VP9F enumerates on rkvdec /dev/video2
- kdirect decode bit-exact vs libavcodec SW reference at -ss 30
- libva decode (firefox/chromium-style consumer) also bit-exact
- vainfo lists VAProfileVP9Profile0 (iter38 multi-device probe auto-picks)
- All three paths agree on sha
  c8624d7c42db66525f53a02a515bc38d0a17ef39f692660cc7bebb1e2d2e1b48

Removes VP9 from the "explicitly not included" comment block — issue
#12 closes with this change.

Also: AV1 stays out-of-scope per issue #6 ask 3 (kernel side fine via
the existing av1-vpu-dec node; backend just needs the 4th-fd
generalization tracked in libva-v4l2-request-fourier#2).

The next linux-ampere-fourier package rebuild from this manifest
will pick up VP9 automatically; ampere's running
7.0.0-rc3-vp9-test+ kernel already has these patches via the
operator's manual build session today.
2026-05-18 13:15:09 +00:00
41 changed files with 9193 additions and 206 deletions
+39 -2
View File
@@ -27,6 +27,7 @@ import argparse
import glob
import hashlib
import os
import re
import subprocess
import sys
from datetime import datetime, timezone
@@ -37,6 +38,17 @@ VERSION = 1
SCHEMA_VERSION = 1
COVER_LETTER = "0000-cover-letter.patch"
# git format-patch trailer: "-- \n<MAJOR>.<MINOR>(.<PATCH>)?\n" at EOF,
# possibly with trailing blank line(s). Strip from each source patch so
# that the cumulative is always well-formed regardless of include order.
# See issue #31.
_TRAILER_RE = re.compile(rb'\n-- \n\d+\.\d+(?:\.\d+)?\n+\Z')
# Canonical separator emitted between concatenated patches in the
# cumulative. Trailing blank line keeps patch(1) happy when the next
# patch starts with "From <sha>".
_CANONICAL_TRAILER = b'-- \n2.54.0\n\n'
def die(msg, code=1):
print(f"ka-promote: error: {msg}", file=sys.stderr)
@@ -124,11 +136,36 @@ def resolve_includes(includes, patches_root):
return resolved
def strip_trailer(data):
"""Strip any trailing git format-patch sentinel from a patch.
Accepts patches in either canonical shape:
- WITH trailer: "...\n-- \n2.54.0\n\n"
- WITHOUT trailer: "...\n" (already stripped)
Returns data ending in a single newline so the caller can either
append a canonical trailer (mid-cumulative) or leave it bare (last).
"""
stripped = _TRAILER_RE.sub(b'\n', data)
if not stripped.endswith(b'\n'):
stripped += b'\n'
return stripped
def write_cumulative(resolved, out_path):
with open(out_path, "wb") as out:
for r in resolved:
n = len(resolved)
for i, r in enumerate(resolved):
with open(r["src"], "rb") as src:
out.write(src.read())
data = src.read()
data = strip_trailer(data)
out.write(data)
# Mid-cumulative patches need a separator so patch(1) knows
# where they end and the next "From <sha>" begins. Last
# patch stays bare — a trailing orphan sentinel reads as
# the start of a malformed new patch at EOF (issue #31).
if i != n - 1:
out.write(_CANONICAL_TRAILER)
with open(out_path, "rb") as f:
b2 = hashlib.blake2b(f.read()).hexdigest()
size = os.path.getsize(out_path)
+11 -4
View File
@@ -53,12 +53,19 @@ includes:
- board/coolpi-cm5-genbook/0005-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-Enable-USB-C-PD-charging-via-FUSB302.patch
- board/coolpi-cm5-genbook/0008-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-Add-lid-switch-and-USB3-PHY-lane-config.patch
- board/coolpi-cm5-genbook/0011-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-wire-internal-microphone.patch
# VP9 enablement for RK3588 rkvdec (issue #12, closed 2026-05-18).
# Cherry-picked from D.V.A.B. Sarma's add-rkvdec-vdpu381-vp9-v8 branch
# at github.com/dvab-sarma/android_kernel_rk_opi. Bit-exact HW==SW==libva
# verified at -ss 30 on bbb_60s_720p.vp9.webm via all three decode paths
# (kdirect / SW / libva); sha c8624d7c42db66525f53a02a515bc38d0a17ef39f692660cc7bebb1e2d2e1b48.
# Apply order is STRICT (0003 depends on the rkvdec-vp9-common refactor
# added in 0002, which depends on the helper rename in 0001).
# See patches/driver/media/README.md for provenance + removal criteria.
- driver/media/0001-rkvdec-vp9-rename-get_ref_buf-to-get_ref_buf_vp9.patch
- driver/media/0002-rkvdec-move-vp9-functions-to-common-file.patch
- driver/media/0003-rkvdec-add-vp9-support-for-vdpu381-variant.patch
# Explicitly NOT included this round (tracked for later sprints):
# - VP9 enablement for RK3588 rkvdec (issue #6 ask 2). /dev/video0 only
# advertises S265 + S264 today; vainfo lists 9 profiles, target is
# 10. Requires identifying the VDPU381/383 patch chain + possible
# DTS additions. RFC-stage work, scope unclear until research lands.
# - AV1 decoder integration (issue #6 ask 3). Kernel side is fine
# (/dev/video4 advertises AV1F). Backend libva-v4l2-request-fourier
# needs iter39 for a third fd. Backend work, not kernel.
+51 -28
View File
@@ -25,40 +25,61 @@ baseline:
# Scope-tagged patch includes. Resolves to patches/<scope>/<file>.patch.
#
# Series-ordering note: the current cumulative-patch generation order on
# boltzmann is A, B, C v3, F, G, D, E, C2, c5.x, c6.x, c7, H — explicitly
# NOT alphabetical. ka-promote MUST honor an apply_order field when
# concatenating series into the build's per-job cumulative patch. The
# legend mapping series-letter → series-name lives in the current
# danctnix-besser-pkgbuild changelog on boltzmann; promote to this
# manifest once auto-generation is wired.
#
# DanctNIX siblings (-danctnix suffix) are selected here because ohm
# runs on the DanctNIX kernel base; the non-suffixed variants exist for
# vanilla mainline consumers that ohm doesn't currently have.
# Per-series reconstruction closing kernel-agent#29 (2026-05-18 evening):
# the 24 in-tree bes2600 -danctnix series-dirs below were repopulated
# from cleanups + bes2600/bh-c-fossil-cleanup via
# git format-patch fe73571..cleanups --no-merges
# with paths rewritten from bes2600/* to drivers/staging/bes2600/*.
# Order matches the original cleanups commit chronology (= the order
# the c5x interim cumulative had folded them in). Replacing
# cumulative-c5x-danctnix with these gives per-fix revertability +
# proper apply_order traceability via the manifest.
includes:
# Default-on series (uncontroversial fixes that ohm already runs):
- driver/bes2600/staging-prep-series-danctnix/
- driver/bes2600/pm-state-resync-danctnix/
- driver/bes2600/pm-timeout-silence-danctnix/
- driver/bes2600/pm-wake-consume-state-danctnix/
- driver/bes2600/pm-gate-on-handshake/
- driver/bes2600/pm-detect-firmware-unsupported-danctnix/
- driver/bes2600/scan-defer-backoff-tune-danctnix/
- driver/bes2600/scan-defer-on-reject-danctnix/
- driver/bes2600/lmac-recover-via-mmc-hw-reset-danctnix/
- driver/bes2600/tx-sdio-dma-oob-danctnix/
# pre-c-stack (factory + early cleanups), in cleanups order
- driver/bes2600/factory-series/
- driver/bes2600/factory-thread-dev/
- driver/bes2600/pm-gate-on-handshake/
- driver/bes2600/remove-chardev-user-interface/
- driver/bes2600/enable-testmode/
- driver/bes2600/tx-sdio-dma-oob-danctnix/
- driver/bes2600/factory-drop-kernel-write-danctnix/
- driver/bes2600/drop-dpd-file-paths-danctnix/
- driver/bes2600/drop-orphan-file-io-danctnix/
- driver/bes2600/remove-chardev-user-interface/
- driver/bes2600/enable-testmode/
- driver/bes2600/pm-timeout-silence-danctnix/
# c-stack (c5.1, c5.1.1, c5.2 + c5.2.1, c6.1, c6.2, c7)
- driver/bes2600/scan-defer-on-reject-danctnix/
- driver/bes2600/scan-defer-backoff-tune-danctnix/
- driver/bes2600/lmac-recover-via-mmc-hw-reset-danctnix/ # c5.2 + c5.2.1 (multi-fn SDIO)
- driver/bes2600/pm-state-resync-danctnix/
- driver/bes2600/pm-wake-consume-state-danctnix/
- driver/bes2600/pm-detect-firmware-unsupported-danctnix/
# Patches A/B/F/C-v3/G/D/E/C2/H (in cleanups merge order)
- driver/bes2600/decrypt-storm-fast-recover-danctnix/ # Patch A
- driver/bes2600/connection-loss-fast-recover-danctnix/ # Patch B
- driver/bes2600/cw1200-fix-backports-danctnix/ # Patches F3 + F2 + F1
- driver/bes2600/sdio-rx-no-relay-danctnix/ # Patch C v3
- driver/bes2600/license-spdx-restore-attribution-danctnix/ # Patch G
- driver/bes2600/ba-lock-atomic-danctnix/ # Patch D
- driver/bes2600/ps-state-lock-skip-pm-disabled-danctnix/ # Patch E
- driver/bes2600/rx-list-batch-delivery-danctnix/ # Patch C2
- driver/bes2600/bh-c-fossil-cleanup-danctnix/ # Patch H
# close besser#1 — refuse multi-channel 5 GHz scans at driver boundary.
- driver/bes2600/scan-filter-5ghz-danctnix/
# GCC 15.2.1 build-fix for arm_neon.h + SHADOW_CALL_STACK interaction.
# Runtime no-op as long as the config has CONFIG_SHADOW_CALL_STACK=n
# (current ohm setting). Kept in the manifest for the day SCS gets
# re-enabled. See reference_arm64_scs_arm_neon_gcc15 memory.
- arch/arm64/scs-arm-neon-build-fix/
# close besser#18 — pending_record_lock SOFTIRQ-safe -> -unsafe inversion.
# Mirror of marfrit/bes2600-dkms#11 (d95453c). 5-site spin_lock -> _bh.
- driver/bes2600/queue-pending-record-lock-bh-danctnix/
# Explicitly NOT included (decision logged):
# - debian-copyright-fsf-address: Debian packaging metadata, not kernel
# - bare (non-danctnix) variants of the above: ohm runs DanctNIX base
# - cumulative-c5x-danctnix: retired in favour of the per-series above
# (kept on disk for one cycle as bisection reference)
# - staging-prep-series-danctnix: duplicate of tx-sdio-dma-oob-danctnix
# under an older branch name; kept on disk for one cycle, dropped here
config:
source: hand-managed config file in boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/config
@@ -91,9 +112,11 @@ verify:
- wlan0 + bt0 (BT/UART) present after boot
- sdio_force_uhs=0 not needed (DMA-OOB-read fix in tx-sdio-dma-oob series)
bar4_per_patch_probe: opt-in
bar5_burn_in: opt-in
- WiFi: 24h iperf3 to LAN host without rxhang
- PM: lid-close → wake cycles × 100 without bes2600 confirm-loss
bar5_burn_in:
mode: opt-in
tests:
- "WiFi: 24h iperf3 to LAN host without rxhang"
- "PM: lid-close → wake cycles × 100 without bes2600 confirm-loss"
build_host:
primary: boltzmann # native aarch64 with ohm's identical .config
@@ -0,0 +1,36 @@
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Mon, 18 May 2026 11:42:00 +0200
Subject: [PATCH] arm64: xor-neon: restore -ffixed-x18 when SHADOW_CALL_STACK=y
(GCC 15+ build fix)
GCC 15.2.1 enforces that -fsanitize=shadow-call-stack requires
-ffixed-x18 inside arm_neon.h's #pragma GCC target() blocks. The
existing CFLAGS_REMOVE_xor-neon.o line strips the kernel-wide
-ffixed-x18 (it's part of CC_FLAGS_NO_FPU) and CC_FLAGS_FPU does not
restore it, so xor-neon.c fails to build on stricter GCC versions
when CONFIG_SHADOW_CALL_STACK=y.
Add an explicit -ffixed-x18 just for this object, gated on the
SCS config so non-SCS builds are unaffected.
Build environment workaround; not a kernel-runtime bug.
---
arch/arm64/lib/Makefile | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
index 1234567..2345678 100644
--- a/arch/arm64/lib/Makefile
+++ b/arch/arm64/lib/Makefile
@@ -9,6 +9,11 @@ ifeq ($(CONFIG_KERNEL_MODE_NEON), y)
obj-$(CONFIG_XOR_BLOCKS) += xor-neon.o
CFLAGS_xor-neon.o += $(CC_FLAGS_FPU)
CFLAGS_REMOVE_xor-neon.o += $(CC_FLAGS_NO_FPU)
+# GCC 15+ enforces that -fsanitize=shadow-call-stack requires -ffixed-x18
+# even after a #pragma GCC pop_options inside arm_neon.h. CC_FLAGS_REMOVE
+# above strips the kernel-wide -ffixed-x18 (part of CC_FLAGS_NO_FPU); add
+# it back here so xor-neon.c still compiles when SHADOW_CALL_STACK=y.
+CFLAGS_xor-neon.o += $(if $(CONFIG_SHADOW_CALL_STACK),-ffixed-x18)
endif
lib-$(CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE) += uaccess_flushcache.o
@@ -0,0 +1,313 @@
From 8fd20308ed53678c863a0ef52fb2c754e3adc63c Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Fri, 8 May 2026 00:17:46 +0200
Subject: [PATCH 27/29] =?UTF-8?q?bes2600:=20Patch=20D=20=E2=80=94=20atomic?=
=?UTF-8?q?ize=20ba=5Flock=20counters,=20drop=20the=20spinlock?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The block-ack policy uses 4 int counters (ba_acc, ba_cnt, ba_acc_rx,
ba_cnt_rx) bumped per data frame in the TX and RX hot paths under
spin_lock_bh(&hw_priv->ba_lock). The lock was the heaviest per-frame
synchronization cost remaining after Patch C v3 (which fixed the
sdio_rx_work relay). Per the Opus structural critique (PR #8), this
pattern matches mac80211 driver convention for per-frame statistics:
atomic_t suffices, no lock needed.
Field-by-field changes in struct bes2600_common:
ba_acc, ba_cnt, ba_acc_rx, ba_cnt_rx: int -> atomic_t
ba_armed: new atomic_t (timer-arm flag)
ba_ena: bool -> atomic_t
ba_lock: removed (spinlock_t deleted)
ba_hist: int (single-writer = ba_timer)
Producer hot path (txrx.c TX submit + RX receive):
- atomic_add for the byte accumulator
- atomic_inc for the frame counter
- atomic_cmpxchg(&ba_armed, 0, 1) to claim the once-per-window
mod_timer arm — at most ONE producer succeeds; race-free
- no spin_lock_bh
Consumer paths (sta.c bes2600_ba_timer, sta.c disconnect-reset, sta.c
bes2600_ba_work, debug.c debugfs reader):
- atomic_read snapshots all 4 counters into locals; the threshold
predicate (acc/cnt >= THLD) tolerates approximate snapshots — the
timer fires periodically, a single misclassification just delays
the policy update by one tick
- atomic_set zeroes the counters at end of timer-callback window;
racing producer increments after the snapshot are lost (acceptable
for stats; same approximation the original lock allowed under
contention)
- atomic_set(&ba_armed, 0) re-enables the next window's arm
Followup-amenable simplification: ba_hist remains int because only
the single ba_timer callback writes it; multiple writers would need
to upgrade it too.
This patch follows the cw1200-mainline-idiom established by Patch C v3
(structural fix, not bandaid). The cw1200 reference doesn't have a
similar lock to compare; bes2600 inherited this from a later
Bestechnic addition rather than the upstream tree.
---
bes2600/bes2600.h | 26 ++++++++++------
bes2600/debug.c | 13 +++++---
bes2600/main.c | 2 +-
bes2600/sta.c | 77 ++++++++++++++++++++++++++++-------------------
bes2600/txrx.c | 23 ++++++++------
5 files changed, 85 insertions(+), 56 deletions(-)
diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h
index 84059c7..32bce5e 100644
--- a/drivers/staging/bes2600/bes2600.h
+++ b/drivers/staging/bes2600/bes2600.h
@@ -353,15 +353,23 @@ struct bes2600_common {
* Keeping in common structure for the time being. Will be moved to VIFF
* after the mechanism is clear */
u8 ba_tid_mask;
- int ba_acc; /*TODO: Same as above */
- int ba_cnt; /*TODO: Same as above */
- int ba_cnt_rx; /*TODO: Same as above */
- int ba_acc_rx; /*TODO: Same as above */
- int ba_hist; /*TODO: Same as above */
- struct timer_list ba_timer;/*TODO: Same as above */
- spinlock_t ba_lock; /*TODO: Same as above */
- bool ba_ena; /*TODO: Same as above */
- struct work_struct ba_work; /*TODO: Same as above */
+ /*
+ * Patch D: ba_lock removed. Per-frame TX/RX hot-path bumped these
+ * counters under spin_lock_bh; the lock did not protect any
+ * compound invariant that atomic ops can't satisfy. Counters are
+ * now atomic_t; ba_armed gates the once-per-window mod_timer
+ * arm via cmpxchg so concurrent TX/RX at a fresh window each
+ * try to claim the arm and exactly one succeeds.
+ */
+ atomic_t ba_acc;
+ atomic_t ba_cnt;
+ atomic_t ba_cnt_rx;
+ atomic_t ba_acc_rx;
+ atomic_t ba_armed;
+ int ba_hist;
+ struct timer_list ba_timer;
+ atomic_t ba_ena;
+ struct work_struct ba_work;
bool is_BT_Present;
bool is_go_thru_go_neg;
u8 conf_listen_interval;
diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c
index 47e27be..0ab79c0 100644
--- a/drivers/staging/bes2600/debug.c
+++ b/drivers/staging/bes2600/debug.c
@@ -110,17 +110,20 @@ static int bes2600_status_show_common(struct seq_file *seq, void *v)
int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0;
bool ba_ena;
- spin_lock_bh(&hw_priv->ba_lock);
- ba_cnt = hw_priv->debug->ba_cnt;
- ba_acc = hw_priv->debug->ba_acc;
+ /*
+ * Patch D: ba_lock removed. hw_priv->debug->ba_* are written only
+ * by the timer callback (single writer); reading without a lock is
+ * fine for stats. ba_ena is atomic_t.
+ */
+ ba_cnt = hw_priv->debug->ba_cnt;
+ ba_acc = hw_priv->debug->ba_acc;
ba_cnt_rx = hw_priv->debug->ba_cnt_rx;
ba_acc_rx = hw_priv->debug->ba_acc_rx;
- ba_ena = hw_priv->ba_ena;
+ ba_ena = !!atomic_read(&hw_priv->ba_ena);
if (ba_cnt)
ba_avg = ba_acc / ba_cnt;
if (ba_cnt_rx)
ba_avg_rx = ba_acc_rx / ba_cnt_rx;
- spin_unlock_bh(&hw_priv->ba_lock);
seq_puts(seq, "BES2600 Wireless LAN driver status\n");
seq_printf(seq, "Hardware: %d.%d\n",
diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c
index 71dc4ae..8fc37b4 100644
--- a/drivers/staging/bes2600/main.c
+++ b/drivers/staging/bes2600/main.c
@@ -501,7 +501,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
INIT_LIST_HEAD(&hw_priv->event_queue);
INIT_WORK(&hw_priv->event_handler, bes2600_event_handler);
INIT_WORK(&hw_priv->ba_work, bes2600_ba_work);
- spin_lock_init(&hw_priv->ba_lock);
+ /* Patch D: ba_lock removed; ba_acc/ba_cnt/etc are atomic_t. */
timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0);
if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats,
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
index 70b12f9..8af8150 100644
--- a/drivers/staging/bes2600/sta.c
+++ b/drivers/staging/bes2600/sta.c
@@ -2362,14 +2362,19 @@ void bes2600_join_work(struct work_struct *work)
//WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id));
WARN_ON(wsm_set_block_ack_policy(hw_priv,
0, hw_priv->ba_tid_mask, priv->if_id));
- spin_lock_bh(&hw_priv->ba_lock);
- hw_priv->ba_ena = false;
- hw_priv->ba_cnt = 0;
- hw_priv->ba_acc = 0;
+ /*
+ * Patch D: ba_lock removed. Disconnect-reset clears the
+ * counters and the arm flag; producers racing here cannot
+ * cause harm — at worst they re-arm the timer and bump
+ * counters that will be cleared on the next timer tick.
+ */
+ atomic_set(&hw_priv->ba_ena, 0);
+ atomic_set(&hw_priv->ba_cnt, 0);
+ atomic_set(&hw_priv->ba_acc, 0);
hw_priv->ba_hist = 0;
- hw_priv->ba_cnt_rx = 0;
- hw_priv->ba_acc_rx = 0;
- spin_unlock_bh(&hw_priv->ba_lock);
+ atomic_set(&hw_priv->ba_cnt_rx, 0);
+ atomic_set(&hw_priv->ba_acc_rx, 0);
+ atomic_set(&hw_priv->ba_armed, 0);
mgmt_policy.protectedMgmtEnable = 0;
mgmt_policy.unprotectedMgmtFramesAllowed = 1;
@@ -2649,10 +2654,11 @@ void bes2600_ba_work(struct work_struct *work)
return;*/
bes_devel("BA work****\n");
- spin_lock_bh(&hw_priv->ba_lock);
-// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0;
+ /*
+ * Patch D: ba_lock removed. ba_tid_mask is u8 set once at init
+ * (main.c); reading it without a lock is fine.
+ */
tx_ba_tid_mask = hw_priv->ba_tid_mask;
- spin_unlock_bh(&hw_priv->ba_lock);
wsm_lock_tx(hw_priv);
@@ -2665,37 +2671,49 @@ void bes2600_ba_work(struct work_struct *work)
void bes2600_ba_timer(struct timer_list *t)
{
bool ba_ena;
+ int cnt, acc, cnt_rx, acc_rx;
struct bes2600_common *hw_priv = timer_container_of(hw_priv, t, ba_timer);
- spin_lock_bh(&hw_priv->ba_lock);
- bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
- hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx);
+ /*
+ * Patch D: ba_lock removed. Snapshot atomic counters into locals
+ * for the predicate evaluation; producers may race incrementing
+ * after the snapshot but the resulting decision is approximate
+ * which the policy already tolerates (next timer tick re-evaluates).
+ */
+ cnt = atomic_read(&hw_priv->ba_cnt);
+ acc = atomic_read(&hw_priv->ba_acc);
+ cnt_rx = atomic_read(&hw_priv->ba_cnt_rx);
+ acc_rx = atomic_read(&hw_priv->ba_acc_rx);
+
+ bes2600_debug_ba(hw_priv, cnt, acc, cnt_rx, acc_rx);
if (atomic_read(&hw_priv->scan.in_progress)) {
- hw_priv->ba_cnt = 0;
- hw_priv->ba_acc = 0;
- hw_priv->ba_cnt_rx = 0;
- hw_priv->ba_acc_rx = 0;
- goto skip_statistic_update;
+ atomic_set(&hw_priv->ba_cnt, 0);
+ atomic_set(&hw_priv->ba_acc, 0);
+ atomic_set(&hw_priv->ba_cnt_rx, 0);
+ atomic_set(&hw_priv->ba_acc_rx, 0);
+ atomic_set(&hw_priv->ba_armed, 0);
+ return;
}
- if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT &&
- (hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD ||
- (hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT &&
- hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >=
+ if (cnt >= BES2600_BLOCK_ACK_CNT &&
+ (acc / cnt >= BES2600_BLOCK_ACK_THLD ||
+ (cnt_rx >= BES2600_BLOCK_ACK_CNT &&
+ acc_rx / cnt_rx >=
BES2600_BLOCK_ACK_THLD)))
ba_ena = true;
else
ba_ena = false;
- hw_priv->ba_cnt = 0;
- hw_priv->ba_acc = 0;
- hw_priv->ba_cnt_rx = 0;
- hw_priv->ba_acc_rx = 0;
+ atomic_set(&hw_priv->ba_cnt, 0);
+ atomic_set(&hw_priv->ba_acc, 0);
+ atomic_set(&hw_priv->ba_cnt_rx, 0);
+ atomic_set(&hw_priv->ba_acc_rx, 0);
+ atomic_set(&hw_priv->ba_armed, 0);
- if (ba_ena != hw_priv->ba_ena) {
+ if (ba_ena != !!atomic_read(&hw_priv->ba_ena)) {
if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) {
- hw_priv->ba_ena = ba_ena;
+ atomic_set(&hw_priv->ba_ena, ba_ena ? 1 : 0);
hw_priv->ba_hist = 0;
#if 0
bes_devel("[STA] %s block ACK:\n",
@@ -2705,9 +2723,6 @@ void bes2600_ba_timer(struct timer_list *t)
}
} else if (hw_priv->ba_hist)
--hw_priv->ba_hist;
-
-skip_statistic_update:
- spin_unlock_bh(&hw_priv->ba_lock);
}
int bes2600_vif_setup(struct bes2600_vif *priv)
diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c
index 3aef009..536b198 100644
--- a/drivers/staging/bes2600/txrx.c
+++ b/drivers/staging/bes2600/txrx.c
@@ -996,14 +996,18 @@ bes2600_tx_h_ba_stat(struct bes2600_vif *priv,
if (!ieee80211_is_data(t->hdr->frame_control))
return;
- spin_lock_bh(&hw_priv->ba_lock);
- hw_priv->ba_acc += t->skb->len - t->hdrlen;
- if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
+ /*
+ * Patch D: lock-free hot-path BA accounting. atomic_inc + atomic_add
+ * each per-frame; the once-per-window timer-arm uses cmpxchg on
+ * ba_armed so concurrent TX/RX can't both try to set the timer and
+ * we don't need cross-counter coherency on the ba_cnt/ba_cnt_rx pair.
+ */
+ atomic_add(t->skb->len - t->hdrlen, &hw_priv->ba_acc);
+ atomic_inc(&hw_priv->ba_cnt);
+ if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) {
mod_timer(&hw_priv->ba_timer,
jiffies + BES2600_BLOCK_ACK_INTERVAL);
}
- hw_priv->ba_cnt++;
- spin_unlock_bh(&hw_priv->ba_lock);
}
static int
@@ -1651,14 +1655,13 @@ bes2600_rx_h_ba_stat(struct bes2600_vif *priv,
if (!priv->setbssparams_done)
return;
- spin_lock_bh(&hw_priv->ba_lock);
- hw_priv->ba_acc_rx += skb_len - hdrlen;
- if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
+ /* Patch D: lock-free hot-path BA accounting; see TX side comment. */
+ atomic_add(skb_len - hdrlen, &hw_priv->ba_acc_rx);
+ atomic_inc(&hw_priv->ba_cnt_rx);
+ if (atomic_cmpxchg(&hw_priv->ba_armed, 0, 1) == 0) {
mod_timer(&hw_priv->ba_timer,
jiffies + BES2600_BLOCK_ACK_INTERVAL);
}
- hw_priv->ba_cnt_rx++;
- spin_unlock_bh(&hw_priv->ba_lock);
}
void bes2600_rx_cb(struct bes2600_vif *priv,
--
2.54.0
@@ -0,0 +1,725 @@
From 1b5374d35bcc75e0f393e3d841288f91812eb7dc Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Fri, 8 May 2026 08:23:20 +0200
Subject: [PATCH] =?UTF-8?q?bes2600:=20Patch=20H=20=E2=80=94=20bh.c=20hygie?=
=?UTF-8?q?ne=20cleanup=20(drop=20fossil=20blocks,=20dead=20stubs)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Per Opus structural critique §4.1 (#if 0 graveyard), §4.3 (asm
volatile("nop") placeholder), §4.4 (BUG_ON in steady-state hot
path). Pure source-tree cleanup, no functional change.
Removed:
1. bh.c lines 319-395 (76-line #if 0 block) — dead helper
functions inherited from cw1200 ancestor:
bes2600_bh_read_ctrl_reg, bes2600_get_skb, bes2600_put_skb,
bes2600_device_wakeup. Compiled out for years.
2. bh.c lines 405-873 + line 1659 (the outer #if 0 / #else /
#endif) — 468-line cw1200-ancestor bes2600_bh() function body,
preserved verbatim alongside the active impl. Same function
name, same goto labels. Maintenance hazard removed.
3. bh.c done: label body — `__bes2600_irq_enable(1)` placeholder
(commented out) + `asm volatile ("nop")` filler. Both
no-ops on bes2600 silicon.
4. bh.c post-loop "Explicitly disable device interrupts" block
(sbus lock + __bes2600_irq_enable(0) + sbus unlock) — the
stub call wrapped in lock/unlock ceremony. Dead.
5. hwio.c __bes2600_irq_enable() function definition —
`int __bes2600_irq_enable(int enable) { return 0; }`. Stub.
Removed entirely.
6. sbus.h __bes2600_irq_enable() forward declaration.
Replaced:
7. bh.c bes2600_bh outer-loop BUG_ON(hw_bufs_used > numInpChBufs)
-> WARN_ON_ONCE. The BUG_ON ran every bh-loop iteration;
tripping it on a bookkeeping bug locks the kernel up during
normal operation — the wrong response to a (recoverable)
accounting drift. WARN_ON_ONCE surfaces the issue without
taking the system down.
Why __bes2600_irq_enable was a stub on bes2600:
cw1200 has the same-named function (drivers/net/wireless/st/cw1200/
hwio.c:267) that does real work — reads ST90TDS_CONFIG_REG_ID and
toggles the ST90TDS_CONF_IRQ_RDY_ENABLE bit. bes2600 inherited
the function name + signature when forked, but the bes2600 chip's
IRQ enable is managed by sdio_claim_irq + chip-side firmware, not
by a driver-side enable register. Bestechnic kept the function as
a no-op stub (return 0). Patch H removes the dead infrastructure.
Diff scope:
- bes2600/bh.c -578/+27 (mostly deletions)
- bes2600/hwio.c -7/+7 (stub function -> comment block)
- bes2600/sbus.h -2/+1 (declaration -> comment)
- net: -578/+28 across 3 files
Build verification deferred — ohm offline. Pure-deletion change,
no semantic risk; the deleted code was either #if 0-gated
(never compiled) or stub-implementations (always returned 0).
---
bes2600/bh.c | 578 ++-----------------------------------------------
bes2600/hwio.c | 11 +-
bes2600/sbus.h | 3 +-
3 files changed, 28 insertions(+), 564 deletions(-)
diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c
index 61f6991..67dfad4 100644
--- a/drivers/staging/bes2600/bh.c
+++ b/drivers/staging/bes2600/bh.c
@@ -317,83 +317,6 @@ int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count)
}
#endif
-#if 0
-static struct sk_buff *bes2600_get_skb(struct bes2600_common *hw_priv, size_t len)
-{
- struct sk_buff *skb;
- size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE;
-
- if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) {
- skb = dev_alloc_skb(alloc_len
- + WSM_TX_EXTRA_HEADROOM
- + 8 /* TKIP IV */
- + 12 /* TKIP ICV + MIC */
- - 2 /* Piggyback */);
- /* In AP mode RXed SKB can be looped back as a broadcast.
- * Here we reserve enough space for headers. */
- skb_reserve(skb, WSM_TX_EXTRA_HEADROOM
- + 8 /* TKIP IV */
- - WSM_RX_EXTRA_HEADROOM);
- } else {
- skb = hw_priv->skb_cache;
- hw_priv->skb_cache = NULL;
- }
- return skb;
-}
-
-static void bes2600_put_skb(struct bes2600_common *hw_priv, struct sk_buff *skb)
-{
- if (hw_priv->skb_cache)
- dev_kfree_skb(skb);
- else
- hw_priv->skb_cache = skb;
-}
-
-static int bes2600_bh_read_ctrl_reg(struct bes2600_common *hw_priv,
- u16 *ctrl_reg)
-{
- int ret;
-
- ret = bes2600_reg_read_16(hw_priv,
- ST90TDS_CONTROL_REG_ID, ctrl_reg);
- if (ret) {
- ret = bes2600_reg_read_16(hw_priv,
- ST90TDS_CONTROL_REG_ID, ctrl_reg);
- if (ret)
- bes_err("[BH] Failed to read control register.\n");
- }
-
- return ret;
-}
-
-static int bes2600_device_wakeup(struct bes2600_common *hw_priv)
-{
- u16 ctrl_reg;
- int ret;
-
- bes_devel("[BH] Device wakeup.\n");
-
- /* To force the device to be always-on, the host sets WLAN_UP to 1 */
- ret = bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID,
- ST90TDS_CONT_WUP_BIT);
- if (WARN_ON(ret))
- return ret;
-
- ret = bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
- if (WARN_ON(ret))
- return ret;
-
- /* If the device returns WLAN_RDY as 1, the device is active and will
- * remain active. */
- if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
- bes_devel("[BH] Device awake.\n");
- return 1;
- }
-
- return 0;
-}
-
-#endif
/* Must be called from BH thraed. */
void bes2600_enable_powersave(struct bes2600_vif *priv,
@@ -403,475 +326,6 @@ void bes2600_enable_powersave(struct bes2600_vif *priv,
priv->powersave_enabled = enable;
}
-#if 0
-#define INTERRUPT_WORKAROUND
-static int bes2600_bh(void *arg)
-{
- struct bes2600_common *hw_priv = arg;
- struct bes2600_vif *priv = NULL;
- struct sk_buff *skb_rx = NULL;
- size_t read_len = 0;
- int rx, tx, term, suspend;
- struct wsm_hdr *wsm;
- size_t wsm_len;
- int wsm_id;
- u8 wsm_seq;
- int rx_resync = 1;
- u16 ctrl_reg = 0;
- int tx_allowed;
- int pending_tx = 0;
- int tx_burst;
- int rx_burst = 0;
- long status;
-#if defined(CONFIG_BES2600_WSM_DUMPS)
- size_t wsm_dump_max = -1;
-#endif
- u32 dummy;
- bool powersave_enabled;
- int i;
- int vif_selected;
-
- for (;;) {
- powersave_enabled = 1;
- spin_lock(&hw_priv->vif_list_lock);
- bes2600_for_each_vif(hw_priv, priv, i) {
-#ifdef P2P_MULTIVIF
- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
-#else
- if (!priv)
-#endif
- continue;
- powersave_enabled &= !!priv->powersave_enabled;
- }
- spin_unlock(&hw_priv->vif_list_lock);
- if (!hw_priv->hw_bufs_used
- && powersave_enabled
- && !hw_priv->device_can_sleep
- && !atomic_read(&hw_priv->recent_scan)) {
- status = HZ/8;
- bes_devel("[BH] No Device wakedown.\n");
-#ifndef FPGA_SETUP
- WARN_ON(bes2600_reg_write_16(hw_priv,
- ST90TDS_CONTROL_REG_ID, 0));
- hw_priv->device_can_sleep = true;
-#endif
- } else if (hw_priv->hw_bufs_used)
- /* Interrupt loss detection */
- status = HZ/8;
- else
- status = HZ/8;
-
- /* Dummy Read for SDIO retry mechanism*/
- if (((atomic_read(&hw_priv->bh_rx) == 0) &&
- (atomic_read(&hw_priv->bh_tx) == 0)))
- bes2600_reg_read(hw_priv, ST90TDS_CONFIG_REG_ID,
- &dummy, sizeof(dummy));
-#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
- wsm_dump_max = hw_priv->wsm_dump_max_size;
-#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
-
-#ifdef INTERRUPT_WORKAROUND
- /* If a packet has already been txed to the device then read the
- control register for a probable interrupt miss before going
- further to wait for interrupt; if the read length is non-zero
- then it means there is some data to be received */
- if (hw_priv->hw_bufs_used) {
- bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
- if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
- {
- rx = 1;
- goto test;
- }
- }
-#endif
-
- status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({
- rx = atomic_xchg(&hw_priv->bh_rx, 0);
- tx = atomic_xchg(&hw_priv->bh_tx, 0);
- term = atomic_xchg(&hw_priv->bh_term, 0);
- suspend = pending_tx ?
- 0 : atomic_read(&hw_priv->bh_suspend);
- (rx || tx || term || suspend || hw_priv->bh_error);
- }), status);
-
- if (status < 0 || term || hw_priv->bh_error)
- break;
-
-#ifdef INTERRUPT_WORKAROUND
- if (!status) {
- bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
- if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
- {
- bes_err("MISS 1\n");
- rx = 1;
- goto test;
- }
- }
-#endif
- if (!status && hw_priv->hw_bufs_used) {
- unsigned long timestamp = jiffies;
- long timeout;
- bool pending = false;
- int i;
-
- wiphy_warn(hw_priv->hw->wiphy, "Missed interrupt?\n");
- rx = 1;
-
- /* Get a timestamp of "oldest" frame */
- for (i = 0; i < 4; ++i)
- pending |= bes2600_queue_get_xmit_timestamp(
- &hw_priv->tx_queue[i],
- &timestamp, -1,
- hw_priv->pending_frame_id);
-
- /* Check if frame transmission is timed out.
- * Add an extra second with respect to possible
- * interrupt loss. */
- timeout = timestamp +
- WSM_CMD_LAST_CHANCE_TIMEOUT +
- 1 * HZ -
- jiffies;
-
- /* And terminate BH tread if the frame is "stuck" */
- if (pending && timeout < 0) {
- //wiphy_warn(priv->hw->wiphy,
- // "Timeout waiting for TX confirm.\n");
- bes_devel("bes2600_bh: Timeout waiting for TX confirm.\n");
- break;
- }
-
-#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
- BUG_ON(1);
-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
- } else if (!status) {
- if (!hw_priv->device_can_sleep
- && !atomic_read(&hw_priv->recent_scan)) {
- bes_devel("[BH] Device wakedown. Timeout.\n");
-#ifndef FPGA_SETUP
- WARN_ON(bes2600_reg_write_16(hw_priv,
- ST90TDS_CONTROL_REG_ID, 0));
- hw_priv->device_can_sleep = true;
-#endif
- }
- continue;
- } else if (suspend) {
- bes_devel("[BH] Device suspend.\n");
- powersave_enabled = 1;
- spin_lock(&hw_priv->vif_list_lock);
- bes2600_for_each_vif(hw_priv, priv, i) {
-#ifdef P2P_MULTIVIF
- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
-#else
- if (!priv)
-#endif
- continue;
- powersave_enabled &= !!priv->powersave_enabled;
- }
- spin_unlock(&hw_priv->vif_list_lock);
- if (powersave_enabled) {
- bes_devel("[BH] No Device wakedown. Suspend.\n");
-#ifndef FPGA_SETUP
- WARN_ON(bes2600_reg_write_16(hw_priv,
- ST90TDS_CONTROL_REG_ID, 0));
- hw_priv->device_can_sleep = true;
-#endif
- }
-
- atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED);
- wake_up(&hw_priv->bh_evt_wq);
- status = wait_event_interruptible(hw_priv->bh_wq,
- BES2600_BH_RESUME == atomic_read(
- &hw_priv->bh_suspend));
- if (status < 0) {
- wiphy_err(hw_priv->hw->wiphy,
- "%s: Failed to wait for resume: %ld.\n",
- __func__, status);
- break;
- }
- bes_devel("[BH] Device resume.\n");
- atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
- wake_up(&hw_priv->bh_evt_wq);
- atomic_inc(&hw_priv->bh_rx);
- continue;
- }
-
-test:
- tx += pending_tx;
- pending_tx = 0;
-
- if (rx) {
- size_t alloc_len;
- u8 *data;
-
-#ifdef INTERRUPT_WORKAROUND
- if(!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK))
-#endif
- if (WARN_ON(bes2600_bh_read_ctrl_reg(
- hw_priv, &ctrl_reg)))
- break;
-rx:
- read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
- if (!read_len) {
- rx_burst = 0;
- goto tx;
- }
-
- if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
- (read_len > EFFECTIVE_BUF_SIZE))) {
- bes_devel("Invalid read len: %d", read_len);
- break;
- }
-
- /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
- * to the NEXT Message length + 2 Bytes for SKB */
- read_len = read_len + 2;
-
-#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
- alloc_len = hw_priv->sbus_ops->align_size(
- hw_priv->sbus_priv, read_len);
-#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
- /* Platform's SDIO workaround */
- alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1);
- if (read_len & (SDIO_BLOCK_SIZE - 1))
- alloc_len += SDIO_BLOCK_SIZE;
-#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
-
- /* Check if not exceeding BES2600 capabilities */
- if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE))
- bes_devel("Read aligned len: %d\n", alloc_len);
-
- skb_rx = bes2600_get_skb(hw_priv, alloc_len);
- if (WARN_ON(!skb_rx))
- break;
-
- skb_trim(skb_rx, 0);
- skb_put(skb_rx, read_len);
- data = skb_rx->data;
- if (WARN_ON(!data))
- break;
-
- if (WARN_ON(bes2600_data_read(hw_priv, data, alloc_len)))
- break;
-
- /* Piggyback */
- ctrl_reg = __le16_to_cpu(
- ((__le16 *)data)[alloc_len / 2 - 1]);
-
- wsm = (struct wsm_hdr *)data;
- wsm_len = __le32_to_cpu(wsm->len);
- if (WARN_ON(wsm_len > read_len))
- break;
-
-#if defined(CONFIG_BES2600_WSM_DUMPS)
- if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
- u16 msgid, ifid;
- u16 *p = (u16 *)data;
- msgid = (*(p + 1)) & 0xC3F;
- ifid = (*(p + 1)) >> 6;
- ifid &= 0xF;
- bes_devel("[DUMP] <<< msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p);
- print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, data, min(wsm_len, wsm_dump_max));
- }
-#endif /* CONFIG_BES2600_WSM_DUMPS */
-
- wsm_id = __le32_to_cpu(wsm->id) & 0xFFF;
- wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7;
-
- skb_trim(skb_rx, wsm_len);
-
- if (unlikely(wsm_id == 0x0800)) {
- wsm_handle_exception(hw_priv,
- &data[sizeof(*wsm)],
- wsm_len - sizeof(*wsm));
- break;
- } else if (unlikely(!rx_resync)) {
- if (WARN_ON(wsm_seq != hw_priv->wsm_rx_seq)) {
-#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
- BUG_ON(1);
-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
- break;
- }
- }
- hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7;
- rx_resync = 0;
-
- if (wsm_id & 0x0400) {
- int rc = wsm_release_tx_buffer(hw_priv, 1);
- if (WARN_ON(rc < 0))
- break;
- else if (rc > 0)
- tx = 1;
- }
-
- /* bes2600_wsm_rx takes care on SKB livetime */
- if (WARN_ON(wsm_handle_rx(hw_priv, wsm_id, wsm,
- &skb_rx)))
- break;
-
- if (skb_rx) {
- bes2600_put_skb(hw_priv, skb_rx);
- skb_rx = NULL;
- }
-
- read_len = 0;
-
- if (rx_burst) {
- bes2600_debug_rx_burst(hw_priv);
- --rx_burst;
- goto rx;
- }
- }
-
-tx:
- BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
- tx_burst = hw_priv->wsm_caps.numInpChBufs -
- hw_priv->hw_bufs_used;
- tx_allowed = tx_burst > 0;
- if (tx && tx_allowed) {
- size_t tx_len;
- u8 *data;
- int ret;
-
- if (hw_priv->device_can_sleep) {
- ret = bes2600_device_wakeup(hw_priv);
- if (WARN_ON(ret < 0))
- break;
- else if (ret)
- hw_priv->device_can_sleep = false;
- else {
- /* Wait for "awake" interrupt */
- pending_tx = tx;
- continue;
- }
- }
-
- wsm_alloc_tx_buffer(hw_priv);
- ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst,
- &vif_selected);
- if (ret <= 0) {
- wsm_release_tx_buffer(hw_priv, 1);
- if (WARN_ON(ret < 0))
- break;
- } else {
- wsm = (struct wsm_hdr *)data;
- BUG_ON(tx_len < sizeof(*wsm));
- BUG_ON(__le32_to_cpu(wsm->len) != tx_len);
-
-#if 0 /* count is not implemented */
- if (ret > 1)
- atomic_inc(&hw_priv->bh_tx);
-#else
- atomic_inc(&hw_priv->bh_tx);
-#endif
-
-#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
- if (tx_len <= 8)
- tx_len = 16;
- tx_len = hw_priv->sbus_ops->align_size(
- hw_priv->sbus_priv, tx_len);
-#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
- /* HACK!!! Platform limitation.
- * It is also supported by upper layer:
- * there is always enough space at the
- * end of the buffer. */
- if (tx_len & (SDIO_BLOCK_SIZE - 1)) {
- tx_len &= ~(SDIO_BLOCK_SIZE - 1);
- tx_len += SDIO_BLOCK_SIZE;
- }
-#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
-
- /* Check if not exceeding BES2600
- capabilities */
- if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
- bes_devel("Write aligned len: %d\n", tx_len);
-
- wsm->id &= __cpu_to_le32(
- ~WSM_TX_SEQ(WSM_TX_SEQ_MAX));
- wsm->id |= cpu_to_le32(WSM_TX_SEQ(
- hw_priv->wsm_tx_seq));
-
- if (WARN_ON(bes2600_data_write(hw_priv,
- data, tx_len))) {
- wsm_release_tx_buffer(hw_priv, 1);
- break;
- }
-
- if (vif_selected != -1) {
- hw_priv->hw_bufs_used_vif[
- vif_selected]++;
- }
-
-#if defined(CONFIG_BES2600_WSM_DUMPS)
- if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
- u16 msgid, ifid;
- u16 *p = (u16 *)data;
- msgid = (*(p + 1)) & 0x3F;
- ifid = (*(p + 1)) >> 6;
- ifid &= 0xF;
- if (msgid == 0x0006)
- bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d MIB 0x%.4X\n", msgid, ifid, *p, *(p + 2));
- else
- bes_devel("[DUMP] >>> msgid 0x%.4X ifid %d len %d\n", msgid, ifid, *p);
- print_hex_dump(KERN_DEBUG, "--> ", DUMP_PREFIX_NONE, data, min(__le32_to_cpu(wsm->len), wsm_dump_max));
- }
-#endif /* CONFIG_BES2600_WSM_DUMPS */
-
- wsm_txed(hw_priv, data);
- hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1)
- & WSM_TX_SEQ_MAX;
-
- if (tx_burst > 1) {
- bes2600_debug_tx_burst(hw_priv);
- ++rx_burst;
- goto tx;
- }
- }
- }
-
- if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
- goto rx;
- }
-
- if (skb_rx) {
- bes2600_put_skb(hw_priv, skb_rx);
- skb_rx = NULL;
- }
-
-
- if (!term) {
- bes_devel("[BH] Fatal error, exitting.\n");
-#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
- BUG_ON(1);
-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
- hw_priv->bh_error = 1;
-#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
- spin_lock(&hw_priv->vif_list_lock);
- bes2600_for_each_vif(hw_priv, priv, i) {
- if (!priv)
- continue;
- ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
- }
- spin_unlock(&hw_priv->vif_list_lock);
- bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
-#endif
- /* TODO: schedule_work(recovery) */
-#ifndef HAS_PUT_TASK_STRUCT
- /* The only reason of having this stupid code here is
- * that __put_task_struct is not exported by kernel. */
- for (;;) {
- int status = wait_event_interruptible(hw_priv->bh_wq, ({
- term = atomic_xchg(&hw_priv->bh_term, 0);
- (term);
- }));
-
- if (status || term)
- break;
- }
-#endif
- }
- return 0;
-}
-#else
extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg);
@@ -1599,7 +1053,15 @@ static int bes2600_bh(void *arg)
tx = 0;
- BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
+ /*
+ * Patch H: BUG_ON -> WARN_ON_ONCE in the steady-state
+ * hot path. The original BUG_ON ran every bh-loop
+ * iteration; tripping it on a bookkeeping bug locks
+ * the kernel up during normal operation, which is
+ * the wrong response. WARN_ON_ONCE surfaces the
+ * issue without taking the system down.
+ */
+ WARN_ON_ONCE(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used;
tx_allowed = tx_burst > 0;
@@ -1643,18 +1105,19 @@ static int bes2600_bh(void *arg)
goto tx;
done:
- /* Re-enable device interrupts */
- //hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
- //__bes2600_irq_enable(1);
- //hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
- asm volatile ("nop");
+ /*
+ * Patch H: dropped the dead `__bes2600_irq_enable(1)` /
+ * `asm volatile("nop")` placeholder that used to sit here.
+ * `__bes2600_irq_enable()` is a stub that returns 0 on
+ * bes2600 silicon — the IRQ is managed by sdio_claim_irq
+ * and chip-side firmware, not by a driver-side enable bit.
+ * (cw1200 inherited the function from a different chip
+ * shape; bes2600 kept the stub but the call sites are
+ * meaningless.)
+ */
+ ;
}
- /* Explicitly disable device interrupts */
- hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
- __bes2600_irq_enable(0);
- hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
-
if (!term) {
bes_err("[BH] Fatal error, exiting.\n");
sdio_work_debug(hw_priv->sbus_priv);
@@ -1663,4 +1126,3 @@ static int bes2600_bh(void *arg)
}
return 0;
}
-#endif
diff --git a/drivers/staging/bes2600/hwio.c b/drivers/staging/bes2600/hwio.c
index 0934a13..1a63e4f 100644
--- a/drivers/staging/bes2600/hwio.c
+++ b/drivers/staging/bes2600/hwio.c
@@ -324,7 +324,10 @@ out:
}
#endif
-int __bes2600_irq_enable(int enable)
-{
- return 0;
-}
+/*
+ * Patch H: __bes2600_irq_enable stub removed. It was a no-op
+ * (always returned 0) inherited from cw1200 where the analogous
+ * function manipulates the chip's IRQ-enable register. bes2600
+ * silicon manages SDIO IRQ via sdio_claim_irq and chip-side
+ * firmware — there is no driver-side enable register to write.
+ */
diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h
index 43c2dae..4193084 100644
--- a/drivers/staging/bes2600/sbus.h
+++ b/drivers/staging/bes2600/sbus.h
@@ -95,7 +95,6 @@ struct sbus_ops {
void bes2600_irq_handler(struct bes2600_common *priv);
-/* This MUST be wrapped with hwbus_ops->lock/unlock! */
-int __bes2600_irq_enable(int enable);
+/* Patch H: __bes2600_irq_enable removed (was a stub). */
#endif /* BES2600_SBUS_H */
--
2.54.0
@@ -0,0 +1,279 @@
From 06fab777454d36ec5178730d8423285c2457d3ba Mon Sep 17 00:00:00 2001
From: "Claude (noether)" <claude@reauktion.de>
Date: Thu, 7 May 2026 11:30:09 +0200
Subject: [PATCH 21/29] bes2600: bus_reset on connection-loss storm to dodge
assoc-comeback blackhole
When mac80211 declares connection loss against this AP (typically driven
by inactivity-deauth or beacon-loss), the userspace reauth that follows
sometimes enters a long blackhole: the AP responds to auth with success
but defers assoc with the 802.11v "assoc comeback" timer; ohm retries
faster than the comeback grants permission; the AP eventually fires an
unprotected deauth-reason-6 ("Class 2 frame received from non-
authenticated station"), and recovery only completes via cross-SSID or
cross-channel fallback. Receipts: ~86 s blackhole observed in the
phase-7 rep on 2026-05-07 02:42, with three subsequent BSSIDs returning
assoc comeback timeouts before reason-9 (STA_REQ_ASSOC_WITHOUT_AUTH)
fired. Documented in marfrit/besser:notes/phase4-2026-05-07.md.
When N=3 driver-side connection_loss decisions fire within a 60 s window
on the same vif, skip the ieee80211_connection_loss() path and trigger
the c5.2-introduced bes2600_chrdev_do_bus_reset() instead. The bus
reset removes and re-probes the chip; userspace re-associates with a
fresh chip state, dodging the AP's comeback-timer rejection cycle.
Predicted Phase 7 delta vs current baseline:
- api_connection_loss rate: unchanged (we don't address the trigger)
- conditional probability of >5 s blackhole given event: <= 30 %
- worst-case recovery: 86 s -> < 10 s
Contract pin: bes2600_chrdev_do_bus_reset(sbus_ops, sbus_priv) at
bes2600/bes_chardev.c:455, introduced by c5.2. The function is async-
returning: sbus_ops->bus_reset() schedules an SDIO rescan; the helper
waits up to 3 s for the remove() callback to clear sbus_priv, then
returns. Per-vif state is gone after this point, so the recover work
lives on bes2600_common (hw_priv) and uses the global bes2600_cdev for
the bus_reset call rather than dereferencing per-vif state.
Threshold (3 / 60 s) is well above the steady-state per-vif
connection_loss rate observed in the patch-A phase-7 rep (0.86/h under
sustained load), so a true storm is required to trip it.
Files touched:
- bes2600/bes2600.h: 3 counter fields on struct bes2600_vif, 1
work_struct on struct bes2600_common, 3 prototypes
- bes2600/sta.c: 3 helpers + storm-account hook in
bes2600_connection_loss_work + storm-init in bes2600_vif_setup +
cancel_work_sync in the hw_priv shutdown path; #include bes_chardev.h
was already pulled in by an earlier c-stack patch
- bes2600/main.c: INIT_WORK alongside other hw_priv work_structs
- bes2600/debug.c: ConnectionLossStormRecoveries seq_printf in the
per-vif status seq_file output
The cw1200/cw1260 ancestor has no equivalent; this is a clean
addition. checkpatch.pl --no-tree --strict: clean (0/0/0).
Signed-off-by: Claude (noether) <claude@reauktion.de>
---
bes2600/bes2600.h | 12 +++++++
bes2600/bes_chardev.c | 12 +++++++
bes2600/bes_chardev.h | 1 +
bes2600/debug.c | 2 ++
bes2600/main.c | 2 ++
bes2600/sta.c | 82 +++++++++++++++++++++++++++++++++++++++++--
6 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h
index 66482f7..ec41141 100644
--- a/drivers/staging/bes2600/bes2600.h
+++ b/drivers/staging/bes2600/bes2600.h
@@ -511,6 +511,9 @@ struct bes2600_common {
struct list_head coex_event_list;
spinlock_t coex_event_lock;
+ /* Connection-loss-storm fast-recover (Trigger A). See sta.c. */
+ struct work_struct connection_loss_storm_recover_work;
+
/* member for low power */
struct bes2600_pwr_t bes_power;
@@ -627,6 +630,10 @@ struct bes2600_vif {
/* CQM Implementation */
struct delayed_work bss_loss_work;
struct delayed_work connection_loss_work;
+ /* Connection-loss-storm fast-recover (Trigger A). See sta.c. */
+ unsigned long connection_loss_storm_window_start;
+ unsigned int connection_loss_storm_count;
+ unsigned int connection_loss_storm_recoveries;
struct work_struct tx_failure_work;
int delayed_link_loss;
spinlock_t bss_loss_lock;
@@ -865,4 +872,9 @@ void bes2600_btusb_uninit(struct usb_interface *interface);
void bes2600_decrypt_storm_init(struct bes2600_vif *priv);
void bes2600_decrypt_storm_account(struct bes2600_vif *priv);
+/* Connection-loss-storm fast-recover helpers — see sta.c. */
+void bes2600_connection_loss_storm_init(struct bes2600_vif *priv);
+bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv);
+void bes2600_connection_loss_storm_recover(struct work_struct *work);
+
#endif /* BES2600_H */
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
index d1375bc..224c62d 100644
--- a/drivers/staging/bes2600/bes_chardev.c
+++ b/drivers/staging/bes2600/bes_chardev.c
@@ -484,6 +484,18 @@ int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_pri
return 0;
}
+/*
+ * Trigger bes2600_chrdev_do_bus_reset() against the file-global
+ * bes2600_cdev. Used by host-side recovery paths outside this
+ * compilation unit (e.g. sta.c connection-loss-storm fast-recover) so
+ * those callers do not need to reach the static bes2600_cdev directly.
+ */
+int bes2600_chrdev_trigger_bus_reset(void)
+{
+ return bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
+ bes2600_cdev.sbus_priv);
+}
+
bool bes2600_chrdev_is_wifi_opened(void)
{
bool wifi_opened = false;
diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h
index ca8419e..2a7cad7 100644
--- a/drivers/staging/bes2600/bes_chardev.h
+++ b/drivers/staging/bes2600/bes_chardev.h
@@ -61,6 +61,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
int bes2600_chrdev_check_system_close(void);
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
+int bes2600_chrdev_trigger_bus_reset(void);
void bes2600_chrdev_wakeup_bt(void);
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c
index ca223dd..0d68392 100644
--- a/drivers/staging/bes2600/debug.c
+++ b/drivers/staging/bes2600/debug.c
@@ -544,6 +544,8 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v)
bes2600_debug_join_status[priv->join_status]);
seq_printf(seq, "DecryptStormRecoveries: %u\n",
priv->decrypt_storm_recoveries);
+ seq_printf(seq, "ConnectionLossStormRecoveries: %u\n",
+ priv->connection_loss_storm_recoveries);
if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs)
diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c
index 7cbb3a9..ff82f4d 100644
--- a/drivers/staging/bes2600/main.c
+++ b/drivers/staging/bes2600/main.c
@@ -489,6 +489,8 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
spin_lock_init(&hw_priv->rtsvalue_lock);
INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work);
INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work);
+ INIT_WORK(&hw_priv->connection_loss_storm_recover_work,
+ bes2600_connection_loss_storm_recover);
spin_lock_init(&hw_priv->event_queue_lock);
INIT_LIST_HEAD(&hw_priv->event_queue);
INIT_WORK(&hw_priv->event_handler, bes2600_event_handler);
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
index 139bdae..5868757 100644
--- a/drivers/staging/bes2600/sta.c
+++ b/drivers/staging/bes2600/sta.c
@@ -268,6 +268,7 @@ void bes2600_stop(struct ieee80211_hw *dev, bool suspend)
cancel_work_sync(&hw_priv->coex_work);
coex_stop(hw_priv);
#endif
+ cancel_work_sync(&hw_priv->connection_loss_storm_recover_work);
bes2600_wifi_stop(hw_priv);
@@ -1675,6 +1676,70 @@ report:
spin_unlock(&priv->bss_loss_lock);
}
+/*
+ * Connection-loss-storm fast-recover (Trigger A).
+ *
+ * bes2600_connection_loss_work below is the driver's own decision-point
+ * to give up on a BSS (after bss-loss detection accumulates beyond
+ * tolerance) and tell mac80211 via ieee80211_connection_loss(). On the
+ * deployed pinetab2 stack a single ieee80211_connection_loss() event
+ * sometimes triggers a userspace reauth blackhole (assoc-comeback
+ * timeouts followed by AP unprotected-deauth-reason-6) that ends only
+ * via cross-channel/cross-SSID fallback and can take 80+ s. Receipts at
+ * https://git.reauktion.de/marfrit/besser, notes/phase4-2026-05-07.md.
+ *
+ * When N connection-loss decisions land within WINDOW on the same vif,
+ * skip the ieee80211_connection_loss() path and trigger a chip-level
+ * bus_reset (the c5.2-introduced bes2600_chrdev_do_bus_reset). The chip
+ * is removed and re-probed; userspace re-associates from a fresh state,
+ * dodging the assoc-comeback loop.
+ *
+ * Threshold (3 / 60 s) is chosen well above the steady-state per-vif
+ * connection-loss rate observed in the patch-A Phase-7 rep
+ * (0.86/h under sustained load), so a true storm is required.
+ *
+ * The recover work_struct lives on bes2600_common (hw_priv) so that
+ * scheduling it does not race with vif teardown after bus_reset frees
+ * the per-vif state.
+ */
+#define BES2600_CONNECTION_LOSS_STORM_THRESHOLD 3
+#define BES2600_CONNECTION_LOSS_STORM_WINDOW_MS 60000
+
+void bes2600_connection_loss_storm_recover(struct work_struct *work)
+{
+ bes_warn("[bes2600] connection-loss-storm fast-recover: bus_reset\n");
+ bes2600_chrdev_trigger_bus_reset();
+ /*
+ * After bes2600_chrdev_do_bus_reset() returns, the SDIO core has
+ * scheduled a remove + rescan; per-vif state may already be gone.
+ * Do not dereference any per-vif pointer here.
+ */
+}
+
+void bes2600_connection_loss_storm_init(struct bes2600_vif *priv)
+{
+ priv->connection_loss_storm_window_start = 0;
+ priv->connection_loss_storm_count = 0;
+ priv->connection_loss_storm_recoveries = 0;
+}
+
+bool bes2600_connection_loss_storm_account(struct bes2600_vif *priv)
+{
+ unsigned long now = jiffies;
+ unsigned long window =
+ msecs_to_jiffies(BES2600_CONNECTION_LOSS_STORM_WINDOW_MS);
+
+ if (priv->connection_loss_storm_window_start == 0 ||
+ time_after(now, priv->connection_loss_storm_window_start + window)) {
+ priv->connection_loss_storm_window_start = now;
+ priv->connection_loss_storm_count = 1;
+ return false;
+ }
+
+ return ++priv->connection_loss_storm_count >=
+ BES2600_CONNECTION_LOSS_STORM_THRESHOLD;
+}
+
void bes2600_connection_loss_work(struct work_struct *work)
{
struct bes2600_vif *priv =
@@ -1684,9 +1749,21 @@ void bes2600_connection_loss_work(struct work_struct *work)
bes_devel("[CQM] Reporting connection loss.\n");
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
- if(bes2600_suspend_status_get(hw_priv)) {
+
+ if (bes2600_connection_loss_storm_account(priv)) {
+ bes_warn("[bes2600] connection-loss storm: %u in %u s, scheduling bus reset\n",
+ priv->connection_loss_storm_count,
+ BES2600_CONNECTION_LOSS_STORM_WINDOW_MS / 1000);
+ priv->connection_loss_storm_count = 0;
+ priv->connection_loss_storm_recoveries++;
+ schedule_work(&hw_priv->connection_loss_storm_recover_work);
+ /* bus_reset will tear the chip down; skip the mac80211 path. */
+ return;
+ }
+
+ if (bes2600_suspend_status_get(hw_priv))
bes2600_pending_unjoin_set(hw_priv, priv->if_id);
- } else
+ else
ieee80211_connection_loss(priv->vif);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
// set disconnected in BSS_CHANGED_ASSOC
@@ -2641,6 +2718,7 @@ int bes2600_vif_setup(struct bes2600_vif *priv)
/* Setup per vif workitems and locks */
spin_lock_init(&priv->vif_lock);
bes2600_decrypt_storm_init(priv);
+ bes2600_connection_loss_storm_init(priv);
INIT_WORK(&priv->join_work, bes2600_join_work);
INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
--
2.54.0
@@ -0,0 +1,35 @@
# cumulative-c5x-danctnix — interim single-file cumulative
**Series role**: ohm manifest's bes2600 driver patchset, c5x stack as
shipped in `linux-pinetab2-danctnix-besser` pkgrel=3 on 2026-05-18.
## Why this is a single .patch and not split per-fix
The 12-ish per-series mirror PR (kernel-agent#17) of the boltzmann-side
`marfrit/besser` series produced patches with DKMS-style paths
(`bes2600/*` at root) rather than in-tree staging paths
(`drivers/staging/bes2600/*`), and at least one entry has corrupted
mixed-prefix headers (`a/drivers/staging/bes2600/foo.c b/bes2600/foo.c`).
Those series do NOT apply cleanly to the linux-pinetab2 baseline.
Audit performed 2026-05-18 during ohm migration:
- ka-promote ohm (using the per-series includes) produces a
172 644-byte cumulative touching 27 file paths, of which 11 are
bogus DKMS-style or mixed-prefix.
- The hand-curated `0001-bes2600-besser-cumulative-series.patch` from
the working `danctnix-besser-pkgbuild` (boltzmann) is 148 149 bytes
touching 48 distinct in-tree staging files — and is what pkgrel=3
actually builds with.
This single-file cumulative is staged here so the ohm migration can
ship through the kernel-agent flow today without first reconstructing
12 series-dirs. The proper per-series split is tracked separately —
see kernel-agent issue (TBD) for the rewrite.
## Provenance
- Source file: `boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/0001-bes2600-besser-cumulative-series.patch`
- Reflects c5x driver state in `marfrit/bes2600-dkms-mobian` branch as
of 2026-05-08, applied against `drivers/staging/bes2600/` in-tree.
- Series legend (A, B, C v3, F, G, D, E, C2, c5.x, c6.x, c7, H — NOT
alphabetical) per the danctnix-besser-pkgbuild changelog comments.
@@ -0,0 +1,92 @@
From 737f28e29c4b8253939e24b1d6b97d5605bb7ac4 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Thu, 7 May 2026 21:19:49 +0200
Subject: [PATCH 22/29] bes2600: replace a set of atomic_add()
Backport of cw1200 mainline commit 07f995ca1951 ("cw1200: replace a set
of atomic_add()", 2020-11-10). atomic_inc() reads more naturally than
atomic_add(1, &x). Mechanical change, no functional impact.
7 sites: 6 in bh.c (bh_term, bh_rx x2, bh_tx x3) and 1 in itp.c
(awaiting_confirm). Two of the bh_rx and three of the bh_tx sites are
inside the cw1200-ancestor #if 0 block; replaced anyway to keep the
file consistent with cw1200 mainline source style.
Cherry-picked from upstream Linux:
07f995ca1951 cw1200: replace a set of atomic_add()
Author: Yejune Deng <yejune.deng@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1604991491-27908-1-git-send-email-yejune.deng@gmail.com
---
bes2600/bh.c | 12 ++++++------
bes2600/itp.c | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c
index 175ab5e..fab3bf0 100644
--- a/drivers/staging/bes2600/bh.c
+++ b/drivers/staging/bes2600/bh.c
@@ -102,7 +102,7 @@ void bes2600_unregister_bh(struct bes2600_common *hw_priv)
coex_deinit_mode(hw_priv);
#endif
- atomic_add(1, &hw_priv->bh_term);
+ atomic_inc(&hw_priv->bh_term);
wake_up(&hw_priv->bh_wq);
flush_workqueue(hw_priv->bh_workqueue);
@@ -591,7 +591,7 @@ static int bes2600_bh(void *arg)
bes_devel("[BH] Device resume.\n");
atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
wake_up(&hw_priv->bh_evt_wq);
- atomic_add(1, &hw_priv->bh_rx);
+ atomic_inc(&hw_priv->bh_rx);
continue;
}
@@ -759,9 +759,9 @@ tx:
#if 0 /* count is not implemented */
if (ret > 1)
- atomic_add(1, &hw_priv->bh_tx);
+ atomic_inc(&hw_priv->bh_tx);
#else
- atomic_add(1, &hw_priv->bh_tx);
+ atomic_inc(&hw_priv->bh_tx);
#endif
#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
@@ -1135,7 +1135,7 @@ static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv,
tx_len += 4;
#endif
- atomic_add(1, &hw_priv->bh_tx);
+ atomic_inc(&hw_priv->bh_tx);
tx_len = hw_priv->sbus_ops->align_size(
hw_priv->sbus_priv, tx_len);
@@ -1442,7 +1442,7 @@ static int bes2600_bh(void *arg)
bes_devel("[BH] Device resume.\n");
atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
wake_up(&hw_priv->bh_evt_wq);
- atomic_add(1, &hw_priv->bh_rx);
+ atomic_inc(&hw_priv->bh_rx);
goto done;
}
diff --git a/drivers/staging/bes2600/itp.c b/drivers/staging/bes2600/itp.c
index e5c2958..c50b29c 100644
--- a/drivers/staging/bes2600/itp.c
+++ b/drivers/staging/bes2600/itp.c
@@ -570,7 +570,7 @@ int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
*burst = 2;
atomic_set(&priv->bh_tx, 1);
ktime_get_ts(&itp->last_sent);
- atomic_add(1, &itp->awaiting_confirm);
+ atomic_inc(&itp->awaiting_confirm);
spin_unlock_bh(&itp->tx_lock);
return 1;
--
2.54.0
@@ -0,0 +1,58 @@
From 2fb72f06e54172479662257ae4ef9a61d6ba7092 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Thu, 7 May 2026 21:20:46 +0200
Subject: [PATCH 23/29] bes2600: fix missing destroy_workqueue() on error in
init_common
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Two error paths between create_singlethread_workqueue() (~main.c:489)
and the success-path destroy_workqueue() in unregister_common (~609)
return without cleaning up the workqueue, leaking it on probe failure:
1. bes2600_queue_stats_init() failure
2. bes2600_queue_init() failure (any of the 4 TID queues)
Both call ieee80211_free_hw(hw); return NULL — without first
destroy_workqueue(hw_priv->workqueue). Add it.
Backport of cw1200 mainline commit 7ec8a926188e ("cw1200: fix missing
destroy_workqueue() on error in cw1200_init_common", 2020-11-19),
which fixed the identical bug in the same code shape we inherited.
Reported on cw1200 by Hulk Robot.
Cherry-picked from upstream Linux:
7ec8a926188e cw1200: fix missing destroy_workqueue() on error
Author: Qinglang Miao <miaoqinglang@huawei.com>
Reported-by: Hulk Robot <hulkci@huawei.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20201119070842.1011-1-miaoqinglang@huawei.com
Fixes: a910e4a94f69 ("cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets")
---
bes2600/main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c
index ff82f4d..89b5e2d 100644
--- a/drivers/staging/bes2600/main.c
+++ b/drivers/staging/bes2600/main.c
@@ -502,6 +502,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
WLAN_LINK_ID_MAX,
bes2600_skb_dtor,
hw_priv))) {
+ destroy_workqueue(hw_priv->workqueue);
ieee80211_free_hw(hw);
return NULL;
}
@@ -513,6 +514,7 @@ static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
for (; i > 0; i--)
bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]);
bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
+ destroy_workqueue(hw_priv->workqueue);
ieee80211_free_hw(hw);
return NULL;
}
--
2.54.0
@@ -0,0 +1,144 @@
From d9e6361cf0c273f07aee94f24533a5f19e7ed4c0 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Thu, 7 May 2026 21:24:01 +0200
Subject: [PATCH 24/29] bes2600: fix concurrency UAF in bes2600_hw_scan and
sched_scan
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
bes2600_bss_info_changed() and bes2600_hw_scan() can run concurrently.
The probe-request SKB allocated by ieee80211_probereq_get() before
scan.lock + conf_lock are taken can be touched by a concurrent
bss_info_changed (via wsm_set_template_frame's path) while we hold no
lock. Reorder to acquire both locks BEFORE the SKB allocation.
Also reorder cleanup paths so dev_kfree_skb() runs BEFORE up() —
otherwise a small window exists where the SKB has been touched but the
lock has been released, allowing concurrent code to also touch it.
Three sites fixed:
- bes2600_hw_scan: lock-take + ENOMEM cleanup + wsm_set_template_frame
error cleanup + success-path SKB free + lock release order
- bes2600_sched_scan_start (#ifdef ROAM_OFFLOAD): same three sub-fixes
(compiled-out at default build, fixed for consistency)
- All success/error paths: dev_kfree_skb before up()
Backport of cw1200 mainline commit 86760e0dfe36 ("cw1200: Fix
concurrency use-after-free bugs in cw1200_hw_scan()", 2018-12-14),
which fixed the identical bug in the same code shape we inherited.
That commit was merged from upstream 4f68ef64cd7f.
Cherry-picked from upstream Linux:
86760e0dfe36 cw1200: Fix concurrency use-after-free bugs in cw1200_hw_scan()
Author: Jia-Ju Bai <baijiaju1990@gmail.com>
Link: https://lore.kernel.org/r/20181214035521.7575-1-baijiaju1990@gmail.com
---
bes2600/scan.c | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
index b944adc..3cd7b64 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -257,18 +257,21 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
+ /* will be unlocked in bes2600_scan_work() */
+ down(&hw_priv->scan.lock);
+ down(&hw_priv->conf_lock);
+
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
- if (!frame.skb)
+ if (!frame.skb) {
+ up(&hw_priv->conf_lock);
+ up(&hw_priv->scan.lock);
return -ENOMEM;
+ }
if (req->ie_len)
skb_put_data(frame.skb, req->ie, req->ie_len);
- /* will be unlocked in bes2600_scan_work() */
- down(&hw_priv->scan.lock);
- down(&hw_priv->conf_lock);
-
if (frame.skb) {
int ret;
//if (priv->if_id == 0)
@@ -286,9 +289,9 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
}
#endif
if (ret) {
+ dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
- dev_kfree_skb(frame.skb);
return ret;
}
}
@@ -318,10 +321,10 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
++hw_priv->scan.n_ssids;
}
- up(&hw_priv->conf_lock);
-
if (frame.skb)
dev_kfree_skb(frame.skb);
+
+ up(&hw_priv->conf_lock);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING);
#endif
@@ -362,14 +365,18 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
if (req->n_ssids > hw->wiphy->max_scan_ssids)
return -EINVAL;
+ /* will be unlocked in bes2600_scan_work() */
+ down(&hw_priv->scan.lock);
+ down(&hw_priv->conf_lock);
+
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
- if (!frame.skb)
+ if (!frame.skb) {
+ up(&hw_priv->conf_lock);
+ up(&hw_priv->scan.lock);
return -ENOMEM;
+ }
- /* will be unlocked in bes2600_scan_work() */
- down(&hw_priv->scan.lock);
- down(&hw_priv->conf_lock);
if (frame.skb) {
int ret;
if (priv->if_id == 0)
@@ -380,9 +387,9 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
ret = wsm_set_probe_responder(priv, true);
}
if (ret) {
+ dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
- dev_kfree_skb(frame.skb);
return ret;
}
}
@@ -414,10 +421,10 @@ int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
}
}
- up(&hw_priv->conf_lock);
-
if (frame.skb)
dev_kfree_skb(frame.skb);
+
+ up(&hw_priv->conf_lock);
queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n");
return 0;
--
2.54.0
@@ -0,0 +1,221 @@
From 91640bd96d36dd5769b1325e1b2130a95277e0e7 Mon Sep 17 00:00:00 2001
From: "Claude (noether)" <claude@reauktion.de>
Date: Wed, 6 May 2026 19:50:52 +0200
Subject: [PATCH 20/29] bes2600: pre-empt AP-deauth-6 with mac80211 reassoc on
decrypt-fail storm
When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a burst
of received frames (typically because the host's PTK or GTK has fallen
out of sync with the AP), the AP eventually concludes that the STA is
not authenticated and emits an unprotected deauth-reason-6 ("Class 2
frame received from non-authenticated station"). On the deployed
pinetab2 + bes2600 stack this AP-initiated deauth has been observed to
leave the link blackholed for up to 109 s before userspace finds a
different SSID/channel to recover on. (Receipts at
https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.)
Add a sliding-window counter on each bes2600_vif: when 5 decrypt
failures fire within 5 s, schedule a worker that calls
ieee80211_connection_loss(vif). mac80211 then performs immediate
disassociation; userspace (NetworkManager / wpa_supplicant) reconnects
with fresh keys before the AP gets a chance to fire its unprotected
deauth.
Predicted Phase 7 delta vs the unpatched baseline:
- decrypt-burst rate: unchanged (this does not address root cause)
- AP-deauth-6 rate: <= 0.2 of baseline
- conditional probability of >5s blackhole given a burst:
100% -> <= 10%
- worst-case recovery time: 109s -> <5s
Contract pin: ieee80211_connection_loss() per
include/net/mac80211.h: "may also be called if the connection needs to
be terminated for some other reason... will cause immediate change to
disassociated state, without connection recovery attempts." Userspace
recovery is the existing NM/wpa_supplicant path. The worker context
satisfies the implicit process-context expectation.
Files touched:
- bes2600/bes2600.h: 4 new fields on struct bes2600_vif + 2 prototypes
- bes2600/txrx.c: new helpers + the call site at the existing
WSM_STATUS_DECRYPTFAILURE log point (the unconditional "goto drop"
branch in bes2600_rx_cb)
- bes2600/sta.c: bes2600_decrypt_storm_init() in bes2600_vif_setup;
cancel_work_sync() in bes2600_remove_interface, alongside the
existing per-vif cancel_*_work_sync block. Safe under the kernel
cancel_work_sync contract: the work_struct is INIT_WORK'd in setup,
so the call is valid; it blocks until any in-flight handler returns,
ensuring no use-after-free of priv when mac80211 frees the vif; and
it is idempotent (subsequent calls just return false).
- bes2600/debug.c: DecryptStormRecoveries seq_printf in the per-vif
status seq_file output
Threshold (5/5s) is set well above the steady-state per-vif decrypt-
fail rate observed in measurement (~1/min even under sustained 1 MB/s
load), so a true storm is required to trip it. The cw1200/cw1260
ancestor has no equivalent storm-recovery; this is a clean addition.
checkpatch.pl --no-tree --strict: clean (0/0/0).
Signed-off-by: Claude (noether) <claude@reauktion.de>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
bes2600/bes2600.h | 9 ++++++
bes2600/debug.c | 2 ++
bes2600/sta.c | 2 ++
bes2600/txrx.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 87 insertions(+)
diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h
index 0e60960..66482f7 100644
--- a/drivers/staging/bes2600/bes2600.h
+++ b/drivers/staging/bes2600/bes2600.h
@@ -596,6 +596,11 @@ struct bes2600_vif {
unsigned long rx_timestamp;
u32 cipherType;
+ /* Decrypt-storm fast-recover (Trigger B). See txrx.c. */
+ unsigned long decrypt_storm_window_start;
+ unsigned int decrypt_storm_count;
+ unsigned int decrypt_storm_recoveries;
+ struct work_struct decrypt_storm_recover_work;
/* AP powersave */
u32 link_id_map;
@@ -856,4 +861,8 @@ int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv);
void bes2600_btusb_uninit(struct usb_interface *interface);
#endif
+/* Decrypt-storm fast-recover helpers — see txrx.c. */
+void bes2600_decrypt_storm_init(struct bes2600_vif *priv);
+void bes2600_decrypt_storm_account(struct bes2600_vif *priv);
+
#endif /* BES2600_H */
diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c
index 5228b22..ca223dd 100644
--- a/drivers/staging/bes2600/debug.c
+++ b/drivers/staging/bes2600/debug.c
@@ -542,6 +542,8 @@ static int bes2600_status_show_priv(struct seq_file *seq, void *v)
priv->listening ? " (listening)" : "");
seq_printf(seq, "Assoc: %s\n",
bes2600_debug_join_status[priv->join_status]);
+ seq_printf(seq, "DecryptStormRecoveries: %u\n",
+ priv->decrypt_storm_recoveries);
if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs)
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
index bc6d483..139bdae 100644
--- a/drivers/staging/bes2600/sta.c
+++ b/drivers/staging/bes2600/sta.c
@@ -464,6 +464,7 @@ void bes2600_remove_interface(struct ieee80211_hw *dev,
cancel_delayed_work_sync(&priv->join_timeout);
cancel_delayed_work_sync(&priv->set_cts_work);
cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
+ cancel_work_sync(&priv->decrypt_storm_recover_work);
timer_delete_sync(&priv->mcast_timeout);
/* TODO:COMBO: May be reset of these variables "delayed_link_loss and
@@ -2639,6 +2640,7 @@ int bes2600_vif_setup(struct bes2600_vif *priv)
/* Setup per vif workitems and locks */
spin_lock_init(&priv->vif_lock);
+ bes2600_decrypt_storm_init(priv);
INIT_WORK(&priv->join_work, bes2600_join_work);
INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c
index 017f0d8..f6a66d6 100644
--- a/drivers/staging/bes2600/txrx.c
+++ b/drivers/staging/bes2600/txrx.c
@@ -26,6 +26,78 @@
#define BES2600_INVALID_RATE_ID (0xFF)
+/*
+ * Decrypt-storm fast-recover (Trigger B).
+ *
+ * When the BES2600 firmware reports WSM_STATUS_DECRYPTFAILURE for a
+ * burst of received frames (typically because the host's PTK or GTK
+ * has fallen out of sync with the AP), the AP eventually concludes that
+ * the STA is not authenticated and emits an unprotected deauth-reason-6
+ * ("Class 2 frame received from non-authenticated station"). On the
+ * deployed pinetab2 + bes2600 stack this AP-initiated deauth has been
+ * observed to leave the link blackholed for up to 109 s before
+ * userspace finds a different SSID/channel to recover on. (Receipts at
+ * https://git.reauktion.de/marfrit/besser, notes/phase5-2026-05-06.md.)
+ *
+ * Recovery here pre-empts the AP: when we see THRESHOLD decrypt
+ * failures within WINDOW, we ask mac80211 for a clean reassoc via
+ * ieee80211_connection_loss(), which causes immediate disassociation
+ * and lets userspace auto-reconnect with fresh keys.
+ *
+ * mac80211 contract: ieee80211_connection_loss() may be called
+ * regardless of IEEE80211_HW_CONNECTION_MONITOR; it causes immediate
+ * disassociation without driver-side recovery attempts. See
+ * include/net/mac80211.h for the canonical doc-comment.
+ *
+ * The threshold is set well above the steady-state per-vif
+ * decrypt-fail rate observed in measurement (~1/min even under
+ * sustained 1 MB/s load), so a true storm is required to trip it.
+ */
+#define BES2600_DECRYPT_STORM_THRESHOLD 5
+#define BES2600_DECRYPT_STORM_WINDOW_MS 5000
+
+static void bes2600_decrypt_storm_recover_work(struct work_struct *work)
+{
+ struct bes2600_vif *priv = container_of(work, struct bes2600_vif,
+ decrypt_storm_recover_work);
+
+ if (!priv->vif)
+ return;
+
+ bes_warn("[bes2600] decrypt-storm fast-recover: forcing reassoc\n");
+ ieee80211_connection_loss(priv->vif);
+ priv->decrypt_storm_recoveries++;
+}
+
+void bes2600_decrypt_storm_init(struct bes2600_vif *priv)
+{
+ INIT_WORK(&priv->decrypt_storm_recover_work,
+ bes2600_decrypt_storm_recover_work);
+ priv->decrypt_storm_window_start = 0;
+ priv->decrypt_storm_count = 0;
+ priv->decrypt_storm_recoveries = 0;
+}
+
+void bes2600_decrypt_storm_account(struct bes2600_vif *priv)
+{
+ unsigned long now = jiffies;
+ unsigned long window = msecs_to_jiffies(BES2600_DECRYPT_STORM_WINDOW_MS);
+
+ if (priv->decrypt_storm_window_start == 0 ||
+ time_after(now, priv->decrypt_storm_window_start + window)) {
+ priv->decrypt_storm_window_start = now;
+ priv->decrypt_storm_count = 1;
+ return;
+ }
+
+ if (++priv->decrypt_storm_count >= BES2600_DECRYPT_STORM_THRESHOLD) {
+ priv->decrypt_storm_count = 0;
+ /* Skew the window so we don't re-fire on the same storm. */
+ priv->decrypt_storm_window_start = now + window;
+ schedule_work(&priv->decrypt_storm_recover_work);
+ }
+}
+
#ifdef CONFIG_BES2600_TESTMODE
#include "bes_nl80211_testmode_msg.h"
#endif /* CONFIG_BES2600_TESTMODE */
@@ -1694,6 +1766,8 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
goto drop;
} else {
bes_warn("[RX] Receive failure: %d.\n", arg->status);
+ if (arg->status == WSM_STATUS_DECRYPTFAILURE)
+ bes2600_decrypt_storm_account(priv);
goto drop;
}
}
--
2.54.0
@@ -1,7 +1,8 @@
From 699871fdc6bf1bed6d919732820183e57faeaddc Mon Sep 17 00:00:00 2001
From 0768e11da638457b3455e426de924f9e2e551641 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
Subject: [PATCH 09/29] 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
@@ -289,5 +290,5 @@ index e2e4f1b..a02d6d9 100644
}
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 44e085360fec09c1c1f7b35a23ec679f7065d3f7 Mon Sep 17 00:00:00 2001
From c3d28aea4603fec51b66cfa438dd546722d53272 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
Subject: [PATCH 10/29] 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
@@ -118,10 +118,10 @@ index 133c945..d612c3c 100644
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
index 3b0b7a3..7cbb3a9 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)
@@ -795,41 +795,6 @@ void bes2600_core_release(struct bes2600_common *self)
return;
}
@@ -164,5 +164,5 @@ index 6ed6b15..9d2aac5 100644
{
int ret = 0, if_id;
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 9398d3028bc9d2f4ccbf8e830f8e9799bf065ce4 Mon Sep 17 00:00:00 2001
From 789ab98e4cd4a0c2c43a54da6462b6b05f3af8f2 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
Subject: [PATCH 06/29] 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
@@ -63,10 +63,10 @@ Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
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
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
@@ -76,10 +76,10 @@ index 300912b..39150e0 100644
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
diff --git a/bes2600/bes_log.h b/bes2600/bes_log.h
diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h
index 605cea8..65cf703 100644
--- a/bes2600/bes_log.h
+++ b/bes2600/bes_log.h
--- 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__)
@@ -107,11 +107,11 @@ index 605cea8..65cf703 100644
+ 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,
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
index ca1c77c..bc6d483 100644
--- a/drivers/staging/bes2600/sta.c
+++ b/drivers/staging/bes2600/sta.c
@@ -3654,7 +3654,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw,
*
* Returns: 0 on success or non zero value on failure
*/
@@ -120,7 +120,7 @@ index aa69eb8..5f1a456 100644
{
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)
@@ -3684,7 +3684,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
*
* Returns: TSM parameters collected
*/
@@ -129,7 +129,7 @@ index aa69eb8..5f1a456 100644
{
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)
@@ -3724,7 +3724,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
*
* Returns: Returns the last measured roam delay
*/
@@ -139,5 +139,5 @@ index aa69eb8..5f1a456 100644
struct bes2600_common *hw_priv = hw->priv;
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 5f475a9624490b07c305329f12016ff4a4df3b47 Mon Sep 17 00:00:00 2001
From 0c1f98df59fc3c330b370f1b5b54e8d780278d2a 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
Subject: [PATCH 08/29] 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
@@ -152,5 +152,5 @@ index 1cda447..1b43b41 100644
}
--
2.53.0
2.54.0
@@ -1,7 +1,7 @@
From 1a5d54a3213041262caf1605bb19c66ddded41f7 Mon Sep 17 00:00:00 2001
From 4a1bbc7444c94be044fae4377ccd612a6cd28460 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
Subject: [PATCH 01/29] 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
@@ -62,10 +62,10 @@ Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
2 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/bes2600/Makefile b/bes2600/Makefile
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
index 300912b..788aee2 100644
--- a/bes2600/Makefile
+++ b/bes2600/Makefile
--- 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
@@ -75,10 +75,10 @@ index 300912b..788aee2 100644
endif
# basic function
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
index dc5d3da..8d60b7c 100644
--- a/bes2600/bes2600_factory.c
+++ b/bes2600/bes2600_factory.c
--- 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>
@@ -140,5 +140,5 @@ index dc5d3da..8d60b7c 100644
}
--
2.53.0
2.54.0
@@ -1,7 +1,7 @@
From 82ba594a444a855310fbbe2a5c8ff02f211d8e83 Mon Sep 17 00:00:00 2001
From 13dd191defab19294d843218833860d0e1e33dcd 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
Subject: [PATCH 02/29] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for
PineTab2 factory.txt format
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
@@ -53,10 +53,10 @@ Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
bes2600/wsm.h | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/bes2600/Makefile b/bes2600/Makefile
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
index 788aee2..2dcba09 100644
--- a/bes2600/Makefile
+++ b/bes2600/Makefile
--- 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)
@@ -66,10 +66,10 @@ index 788aee2..2dcba09 100644
FACTORY_PATH ?= bes2600/bes2600_factory.txt
endif
diff --git a/bes2600/wsm.h b/bes2600/wsm.h
diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h
index 0673131..22845ac 100644
--- a/bes2600/wsm.h
+++ b/bes2600/wsm.h
--- 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);
@@ -79,5 +79,5 @@ index 0673131..22845ac 100644
-#endif
#endif /* BES2600_HWIO_H_INCLUDED */
--
2.53.0
2.54.0
@@ -1,7 +1,7 @@
From 8732881c5916106539b9071b51710489c57e8d73 Mon Sep 17 00:00:00 2001
From 40a0a1a0c72ae5b4ee538f6e8a5d0def522606af 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
Subject: [PATCH 03/29] bes2600: thread struct device * through factory
request_firmware() call
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
@@ -43,10 +43,10 @@ Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
bes2600/bes2600_sdio.c | 4 ++++
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
index 8d60b7c..1cda447 100644
--- a/bes2600/bes2600_factory.c
+++ b/bes2600/bes2600_factory.c
--- a/drivers/staging/bes2600/bes2600_factory.c
+++ b/drivers/staging/bes2600/bes2600_factory.c
@@ -31,6 +31,18 @@
static DEFINE_MUTEX(factory_lock);
@@ -75,10 +75,10 @@ index 8d60b7c..1cda447 100644
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
diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h
index 3835b0d..7dbe9f8 100644
--- a/bes2600/bes2600_factory.h
+++ b/bes2600/bes2600_factory.h
--- 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
@@ -89,10 +89,10 @@ index 3835b0d..7dbe9f8 100644
/* 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
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index 13d4ff1..f172d53 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"
@@ -112,5 +112,5 @@ index b595365..371ef4f 100644
self->func = func;
self->dev = &func->dev;
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 9ea8a8e810ee5eb220de700a5c0a6d1153b15130 Mon Sep 17 00:00:00 2001
From 22b799f5a21c0046aad46676519e5f03a0d105fd 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
Date: Sun, 26 Apr 2026 22:31:58 +0200
Subject: [PATCH 15/29] 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),
@@ -78,14 +78,14 @@ 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 ++++
bes2600/bes2600_sdio.c | 29 +++++++++++++++++++++
bes2600/bes_chardev.c | 59 ++++++++++++++++++++++++++++++++++++++++--
bes2600/bes_chardev.h | 1 +
bes2600/sbus.h | 8 ++++++
4 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index b9d836fab7af..f7f86d765bba 100644
index b9d836f..f7f86d7 100644
--- a/drivers/staging/bes2600/bes2600_sdio.c
+++ b/drivers/staging/bes2600/bes2600_sdio.c
@@ -16,6 +16,7 @@
@@ -139,10 +139,10 @@ index b9d836fab7af..f7f86d765bba 100644
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
index a02d6d9..d1375bc 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_
@@ -442,6 +442,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
return ret;
}
@@ -191,7 +191,7 @@ index 455108a2dd66..b776aab5e062 100644
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)
@@ -540,8 +582,21 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
/* unregister wifi */
bes2600_switch_wifi(0);
@@ -216,7 +216,7 @@ index 455108a2dd66..b776aab5e062 100644
bes2600_cdev.sbus_priv);
}
diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h
index c627bb7c3d65..ca8419eead8f 100644
index c627bb7..ca8419e 100644
--- a/drivers/staging/bes2600/bes_chardev.h
+++ b/drivers/staging/bes2600/bes_chardev.h
@@ -60,6 +60,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
@@ -228,7 +228,7 @@ index c627bb7c3d65..ca8419eead8f 100644
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
index 1f2c0cd..cb90890 100644
--- a/drivers/staging/bes2600/sbus.h
+++ b/drivers/staging/bes2600/sbus.h
@@ -75,6 +75,14 @@ struct sbus_ops {
@@ -247,5 +247,5 @@ index 1f2c0cda73de..cb9089004041 100644
void bes2600_irq_handler(struct bes2600_common *priv);
--
2.53.0
2.54.0
@@ -0,0 +1,83 @@
From 3942404ae16b134a55e48cb796d625b8b90e504f Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Tue, 28 Apr 2026 21:37:37 +0200
Subject: [PATCH 19/29] bes2600: handle multi-function SDIO cards in
mmc_hw_reset bus_reset
c5.2 (recover-wedged-firmware-via-mmc-hw-reset) wraps mmc_hw_reset()
and treats any non-zero return as a recovery failure. On
single-function SDIO cards mmc_hw_reset returns 0 after doing the
remove + rescan inline. On multi-function cards (BES2600 has WLAN
func 1 + BT companion func 2) the kernel's mmc_sdio_hw_reset() does
NOT do the rescan: it tears the card down and returns 1 to signal
"caller must trigger rescan".
Field observation on PineTab2 (linux-pinetab2 6.19.10-danctnix1):
when a real LMAC wedge fired bes2600_chrdev_wifi_force_close ->
bes2600_chrdev_do_bus_reset, mmc_hw_reset returned 1, c5.2's wrapper
treated that as "bus_reset failed: 1", logged the error, and gave
up. The card was already removed (mmc2: card 0001 removed) but
nothing scheduled a rescan; wifi (and the BT companion which shares
the same SDIO host) stayed silent until the user rebooted four
minutes later.
Fix:
- Capture the mmc_host pointer before calling mmc_hw_reset (the
card pointer is invalid after the remove).
- On positive return (multi-function path), log informationally
and call mmc_detect_change(host, 0) to schedule a rescan.
Return 0 so callers see the recovery as successful.
- Negative return is still treated as failure as before.
The mmc_detect_change side effect is asynchronous; the chrdev's
wait_event_timeout(probe_done_wq, !sbus_priv) still observes the
remove half synchronously, and the rescan + re-probe runs out of
the host detect work afterwards.
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
bes2600/bes2600_sdio.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index 5a0694a..c81c244 100644
--- a/drivers/staging/bes2600/bes2600_sdio.c
+++ b/drivers/staging/bes2600/bes2600_sdio.c
@@ -1810,10 +1810,32 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
*/
static int bes2600_sdio_bus_reset(struct sbus_priv *self)
{
+ struct mmc_host *host;
+ int ret;
+
if (!self || !self->func || !self->func->card)
return -EINVAL;
- return mmc_hw_reset(self->func->card);
+ host = self->func->card->host;
+ ret = mmc_hw_reset(self->func->card);
+
+ /*
+ * On multi-function SDIO cards (BES2600 has WLAN func 1 + BT
+ * companion func 2), mmc_sdio_hw_reset() removes the card and
+ * returns 1 to signal "remove happened, caller must trigger
+ * rescan". The kernel does NOT auto-rescan in this case;
+ * single-function cards take the rescan path inline and return 0.
+ * Treat any non-negative return as success and force a rescan if
+ * mmc_hw_reset signalled the multi-function path - otherwise the
+ * card stays removed indefinitely after a wedge recovery,
+ * leaving wifi (and the BT companion) silent until reboot.
+ */
+ if (ret > 0) {
+ bes_info("multi-func mmc_hw_reset removed card; scheduling rescan\n");
+ mmc_detect_change(host, 0);
+ ret = 0;
+ }
+ return ret;
}
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
--
2.54.0
@@ -1,8 +1,8 @@
From d1de35c62930b1bc035d3863d75901356548b6f0 Mon Sep 17 00:00:00 2001
From dc1505f5bab24c5f0960dcc612ce51cd2e5aeddf 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
Date: Tue, 28 Apr 2026 16:54:06 +0200
Subject: [PATCH 18/29] 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
@@ -64,12 +64,12 @@ firing entirely. The firmware-side wedge is observed once per boot
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
drivers/staging/bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++-
drivers/staging/bes2600/bes_pwr.h | 9 ++++
bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-
bes2600/bes_pwr.h | 9 ++++++
2 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
index d54e1a0bab0c..ebaa42e3e61e 100644
index b7b6c2f..620acef 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)
@@ -185,7 +185,7 @@ index d54e1a0bab0c..ebaa42e3e61e 100644
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
index 6bc44ac..92de90b 100644
--- a/drivers/staging/bes2600/bes_pwr.h
+++ b/drivers/staging/bes2600/bes_pwr.h
@@ -121,6 +121,15 @@ struct bes2600_pwr_t
@@ -205,5 +205,5 @@ index 6bc44acd7501..92de90b398c6 100644
#ifdef CONFIG_BES2600_WOWLAN
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 80178ec9b1f83aed1dcce9ea7ca02bc81341ba01 Mon Sep 17 00:00:00 2001
From e8550e55fc7d3910ee690359d89d96c86cfb0347 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
Subject: [PATCH 04/29] 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
@@ -49,10 +49,10 @@ 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
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
index e7a1045..f62ae22 100644
--- a/bes2600/bes_pwr.c
+++ b/bes2600/bes_pwr.c
--- 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;
@@ -101,5 +101,5 @@ index e7a1045..f62ae22 100644
return ret;
}
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 4ab8c790304206abd134de48c878b637a70f3c59 Mon Sep 17 00:00:00 2001
From 40aec44a6e4de5aaf0066982601c99e648b0f1ec 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
Subject: [PATCH 16/29] 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
@@ -67,12 +67,12 @@ 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 +++++
bes2600/bes_pwr.c | 94 ++++++++++++++++++++++++++++++++++++++++++-----
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
index 474b6f1..9b4a4de 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)
@@ -100,7 +100,7 @@ index f62ae226d295..de46e5826ee7 100644
- 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++;
+ /*
+ * The indication callback only fires
@@ -123,7 +123,7 @@ index f62ae226d295..de46e5826ee7 100644
+ */
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
+ 1, 0) == 1) {
+ bes_err("%s, wait pm ind timeout\n", __func__);
+ bes_devel("%s, wait pm ind timeout\n", __func__);
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
+ BES2600_CHIP_PM_UNKNOWN);
+ timeouts++;
@@ -209,7 +209,7 @@ index f62ae226d295..de46e5826ee7 100644
}
diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h
index 1ba866c25c42..6bc44acd7501 100644
index 1ba866c..6bc44ac 100644
--- a/drivers/staging/bes2600/bes_pwr.h
+++ b/drivers/staging/bes2600/bes_pwr.h
@@ -64,6 +64,20 @@ enum power_down_state
@@ -242,5 +242,5 @@ index 1ba866c25c42..6bc44acd7501 100644
#ifdef CONFIG_BES2600_WOWLAN
--
2.53.0
2.54.0
@@ -1,7 +1,7 @@
From ab9e0ad6b4bbb1196c448ed000c8c152b0f04683 Mon Sep 17 00:00:00 2001
From 894c502cd541079a8a26d61cd4289af9001b3046 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
Subject: [PATCH 11/29] 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
@@ -49,5 +49,5 @@ index f62ae22..474b6f1 100644
}
} else {
--
2.53.0
2.54.0
@@ -1,8 +1,8 @@
From 706a594dab68779294e4fff9705a6e1df46ec1af Mon Sep 17 00:00:00 2001
From 7a65dc374c671e20bd6303959ff234a179bc9ff7 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
Date: Tue, 28 Apr 2026 15:23:34 +0200
Subject: [PATCH 17/29] 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
@@ -75,15 +75,15 @@ 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 ++++++++++++++++++++++----
bes2600/bes2600_sdio.c | 15 +++++++++--
bes2600/bes_pwr.c | 56 ++++++++++++++++++++++++++++++++++++------
2 files changed, 62 insertions(+), 9 deletions(-)
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index b9d836fab7af..929503547cfd 100644
index f7f86d7..5a0694a 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)
@@ -1389,7 +1389,14 @@ static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag)
/* error check */
if((self->gpio_wakup_flags & BIT(flag)) != 0) {
@@ -99,7 +99,7 @@ index b9d836fab7af..929503547cfd 100644
mutex_unlock(&self->io_mutex);
return;
}
@@ -1420,7 +1427,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
@@ -1421,7 +1428,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
/* error check */
if((self->gpio_wakup_flags & BIT(flag)) == 0) {
@@ -113,7 +113,7 @@ index b9d836fab7af..929503547cfd 100644
return;
}
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
index de46e5826ee7..d54e1a0bab0c 100644
index 9b4a4de..b7b6c2f 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)
@@ -186,5 +186,5 @@ index de46e5826ee7..d54e1a0bab0c 100644
ret = wsm_set_operational_mode(hw_priv, &mode, 0);
--
2.53.0
2.54.0
@@ -0,0 +1,83 @@
From 445c619da88d69adf68e8cae08ad1b53f76fe57d Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Fri, 8 May 2026 00:22:14 +0200
Subject: [PATCH 28/29] =?UTF-8?q?bes2600:=20Patch=20E=20=E2=80=94=20skip?=
=?UTF-8?q?=20ps=5Fstate=5Flock=20when=20PSM-known-disabled?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Per the Opus structural critique (PR #8 §2.4) and Sonnet review item 5.
The per-RX-frame early-data path takes ps_state_lock to double-check
whether a link entry transitioned to BES2600_LINK_SOFT (AP-side
power-save state machine, soft-link transition).
When c7 has latched pm_unsupported = true (firmware does not honor
PSM, see feedback_bes2600_firmware_no_psm memory), the AP power-save
state machine is dead and link entries never transition to LINK_SOFT.
The per-frame spin_lock_bh + double-check is wasted work.
This patch gates the lock acquisition on !pm_unsupported. When the
latch is on (the steady state on the production-shipped bes2600
firmware), early_data RX frames bypass the spin_lock_bh and go
directly to ieee80211_rx_irqsafe.
If a future firmware drop fixes PSM, c7 self-clears pm_unsupported on
the first real PM_INDICATION and the locked path resumes.
Scope is narrower than Sonnet originally framed: only the per-RX-frame
hot path (txrx.c:1945-1951 in cleanups+G+D) is touched. Other
ps_state_lock sites in txrx.c (lines 657, 1256, 1420, 1528) are TX
submission / multicast-start / link-id paths, not per-frame RX, and
not on the Bug #5 hot path. Leave those alone.
Build verified: srcversion B5922B4933590F33207EE97 on ohm sandbox.
---
bes2600/txrx.c | 30 ++++++++++++++++++++++++------
1 file changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c
index 536b198..cb718ad 100644
--- a/drivers/staging/bes2600/txrx.c
+++ b/drivers/staging/bes2600/txrx.c
@@ -1965,13 +1965,31 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
if (unlikely(bes2600_itp_rxed(hw_priv, skb)))
consume_skb(skb);
else if (unlikely(early_data)) {
- spin_lock_bh(&priv->ps_state_lock);
- /* Double-check status with lock held */
- if (entry->status == BES2600_LINK_SOFT)
- skb_queue_tail(&entry->rx_queue, skb);
- else
+ /*
+ * Patch E: when c7 has latched pm_unsupported (firmware
+ * doesn't honour PSM, see feedback_bes2600_firmware_no_psm),
+ * AP-side power-save state machine is dead and link entries
+ * never transition to BES2600_LINK_SOFT. The double-check
+ * branch under ps_state_lock is unreachable in that case,
+ * so skip the per-frame lock acquisition entirely and
+ * deliver to mac80211 directly.
+ *
+ * On firmware that does honour PSM (the latch self-clears
+ * if a real PM_INDICATION ever arrives — see c7), this
+ * predicate flips back to false and the original locked
+ * path is taken.
+ */
+ if (hw_priv->bes_power.pm_unsupported) {
ieee80211_rx_irqsafe(priv->hw, skb);
- spin_unlock_bh(&priv->ps_state_lock);
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ /* Double-check status with lock held */
+ if (entry->status == BES2600_LINK_SOFT)
+ skb_queue_tail(&entry->rx_queue, skb);
+ else
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ }
} else {
ieee80211_rx_irqsafe(priv->hw, skb);
}
--
2.54.0
@@ -0,0 +1,121 @@
From d95453c98e31d7a47bc227aef5d0b426ac9e334b Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Mon, 18 May 2026 16:58:49 +0200
Subject: [PATCH] =?UTF-8?q?bes2600:=20take=20pending=5Frecord=5Flock=20wit?=
=?UTF-8?q?h=20=5Fbh()=20to=20fix=20SOFTIRQ-safe=20=E2=86=92=20-unsafe=20i?=
=?UTF-8?q?nversion=20(besser#18)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PROVE_LOCKING reports:
WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected
kworker/u16:1 is trying to acquire:
&hw_priv->tx_loop.pending_record_lock at bes2600_queue_clear+0x80
and this task is already holding:
&queue->lock at bes2600_queue_clear+0x60
which would create a new lock dependency:
(&queue->lock){+.-.} -> (&hw_priv->tx_loop.pending_record_lock){+.+.}
but this new dependency connects a SOFTIRQ-irq-safe lock:
(&queue->lock){+.-.}
... which became SOFTIRQ-irq-safe at:
bes2600_tx -> ieee80211_handle_wake_tx_queue -> tasklet_action
to a SOFTIRQ-irq-unsafe lock:
(&hw_priv->tx_loop.pending_record_lock){+.+.}
... which became SOFTIRQ-irq-unsafe at:
bes2600_queue_get_skb -> bes2600_join_work -> process_one_work
queue->lock is taken consistently with spin_lock_bh() at 22 sites;
the nested acquisition of pending_record_lock at queue.c:289 (inside
the outer queue->lock_bh held at line 285) had it implicitly BH-safe
via the outer scope. But pending_record_lock is ALSO taken from
non-BH-disabled contexts:
bes2600_queue_get_skb (queue.c:832) — process context via
bes2600_join_work (workqueue), no outer queue->lock held
bes2600_tx_loop_item_pending_check (tx_loop.c:112)
— TX-loop context, no outer
queue->lock held
When CPU0 holds pending_record_lock from one of those non-BH paths
and a softirq fires that wants queue->lock, and CPU1 in softirq has
queue->lock and is about to acquire pending_record_lock — classic AB-BA
SOFTIRQ deadlock.
The fix is the conservative one: take pending_record_lock with _bh()
at every site that's not already inside a queue->lock_bh-held scope.
That makes the lock consistently SOFTIRQ-safe, eliminating the
inversion. queue.c:289/295 stays as plain spin_lock because BH is
already disabled by the outer queue->lock_bh acquired at queue.c:285.
Five sites converted:
bes2600/queue.c:832 -- spin_lock -> spin_lock_bh
bes2600/queue.c:839 -- spin_unlock -> spin_unlock_bh
bes2600/queue.c:844 -- spin_unlock -> spin_unlock_bh
bes2600/tx_loop.c:112 -- spin_lock -> spin_lock_bh
bes2600/tx_loop.c:114 -- spin_unlock -> spin_unlock_bh
Contract:
- Documentation/locking/locktypes.rst spelling: spin_lock_bh() is
the canonical way to make a non-IRQ spinlock safe against
softirq preemption that might re-enter the same lock.
- Same shape as queue->lock in this driver and as is_drv->lock
in the cw1200 ancestor.
Closes: besser#18
Fixes: <bes2600 base import>
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
bes2600/queue.c | 6 +++---
bes2600/tx_loop.c | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c
index cc606c1..4016b76 100644
--- a/drivers/staging/bes2600/queue.c
+++ b/drivers/staging/bes2600/queue.c
@@ -829,19 +829,19 @@ int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
&item_generation, &item_id, &if_id, &link_id);
- spin_lock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
+ spin_lock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock);
if (!list_empty(&queue->stats->hw_priv->tx_loop.pending_record_list)) {
list_for_each_entry_safe(record_item, temp_record_item, &queue->stats->hw_priv->tx_loop.pending_record_list, head) {
if (record_item->packetID == packetID) {
list_del(&record_item->head);
dev_kfree_skb(record_item->skb);
kfree(record_item);
- spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
+ spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock);
return -EINVAL;
}
}
}
- spin_unlock(&queue->stats->hw_priv->tx_loop.pending_record_lock);
+ spin_unlock_bh(&queue->stats->hw_priv->tx_loop.pending_record_lock);
item = &queue->pool[item_id];
diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c
index e6cf072..0cf7ce1 100644
--- a/drivers/staging/bes2600/tx_loop.c
+++ b/drivers/staging/bes2600/tx_loop.c
@@ -109,9 +109,9 @@ void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv, bool need_warn)
bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i],
bes2600_tx_loop_item_pending_item);
}
- spin_lock(&hw_priv->tx_loop.pending_record_lock);
+ spin_lock_bh(&hw_priv->tx_loop.pending_record_lock);
bes2600_queue_iterate_record_pending_packet(hw_priv, bes2600_tx_loop_item_pending_item);
- spin_unlock(&hw_priv->tx_loop.pending_record_lock);
+ spin_unlock_bh(&hw_priv->tx_loop.pending_record_lock);
if (atomic_read(&hw_priv->bh_rx) > 0)
wake_up(&hw_priv->bh_wq);
--
2.54.0
@@ -0,0 +1,19 @@
# queue-pending-record-lock-bh-danctnix — close besser#18
Converts `pending_record_lock` to `spin_lock_bh()` at the 5 sites
where it is taken in non-BH-disabled contexts (`bes2600_queue_get_skb`
called from `bes2600_join_work`, and `bes2600_tx_loop_item_pending_check`).
Eliminates the PROVE_LOCKING SOFTIRQ-safe → SOFTIRQ-unsafe warning
reported in besser#18: `&queue->lock` (taken with `_bh` everywhere,
including the nested acquisition at `queue.c:289` that holds
`pending_record_lock` as inner) was registered SOFTIRQ-irq-safe by
lockdep, but `pending_record_lock` was sometimes taken without BH
disable, creating an AB-BA deadlock window.
Provenance:
- Source-of-truth commit on `marfrit/bes2600-dkms` branch
`bes2600/queue-pending-record-lock-bh-fix`, commit `d95453c`.
- This file is the same commit's `git format-patch` output with
the DKMS-style `bes2600/foo.c` paths rewritten to in-tree
`drivers/staging/bes2600/foo.c` paths via sed.
@@ -1,7 +1,7 @@
From f43bcc5dda0a9120aee62cce0cec1a8c851cb4ef Mon Sep 17 00:00:00 2001
From cd5f85e10480f02e289ea731b5eeec571000562c 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
Subject: [PATCH 05/29] bes2600: remove userspace /dev/bes2600 character device
interface
bes_chardev.c implemented a custom character device at /dev/bes2600 with
@@ -73,13 +73,13 @@ Follow-ups:
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
bes2600/bes_chardev.c | 519 ------------------------------------------
1 file changed, 519 deletions(-)
bes2600/bes_chardev.c | 568 +-----------------------------------------
1 file changed, 3 insertions(+), 565 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
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
index f89dcb8..e2e4f1b 100644
--- a/drivers/staging/bes2600/bes_chardev.c
+++ b/drivers/staging/bes2600/bes_chardev.c
@@ -43,12 +43,6 @@ enum bus_probe_state {
};
@@ -93,10 +93,74 @@ index 9038e48..e2e4f1b 100644
atomic_t num_proc;
wait_queue_head_t open_wq;
spinlock_t status_lock;
@@ -249,351 +243,18 @@ int bes2600_switch_bt(bool on)
@@ -196,7 +190,7 @@ static int bes2600_switch_wifi(bool on)
return ret;
}
-static int bes2600_switch_bt(bool on)
+int bes2600_switch_bt(bool on)
{
int ret = 0;
long status = 0;
@@ -229,11 +223,11 @@ static int bes2600_switch_bt(bool on)
/* check if there is a error when bootup */
ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
} else {
- bes_devel("bes2600 activate bt.\n");
+ bes_info("enable BT\n");
ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true);
}
} else {
- bes_devel("bes2600 deactivate bt.\n");
+ bes_info("disable BT\n");
bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false);
}
@@ -249,392 +243,18 @@ static int bes2600_switch_bt(bool on)
return ret;
}
-/*
- * This is a global function so we don't have to make many changes to
- * the driver.
- *
- * @wifi: 1 to turn on, 0 to turn off. Otherwise, leave unchanged
- * @bt: 1 to turn on, 0 to turn off. Otherwise, leave unchanged
- */
-int bes2600_chrdev_switch_subsys_glb(int wifi, int bt)
-{
- int ret = 0;
-
- switch (wifi) {
- case 0:
- ret = bes2600_switch_wifi(false);
- break;
- case 1:
- ret = bes2600_switch_wifi(true);
- break;
- default:
- break;
- }
-
- if (ret)
- goto result;
-
- switch (bt) {
- case 0:
- ret = bes2600_switch_bt(false);
- break;
- case 1:
- ret = bes2600_switch_bt(true);
- break;
- default:
- break;
- }
-
-result:
- return ret;
-}
-EXPORT_SYMBOL_GPL(bes2600_chrdev_switch_subsys_glb);
-
-static int bes2600_get_cmd_and_ifname(const char *str, char **result)
-{
- int cmd_len = 0;
@@ -192,11 +256,11 @@ index 9038e48..e2e4f1b 100644
- 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");
@@ -205,7 +269,7 @@ index 9038e48..e2e4f1b 100644
- 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");
@@ -214,7 +278,7 @@ index 9038e48..e2e4f1b 100644
- 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) {
@@ -233,16 +297,16 @@ index 9038e48..e2e4f1b 100644
- }
- 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) {
@@ -255,7 +319,7 @@ index 9038e48..e2e4f1b 100644
- 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);
@@ -344,8 +408,8 @@ index 9038e48..e2e4f1b 100644
- WARN_ON(status <= 0);
-
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
-
-
- return ret;
-}
-
@@ -360,7 +424,7 @@ index 9038e48..e2e4f1b 100644
- 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);
@@ -384,26 +448,26 @@ index 9038e48..e2e4f1b 100644
- 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;
-}
@@ -445,7 +509,7 @@ index 9038e48..e2e4f1b 100644
static int bes2600_chrdev_check_system_close_internal(void)
{
@@ -603,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
@@ -644,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
&& (bes2600_cdev.wifi_opened == false);
}
@@ -569,7 +633,23 @@ index 9038e48..e2e4f1b 100644
#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)
@@ -1126,7 +633,6 @@ void bes2600_chrdev_wakeup_bt(void)
bes_err("Wakeup BT fail in resume\n");
}
}
-EXPORT_SYMBOL_GPL(bes2600_chrdev_wakeup_bt);
int bes2600_chrdev_get_fw_type(void)
{
@@ -1148,7 +654,6 @@ bool bes2600_chrdev_is_bus_error(void)
return error;
}
-EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error);
void bes2600_chrdev_update_signal_mode(void)
{
@@ -1167,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void)
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
{
@@ -582,7 +662,7 @@ index 9038e48..e2e4f1b 100644
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)
@@ -1189,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);
}
@@ -597,7 +677,7 @@ index 9038e48..e2e4f1b 100644
}
}
@@ -1247,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
@@ -1290,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
int bes2600_chrdev_init(struct sbus_ops *ops)
{
@@ -644,7 +724,7 @@ index 9038e48..e2e4f1b 100644
/* 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)
@@ -1361,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
bes_devel("%s done\n", __func__);
return 0;
@@ -660,7 +740,7 @@ index 9038e48..e2e4f1b 100644
}
void bes2600_chrdev_free(void)
@@ -1336,9 +821,5 @@ void bes2600_chrdev_free(void)
@@ -1379,9 +821,5 @@ void bes2600_chrdev_free(void)
bes2600_free_dpd_log_buffer();
#endif
bes2600_chrdev_free_dpd_data();
@@ -671,5 +751,5 @@ index 9038e48..e2e4f1b 100644
bes_devel("%s done\n", __func__);
}
--
2.53.0
2.54.0
@@ -0,0 +1,216 @@
From a70e882f3d5f4f9148206675562dddeecd3f4404 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Fri, 8 May 2026 06:40:00 +0200
Subject: [PATCH 29/29] =?UTF-8?q?bes2600:=20Patch=20C2=20=E2=80=94=20repla?=
=?UTF-8?q?ce=20ieee80211=5Frx=5Firqsafe=20with=20ieee80211=5Frx=5Fni?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Per Phase 4 plan PR #14 + kerneldoc audit (Task #19). Six call sites
deferred per-RX-frame mac80211 dispatch via tasklet; replace with the
synchronous-from-process-context API ieee80211_rx_ni() which does its
own local_bh_disable wrap.
Why _ni and not _list:
Phase 4 plan originally targeted ieee80211_rx_list for batch
delivery. Mining mt76 mainline (the only driver using _list)
showed the canonical pattern requires threading a struct list_head
through the per-frame call chain. bes2600s WSM dispatcher
(wsm_handle_rx -> bes2600_rx_cb / wsm.c beacon path) sits between
the bh threads SDIO read and the mac80211 hand-off; threading a
list_head through the dispatcher is a non-trivial refactor.
ieee80211_rx_ni() is the simpler drop-in: no list management, still
removes the tasklet hop. Per-call local_bh_disable cost is trivial
vs the saved tasklet schedule. Future refactor can revisit _list
if measurements warrant.
Sites converted:
- ap.c:96 (bes2600_sta_add link-id rx_queue drain on AP-mode
STA add). Was inside spin_lock_bh(&ps_state_lock);
refactored to splice the queue under the lock then
deliver after unlock — _ni runs the synchronous
mac80211 RX path inline, would otherwise hold the
lock across mac80211 dispatch. splice via
skb_queue_splice_init into a local sk_buff_head.
- sta.c:1487 (deauth-frame inject in inactivity-event handler).
Not under any lock; direct conversion.
- txrx.c:1960 (early-data + pm_unsupported branch from Patch E).
- txrx.c:1967 (early-data + LINK_SOFT-not-set branch).
- txrx.c:1971 (normal RX path in bes2600_rx_cb).
- wsm.c:2415 (beacon delivery in scan-complete WSM handler).
beacon SKB ownership is preserved by the existing
skb_copy(beacon, GFP_ATOMIC) -> beacon_bkp pattern;
no lifecycle change needed.
Mixing constraint (kerneldoc include/net/mac80211.h:5399-5430):
ieee80211_rx_ni() cannot mix with ieee80211_rx_irqsafe() for a
single hardware. All 6 sites convert atomically; no mixed state.
Build verified clean on ohm sandbox: srcversion 619A51E61BF5479AAC146E6.
Predicted Phase 7 delta: +5-15% over v3+D+E baseline (2.35 MB/s mean
on v3 alone; D+E single-rep was 3.22 MB/s). Modest improvement
expected from removing the tasklet schedule per RX frame. Smaller
deltas would still be a net win for upstream-cleanliness — the
kernel.org submission story benefits from not using _irqsafe from
process context.
---
bes2600/ap.c | 15 +++++++++++++--
bes2600/bes_chardev.c | 33 ++++++++++++++++++++++++++++++++-
bes2600/sta.c | 2 +-
bes2600/txrx.c | 6 +++---
bes2600/wsm.c | 2 +-
5 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/drivers/staging/bes2600/ap.c b/drivers/staging/bes2600/ap.c
index 8a17545..99e2da2 100644
--- a/drivers/staging/bes2600/ap.c
+++ b/drivers/staging/bes2600/ap.c
@@ -63,8 +63,11 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct bes2600_link_entry *entry;
struct sk_buff *skb;
+ struct sk_buff_head local_drain;
struct bes2600_common *hw_priv = hw->priv;
+ __skb_queue_head_init(&local_drain);
+
#ifdef P2P_MULTIVIF
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
#endif
@@ -93,9 +96,17 @@ int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
priv->sta_asleep_mask |= BIT(sta_priv->link_id);
entry->status = BES2600_LINK_HARD;
- while ((skb = skb_dequeue(&entry->rx_queue)))
- ieee80211_rx_irqsafe(priv->hw, skb);
+ /*
+ * Patch C2: splice the rx_queue out under the lock then deliver
+ * after unlock. ieee80211_rx_ni() runs the mac80211 RX path
+ * synchronously (formerly ieee80211_rx_irqsafe deferred to a
+ * tasklet); calling it from inside spin_lock_bh would hold the
+ * lock across mac80211's full RX dispatch.
+ */
+ skb_queue_splice_init(&entry->rx_queue, &local_drain);
spin_unlock_bh(&priv->ps_state_lock);
+ while ((skb = __skb_dequeue(&local_drain)))
+ ieee80211_rx_ni(priv->hw, skb);
#ifdef AP_AGGREGATE_FW_FIX
hw_priv->connected_sta_cnt++;
if(hw_priv->connected_sta_cnt>1) {
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
index 02dcd43..844f1d0 100644
--- a/drivers/staging/bes2600/bes_chardev.c
+++ b/drivers/staging/bes2600/bes_chardev.c
@@ -181,7 +181,7 @@ static int bes2600_switch_wifi(bool on)
return ret;
}
-int bes2600_switch_bt(bool on)
+static int bes2600_switch_bt(bool on)
{
int ret = 0;
long status = 0;
@@ -234,6 +234,36 @@ int bes2600_switch_bt(bool on)
return ret;
}
+/*
+ * Re-added for danctnix's bes2600_btuart.c (a danctnix-only file) which
+ * relies on the chardev utility API for BT power switching and bus-error
+ * checks. The userspace /dev/bes2600 chardev itself is removed by the
+ * remove-chardev-user-interface series; these in-kernel helpers stay.
+ *
+ * @wifi: 1 to turn on, 0 to turn off. Otherwise, leave unchanged
+ * @bt: 1 to turn on, 0 to turn off. Otherwise, leave unchanged
+ */
+int bes2600_chrdev_switch_subsys_glb(int wifi, int bt)
+{
+ int ret = 0;
+
+ switch (wifi) {
+ case 0: ret = bes2600_switch_wifi(false); break;
+ case 1: ret = bes2600_switch_wifi(true); break;
+ default: break;
+ }
+ if (ret)
+ return ret;
+
+ switch (bt) {
+ case 0: ret = bes2600_switch_bt(false); break;
+ case 1: ret = bes2600_switch_bt(true); break;
+ default: break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(bes2600_chrdev_switch_subsys_glb);
+
@@ -562,6 +592,7 @@ bool bes2600_chrdev_is_bus_error(void)
return error;
}
+EXPORT_SYMBOL_GPL(bes2600_chrdev_is_bus_error);
void bes2600_chrdev_update_signal_mode(void)
{
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
index 8af8150..2b63ff2 100644
--- a/drivers/staging/bes2600/sta.c
+++ b/drivers/staging/bes2600/sta.c
@@ -1500,7 +1500,7 @@ void bes2600_event_handler(struct work_struct *work)
IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS);
deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
deauth->seq_ctrl = 0;
- ieee80211_rx_irqsafe(priv->hw, skb);
+ ieee80211_rx_ni(priv->hw, skb);
bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da);
queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
break;
diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c
index cb718ad..9074972 100644
--- a/drivers/staging/bes2600/txrx.c
+++ b/drivers/staging/bes2600/txrx.c
@@ -1980,18 +1980,18 @@ void bes2600_rx_cb(struct bes2600_vif *priv,
* path is taken.
*/
if (hw_priv->bes_power.pm_unsupported) {
- ieee80211_rx_irqsafe(priv->hw, skb);
+ ieee80211_rx_ni(priv->hw, skb);
} else {
spin_lock_bh(&priv->ps_state_lock);
/* Double-check status with lock held */
if (entry->status == BES2600_LINK_SOFT)
skb_queue_tail(&entry->rx_queue, skb);
else
- ieee80211_rx_irqsafe(priv->hw, skb);
+ ieee80211_rx_ni(priv->hw, skb);
spin_unlock_bh(&priv->ps_state_lock);
}
} else {
- ieee80211_rx_irqsafe(priv->hw, skb);
+ ieee80211_rx_ni(priv->hw, skb);
}
*skb_p = NULL;
diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c
index 908c965..2424181 100644
--- a/drivers/staging/bes2600/wsm.c
+++ b/drivers/staging/bes2600/wsm.c
@@ -2412,7 +2412,7 @@ int wsm_handle_rx(struct bes2600_common *hw_priv, int id,
if (!hw_priv->beacon_bkp)
hw_priv->beacon_bkp = \
skb_copy(hw_priv->beacon, GFP_ATOMIC);
- ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon);
+ ieee80211_rx_ni(hw_priv->hw, hw_priv->beacon);
hw_priv->beacon = hw_priv->beacon_bkp;
hw_priv->beacon_bkp = NULL;
--
2.54.0
@@ -1,8 +1,8 @@
From 3d98404c1a85ef33e9fc1422042c71dc90f3b255 Mon Sep 17 00:00:00 2001
From 179c2e0bf852734631acfc56b2478775215cc5f6 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
Subject: [PATCH 14/29] 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
@@ -57,11 +57,11 @@ 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 +++++++++++++++--
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
index 5f6af3b..b944adc 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -22,9 +22,17 @@
@@ -105,5 +105,5 @@ index 5f6af3bc81ba..b944adcaa08c 100644
time_before(jiffies, hw_priv->scan.backoff_until))
return true;
--
2.53.0
2.54.0
@@ -1,7 +1,7 @@
From adc6c1f332d41ee1aadd349eea11809c88139307 Mon Sep 17 00:00:00 2001
From 844e2245a1ed517b3a0bc487fec1a100304f0b44 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
Subject: [PATCH 13/29] 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
@@ -88,7 +88,7 @@ Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
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
index 3bfa535..5f6af3b 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -14,11 +14,50 @@
@@ -142,7 +142,7 @@ index b2c22e7..faa1c90 100644
#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)
@@ -703,10 +742,29 @@ void bes2600_scan_work(struct work_struct *work)
wsm_unlock_tx(hw_priv);
} else
#endif
@@ -222,5 +222,5 @@ index d40df30..55a4e2b 100644
underflow:
--
2.53.0
2.54.0
@@ -0,0 +1,168 @@
From 093a5038b8b68f316d976b7cb69609ca7f24f322 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Mon, 18 May 2026 11:27:40 +0200
Subject: [PATCH 1/2] bes2600: filter 5 GHz scans at the driver boundary
(besser#1)
The BES2600 firmware refuses WSM start-scan for 5 GHz with status 2
("rejected by policy"). This shows up in dmesg as the recurring
wsm_generic_confirm failed for request 0x0007.
[SCAN] Scan failed (-22).
pattern (besser issue #1, ~14-16/h on ohm/PineTab2 baseline).
Trace shows every reject is the second of a back-to-back pair: mac80211
splits multi-band hw_scan requests per band when the driver does not
set IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't), then re-invokes
drv_hw_scan from __ieee80211_scan_completed for each subsequent band.
The 2.4 GHz iteration succeeds; the 5 GHz iteration is what the
firmware rejects. See ieee80211_prep_hw_scan in net/mac80211/scan.c
for the loop, and the existing memory reference_bes2600_5ghz_scan_reject
for the firmware behaviour.
The 056a71a defer-on-reject patch already in this tree handles the
BT-A2DP-coex branch and the consecutive-reject backoff, but it cannot
prevent the per-band-loop reject: by the time defer_should_scan is
consulted, the per-band call is already in flight, and the reject_count
gets reset on every successful 2.4 GHz scan in between (which is
~36% of attempts), so the threshold never trips.
The fix: refuse the 5 GHz iteration upfront in bes2600_hw_scan. The
2.4 GHz scan still runs normally. The 5 GHz portion is reported as
aborted to userspace -- same outcome as today, minus the dmesg storm
and the wsm_generic_confirm WARN cascade.
5 GHz band registration is intentionally left in place: direct-BSSID
association to a known 5 GHz AP still works (no scan is needed for
that path), and a future firmware update that fixes the scan behaviour
should not be foreclosed by changing band advertisement.
Contract: per include/net/mac80211.h ieee80211_ops.hw_scan, a negative
return aborts the scan without requiring ieee80211_scan_completed().
-EOPNOTSUPP is the semantically accurate code (operation is legal,
driver can't service it on this band today).
Phase 3 evidence:
- baseline N=3: rate ~14.3-23.6/h converged at 14.3/h (matches OP)
- back-to-back scan gap: 6/6 rejected pairs <200us, 1/1 successful
pair was 114ms (single-band-only, no 5 GHz leg)
- defer log fires: 0/9 in 30-min window (056a71a structurally bypassed)
Predicted Phase 7 delta: Pattern A 14/h -> 0/h.
---
bes2600/scan.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
index fb1d298..a81afb6 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -238,6 +238,28 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
/* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == BES2600_JOIN_STATUS_AP)
return -EOPNOTSUPP;
+
+ /*
+ * Firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected
+ * by policy"); see besser issue #1. mac80211 splits multi-band
+ * hw_scan requests per-band when the driver does not set
+ * IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't -- see
+ * ieee80211_hw_set() calls in bes2600_main.c), so each per-band call
+ * has req->channels[] from one band only (see ieee80211_prep_hw_scan
+ * in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
+ * boundary so userspace gets a clean aborted-scan for that portion
+ * rather than waiting for the firmware reject to cascade up. 5 GHz
+ * band registration stays intact so direct-BSSID association to a
+ * known 5 GHz AP still works (no scan needed for that path).
+ *
+ * Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
+ * documentation, a negative return aborts the scan without requiring
+ * ieee80211_scan_completed().
+ */
+ if (req->n_channels > 0 &&
+ req->channels[0]->band == NL80211_BAND_5GHZ)
+ return -EOPNOTSUPP;
+
#if 0
if (work_pending(&priv->offchannel_work) ||
(hw_priv->roc_if_id != -1)) {
--
2.54.0
From 8cd10f487c8144d462a510812ba0fa717b3e24df Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Mon, 18 May 2026 15:56:34 +0200
Subject: [PATCH 2/2] bes2600: scan-filter-5ghz: allow targeted single-channel
scans (besser#1 follow-up)
The original Patch I refused EVERY 5 GHz scan request unconditionally
(req->n_channels > 0 && band == NL80211_BAND_5GHZ). This eliminated
the Pattern A storm but also broke 5 GHz association entirely:
NM / wpa_supplicant iterates a freq_list when a connection profile
specifies 802-11-wireless.band=a, issuing per-frequency single-channel
scans to find the BSS before associating. Those single-channel scans
were also refused by our guard, so the BSS was never seen and
'Wi-Fi network could not be found' was the only outcome.
Tighten the guard: refuse only multi-channel 5 GHz scans (n_channels
> 1), which is the per-band-sweep pattern mac80211 issues internally
and the only one that triggers the firmware storm at the per-band
loop boundary. Single-channel 5 GHz scans pass through to firmware,
which generally accepts them -- and when they happen to be rejected,
the failure is isolated and doesn't cascade.
Verified on ohm with pkgrel=3 (srcversion BEB625FA7443171EA8D55F7):
- Pattern A count since boot: 0 (Phase 7 prediction still holds)
- iw dev wlan0 scan freq 5180 -> allowed
- iw dev wlan0 scan freq 5180 5200 ... -> refused -EOPNOTSUPP
- NM 'nmcli connection up' with band=a -> associated to BSSID
c0:25:06:e6:5b:33 on 5240 MHz / ch.48 in ~1 second
- TX bitrate 150 Mbit/s MCS 7 40MHz short-GI (vs 72.2 Mbit/s
HT20 on 2.4 GHz) -- ~2x throughput recovered
The change is a single byte (> 0 -> > 1) plus comment update; the
test confirmation above is what motivates it.
Refs: besser#1 (closed but tracked for follow-up like this), original
Patch I sha 093a503.
---
bes2600/scan.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
index a81afb6..497523b 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -248,15 +248,23 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
* has req->channels[] from one band only (see ieee80211_prep_hw_scan
* in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
* boundary so userspace gets a clean aborted-scan for that portion
- * rather than waiting for the firmware reject to cascade up. 5 GHz
- * band registration stays intact so direct-BSSID association to a
- * known 5 GHz AP still works (no scan needed for that path).
+ * rather than waiting for the firmware reject to cascade up.
+ *
+ * Only the multi-channel case is refused (n_channels > 1): that's
+ * the per-band-sweep pattern mac80211 issues internally and the
+ * one that triggers the firmware storm at the per-band loop
+ * boundary. Single-channel 5 GHz scans (BSS verification, NM's
+ * per-freq iteration when 802-11-wireless.band=a is set) pass
+ * through to firmware, which generally accepts them since the
+ * storm is the back-to-back per-band issue, not a blanket 5 GHz
+ * reject. This preserves 5 GHz association via the
+ * "wpa_supplicant iterates freq_list per channel" path.
*
* Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
* documentation, a negative return aborts the scan without requiring
* ieee80211_scan_completed().
*/
- if (req->n_channels > 0 &&
+ if (req->n_channels > 1 &&
req->channels[0]->band == NL80211_BAND_5GHZ)
return -EOPNOTSUPP;
--
2.54.0
@@ -0,0 +1,19 @@
# scan-filter-5ghz-danctnix — close besser#1
Refuses multi-channel 5 GHz scan requests at the driver boundary with
`-EOPNOTSUPP`, eliminating the WSM 0x0007 reject storm. Single-channel
5 GHz scans still pass through (NM `802-11-wireless.band=a` BSS
verification path stays functional).
Phase 7 baseline on ohm: Pattern A 14.3/h → 0/h (verified 2026-05-18,
30 min window). 5 GHz association achieves 150 Mbit/s MCS 7 HT40 SGI vs
72.2 on 2.4 GHz.
Single combined patch file because the two commits in the source
(initial filter + `n_channels > 1` refinement) form a 2-commit
follow-up series and git apply concatenation handles both. Splitting
into two .patch files would mean a fragile dependency on cross-file
sequencing inside the same series-dir.
Provenance: closes besser#1. Mirror of source-of-truth in
`marfrit/bes2600-dkms` branch `bes2600/scan-filter-5ghz`.
@@ -0,0 +1,168 @@
From 093a5038b8b68f316d976b7cb69609ca7f24f322 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Mon, 18 May 2026 11:27:40 +0200
Subject: [PATCH 1/2] bes2600: filter 5 GHz scans at the driver boundary
(besser#1)
The BES2600 firmware refuses WSM start-scan for 5 GHz with status 2
("rejected by policy"). This shows up in dmesg as the recurring
wsm_generic_confirm failed for request 0x0007.
[SCAN] Scan failed (-22).
pattern (besser issue #1, ~14-16/h on ohm/PineTab2 baseline).
Trace shows every reject is the second of a back-to-back pair: mac80211
splits multi-band hw_scan requests per band when the driver does not
set IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't), then re-invokes
drv_hw_scan from __ieee80211_scan_completed for each subsequent band.
The 2.4 GHz iteration succeeds; the 5 GHz iteration is what the
firmware rejects. See ieee80211_prep_hw_scan in net/mac80211/scan.c
for the loop, and the existing memory reference_bes2600_5ghz_scan_reject
for the firmware behaviour.
The 056a71a defer-on-reject patch already in this tree handles the
BT-A2DP-coex branch and the consecutive-reject backoff, but it cannot
prevent the per-band-loop reject: by the time defer_should_scan is
consulted, the per-band call is already in flight, and the reject_count
gets reset on every successful 2.4 GHz scan in between (which is
~36% of attempts), so the threshold never trips.
The fix: refuse the 5 GHz iteration upfront in bes2600_hw_scan. The
2.4 GHz scan still runs normally. The 5 GHz portion is reported as
aborted to userspace -- same outcome as today, minus the dmesg storm
and the wsm_generic_confirm WARN cascade.
5 GHz band registration is intentionally left in place: direct-BSSID
association to a known 5 GHz AP still works (no scan is needed for
that path), and a future firmware update that fixes the scan behaviour
should not be foreclosed by changing band advertisement.
Contract: per include/net/mac80211.h ieee80211_ops.hw_scan, a negative
return aborts the scan without requiring ieee80211_scan_completed().
-EOPNOTSUPP is the semantically accurate code (operation is legal,
driver can't service it on this band today).
Phase 3 evidence:
- baseline N=3: rate ~14.3-23.6/h converged at 14.3/h (matches OP)
- back-to-back scan gap: 6/6 rejected pairs <200us, 1/1 successful
pair was 114ms (single-band-only, no 5 GHz leg)
- defer log fires: 0/9 in 30-min window (056a71a structurally bypassed)
Predicted Phase 7 delta: Pattern A 14/h -> 0/h.
---
bes2600/scan.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
index fb1d298..a81afb6 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -238,6 +238,28 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
/* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == BES2600_JOIN_STATUS_AP)
return -EOPNOTSUPP;
+
+ /*
+ * Firmware refuses WSM start-scan for 5 GHz with status 2 ("rejected
+ * by policy"); see besser issue #1. mac80211 splits multi-band
+ * hw_scan requests per-band when the driver does not set
+ * IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS (we don't -- see
+ * ieee80211_hw_set() calls in bes2600_main.c), so each per-band call
+ * has req->channels[] from one band only (see ieee80211_prep_hw_scan
+ * in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
+ * boundary so userspace gets a clean aborted-scan for that portion
+ * rather than waiting for the firmware reject to cascade up. 5 GHz
+ * band registration stays intact so direct-BSSID association to a
+ * known 5 GHz AP still works (no scan needed for that path).
+ *
+ * Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
+ * documentation, a negative return aborts the scan without requiring
+ * ieee80211_scan_completed().
+ */
+ if (req->n_channels > 0 &&
+ req->channels[0]->band == NL80211_BAND_5GHZ)
+ return -EOPNOTSUPP;
+
#if 0
if (work_pending(&priv->offchannel_work) ||
(hw_priv->roc_if_id != -1)) {
--
2.54.0
From 8cd10f487c8144d462a510812ba0fa717b3e24df Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Mon, 18 May 2026 15:56:34 +0200
Subject: [PATCH 2/2] bes2600: scan-filter-5ghz: allow targeted single-channel
scans (besser#1 follow-up)
The original Patch I refused EVERY 5 GHz scan request unconditionally
(req->n_channels > 0 && band == NL80211_BAND_5GHZ). This eliminated
the Pattern A storm but also broke 5 GHz association entirely:
NM / wpa_supplicant iterates a freq_list when a connection profile
specifies 802-11-wireless.band=a, issuing per-frequency single-channel
scans to find the BSS before associating. Those single-channel scans
were also refused by our guard, so the BSS was never seen and
'Wi-Fi network could not be found' was the only outcome.
Tighten the guard: refuse only multi-channel 5 GHz scans (n_channels
> 1), which is the per-band-sweep pattern mac80211 issues internally
and the only one that triggers the firmware storm at the per-band
loop boundary. Single-channel 5 GHz scans pass through to firmware,
which generally accepts them -- and when they happen to be rejected,
the failure is isolated and doesn't cascade.
Verified on ohm with pkgrel=3 (srcversion BEB625FA7443171EA8D55F7):
- Pattern A count since boot: 0 (Phase 7 prediction still holds)
- iw dev wlan0 scan freq 5180 -> allowed
- iw dev wlan0 scan freq 5180 5200 ... -> refused -EOPNOTSUPP
- NM 'nmcli connection up' with band=a -> associated to BSSID
c0:25:06:e6:5b:33 on 5240 MHz / ch.48 in ~1 second
- TX bitrate 150 Mbit/s MCS 7 40MHz short-GI (vs 72.2 Mbit/s
HT20 on 2.4 GHz) -- ~2x throughput recovered
The change is a single byte (> 0 -> > 1) plus comment update; the
test confirmation above is what motivates it.
Refs: besser#1 (closed but tracked for follow-up like this), original
Patch I sha 093a503.
---
bes2600/scan.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
index a81afb6..497523b 100644
--- a/drivers/staging/bes2600/scan.c
+++ b/drivers/staging/bes2600/scan.c
@@ -248,15 +248,23 @@ int bes2600_hw_scan(struct ieee80211_hw *hw,
* has req->channels[] from one band only (see ieee80211_prep_hw_scan
* in net/mac80211/scan.c). Refuse the 5 GHz iteration at the driver
* boundary so userspace gets a clean aborted-scan for that portion
- * rather than waiting for the firmware reject to cascade up. 5 GHz
- * band registration stays intact so direct-BSSID association to a
- * known 5 GHz AP still works (no scan needed for that path).
+ * rather than waiting for the firmware reject to cascade up.
+ *
+ * Only the multi-channel case is refused (n_channels > 1): that's
+ * the per-band-sweep pattern mac80211 issues internally and the
+ * one that triggers the firmware storm at the per-band loop
+ * boundary. Single-channel 5 GHz scans (BSS verification, NM's
+ * per-freq iteration when 802-11-wireless.band=a is set) pass
+ * through to firmware, which generally accepts them since the
+ * storm is the back-to-back per-band issue, not a blanket 5 GHz
+ * reject. This preserves 5 GHz association via the
+ * "wpa_supplicant iterates freq_list per channel" path.
*
* Contract: per include/net/mac80211.h struct ieee80211_ops.hw_scan
* documentation, a negative return aborts the scan without requiring
* ieee80211_scan_completed().
*/
- if (req->n_channels > 0 &&
+ if (req->n_channels > 1 &&
req->channels[0]->band == NL80211_BAND_5GHZ)
return -EOPNOTSUPP;
--
2.54.0
@@ -0,0 +1,540 @@
From 0f185172b020818faec9572fd800867db623a40e Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Thu, 7 May 2026 22:34:11 +0200
Subject: [PATCH 25/29] =?UTF-8?q?bes2600:=20drop=20sdio=5Frx=5Fwork=20rela?=
=?UTF-8?q?y,=20IRQ=E2=86=92bh-direct=20(no-relay=20architecture)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Patch C v3 — match cw1200 mainline architecture
(drivers/net/wireless/st/cw1200/). Eliminates the
sdio_rx_work workqueue relay that introduced a thread-safety
race on hw_priv->hw_bufs_used in v1 (PR #3 closed) and that
v2's atomic_t prep was a workaround for (PR #10 superseded by
v3 plan PR #11).
Architectural changes:
- bes2600_gpio_irq_handler: now calls self->irq_handler()
directly instead of queue_work(self->sdio_wq, &self->rx_work).
Bumps bh_rx atomic + wakes bh_wq.
- bes2600_bh_rx_helper (BES_SDIO_RX_MULTIPLE_ENABLE branch):
now calls priv->sbus_ops->bus_rx_batch() to do the SDIO read
inline. No pipe_read, no skb_dequeue.
- bes2600_sdio_read_rx_batch (new): the SDIO read sequence
extracted from sdio_rx_work, registered as
sbus_ops->bus_rx_batch. Runs in bh thread context.
- bes2600_sdio_extract_packets: calls
bes2600_bh_handle_rx_skb() directly per parsed SKB. No
skb_queue_tail, no rx_queue.
- bes2600_bh_handle_rx_skb (new in bh.c): the per-SKB
bookkeeping that bh_rx_helper used to do post-pipe_read
(seq# check, exception, confirm-condition, wsm_handle_rx).
Wakes bh thread for tx-burst via atomic_inc(&priv->bh_tx)
instead of bes2600_bh_wakeup() — we ARE the bh thread.
- Post-tx queue_work(rx_work) site: replaced with
self->irq_handler() to wake bh for piggyback RX check.
Deleted infrastructure:
- struct sbus_priv: rx_queue, rx_queue_lock, rx_work fields
- bes2600_sdio_pipe_read: function deleted (unused)
- sdio_rx_work: function deleted (unused)
- sbus_ops->pipe_read assignment: removed for SDIO bus
- skb_queue_head_init(&self->rx_queue), spin_lock_init(...),
INIT_WORK(rx_work): probe-time setup removed
- cancel_work_sync(rx_work) + drain loop in empty_work: removed
- flush_work(rx_work) in drain helper: replaced with msleep(2)
- work_pending(rx_work) check in suspend predicate: removed
Concurrency invariant restored:
- hw_priv->hw_bufs_used: single-writer (bh thread only)
by construction. No atomic_t needed.
- hw_priv->hw_bufs_used_vif[]: ditto.
- hw_priv->wsm_tx_pending[]: ditto.
- All other shared state: unchanged or already protected.
Phase 7 partial verification (rep 1, 2026-05-07):
- Module loads clean, srcversion 371C6606B73AF19299228CA
- Link associates, no WARN/BUG/oops
- sdio_rx_work dispatches: 0 (function deleted)
- bes2600_bh_work redispatches: 0 (single long-lived
invariant preserved)
- Chip handled stress traffic without wedge
Phase 7 full N=3 stress ramp deferred to follow-up rep series
(rep 2 had a TCP-level nc race; not a bes2600 issue but
invalidated rep 2's throughput number).
---
bes2600/bes2600_sdio.c | 144 ++++++++++++++++++++++++-----------------
bes2600/bh.c | 129 ++++++++++++++++++++++++++++++++++--
bes2600/bh.h | 9 +++
bes2600/sbus.h | 8 +++
4 files changed, 226 insertions(+), 64 deletions(-)
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index c81c244..3834032 100644
--- a/drivers/staging/bes2600/bes2600_sdio.c
+++ b/drivers/staging/bes2600/bes2600_sdio.c
@@ -29,6 +29,7 @@
#include <linux/of_gpio.h>
#include "bes2600.h"
+#include "bh.h"
#include "sbus.h"
#include "bes2600_plat.h"
#include "bes2600_factory.h"
@@ -72,10 +73,12 @@ struct sbus_priv {
int rx_data_toggle;
#endif
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
- spinlock_t rx_queue_lock;
- struct sk_buff_head rx_queue;
+ /*
+ * Patch C v3: rx_queue, rx_queue_lock, rx_work removed (no relay).
+ * The bh thread now reads RX inline; the rx_buffer scratch area
+ * stays. Counters/timestamps stay for debugfs visibility.
+ */
u8 *rx_buffer;
- struct work_struct rx_work;
u32 rx_last_ctrl;
u32 rx_valid_ctrl;
u32 rx_total_ctrl_cnt;
@@ -412,10 +415,19 @@ static void bes2600_sdio_irq_handler(struct sdio_func *func)
bes_devel("%s called, fw_started:%d \n",
__func__, self->fw_started);
- if (likely(self->fw_started && self->core)) {
- queue_work(self->sdio_wq, &self->rx_work);
+ /*
+ * Patch C v3: no more sdio_rx_work relay. Wake the bh thread
+ * directly via self->irq_handler (bes2600_irq_handler in bh.c
+ * which bumps bh_rx atomic + wakes bh_wq). The bh thread will
+ * then call sbus_ops->bus_rx_batch() to do the SDIO read inline.
+ * Matches cw1200 mainline IRQ → bh-direct architecture.
+ */
+ if (likely(self->fw_started && self->core && self->irq_handler)) {
+ spin_lock_irqsave(&self->lock, flags);
+ self->irq_handler(self->irq_priv);
+ spin_unlock_irqrestore(&self->lock, flags);
self->last_irq_timestamp = jiffies;
- } else if(self->irq_handler) {
+ } else if (self->irq_handler) {
spin_lock_irqsave(&self->lock, flags);
self->irq_handler(self->irq_priv);
spin_unlock_irqrestore(&self->lock, flags);
@@ -812,10 +824,15 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8
skb_put(skb, packet_len);
memcpy(skb->data, &data[pos], packet_len);
bes_devel("%s, %d,%d\n", __func__, packet_len, pos);
- spin_lock(&self->rx_queue_lock);
- skb_queue_tail(&self->rx_queue, skb);
self->rx_data_cnt++;
- spin_unlock(&self->rx_queue_lock);
+ /*
+ * Patch C v3: deliver the SKB directly into the WSM/mac80211
+ * stack from the bh thread. No rx_queue, no inter-thread
+ * handoff, no atomic_t needed on the counters that
+ * wsm_release_tx_buffer touches — single-writer-from-bh is
+ * preserved by construction. See bh.c for the contract block.
+ */
+ bes2600_bh_handle_rx_skb(self->core, skb);
packet_len = (packet_len + 3) & (~0x3);
pos += packet_len;
#ifdef BES_SDIO_OPTIMIZED_LEN
@@ -826,17 +843,31 @@ static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8
return 0;
}
-static void sdio_rx_work(struct work_struct *work)
+/*
+ * Patch C v3: bh thread calls this directly via sbus_ops->bus_rx_batch.
+ * No more sdio_rx_work workqueue. SDIO read sequence (lock →
+ * read_ctrl → memcpy_fromio → packets_check → extract_packets) runs
+ * inline in bh-thread context. Each parsed SKB is delivered via
+ * bes2600_bh_handle_rx_skb() from extract_packets — no rx_queue, no
+ * second worker, no inter-thread handoff.
+ *
+ * Architecture matches cw1200 mainline. Single-writer-from-bh
+ * invariant on hw_bufs_used preserved by construction.
+ *
+ * Returns 0 on success (caller's bh outer loop decides whether to
+ * continue), negative on bus read error. On error: triggers
+ * wifi_force_close (same as the old sdio_rx_work).
+ */
+static int bes2600_sdio_read_rx_batch(struct sbus_priv *self)
{
- int ret, again = 0, retry = 0, crc_retry = 0;
+ int ret = 0, again = 0, retry = 0, crc_retry = 0;
u32 ctrl_reg = 0;
int total_len;
- struct sbus_priv *self = container_of(work, struct sbus_priv, rx_work);
u8 *buf = self->rx_buffer;
/* don't read/write sdio when sdio error */
if (bes2600_chrdev_is_bus_error())
- return;
+ return 0;
bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX);
@@ -891,6 +922,10 @@ static void sdio_rx_work(struct work_struct *work)
goto failed;
}
+ /*
+ * extract_packets parses the multi-RX buffer and calls
+ * bes2600_bh_handle_rx_skb() per SKB. No queueing.
+ */
if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) {
bes_err("%s,%d error=%d\n", __func__, __LINE__, ret);
goto failed;
@@ -898,22 +933,16 @@ static void sdio_rx_work(struct work_struct *work)
ctrl_reg = 0;
- if (likely(self->irq_handler)) {
- self->irq_handler(self->irq_priv);
- } else {
- bes_err("%s,%d\n", __func__, __LINE__);
- goto failed;
- }
-
} while (again);
bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX);
- return;
+ return 0;
failed:
bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX);
bes2600_chrdev_wifi_force_close(self->core, false);
WARN_ON(1);
+ return -1;
}
static void sdio_scan_work(struct work_struct *work)
@@ -921,26 +950,11 @@ static void sdio_scan_work(struct work_struct *work)
bes_warn("%s: this function does nothing\n", __FUNCTION__);
}
-static void *bes2600_sdio_pipe_read(struct sbus_priv *self)
-{
- struct sk_buff *skb;
-
- if (bes2600_chrdev_is_bus_error()) {
- return bes2600_tx_loop_read(self->core);
- }
-
- spin_lock(&self->rx_queue_lock);
- skb = skb_dequeue(&self->rx_queue);
- if (skb)
- self->rx_proc_cnt++;
- spin_unlock(&self->rx_queue_lock);
- if (likely(self->fw_started == true &&
- !bes2600_pwr_device_is_idle(self->core) &&
- self->core->hw_bufs_used > 0))
- if (!skb)
- queue_work(self->sdio_wq, &self->rx_work);
- return skb;
-}
+/* Patch C v3: bes2600_sdio_pipe_read deleted. bh thread reads the
+ * SDIO bus inline via bes2600_sdio_read_rx_batch (sbus_ops->bus_rx_batch).
+ * No rx_queue, no skb_dequeue, no relay. bes2600_tx_loop_read remains
+ * for the test bus error-fallback path but is now invoked at higher
+ * level. */
#endif
@@ -1196,7 +1210,14 @@ flush_previous:
}
} while (crc_retry <= 10);
sdio_release_host(self->func);
- queue_work(self->sdio_wq, &self->rx_work);
+ /*
+ * Patch C v3: wake the bh thread to check for any RX
+ * that piggybacked on this TX window. Bumps bh_rx
+ * atomic; bh's wait_event will pick it up and call
+ * sbus_ops->bus_rx_batch().
+ */
+ if (likely(self->irq_handler))
+ self->irq_handler(self->irq_priv);
if (ret) {
bes_err("%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk);
sdio_work_debug(self);
@@ -1247,12 +1268,11 @@ static int bes2600_sdio_misc_init(struct sbus_priv *self, struct bes2600_common
self->next_toggle = 0;
#endif
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
- spin_lock_init(&self->rx_queue_lock);
- skb_queue_head_init(&self->rx_queue);
+ /* Patch C v3: rx_queue / rx_queue_lock removed (no relay). */
self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM));
if (!self->rx_buffer)
return -ENOMEM;
- INIT_WORK(&self->rx_work, sdio_rx_work);
+ /* Patch C v3: sdio_rx_work removed; bh thread does the read. */
#endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
INIT_LIST_HEAD(&self->tx_bufferlist);
@@ -1581,22 +1601,15 @@ err:
static void bes2600_sdio_empty_work(struct sbus_priv *self)
{
-#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
- struct sk_buff *skb;
-#endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
struct bes_sdio_tx_list_t *tx_buffer, *temp;
#endif
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
- cancel_work_sync(&self->rx_work);
- while (1) {
- skb = skb_dequeue(&self->rx_queue);
- if (skb)
- dev_kfree_skb(skb);
- else
- break;
- }
+ /*
+ * Patch C v3: rx_work and rx_queue removed. Counters still
+ * reset for the next attach cycle.
+ */
self->rx_last_ctrl = 0;
self->rx_total_ctrl_cnt = 0;
self->rx_continuous_ctrl_cnt = 0;
@@ -1864,7 +1877,8 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
.sbus_reg_write = bes2600_sdio_reg_write,
.init = bes2600_sdio_misc_init,
#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
- .pipe_read = bes2600_sdio_pipe_read,
+ /* Patch C v3: .pipe_read removed; bus_rx_batch replaces it. */
+ .bus_rx_batch = bes2600_sdio_read_rx_batch,
#endif
#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
.pipe_send = bes2600_sdio_pipe_send,
@@ -1884,9 +1898,15 @@ static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
long unsigned int old_ts, new_ts;
struct sbus_priv *self = hw_priv->sbus_priv;
+ /*
+ * Patch C v3: rx_work removed. Wait for IRQ-timestamp activity
+ * to settle by polling self->last_irq_timestamp via msleep
+ * (best-effort). The caller already knows the bh thread will
+ * process pending bh_rx during its next wait_event round.
+ */
do {
old_ts = self->last_irq_timestamp;
- flush_work(&self->rx_work);
+ msleep(2);
new_ts = self->last_irq_timestamp;
} while(old_ts != new_ts);
}
@@ -2243,8 +2263,12 @@ static int bes2600_sdio_suspend_noirq(struct device *dev)
if (func->num > 1)
return 0;
- if(self->core &&
- (work_pending(&self->rx_work) || atomic_read(&self->core->bh_rx))) {
+ /*
+ * Patch C v3: work_pending(&self->rx_work) check dropped (no
+ * relay). bh_rx atomic alone tells us whether the bh thread
+ * has un-processed RX events queued.
+ */
+ if (self->core && atomic_read(&self->core->bh_rx)) {
bes_devel("%s: Suspend interrupted.\n", __func__);
return -EAGAIN;
}
diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c
index fab3bf0..febcaf4 100644
--- a/drivers/staging/bes2600/bh.c
+++ b/drivers/staging/bes2600/bh.c
@@ -959,6 +959,119 @@ static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct
}
}
+/*
+ * Direct-deliver an RX SKB into the WSM/mac80211 stack.
+ *
+ * Patch C v3 (no-relay architecture, matches cw1200): the bh thread
+ * calls bes2600_sdio_read_rx_batch which calls
+ * bes2600_sdio_extract_packets which calls THIS function per parsed
+ * SKB. No rx_queue, no sdio_rx_work, no inter-thread handoff.
+ *
+ * Single-writer-from-bh invariant on hw_priv->hw_bufs_used,
+ * hw_priv->hw_bufs_used_vif[] and hw_priv->wsm_tx_pending[] is
+ * preserved BY CONSTRUCTION — there is now only one writer (the bh
+ * thread itself), same as cw1200's design. No atomic_t conversion
+ * needed.
+ *
+ * Contract:
+ * - process context, sleepable. wsm_handle_rx (wsm.c, EXPORT_SYMBOL)
+ * acquires wsm_cmd.lock and may sleep on wait_event_timeout.
+ * - caller holds no bes2600 spinlock. bes2600_sdio_unlock(self) is
+ * called inside read_rx_batch before extract_packets is invoked.
+ * - SKB ownership: function frees on every path (success + error).
+ * - No need to wake the bh thread on TX-confirm — we ARE the bh
+ * thread; tx_burst is signalled by returning *tx_out = 1 to the
+ * caller (bh_rx_helper), which propagates it to bh's outer loop.
+ */
+int bes2600_bh_handle_rx_skb(struct bes2600_common *priv, struct sk_buff *skb)
+{
+ struct wsm_hdr *wsm;
+ size_t wsm_len;
+ u16 wsm_id;
+ u8 wsm_seq;
+ int tx = 0;
+ u32 confirm_label = 0x0;
+
+ if (!skb)
+ return 0;
+
+ wsm = (struct wsm_hdr *)skb->data;
+ wsm_len = __le16_to_cpu(wsm->len);
+ if (WARN_ON(wsm_len > skb->len)) {
+ bes_err("wsm_len err %d %d\n", (int)wsm_len, (int)skb->len);
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ if (priv->wsm_enable_wsm_dumps)
+ print_hex_dump(KERN_DEBUG, "<-- ", DUMP_PREFIX_NONE, 16, 1,
+ skb->data, wsm_len, false);
+
+ wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
+ wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+ bes_devel("bes2600_bh_handle_rx_skb wsm_id:0x%04x seq:%d\n",
+ wsm_id, wsm_seq);
+
+ skb_trim(skb, wsm_len);
+
+ if (wsm_id == 0x0800) {
+ wsm_handle_exception(priv,
+ &skb->data[sizeof(*wsm)],
+ wsm_len - sizeof(*wsm));
+ bes_err("wsm exception\n");
+ dev_kfree_skb(skb);
+ return -1;
+ } else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) {
+ bes_err("seq error! %u. %u. 0x%x.", wsm_seq,
+ priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id);
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ bes2600_bh_parse_wakeup_event(priv, skb);
+
+ priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7;
+
+ if (IS_DRIVER_TO_MCU_CMD(wsm_id))
+ confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label);
+
+ if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) {
+ int rc = wsm_release_tx_buffer(priv, 1);
+ bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id));
+
+ if (rc < 0) {
+ bes_err("wsm_release_tx_buffer failed: %d\n", rc);
+ dev_kfree_skb(skb);
+ return rc;
+ } else if (rc > 0) {
+ tx = 1;
+ }
+ }
+
+ /* wsm_handle_rx takes care of SKB lifetime: zeroes *skb_p if consumed. */
+ if (wsm_handle_rx(priv, wsm_id, wsm, &skb)) {
+ bes_err("wsm_handle_rx failed (id=0x%04x)\n", wsm_id);
+ if (skb)
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ if (skb)
+ dev_kfree_skb(skb);
+
+ /*
+ * Signal "tx side has new headroom" via atomic so the bh outer
+ * loop's wait_event predicate notices on its next wait. No
+ * cross-thread wake needed because we are the bh thread; the
+ * outer loop will pick this up after read_rx_batch returns.
+ */
+ if (tx)
+ atomic_inc(&priv->bh_tx);
+
+ return 0;
+}
+EXPORT_SYMBOL(bes2600_bh_handle_rx_skb);
+
static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
{
struct sk_buff *skb = NULL;
@@ -970,10 +1083,18 @@ static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */
#if defined(BES_SDIO_RX_MULTIPLE_ENABLE)
- skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv);
- if (!skb)
- return 0;
- rx = 1; // always consider rx pipe not empty
+ /*
+ * Patch C v3: the bh thread does the SDIO read inline via
+ * sbus_ops->bus_rx_batch. bes2600_sdio_read_rx_batch reads the
+ * multi-RX coalesced frames out of the chip and delivers each
+ * one inline via bes2600_bh_handle_rx_skb (no rx_queue, no
+ * pipe_read, no inter-thread handoff). Return value: 0 on
+ * success (bh outer loop will check whether to continue),
+ * negative on read error.
+ */
+ if (priv->sbus_ops->bus_rx_batch)
+ return priv->sbus_ops->bus_rx_batch(priv->sbus_priv);
+ return 0;
#else
u32 ctrl_reg = 0;
size_t read_len = 0;
diff --git a/drivers/staging/bes2600/bh.h b/drivers/staging/bes2600/bh.h
index 7be82dc..9ed08b1 100644
--- a/drivers/staging/bes2600/bh.h
+++ b/drivers/staging/bes2600/bh.h
@@ -39,6 +39,15 @@ int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id,
int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
struct wsm_tx_confirm *tx_confirm);
+/*
+ * Direct-deliver an RX SKB into the WSM/mac80211 stack from the bh thread.
+ * Called by bes2600_sdio_extract_packets per RX frame, no queueing.
+ * Process context, sleepable, caller holds no bes2600 spinlock.
+ * Function frees skb on every path. See bh.c for full contract.
+ */
+int bes2600_bh_handle_rx_skb(struct bes2600_common *hw_priv,
+ struct sk_buff *skb);
+
void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx);
void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx);
diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h
index cb90890..96b1d4c 100644
--- a/drivers/staging/bes2600/sbus.h
+++ b/drivers/staging/bes2600/sbus.h
@@ -83,6 +83,14 @@ struct sbus_ops {
* Returns 0 on success or a negative errno.
*/
int (*bus_reset)(struct sbus_priv *self);
+ /*
+ * Read a batch of RX frames inline from the bus and deliver each
+ * one via bes2600_bh_handle_rx_skb(). Called from the bh thread
+ * (process context, sleepable). Replaces the
+ * sdio_rx_work + rx_queue + pipe_read relay (Patch C v3, 2026).
+ * Returns 0 on success, negative on read error.
+ */
+ int (*bus_rx_batch)(struct sbus_priv *self);
};
void bes2600_irq_handler(struct bes2600_common *priv);
--
2.54.0
@@ -1,7 +1,7 @@
From 4ec7d25817af09654fb9439e472890f69281840c Mon Sep 17 00:00:00 2001
From 2f9b4c719faf9563895c064439a7da25f35c8fc7 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
Subject: [PATCH 07/29] 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
@@ -44,14 +44,14 @@ claiming the bus.
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
drivers/staging/bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
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
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
index f172d53..b9d836f 100644
--- a/drivers/staging/bes2600/bes2600_sdio.c
+++ b/drivers/staging/bes2600/bes2600_sdio.c
@@ -94,6 +94,7 @@ struct sbus_priv {
@@ -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];
@@ -59,7 +59,7 @@ index b595365..7bc922c 100644
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)
@@ -1136,7 +1137,26 @@ static void sdio_tx_work(struct work_struct *work)
}
}
@@ -87,7 +87,7 @@ index b595365..7bc922c 100644
total_len += align;
++scatters;
/*del_node:*/
@@ -1853,6 +1873,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
if (!self->single_gathered_buffer)
return -ENOMEM;
#endif
@@ -105,7 +105,7 @@ index b595365..7bc922c 100644
#ifdef BES_SDIO_RXTX_TOGGLE
self->fw_started = false;
#endif
@@ -1981,6 +2012,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
@@ -1984,6 +2015,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));
}
@@ -119,5 +119,5 @@ index b595365..7bc922c 100644
kfree(self);
}
--
2.53.0
2.54.0
+45 -2
View File
@@ -12,8 +12,10 @@ set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
fixtures="${repo_root}/tests/ka-promote/fixtures"
# Phase-3 ground truth — recorded 2026-05-18, fresnel cumulative b2sum.
FRESNEL_EXPECTED_B2SUM=4d9d93c655ea701b587bf1383c794f41b1aeb3bc32bca69ce3488852ec2c1474a2f47585608598b39ac05671490b8df63c5bc7d093f87e1afd5a92f908891b67
# Phase-3 ground truth — re-recorded 2026-05-19 after issue #31 fix
# (write_cumulative now strips per-input trailers + emits canonical
# separators between, but not after, concatenated patches).
FRESNEL_EXPECTED_B2SUM=9c21751cc48ab57cdf48058cc4309752de169c567bbb898c342ff3e4a5cc79add53e3fd4217c2ae2ae7c16b0f19518cf1791907367e1ea9ef16458e1e90c05e0
pass=0
fail=0
@@ -110,6 +112,47 @@ echo
echo "Running ka-promote test suite from $repo_root"
echo
# ----- unit: strip_trailer + write_cumulative shape (issue #31) -----
echo "::: strip_trailer + cumulative shape (issue #31)"
python3 - "$repo_root" <<'PY'
import importlib.util, pathlib, sys, tempfile, os
root = pathlib.Path(sys.argv[1])
from importlib.machinery import SourceFileLoader
mod = SourceFileLoader("ka_promote", str(root/"bin"/"ka-promote")).load_module()
# strip_trailer accepts both shapes and yields newline-terminated body
assert mod.strip_trailer(b"...body...\n-- \n2.54.0\n\n") == b"...body...\n"
assert mod.strip_trailer(b"...body...\n-- \n2.53.0\n\n") == b"...body...\n"
assert mod.strip_trailer(b"...body...\n-- \n2.20\n\n") == b"...body...\n"
assert mod.strip_trailer(b"...body...\n") == b"...body...\n"
assert mod.strip_trailer(b"...body...") == b"...body...\n"
# Multiple trailing blanks after the version still strip
assert mod.strip_trailer(b"x\n-- \n2.54.0\n\n\n") == b"x\n"
# write_cumulative: 3 inputs (mix of with-/without-trailer), check ordering
with tempfile.TemporaryDirectory() as d:
p1 = os.path.join(d, "a.patch"); open(p1,"wb").write(b"PA\n-- \n2.54.0\n\n")
p2 = os.path.join(d, "b.patch"); open(p2,"wb").write(b"PB\n") # already bare
p3 = os.path.join(d, "c.patch"); open(p3,"wb").write(b"PC\n-- \n2.40.1\n\n")
out = os.path.join(d, "out.patch")
resolved = [{"src": p1}, {"src": p2}, {"src": p3}]
mod.write_cumulative(resolved, out)
body = open(out,"rb").read()
assert body == b"PA\n-- \n2.54.0\n\nPB\n-- \n2.54.0\n\nPC\n", repr(body)
# Last patch (PC) must NOT carry an orphan trailer at EOF
assert not body.rstrip(b"\n").endswith(b"2.40.1"), \
f"last patch's trailer leaked into cumulative: {body[-40:]!r}"
print("PASS")
PY
if [ $? -eq 0 ]; then
results+=("PASS strip_trailer + cumulative shape (issue #31)")
pass=$((pass+1))
else
results+=("FAIL strip_trailer + cumulative shape (issue #31)")
fail=$((fail+1))
fi
echo
# Use the real fleet/fresnel.yaml — copy into a sandbox so the test is hermetic.
mkdir -p /tmp/ka-promote-parity-fixture
cp "$repo_root/fleet/fresnel.yaml" /tmp/ka-promote-parity-fixture/fresnel.yaml