forked from marfrit/kernel-agent
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b04c8cd501 | |||
| 38fd672940 | |||
| 8b356aa11f | |||
| 443f5e992e | |||
| 2f119a3fb7 | |||
| 7a86ebb587 | |||
| 731e98e079 | |||
| bae99da612 | |||
| 3d10a2c21a | |||
| 3ee0ef7d86 | |||
| 878e86f103 | |||
| 4d98a8169d | |||
| 84734ba527 | |||
| c9e9ad973c | |||
| a840f76907 | |||
| 989b8842fb | |||
| f203b70f4f | |||
| a254b6f0bb | |||
| 43c8f0cba8 | |||
| 42b0c5042a | |||
| 4c80458d1f | |||
| 96af34d775 | |||
| 95be39ef80 | |||
| 9092d9aaaa | |||
| 44c6c3fa4f | |||
| 3c6d3567f0 | |||
| 91fe815c4c | |||
| 0e1075fbdd | |||
| f8d56d8635 | |||
| fb4bfdc2ff | |||
| 2d2b9ee8ba | |||
| 7f60a3a37c | |||
| d5893e65f0 | |||
| 2783d5ec60 | |||
| 91617fae14 | |||
| ea3b2fb813 | |||
| 62a6f88abd | |||
| ad7c61a81b | |||
| a6549605f0 | |||
| e53db55959 | |||
| 18da673ccc |
@@ -0,0 +1,7 @@
|
|||||||
|
# ka-promote / ka-build output
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# transient
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
.DS_Store
|
||||||
@@ -106,20 +106,29 @@ persistent, audit trail per item).
|
|||||||
## Verbs (explicit, parameterized, audit-issue auto-filed)
|
## Verbs (explicit, parameterized, audit-issue auto-filed)
|
||||||
|
|
||||||
```
|
```
|
||||||
ka-promote <campaign> <patch-or-glob> --to <scope>
|
ka-promote <host> # resolve fleet/<host>.yaml → cumulative.patch + manifest.lock [bin/ka-promote — implemented Phase 6, issue #22]
|
||||||
|
ka-import <campaign> <patch-or-glob> --to <scope> # patches from campaign → scope-tagged tree (today: manual git workflow)
|
||||||
ka-close <campaign> --status success
|
ka-close <campaign> --status success
|
||||||
ka-abandon <campaign> --keep-as-archive | --purge-from-fleet
|
ka-abandon <campaign> --keep-as-archive | --purge-from-fleet
|
||||||
ka-install <host>
|
ka-build <host> # render PKGBUILD template with cumulative b2sum, run makepkg [next verb, issue TBD]
|
||||||
|
ka-install <host> # scp + pacman -U + extlinux/mkinitcpio + heartbeat [last verb, issue TBD]
|
||||||
ka-keep <job-id> [--for <duration>]
|
ka-keep <job-id> [--for <duration>]
|
||||||
ka-pause-prune / ka-resume-prune
|
ka-pause-prune / ka-resume-prune
|
||||||
ka-restore-archive <job-id>
|
ka-restore-archive <job-id>
|
||||||
ka-snooze <issue-id> [--for <duration>]
|
ka-snooze <issue-id> [--for <duration>]
|
||||||
ka-debug <job-id> # shells into the same container that ran the build
|
ka-debug <job-id> # shells into the same container that ran the build
|
||||||
ka-status # per-host one-liner with drift/pending state
|
ka-status # per-host one-liner with drift/pending state [bin/ka-status — implemented Phase 1]
|
||||||
ka-migrate-tree --from <p> --to <p>
|
ka-migrate-tree --from <p> --to <p>
|
||||||
ka-wake-data # wraps wake-host data through His
|
ka-wake-data # wraps wake-host data through His
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: the original spec had `ka-promote <campaign> <patch-or-glob> --to <scope>`
|
||||||
|
("promote patches from a campaign into the canonical tree"). That semantic
|
||||||
|
moved to `ka-import` to free `ka-promote` for the manifest-resolution role
|
||||||
|
its issue (#22) and the implemented `bin/ka-promote` actually fulfil. `ka-import`
|
||||||
|
remains unimplemented — patches still land in `patches/` via the regular git
|
||||||
|
+ PR workflow.
|
||||||
|
|
||||||
Conversational invocation triggers a y/n confirmation enumerating what will
|
Conversational invocation triggers a y/n confirmation enumerating what will
|
||||||
happen. Direct CLI invocation executes immediately.
|
happen. Direct CLI invocation executes immediately.
|
||||||
|
|
||||||
@@ -162,11 +171,13 @@ manifest rewrite); paths stable otherwise.
|
|||||||
## Build hosts
|
## Build hosts
|
||||||
|
|
||||||
```
|
```
|
||||||
Host Where Role Wake? Notes
|
Host Where Role Wake? Notes
|
||||||
──────────────────────────────────────────────────────────────────────────
|
──────────────────────────────────────────────────────────────────────────
|
||||||
boltzmann Rock 5 ITX+ aarch64 primary always container kbuild-aarch64
|
boltzmann Rock 5 ITX+ aarch64 primary always container kbuild-aarch64
|
||||||
fermi hertz LXD aarch64 fallback always matches kbuild-aarch64 profile
|
ampere CoolPi GenBook aarch64 secondary on-demand RK3588 32GB; same uarch as boltzmann,
|
||||||
kbuild-x86 data CT x86_64 on-demand wakes via His; idle 30 min → release
|
wakes via His; idle 30 min → release
|
||||||
|
fermi hertz LXD aarch64 fallback always matches kbuild-aarch64 profile
|
||||||
|
kbuild-x86 data CT x86_64 on-demand wakes via His; idle 30 min → release
|
||||||
```
|
```
|
||||||
|
|
||||||
Native make on the assigned build host. **No distcc** for kernel-agent
|
Native make on the assigned build host. **No distcc** for kernel-agent
|
||||||
@@ -224,15 +235,17 @@ via `ka-snooze <issue-id> [--for <duration>]`.
|
|||||||
|
|
||||||
## Bootstrap reference build (2026-05-09 — fresnel)
|
## Bootstrap reference build (2026-05-09 — fresnel)
|
||||||
|
|
||||||
First end-to-end run, before any `ka-*` CLI exists. Documented here as the
|
First end-to-end run, before `ka-promote` / `ka-build` / `ka-install` existed.
|
||||||
canonical worked example so future ka-* implementations have a concrete
|
Documented here as the canonical worked example; the substrate that the ka-*
|
||||||
substrate to replay. Issue #3 (fresnel DTS persistence) closed by this
|
verbs are/will-be implemented against. Issue #3 (fresnel DTS persistence) closed by this
|
||||||
build.
|
build. `ka-promote` (issue #22) replaced the manual step #1 below as of 2026-05-18.
|
||||||
|
|
||||||
### Inputs
|
### Inputs
|
||||||
|
|
||||||
- **Baseline:** mmind/linux-rockchip @ `v7.0` (Heiko Stübner / Collabora,
|
- **Baseline:** torvalds/linux @ `v7.0` (verified during ka-promote Phase 3,
|
||||||
via kernel.org).
|
issue #22 — mmind/linux-rockchip does not ship a plain `v7.0` tag despite
|
||||||
|
earlier docs; mmind kept in fresnel.yaml as informational
|
||||||
|
`patch_authoring_context`).
|
||||||
- **Patches** (scope `board/pinebook-pro`):
|
- **Patches** (scope `board/pinebook-pro`):
|
||||||
- `0001-arm64-dts-rk3399-pinebook-pro-add-OC-OPP-tables-1704-2184.patch`
|
- `0001-arm64-dts-rk3399-pinebook-pro-add-OC-OPP-tables-1704-2184.patch`
|
||||||
- `0002-arm64-dts-rk3399-pinebook-pro-enable-hdmi-sound.patch`
|
- `0002-arm64-dts-rk3399-pinebook-pro-enable-hdmi-sound.patch`
|
||||||
@@ -247,13 +260,14 @@ build.
|
|||||||
|
|
||||||
### Manual substitute for each ka-* verb
|
### Manual substitute for each ka-* verb
|
||||||
|
|
||||||
| Designed verb | What we did manually |
|
| Designed verb | What we did manually | Status |
|
||||||
|---|---|
|
|---|---|---|
|
||||||
| `ka-promote fresnel-fourier <patches> --to board/pinebook-pro` | Authored 3 patches with proper headers/scope tags, pushed to `marfrit/kernel-agent/patches/board/pinebook-pro/` via Gitea contents API as `claude-noether`. |
|
| `ka-import fresnel-fourier <patches> --to board/pinebook-pro` (originally named `ka-promote` in this row) | Authored 3 patches with proper headers/scope tags, pushed to `marfrit/kernel-agent/patches/board/pinebook-pro/` via Gitea contents API as `claude-noether`. | still manual — `ka-import` unimplemented |
|
||||||
| `ka-build fresnel` | On boltzmann: cloned linux v7.0 from kernel.org, ran `makepkg -s --skipchecksums --skippgpcheck` against `marfrit-packages/arch/linux-fresnel-fourier/PKGBUILD`. Native aarch64 (boltzmann is RK3588). One headers-pkg bug discovered (`ln -sr` on missing parent dir) and fixed mid-flight. Repackaged. |
|
| `ka-promote fresnel` (new — manifest → cumulative.patch + manifest.lock) | n/a (didn't exist) | **automated 2026-05-18, issue #22** |
|
||||||
| `ka-sign + push` | scp pkgs hertz → `sudo /opt/herding/bin/marfrit-publish-arch aarch64 <pkg>` per pkg. Script signs with key `92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C`, runs repo-add, rsyncs to nc. |
|
| `ka-build fresnel` | On boltzmann: cloned linux v7.0 from kernel.org, ran `makepkg -s --skipchecksums --skippgpcheck` against `marfrit-packages/arch/linux-fresnel-fourier/PKGBUILD`. Native aarch64 (boltzmann is RK3588). One headers-pkg bug discovered (`ln -sr` on missing parent dir) and fixed mid-flight. Repackaged. | still manual — next verb to implement |
|
||||||
| `ka-install fresnel` (consent-via-action) | `sudo pacman -U /tmp/<pkg>` over LAN scp (HTTPS to nc was throttled by fresnel's wifi). pacman post-transaction hook updated extlinux. mkinitcpio run manually because the standard hook trigger watches `vmlinuz` not `Image`. |
|
| `ka-sign + push` | scp pkgs hertz → `sudo /opt/herding/bin/marfrit-publish-arch aarch64 <pkg>` per pkg. Script signs with key `92D5E96D8F63C75E4116AA1FF5C8C4603D0D250C`, runs repo-add, rsyncs to nc. | still manual — folded into `ka-build` |
|
||||||
| Bar 1..3 verification | SSH heartbeat OK, `pacman -Q linux-fresnel-fourier` = `7.0-1`, post-reboot cluster0 1.704 GHz / cluster1 2.184 GHz confirmed. |
|
| `ka-install fresnel` (consent-via-action) | `sudo pacman -U /tmp/<pkg>` over LAN scp (HTTPS to nc was throttled by fresnel's wifi). pacman post-transaction hook updated extlinux. mkinitcpio run manually because the standard hook trigger watches `vmlinuz` not `Image`. | still manual — last verb to implement |
|
||||||
|
| Bar 1..3 verification | SSH heartbeat OK, `pacman -Q linux-fresnel-fourier` = `7.0-1`, post-reboot cluster0 1.704 GHz / cluster1 2.184 GHz confirmed. | folded into `ka-install` |
|
||||||
|
|
||||||
### Files / locations involved
|
### Files / locations involved
|
||||||
|
|
||||||
@@ -288,10 +302,16 @@ build.
|
|||||||
|
|
||||||
### Out of scope this round (explicit defer)
|
### Out of scope this round (explicit defer)
|
||||||
|
|
||||||
- **vb2 dma_resv RFC v2** + panfrost IOMMU_CACHE for RK3399 — would have closed
|
- **vb2 dma_resv RFC v2** — *resolved 2026-05-15.* Markus iterated v2 locally
|
||||||
the fresnel-fourier campaign criterion-4 readback transitive-proof gap, but
|
on boltzmann reaching pkgrel=14; the v2 series attaches the fence at
|
||||||
v2 isn't implemented (RFC v1 rejected upstream). Deferred to a follow-up
|
`device_run` (slept-OK context per Dufresne's v1 review). Now carried in
|
||||||
build once v2 lands. See `marfrit/dmabuf-modifier-triage#3`.
|
`patches/subsystem/media/videobuf2/dma-resv-release-fence/` and included
|
||||||
|
in `fleet/fresnel.yaml`. Still in scope for upstream targeting; default
|
||||||
|
remains "build-tree only, no PR until explicitly asked"
|
||||||
|
(`feedback_no_upstream.md`).
|
||||||
|
- **panfrost IOMMU_CACHE for RK3399** — sibling kernel work that targets the
|
||||||
|
readback transitive-proof gap that vb2_dma_resv alone doesn't close.
|
||||||
|
Still deferred until that lands; ship together when ready.
|
||||||
- **Replace** `linux-eos-arm` rather than coexist alongside — preserves easy
|
- **Replace** `linux-eos-arm` rather than coexist alongside — preserves easy
|
||||||
rollback at u-boot. Can flip to `provides=(linux-eos-arm) conflicts=(...)`
|
rollback at u-boot. Can flip to `provides=(linux-eos-arm) conflicts=(...)`
|
||||||
later once burn-in proves the OC kernel reliable.
|
later once burn-in proves the OC kernel reliable.
|
||||||
|
|||||||
Executable
+301
@@ -0,0 +1,301 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""ka-promote — resolve fleet/<host>.yaml + emit cumulative.patch + manifest.lock.
|
||||||
|
|
||||||
|
First of the three writing verbs (ka-promote → ka-build → ka-install).
|
||||||
|
Read-mostly: only writes to ${KA_BUILD_DIR:-./build}/<host>/<baseline_ref>/.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
ka-promote <host>
|
||||||
|
ka-promote <host> --output-dir <path>
|
||||||
|
ka-promote <host> --validate-against <linux-checkout>
|
||||||
|
ka-promote --list-hosts
|
||||||
|
ka-promote --version
|
||||||
|
|
||||||
|
Exit codes:
|
||||||
|
0 success
|
||||||
|
2 missing input (manifest, patch file, series-dir)
|
||||||
|
3 --validate-against failed (ref mismatch or apply-check failure)
|
||||||
|
4 manifest parse / schema error
|
||||||
|
|
||||||
|
Language note: pure python3 (not bash like ka-status). The data shape
|
||||||
|
here — YAML in, YAML out, dict construction, per-file hashing, glob
|
||||||
|
resolution — fits python naturally; bash + python -c heredocs would be
|
||||||
|
quoting hell for no readability gain. See issue #22 comment 1132.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
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)
|
||||||
|
sys.exit(code)
|
||||||
|
|
||||||
|
|
||||||
|
def find_repo_root():
|
||||||
|
here = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
root = os.path.dirname(here)
|
||||||
|
if not os.path.isdir(os.path.join(root, "fleet")):
|
||||||
|
die(f"fleet/ not found relative to {here}", 4)
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def list_hosts(fleet_dir):
|
||||||
|
for path in sorted(glob.glob(os.path.join(fleet_dir, "*.yaml"))):
|
||||||
|
print(os.path.basename(path)[:-5])
|
||||||
|
|
||||||
|
|
||||||
|
def load_manifest(path):
|
||||||
|
try:
|
||||||
|
raw = open(path, "rb").read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
die(f"manifest not found: {path}", 2)
|
||||||
|
sha = hashlib.sha256(raw).hexdigest()
|
||||||
|
try:
|
||||||
|
m = yaml.safe_load(raw)
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
die(f"manifest parse error: {e}", 4)
|
||||||
|
if not isinstance(m, dict):
|
||||||
|
die(f"manifest root must be a mapping: {path}", 4)
|
||||||
|
for key in ("host", "baseline", "includes"):
|
||||||
|
if key not in m:
|
||||||
|
die(f"manifest missing required key '{key}': {path}", 4)
|
||||||
|
if not isinstance(m["includes"], list) or not m["includes"]:
|
||||||
|
die(f"manifest.includes must be a non-empty list: {path}", 4)
|
||||||
|
return m, sha
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_includes(includes, patches_root):
|
||||||
|
"""Walk manifest.includes, expand series-dirs, dedupe-check, hash."""
|
||||||
|
seen = set()
|
||||||
|
resolved = []
|
||||||
|
order = 0
|
||||||
|
for entry in includes:
|
||||||
|
if not isinstance(entry, str):
|
||||||
|
die(f"includes entry must be a string, got {type(entry).__name__}: {entry!r}", 4)
|
||||||
|
if entry in seen:
|
||||||
|
die(f"duplicate include: {entry}", 4)
|
||||||
|
seen.add(entry)
|
||||||
|
src_path = os.path.join(patches_root, entry)
|
||||||
|
if entry.endswith(".patch"):
|
||||||
|
if not os.path.isfile(src_path):
|
||||||
|
die(f"missing patch: {src_path}", 2)
|
||||||
|
order += 1
|
||||||
|
resolved.append({
|
||||||
|
"apply_order": order,
|
||||||
|
"include": entry,
|
||||||
|
"src": src_path,
|
||||||
|
"from_series": False,
|
||||||
|
})
|
||||||
|
elif entry.endswith("/"):
|
||||||
|
dir_path = src_path.rstrip("/")
|
||||||
|
if not os.path.isdir(dir_path):
|
||||||
|
die(f"missing series-dir: {dir_path}", 2)
|
||||||
|
files = sorted(glob.glob(os.path.join(dir_path, "*.patch")))
|
||||||
|
files = [f for f in files if os.path.basename(f) != COVER_LETTER]
|
||||||
|
if not files:
|
||||||
|
die(f"series-dir has no applied patches (only cover-letter or empty): {dir_path}", 2)
|
||||||
|
for f in files:
|
||||||
|
order += 1
|
||||||
|
resolved.append({
|
||||||
|
"apply_order": order,
|
||||||
|
"include": entry + os.path.basename(f),
|
||||||
|
"src": f,
|
||||||
|
"from_series": True,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
die(f"include must end in '.patch' or '/': {entry}", 4)
|
||||||
|
for r in resolved:
|
||||||
|
with open(r["src"], "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
r["sha256"] = hashlib.sha256(data).hexdigest()
|
||||||
|
r["size"] = len(data)
|
||||||
|
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:
|
||||||
|
n = len(resolved)
|
||||||
|
for i, r in enumerate(resolved):
|
||||||
|
with open(r["src"], "rb") as src:
|
||||||
|
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)
|
||||||
|
return size, b2
|
||||||
|
|
||||||
|
|
||||||
|
def write_lock(lock_path, *, host, manifest_rel, manifest_sha, baseline,
|
||||||
|
resolved, cumulative_size, cumulative_b2sum):
|
||||||
|
epoch = os.environ.get("SOURCE_DATE_EPOCH")
|
||||||
|
if epoch:
|
||||||
|
generated_at = datetime.fromtimestamp(int(epoch), tz=timezone.utc).isoformat()
|
||||||
|
else:
|
||||||
|
generated_at = datetime.now(tz=timezone.utc).isoformat()
|
||||||
|
lock = {
|
||||||
|
"ka_promote_version": VERSION,
|
||||||
|
"schema_version": SCHEMA_VERSION,
|
||||||
|
"generated_at": generated_at,
|
||||||
|
"host": host,
|
||||||
|
"manifest": {"path": manifest_rel, "sha256": manifest_sha},
|
||||||
|
"baseline": baseline,
|
||||||
|
"resolved_patches": [
|
||||||
|
{
|
||||||
|
"apply_order": r["apply_order"],
|
||||||
|
"include": r["include"],
|
||||||
|
"sha256": r["sha256"],
|
||||||
|
"size": r["size"],
|
||||||
|
"from_series": r["from_series"],
|
||||||
|
}
|
||||||
|
for r in resolved
|
||||||
|
],
|
||||||
|
"cumulative": {
|
||||||
|
"path": "cumulative.patch",
|
||||||
|
"size": cumulative_size,
|
||||||
|
"b2sum": cumulative_b2sum,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(lock_path, "w") as f:
|
||||||
|
yaml.dump(lock, f, sort_keys=True, default_flow_style=False)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_against(checkout, baseline_ref, cumulative_path):
|
||||||
|
# `.git` is a directory in a plain checkout, a file (gitdir pointer)
|
||||||
|
# in a worktree. `os.path.exists` covers both.
|
||||||
|
if not os.path.exists(os.path.join(checkout, ".git")):
|
||||||
|
die(f"--validate-against: not a git checkout: {checkout}", 3)
|
||||||
|
def git(*args):
|
||||||
|
return subprocess.run(
|
||||||
|
["git", *args], cwd=checkout, capture_output=True, text=True
|
||||||
|
)
|
||||||
|
r = git("rev-parse", f"{baseline_ref}^{{tree}}")
|
||||||
|
if r.returncode != 0:
|
||||||
|
die(f"baseline ref '{baseline_ref}' not found in checkout {checkout}", 3)
|
||||||
|
baseline_tree = r.stdout.strip()
|
||||||
|
head_tree = git("rev-parse", "HEAD^{tree}").stdout.strip()
|
||||||
|
if head_tree != baseline_tree:
|
||||||
|
die(f"checkout HEAD tree {head_tree} != baseline.ref {baseline_ref} tree {baseline_tree}. "
|
||||||
|
"Refusing apply-check on diverged tree.", 3)
|
||||||
|
# Working tree must match HEAD too — `git apply --check` runs against
|
||||||
|
# the working tree, not HEAD, so a dirty tree gives false negatives.
|
||||||
|
r = git("status", "--porcelain")
|
||||||
|
if r.stdout.strip():
|
||||||
|
die(f"checkout {checkout} has uncommitted changes. "
|
||||||
|
"`git reset --hard {0} && git clean -fdx` first.".format(baseline_ref), 3)
|
||||||
|
r = git("apply", "--check", cumulative_path)
|
||||||
|
if r.returncode != 0:
|
||||||
|
die(f"git apply --check failed:\n{r.stderr}", 3)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
p = argparse.ArgumentParser(prog="ka-promote", add_help=True)
|
||||||
|
p.add_argument("host", nargs="?", help="fleet host name (omit with --list-hosts/--version)")
|
||||||
|
p.add_argument("--output-dir", help="override ${KA_BUILD_DIR:-<repo>/build}")
|
||||||
|
p.add_argument("--validate-against", metavar="CHECKOUT",
|
||||||
|
help="run git apply --check against a clean baseline.ref checkout")
|
||||||
|
p.add_argument("--list-hosts", action="store_true", help="list available fleet/<host>.yaml manifests")
|
||||||
|
p.add_argument("--version", action="store_true", help="print ka-promote schema version + exit")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
repo_root = find_repo_root()
|
||||||
|
fleet_dir = os.path.join(repo_root, "fleet")
|
||||||
|
patches_root = os.path.join(repo_root, "patches")
|
||||||
|
|
||||||
|
if args.version:
|
||||||
|
print(f"ka-promote version {VERSION} (schema {SCHEMA_VERSION})")
|
||||||
|
return 0
|
||||||
|
if args.list_hosts:
|
||||||
|
list_hosts(fleet_dir)
|
||||||
|
return 0
|
||||||
|
if not args.host:
|
||||||
|
p.error("host is required (or use --list-hosts / --version)")
|
||||||
|
|
||||||
|
manifest_path = os.path.join(fleet_dir, f"{args.host}.yaml")
|
||||||
|
manifest, manifest_sha = load_manifest(manifest_path)
|
||||||
|
|
||||||
|
if manifest.get("host") != args.host:
|
||||||
|
die(f"manifest.host {manifest.get('host')!r} does not match filename {args.host!r}", 4)
|
||||||
|
|
||||||
|
baseline = manifest["baseline"]
|
||||||
|
if "ref" not in baseline:
|
||||||
|
die("manifest.baseline.ref is required", 4)
|
||||||
|
baseline_ref = baseline["ref"]
|
||||||
|
|
||||||
|
resolved = resolve_includes(manifest["includes"], patches_root)
|
||||||
|
|
||||||
|
out_root = args.output_dir or os.environ.get("KA_BUILD_DIR") or os.path.join(repo_root, "build")
|
||||||
|
out_dir = os.path.join(out_root, args.host, baseline_ref)
|
||||||
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
cumulative_path = os.path.join(out_dir, "cumulative.patch")
|
||||||
|
|
||||||
|
size, b2sum = write_cumulative(resolved, cumulative_path)
|
||||||
|
write_lock(
|
||||||
|
os.path.join(out_dir, "manifest.lock"),
|
||||||
|
host=args.host,
|
||||||
|
manifest_rel=os.path.relpath(manifest_path, repo_root),
|
||||||
|
manifest_sha=manifest_sha,
|
||||||
|
baseline=baseline,
|
||||||
|
resolved=resolved,
|
||||||
|
cumulative_size=size,
|
||||||
|
cumulative_b2sum=b2sum,
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.validate_against:
|
||||||
|
validate_against(args.validate_against, baseline_ref, cumulative_path)
|
||||||
|
|
||||||
|
print(f"ka-promote: {args.host} -> {out_dir}")
|
||||||
|
print(f" cumulative: cumulative.patch ({size} bytes)")
|
||||||
|
print(f" b2sum: {b2sum}")
|
||||||
|
print(f" patches: {len(resolved)} resolved ({sum(1 for r in resolved if r['from_series'])} from series-dirs)")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Executable
+136
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# ka-status — per-host kernel-agent state summary.
|
||||||
|
#
|
||||||
|
# Reads fleet/*.yaml manifests + queries Gitea for open [ka:*] issues +
|
||||||
|
# probes each host (where reachable) for the installed kernel-package
|
||||||
|
# version. Designed to give a first-look "what's the state" before any
|
||||||
|
# ka-promote / ka-install action.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ka-status # summary across all manifests
|
||||||
|
# ka-status <host> # detail for one host
|
||||||
|
#
|
||||||
|
# Read-only. Never mutates state. No sudo. No SSH-into-host writes.
|
||||||
|
#
|
||||||
|
# Phase 1 deliverable. Future ka-* CLI verbs (ka-promote / ka-close /
|
||||||
|
# ka-install) build on the same Gitea-API + manifest-parsing skeleton.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GITEA_URL="${GITEA_URL:-https://git.reauktion.de}"
|
||||||
|
REPO="${KERNEL_AGENT_REPO:-marfrit/kernel-agent}"
|
||||||
|
TOKEN_FILE="${KERNEL_AGENT_TOKEN_FILE:-/opt/herding/etc/claude-identities/noether.creds}"
|
||||||
|
|
||||||
|
# Resolve token from per-host claude-identity creds, or env override.
|
||||||
|
token=""
|
||||||
|
if [ -n "${GITEA_TOKEN:-}" ]; then
|
||||||
|
token="$GITEA_TOKEN"
|
||||||
|
elif [ -r "$TOKEN_FILE" ]; then
|
||||||
|
token="$(grep -E '^GITEA_TOKEN=' "$TOKEN_FILE" | head -1 | cut -d= -f2)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Locate fleet/ — script lives in bin/ next to fleet/.
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
fleet_dir="${script_dir}/../fleet"
|
||||||
|
[ -d "$fleet_dir" ] || { echo "fleet/ not found relative to $script_dir" >&2; exit 2; }
|
||||||
|
|
||||||
|
api_get() {
|
||||||
|
local path="$1"
|
||||||
|
local args=(--silent --show-error --max-time 15)
|
||||||
|
[ -n "$token" ] && args+=(-H "Authorization: token $token")
|
||||||
|
curl "${args[@]}" "${GITEA_URL}/api/v1/${path}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open ka-prefixed issues, JSON array on stdout.
|
||||||
|
fetch_ka_issues() {
|
||||||
|
api_get "repos/${REPO}/issues?state=open&type=issues&limit=50" \
|
||||||
|
| python3 -c '
|
||||||
|
import json, sys
|
||||||
|
issues = json.load(sys.stdin)
|
||||||
|
def kind(t):
|
||||||
|
if not t.startswith("["): return None
|
||||||
|
p = t.find("]")
|
||||||
|
return t[1:p] if p > 0 else None
|
||||||
|
out = [{
|
||||||
|
"number": i["number"],
|
||||||
|
"title": i["title"],
|
||||||
|
"kind": kind(i["title"]),
|
||||||
|
} for i in issues if kind(i["title"]) and kind(i["title"]).startswith("ka:")]
|
||||||
|
print(json.dumps(out))
|
||||||
|
' 2>/dev/null || echo "[]"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse a fleet/<host>.yaml manifest (very-narrow YAML subset).
|
||||||
|
manifest_field() {
|
||||||
|
local file="$1" key="$2"
|
||||||
|
grep -E "^[[:space:]]*${key}:" "$file" | head -1 | sed -E "s/^[^:]*:[[:space:]]*//; s/[[:space:]]+#.*//; s/^[\"']//; s/[\"']$//"
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest_pkgname() {
|
||||||
|
local file="$1"
|
||||||
|
awk '/^package:/{p=1; next} p && /^[a-z]/{p=0} p && /^[[:space:]]+name:/{sub(/^[[:space:]]+name:[[:space:]]*/,""); print; exit}' "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Probe a host for installed kernel-package version (best-effort, non-blocking).
|
||||||
|
probe_installed() {
|
||||||
|
local host="$1" pkg="$2"
|
||||||
|
ssh -o ConnectTimeout=3 -o BatchMode=yes -o StrictHostKeyChecking=accept-new \
|
||||||
|
"${host}.fritz.box" "pacman -Q '$pkg' 2>/dev/null || dpkg-query -W -f='\${Package} \${Version}\n' '$pkg' 2>/dev/null || echo 'host-up:not-installed'" 2>/dev/null \
|
||||||
|
|| echo "host-down"
|
||||||
|
}
|
||||||
|
|
||||||
|
issues_json="$(fetch_ka_issues)"
|
||||||
|
|
||||||
|
# Per-host issue grouping — match on title containing the host name (cheap heuristic;
|
||||||
|
# proper kernel-agent will tag issues with a host label).
|
||||||
|
issues_for_host() {
|
||||||
|
local host="$1"
|
||||||
|
echo "$issues_json" | python3 -c "
|
||||||
|
import json, sys
|
||||||
|
host = '$host'
|
||||||
|
issues = json.load(sys.stdin)
|
||||||
|
hits = [i for i in issues if host in i['title'].lower()]
|
||||||
|
for h in hits:
|
||||||
|
print(f\" #{h['number']} [{h['kind']}] {h['title']}\")
|
||||||
|
" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
print_host() {
|
||||||
|
local file="$1"
|
||||||
|
local host pkg
|
||||||
|
host="$(basename "$file" .yaml)"
|
||||||
|
pkg="$(manifest_pkgname "$file")"
|
||||||
|
local arch="$(manifest_field "$file" arch)"
|
||||||
|
local soc="$(manifest_field "$file" soc)"
|
||||||
|
local board="$(manifest_field "$file" board)"
|
||||||
|
|
||||||
|
printf '\n══ %s ══\n' "$host"
|
||||||
|
printf ' manifest: arch=%s soc=%s board=%s\n' "$arch" "$soc" "$board"
|
||||||
|
printf ' package: %s\n' "$pkg"
|
||||||
|
if [ -n "$pkg" ]; then
|
||||||
|
printf ' installed: %s\n' "$(probe_installed "$host" "$pkg")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local n=0
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
if [ $n -eq 0 ]; then printf ' open ka-issues:\n'; fi
|
||||||
|
printf '%s\n' "$line"
|
||||||
|
n=$((n+1))
|
||||||
|
done < <(issues_for_host "$host")
|
||||||
|
[ $n -eq 0 ] && printf ' open ka-issues: (none for this host)\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -ge 1 ]; then
|
||||||
|
f="${fleet_dir}/${1}.yaml"
|
||||||
|
[ -r "$f" ] || { echo "no manifest for host '$1'" >&2; exit 2; }
|
||||||
|
print_host "$f"
|
||||||
|
else
|
||||||
|
printf 'kernel-agent status (repo: %s)\n' "$REPO"
|
||||||
|
printf 'open [ka:*] issues total: %s\n' "$(echo "$issues_json" | python3 -c 'import json,sys; print(len(json.load(sys.stdin)))')"
|
||||||
|
for f in "$fleet_dir"/*.yaml; do
|
||||||
|
[ -e "$f" ] || continue
|
||||||
|
print_host "$f"
|
||||||
|
done
|
||||||
|
fi
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# kernel-agent manifest for ampere (CoolPi GenBook / Rockchip RK3588)
|
||||||
|
#
|
||||||
|
# Status: bootstrap. ka-promote / ka-build / ka-install CLI not yet
|
||||||
|
# implemented; the canonical patch set + baseline below is the input
|
||||||
|
# for the manual flow that produces linux-ampere-fourier (same shape
|
||||||
|
# as fresnel's bootstrap reference build).
|
||||||
|
#
|
||||||
|
# Asks #2 (VP9 enablement on RK3588 rkvdec) and #3 (AV1 dec integration)
|
||||||
|
# from kernel-agent issue #6 are NOT addressed in this manifest —
|
||||||
|
# tracked separately for a follow-up sprint.
|
||||||
|
|
||||||
|
host: ampere
|
||||||
|
arch: arm64
|
||||||
|
soc: rockchip/rk3588
|
||||||
|
module: coolpi-cm5
|
||||||
|
board: coolpi-cm5-genbook
|
||||||
|
distro: archlinux-arm
|
||||||
|
|
||||||
|
baseline:
|
||||||
|
tree: torvalds/linux
|
||||||
|
url: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
|
||||||
|
ref: v7.0-rc3
|
||||||
|
upstream_compat: linux-7.0-rc3
|
||||||
|
|
||||||
|
# 2026-05-16 bisect note: the linux-rk3588-marfrit branch tip @ f8f3ad9
|
||||||
|
# (18 commits ahead of v7.0-rc3) BLACK-SCREENS ampere — connector
|
||||||
|
# reports connected/enabled/dpms On + SDDM starts + backlight on, but
|
||||||
|
# panel shows no pixels. Decomposing the suspend/resume patch (0010
|
||||||
|
# family) into 5 atomic sub-commits and reverting all 5 did NOT recover
|
||||||
|
# display (test-arch-plus-pcie3 branch). The 6 patches listed below
|
||||||
|
# applied on top of v7.0-rc3 directly DO boot with working display
|
||||||
|
# (ampere-minimal-devices branch @ 7c241f2). Regression source is in
|
||||||
|
# one of the remaining 12 commits in f8f3ad9 — top suspect:
|
||||||
|
# 55d1b3dcc05e "clk: rockchip: rk3588: Drop CLK_SET_RATE_PARENT from
|
||||||
|
# DCLK_VOP2_SRC" (touches display controller clock parent rate). Other
|
||||||
|
# excluded commits: Shawn Lin pcie3 phy series, Cristian Ciocaltea
|
||||||
|
# clk/dts/dw-dp fixes, Sebastian Reichel hdmirx Rock 5 ITX, Pedro Alves
|
||||||
|
# btrtl, and the suspend/resume patch 0010 family. Bisect campaign
|
||||||
|
# separately.
|
||||||
|
#
|
||||||
|
# Branch ampere-minimal-devices on git.reauktion.de/marfrit/linux-rk3588-marfrit
|
||||||
|
# is the verified-working tip if you want a ready-to-fetch ref instead
|
||||||
|
# of patches-on-mainline.
|
||||||
|
|
||||||
|
# Scope-tagged patch includes. Resolve to patches/<scope>/<file>.patch.
|
||||||
|
# Apply order matters: pwm15 pinctrl (soc) must precede the genbook
|
||||||
|
# pwm-fan node consumer.
|
||||||
|
includes:
|
||||||
|
- soc/rockchip/rk3588/0001-arm64-dts-rockchip-rk3588-Add-pwm15-pinctrl-entries.patch
|
||||||
|
- board/coolpi-cm5-genbook/0002-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-Add-pwm-fan.patch
|
||||||
|
- module/coolpi-cm5/0003-arm64-dts-rockchip-rk3588-coolpi-cm5-Fix-power-off-by-enabling-RK806-as-system-power-controller.patch
|
||||||
|
- board/coolpi-cm5-genbook/0004-arm64-dts-rockchip-rk3588-coolpi-cm5-genbook-Enable-speaker-output-via-audio-graph-card.patch
|
||||||
|
- 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):
|
||||||
|
# - 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.
|
||||||
|
# - misc_patches/genbook/kernel/0006 (arm64 Kconfig: do not select
|
||||||
|
# HAVE_GCC_PLUGINS). Local-only build-config; not for upstream and
|
||||||
|
# possibly redundant with the linux-rk3588-marfrit branch already.
|
||||||
|
# - misc_patches/genbook/kernel/0009 (Bluetooth btrtl RTL_SEC_PROJ
|
||||||
|
# non-fatal). Different subsystem; promote under driver/bluetooth/
|
||||||
|
# when a campaign demands.
|
||||||
|
# - misc_patches/genbook/kernel/0010 (multi-driver suspend/wakeup fix).
|
||||||
|
# Promote under soc/rockchip/rk3588/ or split per affected driver
|
||||||
|
# once the RockHard campaign decides the upstream-targeting shape.
|
||||||
|
|
||||||
|
config:
|
||||||
|
source: /proc/config.gz on running ampere kernel (7.0.0-rc3-ARCH+, hand-managed before this bootstrap)
|
||||||
|
strategy: snapshot, fold to baseline, accept-new with rationale on diff
|
||||||
|
|
||||||
|
package:
|
||||||
|
name: linux-ampere-fourier
|
||||||
|
versioning: "${baseline_ref}.kafr${pkgrel}" # 7.0.rc3.kafr1 etc.
|
||||||
|
install_mode: alongside
|
||||||
|
conflicts: []
|
||||||
|
provides: []
|
||||||
|
kernel_suffix: -ampere-fourier # /boot/firmware/Image-7.0.0-rc3-ampere-fourier
|
||||||
|
bootloader: extlinux
|
||||||
|
bootloader_path: /boot/firmware/extlinux/extlinux.conf # vfat partition on mmcblk0p1
|
||||||
|
boot_path: /boot/firmware/ # vfat, 1.5G, ~1G free
|
||||||
|
# ampere boots from a separate FAT partition (mmcblk0p1), unlike fresnel which
|
||||||
|
# uses the root partition's /boot/. The extlinux-add hook needs to write to
|
||||||
|
# /boot/firmware/extlinux/extlinux.conf, not /boot/extlinux/.
|
||||||
|
|
||||||
|
verify:
|
||||||
|
bar1_ssh_heartbeat: required
|
||||||
|
bar2_pkg_version: required
|
||||||
|
bar3_dtb_match:
|
||||||
|
- power-off via 'shutdown -h' actually powers down (RK806 system-power-controller wired)
|
||||||
|
- pwm15 pinctrl exposes group3-pwm15-m0..m3 entries
|
||||||
|
- audio: speakers driven via ES8316 graph (not just headphone)
|
||||||
|
bar4_per_patch_probe: opt-in
|
||||||
|
bar5_burn_in: skip # laptop, runtime not constant
|
||||||
|
|
||||||
|
build_host:
|
||||||
|
primary: ampere # self-host, 8 cores RK3588, 32 GB RAM, native arch
|
||||||
|
secondary: boltzmann # also RK3588 32 GB, kbuild-aarch64 container surrogate
|
||||||
|
fallback: fermi # hertz LXD, ALARM aarch64
|
||||||
|
# No distcc for kernel-agent builds — native make on the assigned host only.
|
||||||
|
# ampere is self-hosting today because boltzmann was busy with userspace
|
||||||
|
# builds when the bootstrap ran; either host can take the work.
|
||||||
|
|
||||||
|
backup:
|
||||||
|
pre_install: hertz:/sparfuxdata/kernel-agent-backups/ampere/${replaced_version}/
|
||||||
+17
-10
@@ -1,9 +1,11 @@
|
|||||||
# kernel-agent manifest for fresnel (Pinebook Pro / Rockchip RK3399)
|
# kernel-agent manifest for fresnel (Pinebook Pro / Rockchip RK3399)
|
||||||
#
|
#
|
||||||
# Status: bootstrap, manually-driven. ka-* CLI not yet implemented.
|
# Status: ka-promote-consumable. Used as Phase-3 parity reference for ka-promote
|
||||||
# This manifest is the input ka-promote / ka-build will consume once landed.
|
# bring-up (issue #22). ka-build / ka-install CLI still pending.
|
||||||
# Until then it documents the canonical patch set + baseline for the manual
|
#
|
||||||
# build that produces linux-fresnel-fourier.
|
# baseline.tree corrected 2026-05-18: mmind/linux-rockchip does not ship a
|
||||||
|
# plain v7.0 tag; baseline is torvalds/linux v7.0. mmind kept as informational
|
||||||
|
# patch_authoring_context.
|
||||||
|
|
||||||
host: fresnel
|
host: fresnel
|
||||||
arch: arm64
|
arch: arm64
|
||||||
@@ -12,10 +14,11 @@ board: pinebook-pro
|
|||||||
distro: archlinux-arm # EndeavourOS pacman base
|
distro: archlinux-arm # EndeavourOS pacman base
|
||||||
|
|
||||||
baseline:
|
baseline:
|
||||||
tree: mmind/linux-rockchip
|
tree: torvalds/linux
|
||||||
url: https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git
|
url: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
|
||||||
ref: v7.0
|
ref: v7.0
|
||||||
upstream_compat: linux-7.0 # what the patches target
|
upstream_compat: linux-7.0 # what the patches target
|
||||||
|
patch_authoring_context: mmind/linux-rockchip # informational — patches authored against Rockchip rebase
|
||||||
|
|
||||||
# Scope-tagged patch includes. Each entry resolves to
|
# Scope-tagged patch includes. Each entry resolves to
|
||||||
# patches/<scope>/.../<file>.patch in marfrit/kernel-agent.
|
# patches/<scope>/.../<file>.patch in marfrit/kernel-agent.
|
||||||
@@ -23,13 +26,17 @@ includes:
|
|||||||
- board/pinebook-pro/0001-arm64-dts-rk3399-pinebook-pro-add-OC-OPP-tables-1704-2184.patch
|
- board/pinebook-pro/0001-arm64-dts-rk3399-pinebook-pro-add-OC-OPP-tables-1704-2184.patch
|
||||||
- board/pinebook-pro/0002-arm64-dts-rk3399-pinebook-pro-enable-hdmi-sound.patch
|
- board/pinebook-pro/0002-arm64-dts-rk3399-pinebook-pro-enable-hdmi-sound.patch
|
||||||
- board/pinebook-pro/0003-arm64-dts-rk3399-pinebook-pro-spi1-max-freq-10MHz.patch
|
- board/pinebook-pro/0003-arm64-dts-rk3399-pinebook-pro-spi1-max-freq-10MHz.patch
|
||||||
|
# vb2_dma_resv RFC v2 series — added 2026-05-15 (Markus iterated v2 locally
|
||||||
|
# on boltzmann reaching pkgrel=14; pre-v2 decision was "defer". v2 attaches
|
||||||
|
# the fence at device_run in slept-OK context per Dufresne's v1 review).
|
||||||
|
- subsystem/media/videobuf2/dma-resv-release-fence/0004-media-videobuf2-add-opt-in-dma_resv-producer-fence-h.patch
|
||||||
|
- subsystem/media/videobuf2/dma-resv-release-fence/0005-media-hantro-attach-dma_resv-release-fence-at-device.patch
|
||||||
|
- subsystem/media/videobuf2/dma-resv-release-fence/0006-media-rockchip-rga-attach-dma_resv-release-fence-at-.patch
|
||||||
|
|
||||||
# Explicitly NOT included (tracked elsewhere, decision logged):
|
# Explicitly NOT included (tracked elsewhere, decision logged):
|
||||||
# - subsystem/media/videobuf2/dma-resv-release-fence/ (RFC v1 rejected;
|
|
||||||
# v2 in design — see marfrit/dmabuf-modifier-triage#3. Skip until v2 lands
|
|
||||||
# or we explicitly accept v1-shape parity with ohm.)
|
|
||||||
# - driver/panfrost/iommu-cache-rk3399/ (sibling kernel work; ship together
|
# - driver/panfrost/iommu-cache-rk3399/ (sibling kernel work; ship together
|
||||||
# with vb2_dma_resv when it lands.)
|
# once it lands. Targets the readback transitive-proof gap that vb2_dma_resv
|
||||||
|
# alone doesn't close.)
|
||||||
|
|
||||||
config:
|
config:
|
||||||
source: /proc/config.gz on running fresnel kernel (linux-eos-arm 6.19.9-99)
|
source: /proc/config.gz on running fresnel kernel (linux-eos-arm 6.19.9-99)
|
||||||
|
|||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
# kernel-agent manifest for ohm (PineTab2 / Rockchip RK3566 + BES2600 SDIO WiFi/BT)
|
||||||
|
#
|
||||||
|
# Status: scaffolding from 2026-05-16. Patches/scopes are mirrored;
|
||||||
|
# the build pipeline (cumulative-patch generation, makepkg invocation,
|
||||||
|
# sign+publish) still relies on the hand-managed flow in
|
||||||
|
# boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/.
|
||||||
|
# Once ka-promote / ka-build land, switch this manifest to authoritative
|
||||||
|
# and retire the boltzmann-side checkouts (canonical + the orphan at
|
||||||
|
# ~/src/besser/danctnix-besser-pkgbuild/ that the fourier-campaign
|
||||||
|
# sibling agent accidentally created).
|
||||||
|
#
|
||||||
|
# See kernel-agent issue #5 for the full migration plan.
|
||||||
|
|
||||||
|
host: ohm
|
||||||
|
arch: arm64
|
||||||
|
soc: rockchip/rk3566
|
||||||
|
board: pinetab2
|
||||||
|
distro: archlinux-arm # DanctNIX PineTab2 variant on Arch Linux ARM aarch64
|
||||||
|
|
||||||
|
baseline:
|
||||||
|
tree: DanctNIX/linux-pinetab2
|
||||||
|
url: https://codeberg.org/DanctNIX/linux-pinetab2
|
||||||
|
ref: v7.0-danctnix1 # _srcname=linux-pinetab2 _srctag=v${pkgver%.*}-${pkgver##*.}
|
||||||
|
upstream_compat: linux-7.0 # DanctNIX rebases off mainline + per-tablet delta
|
||||||
|
|
||||||
|
# Scope-tagged patch includes. Resolves to patches/<scope>/<file>.patch.
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
# 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/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
|
||||||
|
# - 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
|
||||||
|
strategy: snapshot, fold to baseline, accept-new with rationale on diff
|
||||||
|
TODO: migrate config into kernel-agent flow once kconfig-by-manifest lands
|
||||||
|
|
||||||
|
package:
|
||||||
|
name: linux-pinetab2-danctnix-besser
|
||||||
|
versioning: "${baseline_ref}.kafr${pkgrel}" # e.g. v7.0-danctnix1.kafr1
|
||||||
|
install_mode: alongside # coexists with linux-pinetab2 if user wants A/B
|
||||||
|
conflicts: []
|
||||||
|
provides: [linux-pinetab2] # drop-in replacement; pacman can satisfy linux-pinetab2 deps
|
||||||
|
kernel_suffix: -danctnix-besser # vmlinuz-linux-pinetab2-danctnix-besser
|
||||||
|
bootloader: extlinux # PineTab2 standard
|
||||||
|
|
||||||
|
# PKGBUILD currently at:
|
||||||
|
# boltzmann:~/src/besser/marfrit-besser/danctnix-besser-pkgbuild/kernel/PKGBUILD
|
||||||
|
# TARGET location (per issue #5):
|
||||||
|
# marfrit/marfrit-packages/arch/linux-pinetab2-danctnix-besser/PKGBUILD
|
||||||
|
# PENDING the kernel-agent template-rendering pipeline (b2sums regen,
|
||||||
|
# pkgrel bump, cumulative-patch generation from manifest scope).
|
||||||
|
template_at: TBD-marfrit-packages-arch-linux-pinetab2-danctnix-besser
|
||||||
|
replaces_dkms: bes2600-dkms # once cumulative series in this manifest is enough
|
||||||
|
|
||||||
|
verify:
|
||||||
|
bar1_ssh_heartbeat: required
|
||||||
|
bar2_pkg_version: required
|
||||||
|
bar3_module_loaded:
|
||||||
|
- bes2600 module loads
|
||||||
|
- 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:
|
||||||
|
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
|
||||||
|
fallback: fermi # hertz LXD, ALARM aarch64
|
||||||
|
# No distcc per feedback_kernel_agent_no_distcc.md.
|
||||||
|
|
||||||
|
backup:
|
||||||
|
pre_install: hertz:/sparfuxdata/kernel-agent-backups/ohm/${replaced_version}/
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────
|
||||||
|
# OPEN — kernel-agent CLI work blocking full migration:
|
||||||
|
#
|
||||||
|
# 1. ka-promote: read includes[] above + apply_order field (to be added),
|
||||||
|
# concatenate referenced series into a single .patch in the build dir.
|
||||||
|
# Validate it applies cleanly on baseline.ref. Compute b2sum for
|
||||||
|
# template substitution.
|
||||||
|
#
|
||||||
|
# 2. PKGBUILD template renderer: stamp pkgrel, ${_cumulative_b2sum},
|
||||||
|
# ${_srctag} from manifest into a PKGBUILD draft at the
|
||||||
|
# template_at location. Sign + publish to packages.reauktion.de.
|
||||||
|
#
|
||||||
|
# 3. Orphan retirement: surface ~/src/besser/danctnix-besser-pkgbuild/
|
||||||
|
# (NO remote, ~/src/besser/marfrit-besser/... is canonical) to
|
||||||
|
# Markus / fourier campaign for working-state migration BEFORE
|
||||||
|
# deleting. See besser issue #17 for the regression that the
|
||||||
|
# orphan caused. After the kernel-agent flow is authoritative,
|
||||||
|
# delete both checkouts.
|
||||||
|
# ─────────────────────────────────────────────────────────────────────
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Tue, 24 Mar 2026 00:00:00 +0000
|
||||||
|
Subject: [PATCH] arm64: Kconfig: do not select HAVE_GCC_PLUGINS
|
||||||
|
|
||||||
|
GCC plugins intercept compilation in a way that ccache cannot observe:
|
||||||
|
the plugin shared objects inject code after preprocessing, so the
|
||||||
|
preprocessed-source hash that ccache computes does not capture plugin
|
||||||
|
output. Every object file built with a plugin is therefore a ccache
|
||||||
|
miss, effectively disabling the cache.
|
||||||
|
|
||||||
|
Remove the `select HAVE_GCC_PLUGINS` line from the ARM64 arch Kconfig.
|
||||||
|
HAVE_GCC_PLUGINS is the only gate that allows GCC_PLUGINS (and the
|
||||||
|
features that depend on it: STACKLEAK, LATENT_ENTROPY, the GCC-based
|
||||||
|
RANDSTRUCT variant) to be enabled; dropping it makes those options
|
||||||
|
invisible and leaves them off without requiring manual intervention in
|
||||||
|
every .config.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
1 file changed, 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/arch/arm64/Kconfig
|
||||||
|
+++ b/arch/arm64/Kconfig
|
||||||
|
@@ -229,7 +229,6 @@
|
||||||
|
select HAVE_FUNCTION_ERROR_INJECTION
|
||||||
|
select HAVE_FUNCTION_GRAPH_FREGS
|
||||||
|
select HAVE_FUNCTION_GRAPH_TRACER
|
||||||
|
- select HAVE_GCC_PLUGINS
|
||||||
|
select HAVE_HARDLOCKUP_DETECTOR_PERF if PERF_EVENTS && \
|
||||||
|
HW_PERF_EVENTS && HAVE_PERF_EVENTS_NMI
|
||||||
|
select HAVE_HW_BREAKPOINT if PERF_EVENTS
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# patches/arch/arm64/
|
||||||
|
|
||||||
|
Cross-cutting patches that touch `arch/arm64/Kconfig` or other
|
||||||
|
non-board-specific arch-level files. Apply only where explicitly
|
||||||
|
manifested.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
### `0006-arm64-Kconfig-do-not-select-HAVE_GCC_PLUGINS.patch`
|
||||||
|
|
||||||
|
Local-build workaround that makes `arch/arm64` not select
|
||||||
|
`HAVE_GCC_PLUGINS`. **NOT for upstream** — it papers over a missing
|
||||||
|
host-side GCC plugin dependency at build time rather than fixing the
|
||||||
|
makepkg/distro packaging that should provide `gcc-plugin-devel` (or
|
||||||
|
equivalent) when needed.
|
||||||
|
|
||||||
|
Used on ampere when building the kernel from a clean Arch ARM userspace
|
||||||
|
that doesn't have the gcc plugins development headers installed. The
|
||||||
|
proper fix is to install the headers; this patch is a fallback for
|
||||||
|
when the user wants a working kernel without touching the userspace
|
||||||
|
package set.
|
||||||
|
|
||||||
|
Source: `github.com/marfrit/misc_patches/genbook/kernel/0006`
|
||||||
|
Author: Markus Fritsche
|
||||||
+36
@@ -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
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Subject: [PATCH 2/2] arm64: dts: rockchip: rk3588-coolpi-cm5-genbook: Add pwm-fan with thermal cooling
|
||||||
|
|
||||||
|
The CoolPi CM5 GenBook has a PWM-controlled fan connected to pwm15
|
||||||
|
(mux m3, GPIO1_D7). Add a pwm-fan node driven at 20 kHz (50000 ns
|
||||||
|
period) with six cooling levels, and wire it into the package thermal
|
||||||
|
zone with two trip points at 55 °C and 65 °C.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
---
|
||||||
|
1 file changed, 43 insertions(+)
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
@@ -23,6 +23,13 @@
|
||||||
|
pwms = <&pwm6 0 25000 0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
+ fan: pwm-fan {
|
||||||
|
+ compatible = "pwm-fan";
|
||||||
|
+ #cooling-cells = <2>;
|
||||||
|
+ pwms = <&pwm15 0 50000 0>;
|
||||||
|
+ cooling-levels = <1 50 100 150 200 254>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
battery: battery {
|
||||||
|
compatible = "simple-battery";
|
||||||
|
charge-full-design-microamp-hours = <9800000>;
|
||||||
|
@@ -443,3 +450,39 @@
|
||||||
|
remote-endpoint = <&edp1_in_vp2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+&package_thermal {
|
||||||
|
+ polling-delay = <1000>;
|
||||||
|
+
|
||||||
|
+ trips {
|
||||||
|
+ package_fan0: package-fan0 {
|
||||||
|
+ temperature = <55000>;
|
||||||
|
+ hysteresis = <2000>;
|
||||||
|
+ type = "active";
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ package_fan1: package-fan1 {
|
||||||
|
+ temperature = <65000>;
|
||||||
|
+ hysteresis = <2000>;
|
||||||
|
+ type = "active";
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ cooling-maps {
|
||||||
|
+ map0 {
|
||||||
|
+ trip = <&package_fan0>;
|
||||||
|
+ cooling-device = <&fan THERMAL_NO_LIMIT 2>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ map1 {
|
||||||
|
+ trip = <&package_fan1>;
|
||||||
|
+ cooling-device = <&fan 3 THERMAL_NO_LIMIT>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+&pwm15 {
|
||||||
|
+ pinctrl-names = "default";
|
||||||
|
+ pinctrl-0 = <&pwm15m3_pins>;
|
||||||
|
+ status = "okay";
|
||||||
|
+};
|
||||||
|
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Subject: [PATCH 4/4] arm64: dts: rockchip: rk3588-coolpi-cm5-genbook: enable speaker output
|
||||||
|
|
||||||
|
The GenBook carrier board routes the ES8316 HPOL/HPOR outputs to both a
|
||||||
|
headphone jack and an external speaker amplifier. The amplifier is
|
||||||
|
enabled by GPIO1_A6 (active-high) and headphone insertion is detected by
|
||||||
|
GPIO1_B5 (active-high, pull-up).
|
||||||
|
|
||||||
|
Add a label to the shared analog-sound node in the CM5 DTSI so the
|
||||||
|
GenBook DTS can extend it, then override the node to:
|
||||||
|
|
||||||
|
- add pa-gpios for the speaker amplifier enable line (GPIO1_A6)
|
||||||
|
- add hp-det-gpios for headphone jack detection (GPIO1_B5)
|
||||||
|
- extend widgets/routing to include the Speaker path through the
|
||||||
|
audio-graph-card built-in "Amplifier" DAPM output-driver widget,
|
||||||
|
which gates the pa-gpios GPIO on widget power-up/down
|
||||||
|
- add the hp-det pinctrl group for GPIO1_B5
|
||||||
|
|
||||||
|
The "Amplifier" DAPM widget (snd_soc_dapm_out_drv) is provided by
|
||||||
|
audio-graph-card.c and registered at card level. Its event handler
|
||||||
|
drives pa-gpios high on SND_SOC_DAPM_POST_PMU and low on
|
||||||
|
SND_SOC_DAPM_PRE_PMD, giving automatic speaker enable/disable in step
|
||||||
|
with DAPM power management.
|
||||||
|
|
||||||
|
DAPM path for speaker output:
|
||||||
|
ES8316 AIF1RX (DAI) -> Left/Right DAC -> Left/Right Headphone Mixer
|
||||||
|
-> Left/Right Headphone Driver -> HPOL/HPOR [codec OUTPUT pins]
|
||||||
|
-> Amplifier [card OUT_DRV, fires pa-gpios] -> Speaker [SPK terminal]
|
||||||
|
|
||||||
|
The Left/Right Headphone Mixer Left/Right DAC Switch controls, which
|
||||||
|
gate the DAC-to-mixer connections in the DAPM graph, are set on by the
|
||||||
|
UCM BootSequence in the rk3588-es8316 ALSA UCM profile and must remain
|
||||||
|
enabled for the path to be traversable.
|
||||||
|
|
||||||
|
The HPOL/HPOR codec output pins also feed the Headphones HP widget:
|
||||||
|
|
||||||
|
HPOL/HPOR -> Headphones [HP terminal, jack-controlled via hp-det-gpios]
|
||||||
|
|
||||||
|
Both the Speaker and Headphones paths are active whenever a PCM stream
|
||||||
|
is running. Speaker-muting when headphones are inserted is handled at
|
||||||
|
the userspace (UCM) level via JackHWMute on the Speaker UCM device: when
|
||||||
|
PipeWire routes audio away from the Speaker sink on headphone insertion,
|
||||||
|
the absence of an active PCM consumer causes DAPM to power down the
|
||||||
|
Amplifier widget and drive GPIO1_A6 low.
|
||||||
|
|
||||||
|
Note: full speaker output also requires a Speaker SectionDevice in the
|
||||||
|
rk3588-es8316 ALSA UCM HiFi.conf. Without it the HiFi profile's only
|
||||||
|
playback port is Headphones (jack-controlled), causing the profile to be
|
||||||
|
reported as "not available" when no headphones are inserted, and
|
||||||
|
PipeWire falls back to the pro-audio profile with no speaker sub-device.
|
||||||
|
A separate patch to alsa-ucm-conf adds the missing Speaker device.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
---
|
||||||
|
2 files changed, 25 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
@@ -283,7 +283,31 @@
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
+&analog_sound {
|
||||||
|
+ pa-gpios = <&gpio1 RK_PA6 GPIO_ACTIVE_HIGH>;
|
||||||
|
+ hp-det-gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>;
|
||||||
|
+ pinctrl-names = "default";
|
||||||
|
+ pinctrl-0 = <&hp_det>;
|
||||||
|
+
|
||||||
|
+ widgets = "Microphone", "Mic Jack",
|
||||||
|
+ "Headphone", "Headphones",
|
||||||
|
+ "Speaker", "Speaker";
|
||||||
|
+
|
||||||
|
+ routing = "MIC2", "Mic Jack",
|
||||||
|
+ "Headphones", "HPOL",
|
||||||
|
+ "Headphones", "HPOR",
|
||||||
|
+ "Amplifier", "HPOL",
|
||||||
|
+ "Amplifier", "HPOR",
|
||||||
|
+ "Speaker", "Amplifier";
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
&pinctrl {
|
||||||
|
+ headphone {
|
||||||
|
+ hp_det: hp-det {
|
||||||
|
+ rockchip,pins = <1 RK_PB5 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
lcd {
|
||||||
|
lcdpwr_en: lcdpwr-en {
|
||||||
|
rockchip,pins = <0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi
|
||||||
|
@@ -21,7 +21,7 @@
|
||||||
|
serial2 = &uart2;
|
||||||
|
};
|
||||||
|
|
||||||
|
- analog-sound {
|
||||||
|
+ analog_sound: analog-sound {
|
||||||
|
compatible = "audio-graph-card";
|
||||||
|
dais = <&i2s0_8ch_p0>;
|
||||||
|
label = "rk3588-es8316";
|
||||||
|
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Tue, 24 Mar 2026 00:00:00 +0000
|
||||||
|
Subject: [PATCH] arm64: dts: rockchip: rk3588-coolpi-cm5-genbook: add USB-C PD charging
|
||||||
|
|
||||||
|
The GenBook carrier board exposes a USB Type-C port driven by the
|
||||||
|
RK3588 USB3OTG0 controller (usb_host0_xhci / usbdp_phy0 combo). The
|
||||||
|
port uses a Fairchild FUSB302 (I²C address 0x22, bus i2c4) for USB
|
||||||
|
Power Delivery negotiation. Hardware signals:
|
||||||
|
|
||||||
|
GPIO0_PD5 – FUSB302 interrupt (active-low, shared with CC logic)
|
||||||
|
GPIO0_PA0 – VBUS switch enable (active-high)
|
||||||
|
|
||||||
|
Without these changes the kernel registers usb_host0_xhci in
|
||||||
|
peripheral-only, high-speed mode, which prevents PD negotiation and
|
||||||
|
leaves the port unable to charge the battery.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* Add #include <dt-bindings/usb/pd.h> for PDO_FIXED macros.
|
||||||
|
|
||||||
|
* Add vcc5v0_otg regulator: GPIO0_PA0 active-high switch that gates
|
||||||
|
VBUS; used as vbus-supply for the FUSB302 and connected via the new
|
||||||
|
typec5v_pwren pinctrl entry. The previously defined but unreferenced
|
||||||
|
usb_otg_pwren entry (same pin, wrong pull direction) is removed.
|
||||||
|
|
||||||
|
* Add usbc0 (FUSB302) node inside &i2c4:
|
||||||
|
- compatible "fcs,fusb302", reg 0x22
|
||||||
|
- interrupt GPIO0_PD5 (IRQ_TYPE_LEVEL_LOW), pinctrl usbc0_int
|
||||||
|
- usb-c-connector child with:
|
||||||
|
data-role / power-role "dual", try-power-role "sink"
|
||||||
|
pd-revision 2.0 Ver 1.2 (maximum supported by FUSB302)
|
||||||
|
sink-pdos: 5 V/3 A, 9 V/3 A, 12 V/3 A, 15 V/3 A
|
||||||
|
source-pdos: 5 V/3 A
|
||||||
|
DisplayPort alt-mode (SVID 0xff01) declared for orientation
|
||||||
|
switching; three connector ports linking HS (→ usb_host0_xhci),
|
||||||
|
SS (→ usbdp_phy0) and SBU (→ usbdp_phy0) endpoints.
|
||||||
|
|
||||||
|
* Expand &usbdp_phy0 to add mode-switch and orientation-switch
|
||||||
|
capabilities; register endpoint@0 (SS, linked to usbc0_ss) and
|
||||||
|
endpoint@1 (SBU, linked to usbc0_sbu).
|
||||||
|
|
||||||
|
* Replace the &usb_host0_xhci override:
|
||||||
|
- remove dr_mode "peripheral" and maximum-speed "high-speed"
|
||||||
|
(the base DTSI already sets dr_mode "otg")
|
||||||
|
- add usb-role-switch and the port endpoint linked to usbc0_hs
|
||||||
|
This allows the TCPM stack to switch the controller between host
|
||||||
|
and device role as the PD contract dictates.
|
||||||
|
|
||||||
|
* Add pinctrl group usb-typec with:
|
||||||
|
- usbc0_int: GPIO0_PD5 pull-up (FUSB302 /INT)
|
||||||
|
- typec5v_pwren: GPIO0_PA0 pull-down (VBUS switch, default off)
|
||||||
|
|
||||||
|
DAPM / power flow:
|
||||||
|
USB-C charger → FUSB302 CC negotiation → TCPM requests VBUS →
|
||||||
|
vcc5v0_otg regulator enables GPIO0_PA0 → VBUS present on port →
|
||||||
|
DWC3 OTG detects VBUS, enters device/host mode per PD data-role.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
1 file changed, 106 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
@@ -8,6 +8,7 @@
|
||||||
|
|
||||||
|
#include <dt-bindings/leds/common.h>
|
||||||
|
#include <dt-bindings/soc/rockchip,vop2.h>
|
||||||
|
+#include <dt-bindings/usb/pd.h>
|
||||||
|
#include "rk3588-coolpi-cm5.dtsi"
|
||||||
|
|
||||||
|
/ {
|
||||||
|
@@ -153,6 +154,18 @@
|
||||||
|
pinctrl-0 = <&usb_host_pwren>;
|
||||||
|
vin-supply = <&vcc5v0_usb>;
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+ vcc5v0_otg: regulator-vcc5v0-otg {
|
||||||
|
+ compatible = "regulator-fixed";
|
||||||
|
+ regulator-name = "vcc5v0_otg";
|
||||||
|
+ regulator-min-microvolt = <5000000>;
|
||||||
|
+ regulator-max-microvolt = <5000000>;
|
||||||
|
+ enable-active-high;
|
||||||
|
+ gpio = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
|
||||||
|
+ pinctrl-names = "default";
|
||||||
|
+ pinctrl-0 = <&typec5v_pwren>;
|
||||||
|
+ vin-supply = <&vcc5v0_sys>;
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
&edp1 {
|
||||||
|
@@ -239,6 +252,65 @@
|
||||||
|
monitored-battery = <&battery>;
|
||||||
|
power-supplies = <&charger>;
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+ usbc0: usb-typec@22 {
|
||||||
|
+ compatible = "fcs,fusb302";
|
||||||
|
+ reg = <0x22>;
|
||||||
|
+ interrupt-parent = <&gpio0>;
|
||||||
|
+ interrupts = <RK_PD5 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
+ pinctrl-names = "default";
|
||||||
|
+ pinctrl-0 = <&usbc0_int>;
|
||||||
|
+ vbus-supply = <&vcc5v0_otg>;
|
||||||
|
+
|
||||||
|
+ usb_con: connector {
|
||||||
|
+ compatible = "usb-c-connector";
|
||||||
|
+ label = "USB-C";
|
||||||
|
+ data-role = "dual";
|
||||||
|
+ power-role = "dual";
|
||||||
|
+ try-power-role = "sink";
|
||||||
|
+ op-sink-microwatt = <1000000>;
|
||||||
|
+ /* FUSB302 supports PD Rev 2.0 Ver 1.2 */
|
||||||
|
+ pd-revision = /bits/ 8 <0x2 0x0 0x1 0x2>;
|
||||||
|
+ sink-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>,
|
||||||
|
+ <PDO_FIXED(9000, 3000, PDO_FIXED_USB_COMM)>,
|
||||||
|
+ <PDO_FIXED(12000, 3000, PDO_FIXED_USB_COMM)>,
|
||||||
|
+ <PDO_FIXED(15000, 3000, PDO_FIXED_USB_COMM)>;
|
||||||
|
+ source-pdos = <PDO_FIXED(5000, 3000, PDO_FIXED_USB_COMM)>;
|
||||||
|
+
|
||||||
|
+ altmodes {
|
||||||
|
+ displayport {
|
||||||
|
+ svid = /bits/ 16 <0xff01>;
|
||||||
|
+ vdo = <0xffffffff>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ ports {
|
||||||
|
+ #address-cells = <1>;
|
||||||
|
+ #size-cells = <0>;
|
||||||
|
+
|
||||||
|
+ port@0 {
|
||||||
|
+ reg = <0>;
|
||||||
|
+ usbc0_hs: endpoint {
|
||||||
|
+ remote-endpoint = <&usb_host0_xhci_to_usbc0>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ port@1 {
|
||||||
|
+ reg = <1>;
|
||||||
|
+ usbc0_ss: endpoint {
|
||||||
|
+ remote-endpoint = <&usbdp_phy0_ss>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ port@2 {
|
||||||
|
+ reg = <2>;
|
||||||
|
+ usbc0_sbu: endpoint {
|
||||||
|
+ remote-endpoint = <&usbdp_phy0_sbu>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
&i2c5 {
|
||||||
|
@@ -323,15 +395,21 @@
|
||||||
|
rockchip,pins = <1 RK_PD5 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
};
|
||||||
|
|
||||||
|
- usb_otg_pwren: usb-otg-pwren {
|
||||||
|
- rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
- };
|
||||||
|
-
|
||||||
|
usb_host_pwren: usb-host-pwren {
|
||||||
|
rockchip,pins = <1 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
+ usb-typec {
|
||||||
|
+ usbc0_int: usbc0-int {
|
||||||
|
+ rockchip,pins = <0 RK_PD5 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ typec5v_pwren: typec5v-pwren {
|
||||||
|
+ rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_down>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
wifi {
|
||||||
|
bt_pwron: bt-pwron {
|
||||||
|
rockchip,pins = <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
@@ -387,7 +465,24 @@
|
||||||
|
};
|
||||||
|
|
||||||
|
&usbdp_phy0 {
|
||||||
|
+ mode-switch;
|
||||||
|
+ orientation-switch;
|
||||||
|
status = "okay";
|
||||||
|
+
|
||||||
|
+ port {
|
||||||
|
+ #address-cells = <1>;
|
||||||
|
+ #size-cells = <0>;
|
||||||
|
+
|
||||||
|
+ usbdp_phy0_ss: endpoint@0 {
|
||||||
|
+ reg = <0>;
|
||||||
|
+ remote-endpoint = <&usbc0_ss>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ usbdp_phy0_sbu: endpoint@1 {
|
||||||
|
+ reg = <1>;
|
||||||
|
+ remote-endpoint = <&usbc0_sbu>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
&u2phy1 {
|
||||||
|
@@ -431,9 +526,14 @@
|
||||||
|
|
||||||
|
/* Type C port */
|
||||||
|
&usb_host0_xhci {
|
||||||
|
- dr_mode = "peripheral";
|
||||||
|
- maximum-speed = "high-speed";
|
||||||
|
+ usb-role-switch;
|
||||||
|
status = "okay";
|
||||||
|
+
|
||||||
|
+ port {
|
||||||
|
+ usb_host0_xhci_to_usbc0: endpoint {
|
||||||
|
+ remote-endpoint = <&usbc0_hs>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
/* connected to a HUB for camera and BT */
|
||||||
|
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Tue, 24 Mar 2026 00:00:00 +0000
|
||||||
|
Subject: [PATCH] arm64: dts: rockchip: rk3588-coolpi-cm5-genbook: add lid switch and USB3 PHY lane config
|
||||||
|
|
||||||
|
The GenBook laptop has two features missing from the mainline DTS:
|
||||||
|
|
||||||
|
1. Lid switch (MH248 hall-effect sensor on GPIO0_PB0)
|
||||||
|
The lid state is reported via a gpio-keys node using SW_LID / EV_SW.
|
||||||
|
The GPIO is active-low (low = lid closed) and pulled up.
|
||||||
|
wakeup-source is set so that opening the lid can wake the system from
|
||||||
|
suspend. A sensor pinctrl group is added for the GPIO configuration.
|
||||||
|
|
||||||
|
2. usbdp_phy1 lane mux (rockchip,dp-lane-mux = <2 3>)
|
||||||
|
The RK3588 usbdp_phy1 is a combo USB3 + DisplayPort PHY shared between
|
||||||
|
the USB3 host1 port (USB-A connector) and the DisplayPort output on the
|
||||||
|
GenBook board. Lanes 0+1 carry USB3 SuperSpeed to the USB-A socket;
|
||||||
|
lanes 2+3 are routed to the DP connector.
|
||||||
|
|
||||||
|
Without this property the phy-rockchip-usbdp driver selects
|
||||||
|
UDPHY_MODE_USB and configures all four lanes as USB3. That conflicts
|
||||||
|
with the physical routing on this board: lanes 2+3 are not connected to
|
||||||
|
the USB-A socket, so the 4-lane USB3 training fails silently and the
|
||||||
|
USB3 host1 DWC3 controller (usb_host1_xhci) cannot enumerate devices.
|
||||||
|
|
||||||
|
Setting rockchip,dp-lane-mux = <2 3> restricts USB3 to lanes 0+1,
|
||||||
|
matching the physical wiring, and enables correct SuperSpeed operation
|
||||||
|
on the USB-A port.
|
||||||
|
|
||||||
|
The USB2 EHCI host1 controller (usb_host1_ehci) shares the USB power
|
||||||
|
domain with usb_host1_xhci. A stalled USB3 DWC3 probe can delay
|
||||||
|
power-domain activation in a way that prevents the EHCI hub (which
|
||||||
|
carries the integrated webcam) from being enumerated. With the correct
|
||||||
|
lane mux both controllers probe cleanly and the webcam is detected.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
|
||||||
|
1 file changed, 23 insertions(+)
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
@@ -8,6 +8,7 @@
|
||||||
|
|
||||||
|
#include <dt-bindings/leds/common.h>
|
||||||
|
#include <dt-bindings/soc/rockchip,vop2.h>
|
||||||
|
+#include <dt-bindings/input/linux-event-codes.h>
|
||||||
|
#include <dt-bindings/usb/pd.h>
|
||||||
|
#include "rk3588-coolpi-cm5.dtsi"
|
||||||
|
|
||||||
|
@@ -166,6 +167,21 @@
|
||||||
|
pinctrl-0 = <&typec5v_pwren>;
|
||||||
|
vin-supply = <&vcc5v0_sys>;
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+ gpio-key-lid {
|
||||||
|
+ compatible = "gpio-keys";
|
||||||
|
+ pinctrl-names = "default";
|
||||||
|
+ pinctrl-0 = <&mh248_irq_gpio>;
|
||||||
|
+
|
||||||
|
+ lid {
|
||||||
|
+ debounce-interval = <20>;
|
||||||
|
+ gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_LOW>;
|
||||||
|
+ label = "Lid";
|
||||||
|
+ linux,code = <SW_LID>;
|
||||||
|
+ linux,input-type = <EV_SW>;
|
||||||
|
+ wakeup-source;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
&edp1 {
|
||||||
|
@@ -431,6 +447,12 @@
|
||||||
|
rockchip,pins = <4 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+ sensor {
|
||||||
|
+ mh248_irq_gpio: mh248-irq-gpio {
|
||||||
|
+ rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
&pwm6 {
|
||||||
|
@@ -512,6 +534,7 @@
|
||||||
|
};
|
||||||
|
|
||||||
|
&usbdp_phy1 {
|
||||||
|
+ rockchip,dp-lane-mux = <2 3>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
From 0aa15733f32277939a0523276bec8f092a67e28c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Thu, 16 Apr 2026 23:53:05 +0200
|
||||||
|
Subject: [PATCH 5/9] arm64: dts: rockchip: rk3588-coolpi-cm5-genbook: add NPU
|
||||||
|
power-domain link and touchpad wakeup-source
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
- i2c-hid touchpad: add wakeup-source so the PM core arms the IRQ
|
||||||
|
- Add NPU power domain (matching Rock 5B+ which has working suspend),
|
||||||
|
required for proper power-down sequencing.
|
||||||
|
|
||||||
|
Note: this DTS hunk is the suspect for breaking cold-boot eDP probe on
|
||||||
|
ampere (panel-edp WARN_ON + Fixed dependency cycle between /edp@fded0000
|
||||||
|
and panel) — kept as its own commit so it can be reverted in isolation
|
||||||
|
without losing the suspend/resume PM fixes.
|
||||||
|
|
||||||
|
Generated-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
index e3954851b0cb..cabfb380fe27 100644
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
@@ -340,6 +340,7 @@ touchpad: touchpad@2c {
|
||||||
|
interrupt-parent = <&gpio1>;
|
||||||
|
interrupts = <RK_PD6 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
hid-descr-addr = <0x0020>;
|
||||||
|
+ wakeup-source;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -455,6 +456,10 @@ mh248_irq_gpio: mh248-irq-gpio {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
+&pd_npu {
|
||||||
|
+ domain-supply = <&vdd_npu_s0>;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
&pwm6 {
|
||||||
|
pinctrl-0 = <&pwm6m1_pins>;
|
||||||
|
status = "okay";
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
From dd545fa1532921fe9c63360a99c35f60e981aad2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Date: Sun, 17 May 2026 23:18:30 +0200
|
||||||
|
Subject: [PATCH] arm64: dts: rockchip: rk3588-coolpi-cm5-genbook: wire
|
||||||
|
internal microphone
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The CoolPi CM5 GenBook has an internal microphone wired to the ES8316
|
||||||
|
codec MIC1 input. The upstream DTS only declared Mic Jack on MIC2
|
||||||
|
(headset jack), so the DAPM graph has no path between MIC1 and a
|
||||||
|
named widget — internal mic capture cannot work regardless of mixer
|
||||||
|
settings.
|
||||||
|
|
||||||
|
Vendor coolpi-kernel reference (rk3588-cpcm5-genbook.dts):
|
||||||
|
rockchip,audio-routing =
|
||||||
|
"MIC1", "Main Mic",
|
||||||
|
"MIC2", "Headset Mic";
|
||||||
|
|
||||||
|
Mirror in the upstream audio-graph-card binding:
|
||||||
|
- widgets: split Mic Jack into Main Mic (internal) + Headset Mic
|
||||||
|
(external) so each input is addressable from ALSA UCM
|
||||||
|
- routing: MIC1 <- Main Mic, MIC2 <- Headset Mic
|
||||||
|
|
||||||
|
This patch is necessary but not sufficient for working internal mic
|
||||||
|
capture: a separate issue prevents the ES8316 RX aif_out widget from
|
||||||
|
powering up via DAPM when arecord opens the capture PCM (I2S OUT stays
|
||||||
|
inactive, bias_level stays Off). That second issue is not addressed
|
||||||
|
here — this patch only fixes the static DTS topology so a future codec
|
||||||
|
or audio-graph-card driver fix can complete the chain.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
Generated-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||||||
|
---
|
||||||
|
arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts | 7 +++++--
|
||||||
|
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
index e3954851b0cb..e95545eea16a 100644
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5-genbook.dts
|
||||||
|
@@ -377,11 +377,13 @@ &analog_sound {
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&hp_det>;
|
||||||
|
|
||||||
|
- widgets = "Microphone", "Mic Jack",
|
||||||
|
+ widgets = "Microphone", "Main Mic",
|
||||||
|
+ "Microphone", "Headset Mic",
|
||||||
|
"Headphone", "Headphones",
|
||||||
|
"Speaker", "Speaker";
|
||||||
|
|
||||||
|
- routing = "MIC2", "Mic Jack",
|
||||||
|
+ routing = "MIC1", "Main Mic",
|
||||||
|
+ "MIC2", "Headset Mic",
|
||||||
|
"Headphones", "HPOL",
|
||||||
|
"Headphones", "HPOR",
|
||||||
|
"Amplifier", "HPOL",
|
||||||
|
@@ -633,3 +635,4 @@ &pwm15 {
|
||||||
|
pinctrl-0 = <&pwm15m3_pins>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
+
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# board/coolpi-cm5-genbook
|
||||||
|
|
||||||
|
Patches specific to the CoolPi GenBook laptop carrier (RK3588 +
|
||||||
|
CoolPi-CM5 SoM, ARM laptop form factor). The board adds an
|
||||||
|
ES8316-routed audio path, FUSB302 USB-C-PD controller, lid switch,
|
||||||
|
USB3 phy-lane config, and a pwm-controlled fan — none of which are
|
||||||
|
in mainline upstream because the board ships outside Rockchip's
|
||||||
|
official reference designs.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
| File | Adds |
|
||||||
|
|---|---|
|
||||||
|
| `0002-...Add-pwm-fan.patch` | pwm15-driven thermal cooling fan node (depends on `soc/rockchip/rk3588/0001-...pwm15-pinctrl-entries.patch`) |
|
||||||
|
| `0004-...Enable-speaker-output-via-audio-graph-card.patch` | rewires the ES8316 sound graph so HPOL/HPOR also drive the laptop speakers |
|
||||||
|
| `0005-...Enable-USB-C-PD-charging-via-FUSB302.patch` | FUSB302 controller binding for USB-C PD charging |
|
||||||
|
| `0008-...Add-lid-switch-and-USB3-PHY-lane-config.patch` | lid GPIO + USB3 phy-lane config |
|
||||||
|
|
||||||
|
Numbered 2/4/5/8 to match the upstream-series numbering Markus uses in
|
||||||
|
`misc_patches/genbook/kernel/` — gap at 0006 (an SoC-wide
|
||||||
|
`arm64/Kconfig` build-flag patch tracked elsewhere) and 0007 (skipped
|
||||||
|
in the upstream series). 0009 (Bluetooth btrtl) and 0010 (multi-driver
|
||||||
|
suspend/wakeup fix) are tracked in other scopes (driver/bluetooth,
|
||||||
|
soc/rockchip/rk3588) and will be promoted as their respective
|
||||||
|
campaigns demand.
|
||||||
|
|
||||||
|
## Fleet eligibility
|
||||||
|
|
||||||
|
- **ampere** (CoolPi CM5 GenBook): primary target, all 4 patches
|
||||||
|
included in `fleet/ampere.yaml`.
|
||||||
|
|
||||||
|
No other current fleet host uses the CoolPi CM5 GenBook carrier, so
|
||||||
|
no second consumer yet.
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# patches/driver/bes2600/
|
||||||
|
|
||||||
|
BES2600 WiFi driver patches (`drivers/staging/bes2600/*`, mainline-bound).
|
||||||
|
Mirrored from `marfrit/besser/patches/` on 2026-05-16.
|
||||||
|
|
||||||
|
Scope tag: `driver:bes2600` (see `fleet/ohm.yaml` for the consumer).
|
||||||
|
Consumer: ohm (PineTab2, RK3566 + BES2600 SDIO).
|
||||||
|
|
||||||
|
## Series taxonomy
|
||||||
|
|
||||||
|
30 series (15 base + 15 `-danctnix` siblings). The `-danctnix`
|
||||||
|
variants exist because vanilla series don't apply on the DanctNIX
|
||||||
|
kernel base (slightly different in-tree state for `drivers/staging/bes2600/*`).
|
||||||
|
Keep both as separate series until BES2600 lands upstream, then
|
||||||
|
collapse — issue #2 acceptance criterion.
|
||||||
|
|
||||||
|
Each series directory contains numbered `.patch` files plus
|
||||||
|
optionally a `0000-cover-letter.patch` for multi-patch series.
|
||||||
|
|
||||||
|
## Promotion eligibility (per series)
|
||||||
|
|
||||||
|
Marked here for the kernel-agent CLI (`ka-promote`) to pick up.
|
||||||
|
Markus to update as series mature. Default UNSET means "ask before
|
||||||
|
including in a build".
|
||||||
|
|
||||||
|
| Series | promote_eligible | Notes |
|
||||||
|
|------------------------------------|------------------|-------------------------------------------------------------|
|
||||||
|
| `debian-copyright-fsf-address` | unset | Debian packaging metadata; not kernel-side |
|
||||||
|
| `drop-dpd-file-paths` | unset | |
|
||||||
|
| `drop-dpd-file-paths-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `drop-orphan-file-io` | unset | |
|
||||||
|
| `drop-orphan-file-io-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `enable-testmode` | unset | |
|
||||||
|
| `factory-drop-kernel-write` | unset | |
|
||||||
|
| `factory-drop-kernel-write-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `factory-series` | unset | |
|
||||||
|
| `factory-thread-dev` | unset | |
|
||||||
|
| `lmac-recover-via-mmc-hw-reset` | unset | |
|
||||||
|
| `lmac-recover-via-mmc-hw-reset-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-detect-firmware-unsupported` | unset | |
|
||||||
|
| `pm-detect-firmware-unsupported-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-gate-on-handshake` | unset | |
|
||||||
|
| `pm-state-resync` | unset | |
|
||||||
|
| `pm-state-resync-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-timeout-silence` | unset | |
|
||||||
|
| `pm-timeout-silence-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `pm-wake-consume-state` | unset | |
|
||||||
|
| `pm-wake-consume-state-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `remove-chardev-user-interface` | unset | Cross-ref `bes_chardev` merge regression (besser #17) |
|
||||||
|
| `scan-defer-backoff-tune` | unset | |
|
||||||
|
| `scan-defer-backoff-tune-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `scan-defer-on-reject` | unset | |
|
||||||
|
| `scan-defer-on-reject-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `staging-prep-series` | unset | 7-patch cover-letter series; upstream-staging-prep work |
|
||||||
|
| `staging-prep-series-danctnix` | unset | DanctNIX sibling |
|
||||||
|
| `tx-sdio-dma-oob` | unset | |
|
||||||
|
| `tx-sdio-dma-oob-danctnix` | unset | DanctNIX sibling |
|
||||||
|
|
||||||
|
## DKMS-to-in-tree transition path
|
||||||
|
|
||||||
|
`bes2600-dkms` (Mobian fork, in `marfrit/bes2600-dkms`) is the
|
||||||
|
out-of-tree shim that ohm currently uses for the BES2600 wifi+BT.
|
||||||
|
Once these `driver/bes2600/` series land in mainline (or at least in
|
||||||
|
DanctNIX's PineTab2 kernel base):
|
||||||
|
|
||||||
|
1. ohm's manifest drops the `bes2600-dkms` package dependency
|
||||||
|
2. `kernel-agent` builds the in-tree variant via the series listed here
|
||||||
|
3. `marfrit/bes2600-dkms` repo gets archived (kept as history)
|
||||||
|
4. PineTab2 buyers from then on get bes2600 directly out of the kernel
|
||||||
|
|
||||||
|
Track the dropdown in `fleet/ohm.yaml` (`replaces_dkms: bes2600-dkms`
|
||||||
|
once the cumulative series is enough to replace it).
|
||||||
|
|
||||||
|
## Cumulative-patch generation order
|
||||||
|
|
||||||
|
The current single-patch cumulative (`0001-bes2600-besser-cumulative-series.patch`
|
||||||
|
in the existing PKGBUILD) is generated in this order on boltzmann:
|
||||||
|
|
||||||
|
A, B, C v3, F, G, D, E, C2, c5.x, c6.x, c7, H
|
||||||
|
|
||||||
|
This is NOT alphabetical — `C2` follows `E` rather than coming after
|
||||||
|
`C v3`. `ka-promote` MUST honor an explicit series-ordering field
|
||||||
|
when concatenating, not sort by series name. Field name TBD; suggest
|
||||||
|
adding `apply_order:` to `fleet/ohm.yaml` (issue #5 will surface this
|
||||||
|
when the cumulative gets regenerated).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Issue: `git.reauktion.de/marfrit/kernel-agent/issues/2`
|
||||||
|
- Source repo: `git.reauktion.de/marfrit/besser/patches/`
|
||||||
|
- Consumer: ohm (`fleet/ohm.yaml`)
|
||||||
|
- Related: `bes2600-dkms`, `linux-pinetab2-danctnix-besser` PKGBUILD
|
||||||
+313
@@ -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
|
||||||
|
|
||||||
+725
@@ -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],
|
||||||
|
- ×tamp, -1,
|
||||||
|
- hw_priv->pending_frame_id);
|
||||||
|
-
|
||||||
|
- /* Check if frame transmission is timed out.
|
||||||
|
- * Add an extra second with respect to possible
|
||||||
|
- * interrupt loss. */
|
||||||
|
- timeout = timestamp +
|
||||||
|
- WSM_CMD_LAST_CHANCE_TIMEOUT +
|
||||||
|
- 1 * HZ -
|
||||||
|
- jiffies;
|
||||||
|
-
|
||||||
|
- /* And terminate BH tread if the frame is "stuck" */
|
||||||
|
- if (pending && timeout < 0) {
|
||||||
|
- //wiphy_warn(priv->hw->wiphy,
|
||||||
|
- // "Timeout waiting for TX confirm.\n");
|
||||||
|
- bes_devel("bes2600_bh: Timeout waiting for TX confirm.\n");
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
|
||||||
|
- BUG_ON(1);
|
||||||
|
-#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
|
||||||
|
- } else if (!status) {
|
||||||
|
- if (!hw_priv->device_can_sleep
|
||||||
|
- && !atomic_read(&hw_priv->recent_scan)) {
|
||||||
|
- bes_devel("[BH] Device wakedown. Timeout.\n");
|
||||||
|
-#ifndef FPGA_SETUP
|
||||||
|
- WARN_ON(bes2600_reg_write_16(hw_priv,
|
||||||
|
- ST90TDS_CONTROL_REG_ID, 0));
|
||||||
|
- hw_priv->device_can_sleep = true;
|
||||||
|
-#endif
|
||||||
|
- }
|
||||||
|
- continue;
|
||||||
|
- } else if (suspend) {
|
||||||
|
- bes_devel("[BH] Device suspend.\n");
|
||||||
|
- powersave_enabled = 1;
|
||||||
|
- spin_lock(&hw_priv->vif_list_lock);
|
||||||
|
- bes2600_for_each_vif(hw_priv, priv, i) {
|
||||||
|
-#ifdef P2P_MULTIVIF
|
||||||
|
- if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
|
||||||
|
-#else
|
||||||
|
- if (!priv)
|
||||||
|
-#endif
|
||||||
|
- continue;
|
||||||
|
- powersave_enabled &= !!priv->powersave_enabled;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&hw_priv->vif_list_lock);
|
||||||
|
- if (powersave_enabled) {
|
||||||
|
- bes_devel("[BH] No Device wakedown. Suspend.\n");
|
||||||
|
-#ifndef FPGA_SETUP
|
||||||
|
- WARN_ON(bes2600_reg_write_16(hw_priv,
|
||||||
|
- ST90TDS_CONTROL_REG_ID, 0));
|
||||||
|
- hw_priv->device_can_sleep = true;
|
||||||
|
-#endif
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED);
|
||||||
|
- wake_up(&hw_priv->bh_evt_wq);
|
||||||
|
- status = wait_event_interruptible(hw_priv->bh_wq,
|
||||||
|
- BES2600_BH_RESUME == atomic_read(
|
||||||
|
- &hw_priv->bh_suspend));
|
||||||
|
- if (status < 0) {
|
||||||
|
- wiphy_err(hw_priv->hw->wiphy,
|
||||||
|
- "%s: Failed to wait for resume: %ld.\n",
|
||||||
|
- __func__, status);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- bes_devel("[BH] Device resume.\n");
|
||||||
|
- atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
|
||||||
|
- wake_up(&hw_priv->bh_evt_wq);
|
||||||
|
- atomic_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
|
||||||
|
|
||||||
+279
@@ -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
|
||||||
|
|
||||||
+4322
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||||
+92
@@ -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
|
||||||
|
|
||||||
+58
@@ -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
|
||||||
|
|
||||||
+144
@@ -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
|
||||||
|
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
From f31c57adf736df52b3f393f2650920af98b8e8f1 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Fri, 24 Apr 2026 09:40:44 +0200
|
||||||
|
Subject: [PATCH] debian/copyright: drop obsolete FSF street address
|
||||||
|
|
||||||
|
The 'You should have received a copy ... write to the Free
|
||||||
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301 USA' paragraph flags the lintian tag
|
||||||
|
'old-fsf-address-in-copyright-file'. Debian prefers either no
|
||||||
|
address at all or an https://www.gnu.org/licenses/ reference;
|
||||||
|
in this file /usr/share/common-licenses/LGPL-2.1 is already
|
||||||
|
cited a few lines below, so the address is redundant. Replace
|
||||||
|
with the gnu.org URL per current FSF boilerplate.
|
||||||
|
|
||||||
|
Pre-existing text, no change to the licence terms.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
debian/copyright | 3 +--
|
||||||
|
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/debian/copyright b/debian/copyright
|
||||||
|
index 961fc90..3228eec 100644
|
||||||
|
--- a/debian/copyright
|
||||||
|
+++ b/debian/copyright
|
||||||
|
@@ -18,8 +18,7 @@ License: LGPL-2.1
|
||||||
|
License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
- along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
+ along with this library; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
On Debian systems, the full text of the GNU Lesser General Public License
|
||||||
|
version 2.1 can be found in the file "/usr/share/common-licenses/LGPL-2.1".
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+221
@@ -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
|
||||||
|
|
||||||
+294
@@ -0,0 +1,294 @@
|
|||||||
|
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 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
|
||||||
|
|
||||||
|
bes_chardev.c carried three functions gated behind the
|
||||||
|
BES2600_WRITE_DPD_TO_FILE Kconfig/make-flag (default off):
|
||||||
|
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file()
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write()
|
||||||
|
writing a raw DPD calibration blob back to
|
||||||
|
BES2600_DPD_PATH (default /data/cfg/bes2600_dpd.bin, an
|
||||||
|
Android-AOSP path).
|
||||||
|
|
||||||
|
- bes2600_chrdev_read_and_check_dpd_data()
|
||||||
|
filp_open(O_RDONLY) + kernel_read() reading the DPD blob
|
||||||
|
from either BES2600_DPD_GOLDEN_PATH (/data/cfg/…) or
|
||||||
|
BES2600_DEFAULT_DPD_PATH (/lib/firmware/bes2600_dpd.bin),
|
||||||
|
followed by a CRC/version sanity check.
|
||||||
|
|
||||||
|
- bes2600_chrdev_dpd_is_vaild() (sic), the CRC/version helper
|
||||||
|
used only by the read path.
|
||||||
|
|
||||||
|
Plus the bes_cdev.no_dpd field, its module_param, and two
|
||||||
|
intrusion sites in bes2600_chrdev_get_dpd_data() and
|
||||||
|
bes2600_chrdev_update_dpd_data() that invoke the above.
|
||||||
|
|
||||||
|
The Makefile defaults BES2600_WRITE_DPD_TO_FILE=n, so in a stock
|
||||||
|
build all of this is dead code. It is still a standing upstream
|
||||||
|
blocker for exactly the same reasons as the factory-txt write
|
||||||
|
path removed in the preceding patch:
|
||||||
|
|
||||||
|
- filp_open() + kernel_read()/kernel_write() bypass the
|
||||||
|
firmware-class abstraction and LSM-governed access control
|
||||||
|
that apply to /lib/firmware/.
|
||||||
|
- The write target /data/cfg/ is an Android AOSP convention
|
||||||
|
that does not exist on a Linux distribution and cannot be
|
||||||
|
created by the kernel anyway.
|
||||||
|
- A runtime DPD re-calibration is intended to reduce TX EVM
|
||||||
|
after temperature or aging drift; persisting the result via
|
||||||
|
kernel_write() is fundamentally a userspace concern (debugfs
|
||||||
|
dump + userspace tool is the expected route).
|
||||||
|
|
||||||
|
Remove the entire #ifdef BES2600_WRITE_DPD_TO_FILE block from
|
||||||
|
bes_chardev.c (including the inner #ifdef inside
|
||||||
|
bes2600_chrdev_read_and_check_dpd_data() guarding a
|
||||||
|
DPD_BIN_FILE_SIZE size check that only applied to the read-back-
|
||||||
|
its-own-write case), the no_dpd field and module_param, and the
|
||||||
|
two invocation sites. Drop the Kconfig/make-flag and the three
|
||||||
|
associated PATH macros from the Makefile. Net: -155 lines, no
|
||||||
|
remaining filp_open/kernel_read/kernel_write anywhere in
|
||||||
|
bes_chardev.c.
|
||||||
|
|
||||||
|
The in-memory DPD state path is unchanged: bes2600_chrdev_get_dpd_
|
||||||
|
buffer() still allocates a kmalloc'd buffer used by the firmware-
|
||||||
|
download path, bes2600_chrdev_update_dpd_data() still validates
|
||||||
|
the buffer's CRC and transitions bes2600_cdev.wait_state on
|
||||||
|
success, and bes2600_chrdev_free_dpd_data() still releases the
|
||||||
|
buffer on unload. Only the file-I/O side-channel is removed.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 12 ----
|
||||||
|
bes2600/bes_chardev.c | 143 ------------------------------------------
|
||||||
|
2 files changed, 155 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 2c1a850..0dd3606 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -28,7 +28,6 @@ CONFIG_BES2600_WIFI_BOOT_ON ?= y
|
||||||
|
CONFIG_BES2600_BT_BOOT_ON ?= n
|
||||||
|
|
||||||
|
BES2600_GPIO_WAKEUP_AP ?= n
|
||||||
|
-BES2600_WRITE_DPD_TO_FILE ?= n
|
||||||
|
BES2600_TX_MORE_RETRY ?= n
|
||||||
|
|
||||||
|
# bes evb
|
||||||
|
@@ -93,12 +92,6 @@ ccflags-y += -DBES_UNIFIED_PM
|
||||||
|
ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
|
||||||
|
ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG
|
||||||
|
|
||||||
|
-ifeq ($(BES2600_WRITE_DPD_TO_FILE),y)
|
||||||
|
-BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin
|
||||||
|
-BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin
|
||||||
|
-BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin
|
||||||
|
-endif
|
||||||
|
-
|
||||||
|
ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
|
||||||
|
BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
|
||||||
|
endif
|
||||||
|
@@ -135,9 +128,6 @@ ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DRV_VERSION)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y)
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
|
||||||
|
@@ -159,8 +149,6 @@ ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
|
||||||
|
ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y)
|
||||||
|
-ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y)
|
||||||
|
-
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
index e2e4f1b..a02d6d9 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
@@ -63,9 +63,6 @@ struct bes_cdev {
|
||||||
|
struct delayed_work probe_timeout_work;
|
||||||
|
enum bus_probe_state bus_probe;
|
||||||
|
struct work_struct wifi_force_close_work;
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- int no_dpd;
|
||||||
|
-#endif
|
||||||
|
enum pend_read_op read_flag;
|
||||||
|
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
|
||||||
|
u16 wakeup_state; /* for userspace check wakeup reason */
|
||||||
|
@@ -85,9 +82,6 @@ struct bes2600_op_map {
|
||||||
|
|
||||||
|
static struct bes_cdev bes2600_cdev;
|
||||||
|
module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644);
|
||||||
|
-#endif
|
||||||
|
|
||||||
|
extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
@@ -269,137 +263,8 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- if (buffer == NULL || size == 0)
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
- if (ret < 0)
|
||||||
|
- bes_err("write dpd to file failed\n");
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_devel("write dpd to %s\n", path);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool bes2600_chrdev_dpd_is_vaild(u8 *dpd_data)
|
||||||
|
-{
|
||||||
|
- u32 cal_crc = 0;
|
||||||
|
- u32 dpd_crc = le32_to_cpup((__le32 *)(dpd_data));
|
||||||
|
- u32 dpd_ver = le32_to_cpup((__le32 *)(dpd_data + DPD_VERSION_OFFSET));
|
||||||
|
-
|
||||||
|
- /* check version */
|
||||||
|
- if (dpd_ver < DPD_CUR_VERSION)
|
||||||
|
- return false;
|
||||||
|
-
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
- cal_crc = crc32_le(cal_crc, dpd_data + 4, DPD_BIN_SIZE - 4);
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
-
|
||||||
|
- /* check if the dpd data is valid */
|
||||||
|
- if (cal_crc != dpd_crc) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
|
||||||
|
- cal_crc, dpd_crc);
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- u8* read_data = NULL;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- /* open file */
|
||||||
|
- fp = filp_open(file, O_RDONLY, 0);//S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",file);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, DPD_BIN_FILE_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
- /* allocate memory for storing reading data */
|
||||||
|
- read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL);
|
||||||
|
- if (read_data == NULL) {
|
||||||
|
- bes_devel("%s alloc mem fail\n", __func__);
|
||||||
|
- goto err1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* read data from file */
|
||||||
|
- ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
- if (ret < DPD_BIN_SIZE) {
|
||||||
|
- bes_err("%s read fail, ret=%d\n", __func__, ret);
|
||||||
|
- goto err2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* check dpd version and crc */
|
||||||
|
- if (!bes2600_chrdev_dpd_is_vaild(read_data))
|
||||||
|
- goto err2;
|
||||||
|
-
|
||||||
|
- /* close file */
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- /* copy data to external */
|
||||||
|
- *data = read_data;
|
||||||
|
- *len = DPD_BIN_SIZE;;
|
||||||
|
-
|
||||||
|
- /* output debug information */
|
||||||
|
- bes_devel("read dpd data from %s\n", file);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
-err2:
|
||||||
|
- kfree(read_data);
|
||||||
|
-err1:
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- *data = NULL;
|
||||||
|
- *len = 0;
|
||||||
|
- return -1;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
const u8* bes2600_chrdev_get_dpd_data(u32 *len)
|
||||||
|
{
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) {
|
||||||
|
- /* read dpd data from file that stores factory dpd calibration data */
|
||||||
|
- if ((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) &&
|
||||||
|
- (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) {
|
||||||
|
- bes_err("%s read dpd data fail\n", __func__);
|
||||||
|
- return NULL;
|
||||||
|
- } else {
|
||||||
|
- bes2600_cdev.dpd_calied = true;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
if (!bes2600_cdev.dpd_calied)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
@@ -460,14 +325,6 @@ int bes2600_chrdev_update_dpd_data(void)
|
||||||
|
}
|
||||||
|
spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- /* write dpd data to file */
|
||||||
|
- memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE);
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH,
|
||||||
|
- bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE);
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+293
@@ -0,0 +1,293 @@
|
|||||||
|
From 699871fdc6bf1bed6d919732820183e57faeaddc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:04:11 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop BES2600_WRITE_DPD_TO_FILE kernel_*() file paths
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
bes_chardev.c carried three functions gated behind the
|
||||||
|
BES2600_WRITE_DPD_TO_FILE Kconfig/make-flag (default off):
|
||||||
|
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file()
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write()
|
||||||
|
writing a raw DPD calibration blob back to
|
||||||
|
BES2600_DPD_PATH (default /data/cfg/bes2600_dpd.bin, an
|
||||||
|
Android-AOSP path).
|
||||||
|
|
||||||
|
- bes2600_chrdev_read_and_check_dpd_data()
|
||||||
|
filp_open(O_RDONLY) + kernel_read() reading the DPD blob
|
||||||
|
from either BES2600_DPD_GOLDEN_PATH (/data/cfg/…) or
|
||||||
|
BES2600_DEFAULT_DPD_PATH (/lib/firmware/bes2600_dpd.bin),
|
||||||
|
followed by a CRC/version sanity check.
|
||||||
|
|
||||||
|
- bes2600_chrdev_dpd_is_vaild() (sic), the CRC/version helper
|
||||||
|
used only by the read path.
|
||||||
|
|
||||||
|
Plus the bes_cdev.no_dpd field, its module_param, and two
|
||||||
|
intrusion sites in bes2600_chrdev_get_dpd_data() and
|
||||||
|
bes2600_chrdev_update_dpd_data() that invoke the above.
|
||||||
|
|
||||||
|
The Makefile defaults BES2600_WRITE_DPD_TO_FILE=n, so in a stock
|
||||||
|
build all of this is dead code. It is still a standing upstream
|
||||||
|
blocker for exactly the same reasons as the factory-txt write
|
||||||
|
path removed in the preceding patch:
|
||||||
|
|
||||||
|
- filp_open() + kernel_read()/kernel_write() bypass the
|
||||||
|
firmware-class abstraction and LSM-governed access control
|
||||||
|
that apply to /lib/firmware/.
|
||||||
|
- The write target /data/cfg/ is an Android AOSP convention
|
||||||
|
that does not exist on a Linux distribution and cannot be
|
||||||
|
created by the kernel anyway.
|
||||||
|
- A runtime DPD re-calibration is intended to reduce TX EVM
|
||||||
|
after temperature or aging drift; persisting the result via
|
||||||
|
kernel_write() is fundamentally a userspace concern (debugfs
|
||||||
|
dump + userspace tool is the expected route).
|
||||||
|
|
||||||
|
Remove the entire #ifdef BES2600_WRITE_DPD_TO_FILE block from
|
||||||
|
bes_chardev.c (including the inner #ifdef inside
|
||||||
|
bes2600_chrdev_read_and_check_dpd_data() guarding a
|
||||||
|
DPD_BIN_FILE_SIZE size check that only applied to the read-back-
|
||||||
|
its-own-write case), the no_dpd field and module_param, and the
|
||||||
|
two invocation sites. Drop the Kconfig/make-flag and the three
|
||||||
|
associated PATH macros from the Makefile. Net: -155 lines, no
|
||||||
|
remaining filp_open/kernel_read/kernel_write anywhere in
|
||||||
|
bes_chardev.c.
|
||||||
|
|
||||||
|
The in-memory DPD state path is unchanged: bes2600_chrdev_get_dpd_
|
||||||
|
buffer() still allocates a kmalloc'd buffer used by the firmware-
|
||||||
|
download path, bes2600_chrdev_update_dpd_data() still validates
|
||||||
|
the buffer's CRC and transitions bes2600_cdev.wait_state on
|
||||||
|
success, and bes2600_chrdev_free_dpd_data() still releases the
|
||||||
|
buffer on unload. Only the file-I/O side-channel is removed.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 12 ----
|
||||||
|
bes2600/bes_chardev.c | 143 ------------------------------------------
|
||||||
|
2 files changed, 155 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 2c1a850..0dd3606 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -28,7 +28,6 @@ CONFIG_BES2600_WIFI_BOOT_ON ?= y
|
||||||
|
CONFIG_BES2600_BT_BOOT_ON ?= n
|
||||||
|
|
||||||
|
BES2600_GPIO_WAKEUP_AP ?= n
|
||||||
|
-BES2600_WRITE_DPD_TO_FILE ?= n
|
||||||
|
BES2600_TX_MORE_RETRY ?= n
|
||||||
|
|
||||||
|
# bes evb
|
||||||
|
@@ -93,12 +92,6 @@ ccflags-y += -DBES_UNIFIED_PM
|
||||||
|
ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
|
||||||
|
ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG
|
||||||
|
|
||||||
|
-ifeq ($(BES2600_WRITE_DPD_TO_FILE),y)
|
||||||
|
-BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin
|
||||||
|
-BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin
|
||||||
|
-BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin
|
||||||
|
-endif
|
||||||
|
-
|
||||||
|
ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
|
||||||
|
BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
|
||||||
|
endif
|
||||||
|
@@ -135,9 +128,6 @@ ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DRV_VERSION)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH)
|
||||||
|
-ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y)
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
|
||||||
|
@@ -159,8 +149,6 @@ ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
|
||||||
|
ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y)
|
||||||
|
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y)
|
||||||
|
-ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y)
|
||||||
|
-
|
||||||
|
ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
|
||||||
|
ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index e2e4f1b..a02d6d9 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -63,9 +63,6 @@ struct bes_cdev {
|
||||||
|
struct delayed_work probe_timeout_work;
|
||||||
|
enum bus_probe_state bus_probe;
|
||||||
|
struct work_struct wifi_force_close_work;
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- int no_dpd;
|
||||||
|
-#endif
|
||||||
|
enum pend_read_op read_flag;
|
||||||
|
enum wakeup_event wakeup_by_event; /* used to filter unwanted event wakeup reason report */
|
||||||
|
u16 wakeup_state; /* for userspace check wakeup reason */
|
||||||
|
@@ -85,9 +82,6 @@ struct bes2600_op_map {
|
||||||
|
|
||||||
|
static struct bes_cdev bes2600_cdev;
|
||||||
|
module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644);
|
||||||
|
-#endif
|
||||||
|
|
||||||
|
extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
|
||||||
|
@@ -269,137 +263,8 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
-static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- if (buffer == NULL || size == 0)
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
- if (ret < 0)
|
||||||
|
- bes_err("write dpd to file failed\n");
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_devel("write dpd to %s\n", path);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static bool bes2600_chrdev_dpd_is_vaild(u8 *dpd_data)
|
||||||
|
-{
|
||||||
|
- u32 cal_crc = 0;
|
||||||
|
- u32 dpd_crc = le32_to_cpup((__le32 *)(dpd_data));
|
||||||
|
- u32 dpd_ver = le32_to_cpup((__le32 *)(dpd_data + DPD_VERSION_OFFSET));
|
||||||
|
-
|
||||||
|
- /* check version */
|
||||||
|
- if (dpd_ver < DPD_CUR_VERSION)
|
||||||
|
- return false;
|
||||||
|
-
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
- cal_crc = crc32_le(cal_crc, dpd_data + 4, DPD_BIN_SIZE - 4);
|
||||||
|
- cal_crc ^= 0xffffffffL;
|
||||||
|
-
|
||||||
|
- /* check if the dpd data is valid */
|
||||||
|
- if (cal_crc != dpd_crc) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
|
||||||
|
- cal_crc, dpd_crc);
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return true;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- u8* read_data = NULL;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- /* open file */
|
||||||
|
- fp = filp_open(file, O_RDONLY, 0);//S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",file);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) {
|
||||||
|
- bes_err(
|
||||||
|
- "bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, DPD_BIN_FILE_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
- /* allocate memory for storing reading data */
|
||||||
|
- read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL);
|
||||||
|
- if (read_data == NULL) {
|
||||||
|
- bes_devel("%s alloc mem fail\n", __func__);
|
||||||
|
- goto err1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* read data from file */
|
||||||
|
- ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
- if (ret < DPD_BIN_SIZE) {
|
||||||
|
- bes_err("%s read fail, ret=%d\n", __func__, ret);
|
||||||
|
- goto err2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* check dpd version and crc */
|
||||||
|
- if (!bes2600_chrdev_dpd_is_vaild(read_data))
|
||||||
|
- goto err2;
|
||||||
|
-
|
||||||
|
- /* close file */
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- /* copy data to external */
|
||||||
|
- *data = read_data;
|
||||||
|
- *len = DPD_BIN_SIZE;;
|
||||||
|
-
|
||||||
|
- /* output debug information */
|
||||||
|
- bes_devel("read dpd data from %s\n", file);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-
|
||||||
|
-err2:
|
||||||
|
- kfree(read_data);
|
||||||
|
-err1:
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
- *data = NULL;
|
||||||
|
- *len = 0;
|
||||||
|
- return -1;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
const u8* bes2600_chrdev_get_dpd_data(u32 *len)
|
||||||
|
{
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- if (!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) {
|
||||||
|
- /* read dpd data from file that stores factory dpd calibration data */
|
||||||
|
- if ((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) &&
|
||||||
|
- (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH,
|
||||||
|
- &bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) {
|
||||||
|
- bes_err("%s read dpd data fail\n", __func__);
|
||||||
|
- return NULL;
|
||||||
|
- } else {
|
||||||
|
- bes2600_cdev.dpd_calied = true;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
if (!bes2600_cdev.dpd_calied)
|
||||||
|
return NULL;
|
||||||
|
if (len)
|
||||||
|
@@ -460,14 +325,6 @@ int bes2600_chrdev_update_dpd_data(void)
|
||||||
|
}
|
||||||
|
spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
-#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
- /* write dpd data to file */
|
||||||
|
- memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE);
|
||||||
|
- bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH,
|
||||||
|
- bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE);
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
From 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 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
|
||||||
|
patches:
|
||||||
|
|
||||||
|
- bes_fw.c DATA_DUMP_OBSERVE: four #ifdef DATA_DUMP_OBSERVE
|
||||||
|
blocks built around the firmware-download path that open
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open(O_CREAT |
|
||||||
|
O_RDWR), then log every transmitted firmware chunk via
|
||||||
|
vfs_write() inside a get_fs()/set_fs(KERNEL_DS) wrapper. The
|
||||||
|
controlling #define at bes_fw.c line 128 is commented out
|
||||||
|
('//#define DATA_DUMP_OBSERVE'), so none of this is ever
|
||||||
|
compiled in a stock build.
|
||||||
|
|
||||||
|
- main.c access_file(): a helper gated on
|
||||||
|
GET_MAC_ADDR_METHOD == 2 || == 3 (default 4) using the same
|
||||||
|
get_fs()/set_fs()/vfs_read()/vfs_write() pattern. No caller
|
||||||
|
in the tree references it -- it was orphaned when the methods
|
||||||
|
that consumed it were refactored out.
|
||||||
|
|
||||||
|
Both sites are unbuildable on modern kernels anyway: get_fs() /
|
||||||
|
set_fs() were removed from arm64 and the generic uaccess path in
|
||||||
|
the v5.10 era, and the legacy vfs_read() / vfs_write() variants
|
||||||
|
that took userspace-typed buffers went with them. The in-kernel
|
||||||
|
replacements would be kernel_read() / kernel_write(), which this
|
||||||
|
series is explicitly removing from the driver.
|
||||||
|
|
||||||
|
Remove both blocks, the commented-out '//#define DATA_DUMP_OBSERVE'
|
||||||
|
line, and the access_file() definition and its #if gate. No
|
||||||
|
behaviour change in any default or non-default build, because
|
||||||
|
nothing compiled or linked in the first place. After this patch
|
||||||
|
the driver contains zero filp_open / kernel_read / kernel_write /
|
||||||
|
vfs_read / vfs_write references -- a precondition for a
|
||||||
|
drivers/staging/bes2600/ linux-wireless RFC.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_fw.c | 34 ----------------------------------
|
||||||
|
bes2600/main.c | 35 -----------------------------------
|
||||||
|
2 files changed, 69 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c
|
||||||
|
index 133c945..d612c3c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_fw.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_fw.c
|
||||||
|
@@ -125,8 +125,6 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
-//#define DATA_DUMP_OBSERVE
|
||||||
|
-
|
||||||
|
static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val)
|
||||||
|
{
|
||||||
|
u8 frame_num = 0;
|
||||||
|
@@ -468,14 +466,6 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n
|
||||||
|
|
||||||
|
const struct firmware *fw_bin;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- char *observe;
|
||||||
|
- size_t observe_len;
|
||||||
|
- loff_t observe_off = 0;
|
||||||
|
- mm_segment_t old_fs;
|
||||||
|
- struct file *observe_file = NULL;
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
struct fw_msg_hdr_t header;
|
||||||
|
struct fw_info_t fw_info;
|
||||||
|
struct download_fw_t download_addr;
|
||||||
|
@@ -583,14 +573,6 @@ retry:
|
||||||
|
}
|
||||||
|
download_addr.addr = fw_info.addr;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0);
|
||||||
|
- if (IS_ERR(observe_file)) {
|
||||||
|
- bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file));
|
||||||
|
- observe_file = NULL;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
while (code_length) {
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
@@ -640,17 +622,6 @@ retry:
|
||||||
|
//mdelay(5000);
|
||||||
|
bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length);
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
|
||||||
|
- observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t);
|
||||||
|
- old_fs = get_fs();
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- vfs_write(observe_file, observe, observe_len, &observe_off);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
|
||||||
|
if (ret) {
|
||||||
|
bes_err("tx download fw data err:%d\n", ret);
|
||||||
|
@@ -832,11 +803,6 @@ retry:
|
||||||
|
|
||||||
|
err2:
|
||||||
|
kfree(long_buf);
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- filp_close(observe_file, NULL);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
err1:
|
||||||
|
kfree(short_buf);
|
||||||
|
release_firmware(fw_bin);
|
||||||
|
diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c
|
||||||
|
index 3b0b7a3..7cbb3a9 100644
|
||||||
|
--- a/drivers/staging/bes2600/main.c
|
||||||
|
+++ b/drivers/staging/bes2600/main.c
|
||||||
|
@@ -795,41 +795,6 @@ void bes2600_core_release(struct bes2600_common *self)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */
|
||||||
|
-int access_file(char *path, char *buffer, int size, int isRead)
|
||||||
|
-{
|
||||||
|
- int ret=0;
|
||||||
|
- struct file *fp;
|
||||||
|
- mm_segment_t old_fs = get_fs();
|
||||||
|
-
|
||||||
|
- if(isRead)
|
||||||
|
- fp = filp_open(path,O_RDONLY,S_IRUSR);
|
||||||
|
- else
|
||||||
|
- fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR);
|
||||||
|
-
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n", path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (isRead) {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_read(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- } else {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_write(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_info("BES2600 : access_file return code(%d)\n", ret);
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
int bes2600_wifi_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0, if_id;
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
From 44e085360fec09c1c1f7b35a23ec679f7065d3f7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:19:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop orphan DATA_DUMP_OBSERVE and access_file() file
|
||||||
|
I/O
|
||||||
|
|
||||||
|
Two dead-in-default-build file-I/O sites remain in the driver
|
||||||
|
after the factory and chardev kernel_*() removals in the preceding
|
||||||
|
patches:
|
||||||
|
|
||||||
|
- bes_fw.c DATA_DUMP_OBSERVE: four #ifdef DATA_DUMP_OBSERVE
|
||||||
|
blocks built around the firmware-download path that open
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open(O_CREAT |
|
||||||
|
O_RDWR), then log every transmitted firmware chunk via
|
||||||
|
vfs_write() inside a get_fs()/set_fs(KERNEL_DS) wrapper. The
|
||||||
|
controlling #define at bes_fw.c line 128 is commented out
|
||||||
|
('//#define DATA_DUMP_OBSERVE'), so none of this is ever
|
||||||
|
compiled in a stock build.
|
||||||
|
|
||||||
|
- main.c access_file(): a helper gated on
|
||||||
|
GET_MAC_ADDR_METHOD == 2 || == 3 (default 4) using the same
|
||||||
|
get_fs()/set_fs()/vfs_read()/vfs_write() pattern. No caller
|
||||||
|
in the tree references it -- it was orphaned when the methods
|
||||||
|
that consumed it were refactored out.
|
||||||
|
|
||||||
|
Both sites are unbuildable on modern kernels anyway: get_fs() /
|
||||||
|
set_fs() were removed from arm64 and the generic uaccess path in
|
||||||
|
the v5.10 era, and the legacy vfs_read() / vfs_write() variants
|
||||||
|
that took userspace-typed buffers went with them. The in-kernel
|
||||||
|
replacements would be kernel_read() / kernel_write(), which this
|
||||||
|
series is explicitly removing from the driver.
|
||||||
|
|
||||||
|
Remove both blocks, the commented-out '//#define DATA_DUMP_OBSERVE'
|
||||||
|
line, and the access_file() definition and its #if gate. No
|
||||||
|
behaviour change in any default or non-default build, because
|
||||||
|
nothing compiled or linked in the first place. After this patch
|
||||||
|
the driver contains zero filp_open / kernel_read / kernel_write /
|
||||||
|
vfs_read / vfs_write references -- a precondition for a
|
||||||
|
drivers/staging/bes2600/ linux-wireless RFC.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_fw.c | 34 ----------------------------------
|
||||||
|
bes2600/main.c | 35 -----------------------------------
|
||||||
|
2 files changed, 69 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_fw.c b/bes2600/bes_fw.c
|
||||||
|
index 133c945..d612c3c 100644
|
||||||
|
--- a/bes2600/bes_fw.c
|
||||||
|
+++ b/bes2600/bes_fw.c
|
||||||
|
@@ -125,8 +125,6 @@ int bes_host_slave_sync(struct bes2600_common *hw_priv)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
-//#define DATA_DUMP_OBSERVE
|
||||||
|
-
|
||||||
|
static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val)
|
||||||
|
{
|
||||||
|
u8 frame_num = 0;
|
||||||
|
@@ -468,14 +466,6 @@ static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_n
|
||||||
|
|
||||||
|
const struct firmware *fw_bin;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- char *observe;
|
||||||
|
- size_t observe_len;
|
||||||
|
- loff_t observe_off = 0;
|
||||||
|
- mm_segment_t old_fs;
|
||||||
|
- struct file *observe_file = NULL;
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
struct fw_msg_hdr_t header;
|
||||||
|
struct fw_info_t fw_info;
|
||||||
|
struct download_fw_t download_addr;
|
||||||
|
@@ -583,14 +573,6 @@ retry:
|
||||||
|
}
|
||||||
|
download_addr.addr = fw_info.addr;
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0);
|
||||||
|
- if (IS_ERR(observe_file)) {
|
||||||
|
- bes_err("create data_dump file err:%ld\n", IS_ERR(observe_file));
|
||||||
|
- observe_file = NULL;
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
while (code_length) {
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
@@ -640,17 +622,6 @@ retry:
|
||||||
|
//mdelay(5000);
|
||||||
|
bes_devel("tx_download_firmware_data:%x %d\n", download_addr.addr, length);
|
||||||
|
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
|
||||||
|
- observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t);
|
||||||
|
- old_fs = get_fs();
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- vfs_write(observe_file, observe, observe_len, &observe_off);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
|
||||||
|
if (ret) {
|
||||||
|
bes_err("tx download fw data err:%d\n", ret);
|
||||||
|
@@ -832,11 +803,6 @@ retry:
|
||||||
|
|
||||||
|
err2:
|
||||||
|
kfree(long_buf);
|
||||||
|
-#ifdef DATA_DUMP_OBSERVE
|
||||||
|
- if (observe_file) {
|
||||||
|
- filp_close(observe_file, NULL);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
err1:
|
||||||
|
kfree(short_buf);
|
||||||
|
release_firmware(fw_bin);
|
||||||
|
diff --git a/bes2600/main.c b/bes2600/main.c
|
||||||
|
index 6ed6b15..9d2aac5 100644
|
||||||
|
--- a/bes2600/main.c
|
||||||
|
+++ b/bes2600/main.c
|
||||||
|
@@ -790,41 +790,6 @@ void bes2600_core_release(struct bes2600_common *self)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#if (GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */
|
||||||
|
-int access_file(char *path, char *buffer, int size, int isRead)
|
||||||
|
-{
|
||||||
|
- int ret=0;
|
||||||
|
- struct file *fp;
|
||||||
|
- mm_segment_t old_fs = get_fs();
|
||||||
|
-
|
||||||
|
- if(isRead)
|
||||||
|
- fp = filp_open(path,O_RDONLY,S_IRUSR);
|
||||||
|
- else
|
||||||
|
- fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR);
|
||||||
|
-
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_err("BES2600 : can't open %s\n", path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (isRead) {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_read(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- } else {
|
||||||
|
- fp->f_pos = 0;
|
||||||
|
- set_fs(KERNEL_DS);
|
||||||
|
- ret = vfs_write(fp,buffer,size,&fp->f_pos);
|
||||||
|
- set_fs(old_fs);
|
||||||
|
- }
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- bes_info("BES2600 : access_file return code(%d)\n", ret);
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-#endif
|
||||||
|
-
|
||||||
|
int bes2600_wifi_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0, if_id;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+143
@@ -0,0 +1,143 @@
|
|||||||
|
From 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 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
|
||||||
|
|
||||||
|
The driver implements a mac80211 testmode_cmd operation that dispatches
|
||||||
|
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
|
||||||
|
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
|
||||||
|
BES2600 RF-test path (bes2600_vendor_rf_cmd → firmware
|
||||||
|
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
|
||||||
|
binding in struct ieee80211_ops are conditionally compiled under
|
||||||
|
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
|
||||||
|
|
||||||
|
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
|
||||||
|
included in the build and the .testmode_cmd op is populated. On the
|
||||||
|
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
|
||||||
|
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
|
||||||
|
through the standard nl80211 testmode interface ('iw phy0 ...').
|
||||||
|
|
||||||
|
This also makes visible two classes of bit-rot that had accumulated
|
||||||
|
while nobody was building with CONFIG_BES2600_TESTMODE=y:
|
||||||
|
|
||||||
|
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
|
||||||
|
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
|
||||||
|
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
|
||||||
|
first argument. Neither the macros nor any of the BES2600_DBG_*
|
||||||
|
constants are defined anywhere in the tree. The same call pattern
|
||||||
|
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
|
||||||
|
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
|
||||||
|
|
||||||
|
Add minimal shim macros to bes_log.h that rewire the calls onto
|
||||||
|
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
|
||||||
|
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
|
||||||
|
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
|
||||||
|
as 0 constants for documentation / grep.
|
||||||
|
|
||||||
|
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
|
||||||
|
bes2600_get_roam_delay() are declared in sta.c with external
|
||||||
|
linkage but have no prototype in any header. All callers live in
|
||||||
|
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
|
||||||
|
off the compiler never sees them; with it on gcc
|
||||||
|
-Werror=missing-prototypes breaks the build.
|
||||||
|
|
||||||
|
Mark the three functions static. (Keeping them file-local also
|
||||||
|
matches their actual usage.)
|
||||||
|
|
||||||
|
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
|
||||||
|
buildable; no behavioural change when the flag is off.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
|
||||||
|
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes_log.h | 23 +++++++++++++++++++++++
|
||||||
|
bes2600/sta.c | 6 +++---
|
||||||
|
3 files changed, 27 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 2dcba09..2c1a850 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||||
|
# feature option
|
||||||
|
BES2600 ?= m
|
||||||
|
|
||||||
|
-CONFIG_BES2600_TESTMODE ?= n
|
||||||
|
+CONFIG_BES2600_TESTMODE ?= y
|
||||||
|
|
||||||
|
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h
|
||||||
|
index 605cea8..65cf703 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_log.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes_log.h
|
||||||
|
@@ -8,3 +8,26 @@ extern struct device *global_dev;
|
||||||
|
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
|
||||||
|
+ * was never implemented in-tree; these shims let code paths gated by
|
||||||
|
+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
|
||||||
|
+ * build when their conditions are enabled. The first argument is
|
||||||
|
+ * currently unused; pick one of the BES2600_DBG_* constants below for
|
||||||
|
+ * documentation.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_DBG_SBUS 0
|
||||||
|
+#define BES2600_DBG_DOWNLOAD 0
|
||||||
|
+#define BES2600_DBG_ITP 0
|
||||||
|
+#define BES2600_DBG_TEST_MODE 0
|
||||||
|
+
|
||||||
|
+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_cond) \
|
||||||
|
+ bes_err(fmt, ##__VA_ARGS__); \
|
||||||
|
+ } while (0)
|
||||||
|
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
|
||||||
|
index 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
|
||||||
|
*/
|
||||||
|
-int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
+static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
{
|
||||||
|
struct bes_msg_start_stop_tsm *start_stop_tsm =
|
||||||
|
(struct bes_msg_start_stop_tsm *) data;
|
||||||
|
@@ -3684,7 +3684,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
*
|
||||||
|
* Returns: TSM parameters collected
|
||||||
|
*/
|
||||||
|
-int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
struct bes_tsm_stats tsm_stats;
|
||||||
|
@@ -3724,7 +3724,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
*
|
||||||
|
* Returns: Returns the last measured roam delay
|
||||||
|
*/
|
||||||
|
-int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
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 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
|
||||||
|
WRITE path in factory_section_write_file() was still using
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write() to persist
|
||||||
|
updated calibration data back to FACTORY_PATH
|
||||||
|
(default /lib/firmware/bes2600/bes2600_factory.txt).
|
||||||
|
|
||||||
|
Writing to files under /lib/firmware/ from kernel code is a
|
||||||
|
standing upstream blocker for staging and for drivers/net/wireless/
|
||||||
|
submission generally:
|
||||||
|
|
||||||
|
- filp_open()/kernel_write() bypass the firmware-class abstraction,
|
||||||
|
the LSM framework, and user/group/mode enforcement that governs
|
||||||
|
the firmware search paths. They have been repeatedly called out
|
||||||
|
in staging-prep reviews.
|
||||||
|
- The kernel runs with capabilities that userspace does not (CAP_
|
||||||
|
DAC_OVERRIDE effectively); quietly rewriting firmware blobs that
|
||||||
|
userspace owns is a surprise contract.
|
||||||
|
- A module unload / reboot immediately after the write races the
|
||||||
|
writeback and can leave a truncated calibration file on disk.
|
||||||
|
|
||||||
|
Remove factory_section_write_file() and its two call sites in
|
||||||
|
bes2600_wifi_cali_table_save(). The in-memory factory_save_p
|
||||||
|
remains authoritative for the duration of the session: the WSM
|
||||||
|
command handlers that triggered this path (power-cali-table,
|
||||||
|
freq-cali, efuse-flag, power-cali-flag) already update the live
|
||||||
|
struct factory_t, and reads served from file_buffer pick up the
|
||||||
|
rebuilt serialised form immediately. On the next probe the
|
||||||
|
firmware-class file is re-read read-only via request_firmware(),
|
||||||
|
as set up by the earlier patch.
|
||||||
|
|
||||||
|
If cross-reboot persistence of runtime-updated calibration becomes
|
||||||
|
a requirement, the expected route is a userspace-visible dump
|
||||||
|
interface -- a read-only debugfs file exporting the serialised
|
||||||
|
blob, or an nl80211 vendor command -- that lets userspace copy the
|
||||||
|
values to a chosen location under its own privileges. Such a
|
||||||
|
facility can land as a follow-up without touching the core driver
|
||||||
|
write path again.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 63 +++++++++++----------------------------
|
||||||
|
1 file changed, 17 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index 1cda447..1b43b41 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -179,34 +179,6 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-/**
|
||||||
|
- * factory_section_write_file - Write data of specified length to file
|
||||||
|
- * @path: path of the file
|
||||||
|
- * @buffer: storage of write data
|
||||||
|
- * @size: length of data to write
|
||||||
|
- *
|
||||||
|
- * Return: length on success, negative error code otherwise.
|
||||||
|
- */
|
||||||
|
-static int factory_section_write_file(char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- bes_devel("writing %s \n", path);
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
@@ -898,9 +870,22 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Rebuild the serialised calibration blob in file_buffer from the live
|
||||||
|
+ * in-memory factory_save_p. Previously this function also persisted the
|
||||||
|
+ * blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that
|
||||||
|
+ * is not acceptable in mainline, so the persistence step has been removed.
|
||||||
|
+ *
|
||||||
|
+ * The in-memory factory_save_p remains authoritative for the duration of
|
||||||
|
+ * the session; on the next probe the firmware-class file is read back
|
||||||
|
+ * read-only via request_firmware(). If cross-reboot persistence of runtime
|
||||||
|
+ * calibration updates becomes a requirement, the expected route is a
|
||||||
|
+ * userspace-facing dump interface (debugfs read-only blob, or nl80211
|
||||||
|
+ * vendor command) that lets userspace read the serialised form and store
|
||||||
|
+ * it under its own privileges.
|
||||||
|
+ */
|
||||||
|
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
int w_size;
|
||||||
|
u32 crc_len = sizeof(factory_data_t);
|
||||||
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||||
|
@@ -909,13 +894,11 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
|
||||||
|
bes_devel("enter %s\n", __func__);
|
||||||
|
|
||||||
|
- if (!file_buffer) {
|
||||||
|
+ if (!file_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
- }
|
||||||
|
|
||||||
|
- if (!factory_save_p) {
|
||||||
|
+ if (!factory_save_p)
|
||||||
|
return -ENOENT;
|
||||||
|
- }
|
||||||
|
|
||||||
|
/* All initialized to space */
|
||||||
|
memset(file_buffer, 32, FACTORY_MAX_SIZE);
|
||||||
|
@@ -927,22 +910,10 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
w_size = factory_build(file_buffer, factory_save_p);
|
||||||
|
|
||||||
|
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err("%s: build failed! ret = %d.", __func__, ret);
|
||||||
|
+ bes_err("%s: build failed! w_size = %d.", __func__, w_size);
|
||||||
|
return -ETXTBSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#ifdef FACTORY_SAVE_MULTI_PATH
|
||||||
|
- /* avoid trailing characters '\0' */
|
||||||
|
- file_buffer[w_size] = 32;
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE);
|
||||||
|
-#else
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size);
|
||||||
|
-#endif
|
||||||
|
- if(ret < 0) {
|
||||||
|
- bes_err("%s: write failed! ret = %d.", __func__, ret);
|
||||||
|
- return ret;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
From 5f475a9624490b07c305329f12016ff4a4df3b47 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 19:31:25 +0200
|
||||||
|
Subject: [PATCH] bes2600: drop kernel_write() persistence from factory cali
|
||||||
|
save
|
||||||
|
|
||||||
|
Following the conversion of the factory-calibration READ path to
|
||||||
|
request_firmware() (earlier in this series), the factory-calibration
|
||||||
|
WRITE path in factory_section_write_file() was still using
|
||||||
|
filp_open(O_CREAT | O_TRUNC | O_RDWR) + kernel_write() to persist
|
||||||
|
updated calibration data back to FACTORY_PATH
|
||||||
|
(default /lib/firmware/bes2600/bes2600_factory.txt).
|
||||||
|
|
||||||
|
Writing to files under /lib/firmware/ from kernel code is a
|
||||||
|
standing upstream blocker for staging and for drivers/net/wireless/
|
||||||
|
submission generally:
|
||||||
|
|
||||||
|
- filp_open()/kernel_write() bypass the firmware-class abstraction,
|
||||||
|
the LSM framework, and user/group/mode enforcement that governs
|
||||||
|
the firmware search paths. They have been repeatedly called out
|
||||||
|
in staging-prep reviews.
|
||||||
|
- The kernel runs with capabilities that userspace does not (CAP_
|
||||||
|
DAC_OVERRIDE effectively); quietly rewriting firmware blobs that
|
||||||
|
userspace owns is a surprise contract.
|
||||||
|
- A module unload / reboot immediately after the write races the
|
||||||
|
writeback and can leave a truncated calibration file on disk.
|
||||||
|
|
||||||
|
Remove factory_section_write_file() and its two call sites in
|
||||||
|
bes2600_wifi_cali_table_save(). The in-memory factory_save_p
|
||||||
|
remains authoritative for the duration of the session: the WSM
|
||||||
|
command handlers that triggered this path (power-cali-table,
|
||||||
|
freq-cali, efuse-flag, power-cali-flag) already update the live
|
||||||
|
struct factory_t, and reads served from file_buffer pick up the
|
||||||
|
rebuilt serialised form immediately. On the next probe the
|
||||||
|
firmware-class file is re-read read-only via request_firmware(),
|
||||||
|
as set up by the earlier patch.
|
||||||
|
|
||||||
|
If cross-reboot persistence of runtime-updated calibration becomes
|
||||||
|
a requirement, the expected route is a userspace-visible dump
|
||||||
|
interface -- a read-only debugfs file exporting the serialised
|
||||||
|
blob, or an nl80211 vendor command -- that lets userspace copy the
|
||||||
|
values to a chosen location under its own privileges. Such a
|
||||||
|
facility can land as a follow-up without touching the core driver
|
||||||
|
write path again.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 63 +++++++++++----------------------------
|
||||||
|
1 file changed, 17 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index 1cda447..1b43b41 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -179,34 +179,6 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-/**
|
||||||
|
- * factory_section_write_file - Write data of specified length to file
|
||||||
|
- * @path: path of the file
|
||||||
|
- * @buffer: storage of write data
|
||||||
|
- * @size: length of data to write
|
||||||
|
- *
|
||||||
|
- * Return: length on success, negative error code otherwise.
|
||||||
|
- */
|
||||||
|
-static int factory_section_write_file(char *path, void *buffer, int size)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
-
|
||||||
|
- bes_devel("writing %s \n", path);
|
||||||
|
-
|
||||||
|
- fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- ret = kernel_write(fp, buffer, size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp,NULL);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
@@ -898,9 +870,22 @@ static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Rebuild the serialised calibration blob in file_buffer from the live
|
||||||
|
+ * in-memory factory_save_p. Previously this function also persisted the
|
||||||
|
+ * blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that
|
||||||
|
+ * is not acceptable in mainline, so the persistence step has been removed.
|
||||||
|
+ *
|
||||||
|
+ * The in-memory factory_save_p remains authoritative for the duration of
|
||||||
|
+ * the session; on the next probe the firmware-class file is read back
|
||||||
|
+ * read-only via request_firmware(). If cross-reboot persistence of runtime
|
||||||
|
+ * calibration updates becomes a requirement, the expected route is a
|
||||||
|
+ * userspace-facing dump interface (debugfs read-only blob, or nl80211
|
||||||
|
+ * vendor command) that lets userspace read the serialised form and store
|
||||||
|
+ * it under its own privileges.
|
||||||
|
+ */
|
||||||
|
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
int w_size;
|
||||||
|
u32 crc_len = sizeof(factory_data_t);
|
||||||
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
||||||
|
@@ -909,13 +894,11 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
|
||||||
|
bes_devel("enter %s\n", __func__);
|
||||||
|
|
||||||
|
- if (!file_buffer) {
|
||||||
|
+ if (!file_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
- }
|
||||||
|
|
||||||
|
- if (!factory_save_p) {
|
||||||
|
+ if (!factory_save_p)
|
||||||
|
return -ENOENT;
|
||||||
|
- }
|
||||||
|
|
||||||
|
/* All initialized to space */
|
||||||
|
memset(file_buffer, 32, FACTORY_MAX_SIZE);
|
||||||
|
@@ -927,22 +910,10 @@ static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *facto
|
||||||
|
w_size = factory_build(file_buffer, factory_save_p);
|
||||||
|
|
||||||
|
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err("%s: build failed! ret = %d.", __func__, ret);
|
||||||
|
+ bes_err("%s: build failed! w_size = %d.", __func__, w_size);
|
||||||
|
return -ETXTBSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
-#ifdef FACTORY_SAVE_MULTI_PATH
|
||||||
|
- /* avoid trailing characters '\0' */
|
||||||
|
- file_buffer[w_size] = 32;
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE);
|
||||||
|
-#else
|
||||||
|
- ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size);
|
||||||
|
-#endif
|
||||||
|
- if(ret < 0) {
|
||||||
|
- bes_err("%s: write failed! ret = %d.", __func__, ret);
|
||||||
|
- return ret;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
From 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 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
|
||||||
|
at compile time via the FACTORY_PATH Makefile macro
|
||||||
|
(default: /lib/firmware/bes2600_factory.txt).
|
||||||
|
|
||||||
|
This had several problems:
|
||||||
|
|
||||||
|
1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
|
||||||
|
device-pine64-pinetab2) ships the file at
|
||||||
|
/lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
|
||||||
|
driver logged '(NULL device *): read and check
|
||||||
|
/lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
|
||||||
|
running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
|
||||||
|
2. Direct filesystem access via filp_open() / kernel_read() from a driver
|
||||||
|
is an anti-pattern that upstream rejects: drivers should use
|
||||||
|
request_firmware() to get binary data from userspace-managed firmware
|
||||||
|
directories. request_firmware() natively searches the firmware_class
|
||||||
|
path list (typically /lib/firmware + derivatives), associates the load
|
||||||
|
with a uevent, and respects the firmware-loading infrastructure.
|
||||||
|
|
||||||
|
3. The (NULL device *) prefix in error messages indicated the absence of
|
||||||
|
proper device-context logging. While this patch does not yet thread
|
||||||
|
struct device through, the upstream path uses request_firmware() which
|
||||||
|
works with dev=NULL and is the building block for a follow-up patch
|
||||||
|
that adds per-chip device context.
|
||||||
|
|
||||||
|
Repoint the FACTORY_PATH default to the firmware-class name
|
||||||
|
(bes2600/bes2600_factory.txt) - request_firmware() prepends
|
||||||
|
/lib/firmware/ from the configured search paths. The macro remains
|
||||||
|
overridable at build time for non-standard deployments.
|
||||||
|
|
||||||
|
Rewrite factory_section_read_file() to:
|
||||||
|
* Call request_firmware(&fw, path, NULL).
|
||||||
|
* Size-check fw->size against FACTORY_MAX_SIZE.
|
||||||
|
* memcpy the data into the caller's buffer.
|
||||||
|
* Always call release_firmware() on exit.
|
||||||
|
|
||||||
|
The file write path (factory_section_write_file + kernel_write) is left
|
||||||
|
unchanged in this patch; it is the subject of a follow-up patch that
|
||||||
|
removes kernel_write and moves any remaining userspace-visible factory
|
||||||
|
configuration to a standard kernel-userspace boundary (debugfs or
|
||||||
|
nl80211 testmode).
|
||||||
|
|
||||||
|
No caller signature changes. No Makefile flag drops. Bisectable.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
|
||||||
|
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' is gone; request_firmware reads the file successfully (a separate
|
||||||
|
factory_parse() bug, previously masked by the read failure, is now
|
||||||
|
exposed and tracked separately).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
|
||||||
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 300912b..788aee2 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
-FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||||
|
+FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
# basic function
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index dc5d3da..8d60b7c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
+#include <linux/firmware.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/crc32.h>
|
||||||
|
@@ -137,38 +138,32 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data)
|
||||||
|
*/
|
||||||
|
static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
+ const struct firmware *fw;
|
||||||
|
+ int ret;
|
||||||
|
|
||||||
|
if (!path || !buffer) {
|
||||||
|
bes_err("%s NULL pointer err\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- bes_devel("reading %s \n", path);
|
||||||
|
+ bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
+ ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, FACTORY_MAX_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
+ if (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) {
|
||||||
|
+ bes_err("bes2600_factory.txt size check failed, read_size: %zu max_size: %d\n",
|
||||||
|
+ fw->size, FACTORY_MAX_SIZE);
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- if (ret != fp->f_inode->i_size) {
|
||||||
|
- bes_err("bes2600_factory.txt read fail\n");
|
||||||
|
- ret = -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ memcpy(buffer, fw->data, fw->size);
|
||||||
|
+ ret = (int)fw->size;
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
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 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
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The shipped factory calibration file bes2600_factory.txt on PineTab2
|
||||||
|
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
|
||||||
|
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
|
||||||
|
bt (4). The file terminates with '%%\n' directly after edr_power.
|
||||||
|
|
||||||
|
When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
|
||||||
|
assembles STANDARD_FACTORY with an extra select_efuse_flag section
|
||||||
|
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):
|
||||||
|
|
||||||
|
__STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
|
||||||
|
+ \"%%%%\\n\"
|
||||||
|
|
||||||
|
The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
|
||||||
|
stops after field 30 and factory_parse() returns -1 with:
|
||||||
|
|
||||||
|
bes2600_factory.txt parse fail
|
||||||
|
read and check bes2600/bes2600_factory.txt error
|
||||||
|
factory cali data get failed.
|
||||||
|
|
||||||
|
This was latent until the preceding patch (use request_firmware() for
|
||||||
|
factory.txt read) fixed the path bug that masked the parse failure.
|
||||||
|
|
||||||
|
Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
|
||||||
|
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
|
||||||
|
firmware packages that do ship the select_efuse_flag section.
|
||||||
|
|
||||||
|
Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
|
||||||
|
inconsistently wrapped in a conditional that keyed on
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
|
||||||
|
the call site in sta.c are ungated. With the flag now defaulting to
|
||||||
|
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
|
||||||
|
conditional wrapper around the prototype — the function exists and is
|
||||||
|
used regardless of the factory-parse flag.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
|
||||||
|
succeeds on the shipped factory.txt, factory_cali_data is populated,
|
||||||
|
and dmesg no longer shows the parse-fail / read-and-check-error /
|
||||||
|
factory-cali-data-get-failed sequence.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/wsm.h | 2 --
|
||||||
|
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 788aee2..2dcba09 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
-STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
+STANDARD_FACTORY_EFUSE_FLAG ?= n
|
||||||
|
FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h
|
||||||
|
index 0673131..22845ac 100644
|
||||||
|
--- a/drivers/staging/bes2600/wsm.h
|
||||||
|
+++ b/drivers/staging/bes2600/wsm.h
|
||||||
|
@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
|
||||||
|
|
||||||
|
-#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
||||||
|
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
|
||||||
|
-#endif
|
||||||
|
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
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 03/29] bes2600: thread struct device * through factory
|
||||||
|
request_firmware() call
|
||||||
|
|
||||||
|
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
|
||||||
|
That patch switched the factory calibration read path from filp_open()
|
||||||
|
+ kernel_read() to request_firmware(), but passed dev=NULL to
|
||||||
|
request_firmware() because factory_section_read_file() did not have a
|
||||||
|
struct device * in scope. The resulting logs carry the
|
||||||
|
'(NULL device *):' prefix and do not propagate a udev association.
|
||||||
|
|
||||||
|
Add a module-local static struct device * used as the firmware-class
|
||||||
|
load context, plus a small exported setter:
|
||||||
|
|
||||||
|
static struct device *bes2600_factory_dev;
|
||||||
|
void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
|
||||||
|
Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
|
||||||
|
right after bes2600_platform_data_init() so the platform layer has
|
||||||
|
already had a chance to use the same struct device for its own
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
factory_section_read_file() now passes bes2600_factory_dev (instead
|
||||||
|
of NULL) to request_firmware(). When the factory read happens before
|
||||||
|
probe (not currently the case on PineTab2) the pointer is still NULL
|
||||||
|
and request_firmware() accepts that; no regression.
|
||||||
|
|
||||||
|
No API changes to bes2600_get_factory_cali_data() callers. The
|
||||||
|
char *path parameter remains (it is the firmware-class name fed
|
||||||
|
straight to request_firmware()).
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
|
||||||
|
post-c5 factory diagnostics now carry the SDIO device identity
|
||||||
|
instead of '(NULL device *)'.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 14 +++++++++++++-
|
||||||
|
bes2600/bes2600_factory.h | 3 +++
|
||||||
|
bes2600/bes2600_sdio.c | 4 ++++
|
||||||
|
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index 8d60b7c..1cda447 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -31,6 +31,18 @@
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(factory_lock);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * struct device * for request_firmware() context. Set once at SDIO
|
||||||
|
+ * probe via bes2600_factory_set_dev(). NULL is tolerated (falls back
|
||||||
|
+ * to the udev-less firmware-class path) but loses per-device logging.
|
||||||
|
+ */
|
||||||
|
+static struct device *bes2600_factory_dev;
|
||||||
|
+
|
||||||
|
+void bes2600_factory_set_dev(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ bes2600_factory_dev = dev;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* It is only used for temporary storage.
|
||||||
|
* Every time get the factory, it will read from the
|
||||||
|
@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
|
||||||
|
bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ ret = request_firmware(&fw, path, bes2600_factory_dev);
|
||||||
|
if (ret) {
|
||||||
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
index 3835b0d..7dbe9f8 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
@@ -199,6 +199,9 @@ enum factory_cali_status {
|
||||||
|
/* just calibrate 11n, other protocols are automatically mapped */
|
||||||
|
#define WIFI_RF_11N_MODE 0x15
|
||||||
|
|
||||||
|
+/* set the struct device * used for request_firmware() context */
|
||||||
|
+void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
+
|
||||||
|
/* read wifi & bt factory cali value*/
|
||||||
|
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
|
||||||
|
void factory_little_endian_cvrt(u8 *data);
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index 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"
|
||||||
|
#include "bes2600_plat.h"
|
||||||
|
+#include "bes2600_factory.h"
|
||||||
|
#include "hwio.h"
|
||||||
|
#include "bes_chardev.h"
|
||||||
|
#include "bes_log.h"
|
||||||
|
@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
+ /* wire struct device into factory.c for request_firmware() context */
|
||||||
|
+ bes2600_factory_set_dev(dev);
|
||||||
|
+
|
||||||
|
self->pdata = bes2600_get_platform_data();
|
||||||
|
self->func = func;
|
||||||
|
self->dev = &func->dev;
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+1154
File diff suppressed because it is too large
Load Diff
+251
@@ -0,0 +1,251 @@
|
|||||||
|
From 22b799f5a21c0046aad46676519e5f03a0d105fd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Sun, 26 Apr 2026 22:31:58 +0200
|
||||||
|
Subject: [PATCH 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),
|
||||||
|
bes2600_chrdev_wifi_force_close(hw_priv, true) is invoked to tear the
|
||||||
|
device down and prepare for a fresh probe. On the wifi_force_close_work
|
||||||
|
side this calls bes2600_chrdev_do_system_close() which dispatches
|
||||||
|
sbus_ops->power_switch(0).
|
||||||
|
|
||||||
|
On PineTab2 (RK3566 + BES2600WM over SDIO) this recovery path is a
|
||||||
|
no-op:
|
||||||
|
|
||||||
|
* bes2600_sdio_power_down() writes a SYSTEM_CLOSE host-int message,
|
||||||
|
clears MMC_CAP_NONREMOVABLE, and schedules sdio_scan_work, which is
|
||||||
|
the literal one-line stub bes_warn("...this function does
|
||||||
|
nothing\n").
|
||||||
|
* bes2600_sdio_on() (the eventual power_switch(1) counterpart)
|
||||||
|
toggles pdata->powerup, which is NULL on PineTab2 because the
|
||||||
|
wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 device
|
||||||
|
tree node (see arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi:
|
||||||
|
'The reset pin is claimed by sdio_mmcseq, It is better to move it
|
||||||
|
to U-Boot so the OS can use it.').
|
||||||
|
|
||||||
|
Net result: the chip is never reset. The function drivers are not
|
||||||
|
removed (the SDIO core has no signal that the card is gone), the
|
||||||
|
firmware stays wedged, and a subsequent rmmod bes2600 leaves the SDIO
|
||||||
|
function in a half-torn-down state. modprobe bes2600 then fails with
|
||||||
|
'probe with driver bes2600_wlan failed with error -123' (-ENOMEDIUM)
|
||||||
|
on both functions (:1 wifi, :2 BT-companion) until a full system
|
||||||
|
reboot.
|
||||||
|
|
||||||
|
Observed on PineTab2 (linux-pinetab2 6.19.10-danctnix1-1) after ~150
|
||||||
|
minutes of background-scan rejects (wsm_generic_confirm 0x0007,
|
||||||
|
[SCAN] Scan failed (-22)) accumulating until the LMAC stopped
|
||||||
|
acknowledging TX buffers (hw_buf_used:24 pending:24). Reproducible
|
||||||
|
under sustained scan pressure.
|
||||||
|
|
||||||
|
Add a sbus operation bus_reset() that the recovery path can call when
|
||||||
|
power_switch() has no effective chip-reset signal of its own. Provide
|
||||||
|
an SDIO implementation that calls mmc_hw_reset(self->func->card),
|
||||||
|
which on a multi-function SDIO card (PineTab2 binds func 1 for WLAN
|
||||||
|
and func 2 for the BT-companion path) takes the remove-and-rescan
|
||||||
|
path: mmc_sdio_hw_reset() marks the card removed and schedules
|
||||||
|
mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
directly, which on PineTab2 toggles the wifi-reset GPIO via
|
||||||
|
sdio_pwrseq.
|
||||||
|
|
||||||
|
Add bes2600_chrdev_do_bus_reset() as the chrdev-side helper. It
|
||||||
|
invokes the bus op and then waits on probe_done_wq for the SDIO
|
||||||
|
remove() callback to clear sbus_priv, mirroring the wait pattern
|
||||||
|
already used by bes2600_chrdev_do_system_close() so that a subsequent
|
||||||
|
bes2600_switch_wifi(true) sees a clean state and can wait on the
|
||||||
|
fresh probe.
|
||||||
|
|
||||||
|
Wire it into bes2600_chrdev_wifi_force_close_work(): when halt_dev is
|
||||||
|
set (the hard-exception path used by both
|
||||||
|
bes2600_bh_lmac_active_monitor and bes2600_bh_mcu_active_monitor) and
|
||||||
|
the underlying bus implements bus_reset, take the new recovery path;
|
||||||
|
otherwise fall back to the legacy power_switch(0) sequence so this
|
||||||
|
patch is a no-op on USB or any other future bus that does not provide
|
||||||
|
bus_reset.
|
||||||
|
|
||||||
|
mmc_hw_reset() is exported by the MMC core and is the canonical
|
||||||
|
recovery primitive; calling it without holding the SDIO host claim is
|
||||||
|
correct because the multi-func remove-and-rescan path acquires the
|
||||||
|
host claim via the mmc workqueue, and the single-func mmc_power_cycle
|
||||||
|
path does not require the host claim.
|
||||||
|
|
||||||
|
No DT change is required: this works against the existing PineTab2
|
||||||
|
DTS, where the wifi-reset GPIO and the optional sdio_pwrkey GPIO (on
|
||||||
|
v2.0 boards) are both already configured as MMC pwrseq resets.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 29 +++++++++++++++++++++
|
||||||
|
bes2600/bes_chardev.c | 59 ++++++++++++++++++++++++++++++++++++++++--
|
||||||
|
bes2600/bes_chardev.h | 1 +
|
||||||
|
bes2600/sbus.h | 8 ++++++
|
||||||
|
4 files changed, 95 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index b9d836f..f7f86d7 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -16,6 +16,7 @@
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/sdio_func.h>
|
||||||
|
#include <linux/mmc/card.h>
|
||||||
|
+#include <linux/mmc/core.h>
|
||||||
|
#include <linux/mmc/sdio.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <net/mac80211.h>
|
||||||
|
@@ -1777,6 +1778,33 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
|
||||||
|
sdio_work_debug(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Trigger an SDIO bus reset via mmc_hw_reset().
|
||||||
|
+ *
|
||||||
|
+ * With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and
|
||||||
|
+ * func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the
|
||||||
|
+ * remove-and-rescan path: it marks the card removed and schedules
|
||||||
|
+ * mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
+ * the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
+ *
|
||||||
|
+ * With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
+ * directly, which on PineTab2 toggles the wifi-reset GPIO via sdio_pwrseq.
|
||||||
|
+ *
|
||||||
|
+ * In both cases the chip ends up in a freshly reset state, which is the
|
||||||
|
+ * goal of the recovery path.
|
||||||
|
+ *
|
||||||
|
+ * mmc_hw_reset() must be called without holding the SDIO host claim --
|
||||||
|
+ * the multi-func remove-and-rescan path acquires the host claim via the
|
||||||
|
+ * mmc workqueue.
|
||||||
|
+ */
|
||||||
|
+static int bes2600_sdio_bus_reset(struct sbus_priv *self)
|
||||||
|
+{
|
||||||
|
+ if (!self || !self->func || !self->func->card)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ return mmc_hw_reset(self->func->card);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
|
||||||
|
{
|
||||||
|
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
|
||||||
|
@@ -1815,6 +1843,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
|
||||||
|
.gpio_sleep = bes2600_gpio_allow_mcu_sleep,
|
||||||
|
.halt_device = bes2600_sdio_halt_device,
|
||||||
|
.wakeup_source = bes2600_sdio_wakeup_source,
|
||||||
|
+ .bus_reset = bes2600_sdio_bus_reset,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
index a02d6d9..d1375bc 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
@@ -442,6 +442,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Hard-reset the bus and wait for the bus core to remove the chip.
|
||||||
|
+ *
|
||||||
|
+ * Used by the firmware-wedge recovery path on platforms where the normal
|
||||||
|
+ * power_switch(0) sequence has no effective chip-reset signal. The bus
|
||||||
|
+ * implementation triggers an asynchronous re-detect; this helper waits for
|
||||||
|
+ * the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
|
||||||
|
+ * subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
|
||||||
|
+ * the fresh probe.
|
||||||
|
+ */
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ long status;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops || !priv)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops->bus_reset)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
+ bes_info("trigger bus reset to recover wedged firmware.\n");
|
||||||
|
+
|
||||||
|
+ ret = sbus_ops->bus_reset(priv);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_err("bus_reset failed: %d\n", ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * The bus reset is asynchronous: the bus core schedules a rescan
|
||||||
|
+ * which removes the bound function drivers and then re-detects the
|
||||||
|
+ * chip. Wait for the remove callback to clear sbus_priv. Do not
|
||||||
|
+ * dereference 'priv' after this point -- it may already be freed.
|
||||||
|
+ */
|
||||||
|
+ status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
+ !bes2600_cdev.sbus_priv, HZ * 3);
|
||||||
|
+ WARN_ON(status <= 0);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
bool bes2600_chrdev_is_wifi_opened(void)
|
||||||
|
{
|
||||||
|
bool wifi_opened = false;
|
||||||
|
@@ -540,8 +582,21 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
/* unregister wifi */
|
||||||
|
bes2600_switch_wifi(0);
|
||||||
|
|
||||||
|
- /* power down device if wifi is only opened */
|
||||||
|
- if (bes2600_chrdev_check_system_close()) {
|
||||||
|
+ /*
|
||||||
|
+ * Hard exception with a bus_reset implementation: tear the
|
||||||
|
+ * bus down via mmc_hw_reset() (or equivalent) so the next
|
||||||
|
+ * bringup probes a freshly reset chip. On PineTab2 this is
|
||||||
|
+ * the only effective recovery path -- the existing
|
||||||
|
+ * power_switch(0)/(1) sequence has no chip-reset signal of
|
||||||
|
+ * its own (sdio_pwrseq owns wifi_reset).
|
||||||
|
+ *
|
||||||
|
+ * Soft close, or hard close on a board without bus_reset:
|
||||||
|
+ * fall back to the legacy power_switch(0) sequence.
|
||||||
|
+ */
|
||||||
|
+ if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
|
||||||
|
+ bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
|
||||||
|
+ bes2600_cdev.sbus_priv);
|
||||||
|
+ } else if (bes2600_chrdev_check_system_close()) {
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h
|
||||||
|
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);
|
||||||
|
/* used to control device power down */
|
||||||
|
int bes2600_chrdev_check_system_close(void);
|
||||||
|
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
void bes2600_chrdev_wakeup_bt(void);
|
||||||
|
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
|
||||||
|
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
|
||||||
|
diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h
|
||||||
|
index 1f2c0cd..cb90890 100644
|
||||||
|
--- a/drivers/staging/bes2600/sbus.h
|
||||||
|
+++ b/drivers/staging/bes2600/sbus.h
|
||||||
|
@@ -75,6 +75,14 @@ struct sbus_ops {
|
||||||
|
void (*halt_device)(struct sbus_priv *self);
|
||||||
|
bool (*wakeup_source)(struct sbus_priv *self);
|
||||||
|
int (*reboot)(struct sbus_priv *self);
|
||||||
|
+ /*
|
||||||
|
+ * Force the host bus to re-detect and re-probe the chip. Called
|
||||||
|
+ * from the firmware-wedge recovery path when power_switch() has no
|
||||||
|
+ * effective chip-reset signal of its own (e.g. PineTab2, where the
|
||||||
|
+ * wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node).
|
||||||
|
+ * Returns 0 on success or a negative errno.
|
||||||
|
+ */
|
||||||
|
+ int (*bus_reset)(struct sbus_priv *self);
|
||||||
|
};
|
||||||
|
|
||||||
|
void bes2600_irq_handler(struct bes2600_common *priv);
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+83
@@ -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
|
||||||
|
|
||||||
+251
@@ -0,0 +1,251 @@
|
|||||||
|
From 460495803346f71a9d5dcc634180e5368ff9b1dc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Sun, 26 Apr 2026 22:31:58 +0200
|
||||||
|
Subject: [PATCH] bes2600: recover wedged firmware via mmc_hw_reset on link
|
||||||
|
break
|
||||||
|
|
||||||
|
When the LMAC active monitor detects 'link break between lmac and host'
|
||||||
|
(the hw_buf_used==pending watchdog in bes2600_bh_lmac_active_monitor),
|
||||||
|
bes2600_chrdev_wifi_force_close(hw_priv, true) is invoked to tear the
|
||||||
|
device down and prepare for a fresh probe. On the wifi_force_close_work
|
||||||
|
side this calls bes2600_chrdev_do_system_close() which dispatches
|
||||||
|
sbus_ops->power_switch(0).
|
||||||
|
|
||||||
|
On PineTab2 (RK3566 + BES2600WM over SDIO) this recovery path is a
|
||||||
|
no-op:
|
||||||
|
|
||||||
|
* bes2600_sdio_power_down() writes a SYSTEM_CLOSE host-int message,
|
||||||
|
clears MMC_CAP_NONREMOVABLE, and schedules sdio_scan_work, which is
|
||||||
|
the literal one-line stub bes_warn("...this function does
|
||||||
|
nothing\n").
|
||||||
|
* bes2600_sdio_on() (the eventual power_switch(1) counterpart)
|
||||||
|
toggles pdata->powerup, which is NULL on PineTab2 because the
|
||||||
|
wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 device
|
||||||
|
tree node (see arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi:
|
||||||
|
'The reset pin is claimed by sdio_mmcseq, It is better to move it
|
||||||
|
to U-Boot so the OS can use it.').
|
||||||
|
|
||||||
|
Net result: the chip is never reset. The function drivers are not
|
||||||
|
removed (the SDIO core has no signal that the card is gone), the
|
||||||
|
firmware stays wedged, and a subsequent rmmod bes2600 leaves the SDIO
|
||||||
|
function in a half-torn-down state. modprobe bes2600 then fails with
|
||||||
|
'probe with driver bes2600_wlan failed with error -123' (-ENOMEDIUM)
|
||||||
|
on both functions (:1 wifi, :2 BT-companion) until a full system
|
||||||
|
reboot.
|
||||||
|
|
||||||
|
Observed on PineTab2 (linux-pinetab2 6.19.10-danctnix1-1) after ~150
|
||||||
|
minutes of background-scan rejects (wsm_generic_confirm 0x0007,
|
||||||
|
[SCAN] Scan failed (-22)) accumulating until the LMAC stopped
|
||||||
|
acknowledging TX buffers (hw_buf_used:24 pending:24). Reproducible
|
||||||
|
under sustained scan pressure.
|
||||||
|
|
||||||
|
Add a sbus operation bus_reset() that the recovery path can call when
|
||||||
|
power_switch() has no effective chip-reset signal of its own. Provide
|
||||||
|
an SDIO implementation that calls mmc_hw_reset(self->func->card),
|
||||||
|
which on a multi-function SDIO card (PineTab2 binds func 1 for WLAN
|
||||||
|
and func 2 for the BT-companion path) takes the remove-and-rescan
|
||||||
|
path: mmc_sdio_hw_reset() marks the card removed and schedules
|
||||||
|
mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
directly, which on PineTab2 toggles the wifi-reset GPIO via
|
||||||
|
sdio_pwrseq.
|
||||||
|
|
||||||
|
Add bes2600_chrdev_do_bus_reset() as the chrdev-side helper. It
|
||||||
|
invokes the bus op and then waits on probe_done_wq for the SDIO
|
||||||
|
remove() callback to clear sbus_priv, mirroring the wait pattern
|
||||||
|
already used by bes2600_chrdev_do_system_close() so that a subsequent
|
||||||
|
bes2600_switch_wifi(true) sees a clean state and can wait on the
|
||||||
|
fresh probe.
|
||||||
|
|
||||||
|
Wire it into bes2600_chrdev_wifi_force_close_work(): when halt_dev is
|
||||||
|
set (the hard-exception path used by both
|
||||||
|
bes2600_bh_lmac_active_monitor and bes2600_bh_mcu_active_monitor) and
|
||||||
|
the underlying bus implements bus_reset, take the new recovery path;
|
||||||
|
otherwise fall back to the legacy power_switch(0) sequence so this
|
||||||
|
patch is a no-op on USB or any other future bus that does not provide
|
||||||
|
bus_reset.
|
||||||
|
|
||||||
|
mmc_hw_reset() is exported by the MMC core and is the canonical
|
||||||
|
recovery primitive; calling it without holding the SDIO host claim is
|
||||||
|
correct because the multi-func remove-and-rescan path acquires the
|
||||||
|
host claim via the mmc workqueue, and the single-func mmc_power_cycle
|
||||||
|
path does not require the host claim.
|
||||||
|
|
||||||
|
No DT change is required: this works against the existing PineTab2
|
||||||
|
DTS, where the wifi-reset GPIO and the optional sdio_pwrkey GPIO (on
|
||||||
|
v2.0 boards) are both already configured as MMC pwrseq resets.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 29 +++++++++++++++++++++
|
||||||
|
bes2600/bes_chardev.c | 59 ++++++++++++++++++++++++++++++++++++++++--
|
||||||
|
bes2600/bes_chardev.h | 1 +
|
||||||
|
bes2600/sbus.h | 8 ++++++
|
||||||
|
4 files changed, 95 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index 3e04e8c..e5840c8 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -16,6 +16,7 @@
|
||||||
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/sdio_func.h>
|
||||||
|
#include <linux/mmc/card.h>
|
||||||
|
+#include <linux/mmc/core.h>
|
||||||
|
#include <linux/mmc/sdio.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <net/mac80211.h>
|
||||||
|
@@ -1777,6 +1778,33 @@ static void bes2600_sdio_halt_device(struct sbus_priv *self)
|
||||||
|
sdio_work_debug(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Trigger an SDIO bus reset via mmc_hw_reset().
|
||||||
|
+ *
|
||||||
|
+ * With multiple SDIO functions probed (PineTab2 binds func 1 for WLAN and
|
||||||
|
+ * func 2 for the BT-companion path) mmc_sdio_hw_reset() takes the
|
||||||
|
+ * remove-and-rescan path: it marks the card removed and schedules
|
||||||
|
+ * mmc_rescan, which tears down the bound function drivers and re-detects
|
||||||
|
+ * the card on the next sweep, in turn reinvoking bes2600_sdio_probe().
|
||||||
|
+ *
|
||||||
|
+ * With a single function probed it instead invokes mmc_power_cycle()
|
||||||
|
+ * directly, which on PineTab2 toggles the wifi-reset GPIO via sdio_pwrseq.
|
||||||
|
+ *
|
||||||
|
+ * In both cases the chip ends up in a freshly reset state, which is the
|
||||||
|
+ * goal of the recovery path.
|
||||||
|
+ *
|
||||||
|
+ * mmc_hw_reset() must be called without holding the SDIO host claim --
|
||||||
|
+ * the multi-func remove-and-rescan path acquires the host claim via the
|
||||||
|
+ * mmc workqueue.
|
||||||
|
+ */
|
||||||
|
+static int bes2600_sdio_bus_reset(struct sbus_priv *self)
|
||||||
|
+{
|
||||||
|
+ if (!self || !self->func || !self->func->card)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ return mmc_hw_reset(self->func->card);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
|
||||||
|
{
|
||||||
|
struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
|
||||||
|
@@ -1815,6 +1843,7 @@ static struct sbus_ops bes2600_sdio_sbus_ops = {
|
||||||
|
.gpio_sleep = bes2600_gpio_allow_mcu_sleep,
|
||||||
|
.halt_device = bes2600_sdio_halt_device,
|
||||||
|
.wakeup_source = bes2600_sdio_wakeup_source,
|
||||||
|
+ .bus_reset = bes2600_sdio_bus_reset,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index a02d6d9..d1375bc 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -442,6 +442,48 @@ int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Hard-reset the bus and wait for the bus core to remove the chip.
|
||||||
|
+ *
|
||||||
|
+ * Used by the firmware-wedge recovery path on platforms where the normal
|
||||||
|
+ * power_switch(0) sequence has no effective chip-reset signal. The bus
|
||||||
|
+ * implementation triggers an asynchronous re-detect; this helper waits for
|
||||||
|
+ * the resulting remove() callback to clear bes2600_cdev.sbus_priv so that a
|
||||||
|
+ * subsequent bes2600_switch_wifi(true) sees a clean state and can wait on
|
||||||
|
+ * the fresh probe.
|
||||||
|
+ */
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ long status;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops || !priv)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ if (!sbus_ops->bus_reset)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
+ bes_info("trigger bus reset to recover wedged firmware.\n");
|
||||||
|
+
|
||||||
|
+ ret = sbus_ops->bus_reset(priv);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_err("bus_reset failed: %d\n", ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * The bus reset is asynchronous: the bus core schedules a rescan
|
||||||
|
+ * which removes the bound function drivers and then re-detects the
|
||||||
|
+ * chip. Wait for the remove callback to clear sbus_priv. Do not
|
||||||
|
+ * dereference 'priv' after this point -- it may already be freed.
|
||||||
|
+ */
|
||||||
|
+ status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
+ !bes2600_cdev.sbus_priv, HZ * 3);
|
||||||
|
+ WARN_ON(status <= 0);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
bool bes2600_chrdev_is_wifi_opened(void)
|
||||||
|
{
|
||||||
|
bool wifi_opened = false;
|
||||||
|
@@ -540,8 +582,21 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
/* unregister wifi */
|
||||||
|
bes2600_switch_wifi(0);
|
||||||
|
|
||||||
|
- /* power down device if wifi is only opened */
|
||||||
|
- if (bes2600_chrdev_check_system_close()) {
|
||||||
|
+ /*
|
||||||
|
+ * Hard exception with a bus_reset implementation: tear the
|
||||||
|
+ * bus down via mmc_hw_reset() (or equivalent) so the next
|
||||||
|
+ * bringup probes a freshly reset chip. On PineTab2 this is
|
||||||
|
+ * the only effective recovery path -- the existing
|
||||||
|
+ * power_switch(0)/(1) sequence has no chip-reset signal of
|
||||||
|
+ * its own (sdio_pwrseq owns wifi_reset).
|
||||||
|
+ *
|
||||||
|
+ * Soft close, or hard close on a board without bus_reset:
|
||||||
|
+ * fall back to the legacy power_switch(0) sequence.
|
||||||
|
+ */
|
||||||
|
+ if (bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->bus_reset) {
|
||||||
|
+ bes2600_chrdev_do_bus_reset(bes2600_cdev.sbus_ops,
|
||||||
|
+ bes2600_cdev.sbus_priv);
|
||||||
|
+ } else if (bes2600_chrdev_check_system_close()) {
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
diff --git a/bes2600/bes_chardev.h b/bes2600/bes_chardev.h
|
||||||
|
index 15602ba..3f0c59b 100644
|
||||||
|
--- a/bes2600/bes_chardev.h
|
||||||
|
+++ b/bes2600/bes_chardev.h
|
||||||
|
@@ -60,6 +60,7 @@ struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
|
||||||
|
/* used to control device power down */
|
||||||
|
int bes2600_chrdev_check_system_close(void);
|
||||||
|
int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
+int bes2600_chrdev_do_bus_reset(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
|
||||||
|
void bes2600_chrdev_wakeup_bt(void);
|
||||||
|
void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
|
||||||
|
void bes2600_chrdev_usb_remove(struct bes2600_common *hw_priv);
|
||||||
|
diff --git a/bes2600/sbus.h b/bes2600/sbus.h
|
||||||
|
index 1f2c0cd..cb90890 100644
|
||||||
|
--- a/bes2600/sbus.h
|
||||||
|
+++ b/bes2600/sbus.h
|
||||||
|
@@ -75,6 +75,14 @@ struct sbus_ops {
|
||||||
|
void (*halt_device)(struct sbus_priv *self);
|
||||||
|
bool (*wakeup_source)(struct sbus_priv *self);
|
||||||
|
int (*reboot)(struct sbus_priv *self);
|
||||||
|
+ /*
|
||||||
|
+ * Force the host bus to re-detect and re-probe the chip. Called
|
||||||
|
+ * from the firmware-wedge recovery path when power_switch() has no
|
||||||
|
+ * effective chip-reset signal of its own (e.g. PineTab2, where the
|
||||||
|
+ * wifi-reset GPIO is owned by sdio_pwrseq, not the bes2600 node).
|
||||||
|
+ * Returns 0 on success or a negative errno.
|
||||||
|
+ */
|
||||||
|
+ int (*bus_reset)(struct sbus_priv *self);
|
||||||
|
};
|
||||||
|
|
||||||
|
void bes2600_irq_handler(struct bes2600_common *priv);
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
From dc1505f5bab24c5f0960dcc612ce51cd2e5aeddf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 16:54:06 +0200
|
||||||
|
Subject: [PATCH 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
|
||||||
|
firmware (BES2600 with the Bestechnic Dec 2023 build that ships on
|
||||||
|
PineTab2 and most danctnix images) silently drops every WSM_set_pm
|
||||||
|
request without emitting the corresponding PM_INDICATION. The driver's
|
||||||
|
own power_down_work delayed work calls bes2600_pwr_enter_lp_mode every
|
||||||
|
~10s; without firmware acknowledgment each call burns 5s on
|
||||||
|
wait_for_completion_timeout(pm_enter_cmpl, 5*HZ) and produces a
|
||||||
|
recurring three-line cascade in dmesg:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
Confirmed by tripwire instrumentation on PineTab2 (linux-pinetab2
|
||||||
|
6.19.10-danctnix1, ohm) running the c5+c6 stack: zero
|
||||||
|
wsm_set_pm_indication() invocations across an entire boot, while
|
||||||
|
bes2600_pwr_enter_lp_mode timed out repeatedly, and
|
||||||
|
bes2600_sdio_active() consistently saw BES_SLAVE_STATUS_REG_ID return
|
||||||
|
0x2f (every "ready" bit set except MCU_WAKEUP_READY (bit 4) - the
|
||||||
|
firmware reports "I'm awake, there's nothing to wake from").
|
||||||
|
|
||||||
|
This patch makes the driver self-heal:
|
||||||
|
|
||||||
|
* struct bes2600_pwr_t gains pm_unsupported (bool) and
|
||||||
|
pm_consecutive_timeouts (unsigned int). Both initialised to
|
||||||
|
0/false.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode early-returns -EOPNOTSUPP when
|
||||||
|
pm_unsupported is set. Skips the per-VIF set_pm round-trip and
|
||||||
|
the wait_for_completion entirely.
|
||||||
|
|
||||||
|
* On the cmpxchg-success branch of the timeout path, we increment
|
||||||
|
pm_consecutive_timeouts. When it crosses
|
||||||
|
BES2600_PM_UNSUPPORTED_THRESHOLD (3, ~15s of trying), we latch
|
||||||
|
pm_unsupported = true and force chip_pm_state = ACTIVE so that
|
||||||
|
bes2600_pwr_device_exit_lp_mode's c6.2 skip branch covers the
|
||||||
|
wake side (no gpio_wake / sbus_active / WSM_set_operational_mode
|
||||||
|
reissue past the first one).
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed resets pm_consecutive_timeouts to 0
|
||||||
|
on any incoming PM indication, and clears pm_unsupported if it
|
||||||
|
was previously latched. So a firmware update that fixes PM_IND
|
||||||
|
delivery automatically re-enables PSM transitions without a
|
||||||
|
driver rebuild.
|
||||||
|
|
||||||
|
mac80211's PSM requests via bes2600_set_pm() still flow to the
|
||||||
|
firmware unchanged; they just don't have host-side timeouts so they
|
||||||
|
remain silent regardless of firmware acknowledgment. Power
|
||||||
|
consumption goes up if the firmware actually CAN do PSM (we'd be
|
||||||
|
keeping the chip awake unnecessarily), but on a chip where the
|
||||||
|
counter trips this trade-off is forced anyway: the chip stayed awake
|
||||||
|
under the broken cascade as well, just with constant SDIO churn.
|
||||||
|
|
||||||
|
Net effect on dmesg: after ~15s of boot, the three-line cascade stops
|
||||||
|
firing entirely. The firmware-side wedge is observed once per boot
|
||||||
|
(captured by the pm_unsupported latch) instead of per-cycle.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/bes_pwr.h | 9 ++++++
|
||||||
|
2 files changed, 78 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
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)
|
||||||
|
bes_devel("device enter sleep\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero
|
||||||
|
+ * PM_INDICATIONs received) before we conclude the firmware does not
|
||||||
|
+ * honor host-driven PSM and switch to a sticky skip path.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_PM_UNSUPPORTED_THRESHOLD 3
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the
|
||||||
|
+ * c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode.
|
||||||
|
+ * Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode
|
||||||
|
+ * timeouts with zero PM_INDICATIONs.
|
||||||
|
+ */
|
||||||
|
+static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+ bes_warn("PSM not honored (%u timeouts), switching to skip mode\n",
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts);
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = true;
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Hold the MCU wake-flag bit permanently. Without this, every
|
||||||
|
+ * sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX)
|
||||||
|
+ * when gpio_wakup_flags == 0, drives the GPIO high and msleeps
|
||||||
|
+ * 10 ms per RX. With ~50 RX/s of beacons + multicast that's
|
||||||
|
+ * ~50%% of the bes_sdio workqueue thread blocked in msleep,
|
||||||
|
+ * which directly caps RX throughput. Holding the MCU bit makes
|
||||||
|
+ * those calls bit-only bookkeeping (gpio_wakeup = (flags == 0)
|
||||||
|
+ * stays false, no GPIO toggle, no msleep). The bit is never
|
||||||
|
+ * cleared once pm_unsupported is set because
|
||||||
|
+ * bes2600_pwr_device_enter_lp_mode is unreachable under the
|
||||||
|
+ * early-return.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
@@ -476,6 +515,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * Sticky early-return when we've previously concluded the firmware
|
||||||
|
+ * doesn't honor PSM. Each attempt would otherwise burn 5s on a
|
||||||
|
+ * doomed wait_for_completion_timeout and produce a noisy three-line
|
||||||
|
+ * cascade in dmesg every time power_down_work retries (every
|
||||||
|
+ * ~10s). The chip stays in active mode, which on this firmware is
|
||||||
|
+ * the de-facto state anyway.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
/* set interface low power configuration */
|
||||||
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||||
|
#ifdef P2P_MULTIVIF
|
||||||
|
@@ -571,6 +621,9 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
timeouts++;
|
||||||
|
+ if (++hw_priv->bes_power.pm_consecutive_timeouts
|
||||||
|
+ >= BES2600_PM_UNSUPPORTED_THRESHOLD)
|
||||||
|
+ bes2600_pwr_latch_pm_unsupported(hw_priv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -609,7 +662,8 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* GPIO stays high and the bit clear here is purely
|
||||||
|
* bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
*/
|
||||||
|
- if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ if (!hw_priv->bes_power.pm_unsupported &&
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
@@ -932,6 +986,8 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = false;
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts = 0;
|
||||||
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
|
@@ -1321,6 +1377,18 @@ void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
* indication can prime a future wait against a freshly
|
||||||
|
* reinit_completion()'ed state.
|
||||||
|
*/
|
||||||
|
+ /*
|
||||||
|
+ * Any PM indication, whatever its psmode, proves the firmware is
|
||||||
|
+ * actually emitting them. Reset the consecutive-timeout counter
|
||||||
|
+ * so a transient stall doesn't permanently disable PSM, and clear
|
||||||
|
+ * pm_unsupported if a previous run had latched it.
|
||||||
|
+ */
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts = 0;
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported) {
|
||||||
|
+ bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n");
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_LP);
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
index 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
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
atomic_t chip_pm_state;
|
||||||
|
+ /*
|
||||||
|
+ * Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD
|
||||||
|
+ * consecutive enter_lp_mode timeouts with zero PM_INDICATIONs
|
||||||
|
+ * received from firmware. Indicates this chip's firmware does
|
||||||
|
+ * not honor host-driven PSM transitions; further attempts are
|
||||||
|
+ * skipped to avoid the 5s timeout cascade.
|
||||||
|
+ */
|
||||||
|
+ bool pm_unsupported;
|
||||||
|
+ unsigned int pm_consecutive_timeouts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
From f12e87002576f094c441ac6c945a451c88868592 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 16:54:06 +0200
|
||||||
|
Subject: [PATCH] bes2600: self-detect when firmware does not honor PSM and
|
||||||
|
skip the cycle
|
||||||
|
|
||||||
|
The c6 series fixed several host-side bookkeeping bugs around PSM
|
||||||
|
transitions, but didn't address the underlying contract: this chip's
|
||||||
|
firmware (BES2600 with the Bestechnic Dec 2023 build that ships on
|
||||||
|
PineTab2 and most danctnix images) silently drops every WSM_set_pm
|
||||||
|
request without emitting the corresponding PM_INDICATION. The driver's
|
||||||
|
own power_down_work delayed work calls bes2600_pwr_enter_lp_mode every
|
||||||
|
~10s; without firmware acknowledgment each call burns 5s on
|
||||||
|
wait_for_completion_timeout(pm_enter_cmpl, 5*HZ) and produces a
|
||||||
|
recurring three-line cascade in dmesg:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
Confirmed by tripwire instrumentation on PineTab2 (linux-pinetab2
|
||||||
|
6.19.10-danctnix1, ohm) running the c5+c6 stack: zero
|
||||||
|
wsm_set_pm_indication() invocations across an entire boot, while
|
||||||
|
bes2600_pwr_enter_lp_mode timed out repeatedly, and
|
||||||
|
bes2600_sdio_active() consistently saw BES_SLAVE_STATUS_REG_ID return
|
||||||
|
0x2f (every "ready" bit set except MCU_WAKEUP_READY (bit 4) - the
|
||||||
|
firmware reports "I'm awake, there's nothing to wake from").
|
||||||
|
|
||||||
|
This patch makes the driver self-heal:
|
||||||
|
|
||||||
|
* struct bes2600_pwr_t gains pm_unsupported (bool) and
|
||||||
|
pm_consecutive_timeouts (unsigned int). Both initialised to
|
||||||
|
0/false.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode early-returns -EOPNOTSUPP when
|
||||||
|
pm_unsupported is set. Skips the per-VIF set_pm round-trip and
|
||||||
|
the wait_for_completion entirely.
|
||||||
|
|
||||||
|
* On the cmpxchg-success branch of the timeout path, we increment
|
||||||
|
pm_consecutive_timeouts. When it crosses
|
||||||
|
BES2600_PM_UNSUPPORTED_THRESHOLD (3, ~15s of trying), we latch
|
||||||
|
pm_unsupported = true and force chip_pm_state = ACTIVE so that
|
||||||
|
bes2600_pwr_device_exit_lp_mode's c6.2 skip branch covers the
|
||||||
|
wake side (no gpio_wake / sbus_active / WSM_set_operational_mode
|
||||||
|
reissue past the first one).
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed resets pm_consecutive_timeouts to 0
|
||||||
|
on any incoming PM indication, and clears pm_unsupported if it
|
||||||
|
was previously latched. So a firmware update that fixes PM_IND
|
||||||
|
delivery automatically re-enables PSM transitions without a
|
||||||
|
driver rebuild.
|
||||||
|
|
||||||
|
mac80211's PSM requests via bes2600_set_pm() still flow to the
|
||||||
|
firmware unchanged; they just don't have host-side timeouts so they
|
||||||
|
remain silent regardless of firmware acknowledgment. Power
|
||||||
|
consumption goes up if the firmware actually CAN do PSM (we'd be
|
||||||
|
keeping the chip awake unnecessarily), but on a chip where the
|
||||||
|
counter trips this trade-off is forced anyway: the chip stayed awake
|
||||||
|
under the broken cascade as well, just with constant SDIO churn.
|
||||||
|
|
||||||
|
Net effect on dmesg: after ~15s of boot, the three-line cascade stops
|
||||||
|
firing entirely. The firmware-side wedge is observed once per boot
|
||||||
|
(captured by the pm_unsupported latch) instead of per-cycle.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/bes_pwr.h | 9 ++++++
|
||||||
|
2 files changed, 78 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index b7b6c2f..620acef 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -467,6 +467,45 @@ static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
bes_devel("device enter sleep\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Number of consecutive bes2600_pwr_enter_lp_mode timeouts (with zero
|
||||||
|
+ * PM_INDICATIONs received) before we conclude the firmware does not
|
||||||
|
+ * honor host-driven PSM and switch to a sticky skip path.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_PM_UNSUPPORTED_THRESHOLD 3
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Latch pm_unsupported = true and force chip_pm_state = ACTIVE so the
|
||||||
|
+ * c6.2 wake-side skip branch covers bes2600_pwr_device_exit_lp_mode.
|
||||||
|
+ * Called after BES2600_PM_UNSUPPORTED_THRESHOLD consecutive enter_lp_mode
|
||||||
|
+ * timeouts with zero PM_INDICATIONs.
|
||||||
|
+ */
|
||||||
|
+static void bes2600_pwr_latch_pm_unsupported(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+ bes_warn("PSM not honored (%u timeouts), switching to skip mode\n",
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts);
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = true;
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Hold the MCU wake-flag bit permanently. Without this, every
|
||||||
|
+ * sdio_rx_work invocation hits bes2600_gpio_wakeup_mcu(SDIO_RX)
|
||||||
|
+ * when gpio_wakup_flags == 0, drives the GPIO high and msleeps
|
||||||
|
+ * 10 ms per RX. With ~50 RX/s of beacons + multicast that's
|
||||||
|
+ * ~50%% of the bes_sdio workqueue thread blocked in msleep,
|
||||||
|
+ * which directly caps RX throughput. Holding the MCU bit makes
|
||||||
|
+ * those calls bit-only bookkeeping (gpio_wakeup = (flags == 0)
|
||||||
|
+ * stays false, no GPIO toggle, no msleep). The bit is never
|
||||||
|
+ * cleared once pm_unsupported is set because
|
||||||
|
+ * bes2600_pwr_device_enter_lp_mode is unreachable under the
|
||||||
|
+ * early-return.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
@@ -476,6 +515,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
+ /*
|
||||||
|
+ * Sticky early-return when we've previously concluded the firmware
|
||||||
|
+ * doesn't honor PSM. Each attempt would otherwise burn 5s on a
|
||||||
|
+ * doomed wait_for_completion_timeout and produce a noisy three-line
|
||||||
|
+ * cascade in dmesg every time power_down_work retries (every
|
||||||
|
+ * ~10s). The chip stays in active mode, which on this firmware is
|
||||||
|
+ * the de-facto state anyway.
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported)
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+
|
||||||
|
/* set interface low power configuration */
|
||||||
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
||||||
|
#ifdef P2P_MULTIVIF
|
||||||
|
@@ -571,6 +621,9 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
timeouts++;
|
||||||
|
+ if (++hw_priv->bes_power.pm_consecutive_timeouts
|
||||||
|
+ >= BES2600_PM_UNSUPPORTED_THRESHOLD)
|
||||||
|
+ bes2600_pwr_latch_pm_unsupported(hw_priv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -609,7 +662,8 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* GPIO stays high and the bit clear here is purely
|
||||||
|
* bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
*/
|
||||||
|
- if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ if (!hw_priv->bes_power.pm_unsupported &&
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
@@ -932,6 +986,8 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = false;
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts = 0;
|
||||||
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
|
@@ -1321,6 +1377,18 @@ void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
* indication can prime a future wait against a freshly
|
||||||
|
* reinit_completion()'ed state.
|
||||||
|
*/
|
||||||
|
+ /*
|
||||||
|
+ * Any PM indication, whatever its psmode, proves the firmware is
|
||||||
|
+ * actually emitting them. Reset the consecutive-timeout counter
|
||||||
|
+ * so a transient stall doesn't permanently disable PSM, and clear
|
||||||
|
+ * pm_unsupported if a previous run had latched it.
|
||||||
|
+ */
|
||||||
|
+ hw_priv->bes_power.pm_consecutive_timeouts = 0;
|
||||||
|
+ if (hw_priv->bes_power.pm_unsupported) {
|
||||||
|
+ bes_warn("PM indication arrived after pm_unsupported was set; re-enabling PSM transitions\n");
|
||||||
|
+ hw_priv->bes_power.pm_unsupported = false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
BES2600_CHIP_PM_LP);
|
||||||
|
diff --git a/bes2600/bes_pwr.h b/bes2600/bes_pwr.h
|
||||||
|
index 6bc44ac..92de90b 100644
|
||||||
|
--- a/bes2600/bes_pwr.h
|
||||||
|
+++ b/bes2600/bes_pwr.h
|
||||||
|
@@ -121,6 +121,15 @@ struct bes2600_pwr_t
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
atomic_t chip_pm_state;
|
||||||
|
+ /*
|
||||||
|
+ * Sticky flag set after BES2600_PM_UNSUPPORTED_THRESHOLD
|
||||||
|
+ * consecutive enter_lp_mode timeouts with zero PM_INDICATIONs
|
||||||
|
+ * received from firmware. Indicates this chip's firmware does
|
||||||
|
+ * not honor host-driven PSM transitions; further attempts are
|
||||||
|
+ * skipped to avoid the 5s timeout cascade.
|
||||||
|
+ */
|
||||||
|
+ bool pm_unsupported;
|
||||||
|
+ unsigned int pm_consecutive_timeouts;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
From 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 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
|
||||||
|
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
|
||||||
|
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Two bugs:
|
||||||
|
|
||||||
|
1. A failed wsm_set_pm() only logs an error, then still falls into
|
||||||
|
wait_for_completion_timeout() on a completion the firmware will
|
||||||
|
never post (the set-mode command never reached it). The loop
|
||||||
|
therefore always blocks the full 5 s, logs a second error, and
|
||||||
|
proceeds.
|
||||||
|
|
||||||
|
2. A genuine wait-timeout (firmware received the set-mode command but
|
||||||
|
never posted the indication) also only logs a warning. The code
|
||||||
|
then drops to bes2600_pwr_device_enter_lp_mode(), handing the
|
||||||
|
device subsystem an inconsistent view of mac-layer state.
|
||||||
|
|
||||||
|
On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
|
||||||
|
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
|
||||||
|
message flooding dmesg every 5-10 s when the interface is associated
|
||||||
|
and idle. Sending the device to LP in that state cascades into the
|
||||||
|
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
|
||||||
|
WARN splat.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Add a 'timeouts' counter; bump it on both failure paths.
|
||||||
|
- Skip the wait_for_completion entirely when wsm_set_pm() failed
|
||||||
|
(there is no completion to wait for).
|
||||||
|
- Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
|
||||||
|
handshake reached firmware-ACKed completion; otherwise return
|
||||||
|
-ETIMEDOUT and leave the device in its current power state.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
Post-patch the handshake still fails on this particular firmware
|
||||||
|
revision (separate root-cause investigation outside this patch), but
|
||||||
|
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
|
||||||
|
and destabilising the SDIO path.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 20 +++++++++++++++++---
|
||||||
|
1 file changed, 17 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index e7a1045..f62ae22 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
int i = 0;
|
||||||
|
struct bes2600_vif *priv;
|
||||||
|
int ret = 0;
|
||||||
|
+ int timeouts = 0;
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
bes_err("%s, set operation mode fail\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
- if (!status)
|
||||||
|
+ if (!status) {
|
||||||
|
bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* set device low power configuration */
|
||||||
|
- bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ /*
|
||||||
|
+ * Enter the device-end of the LP transition only if every per-VIF
|
||||||
|
+ * mac80211 handshake reached firmware-ACKed completion. Doing the
|
||||||
|
+ * device-LP setup while any VIF is still pending leaves the driver
|
||||||
|
+ * in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
+ * the BES2600.
|
||||||
|
+ */
|
||||||
|
+ if (timeouts == 0)
|
||||||
|
+ bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ else
|
||||||
|
+ ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
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 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
|
||||||
|
PM-changed indication confirming the transition. Three sequenced
|
||||||
|
flaws make the wait-and-confirm racy and leave host/chip bookkeeping
|
||||||
|
desynced when anything misfires:
|
||||||
|
|
||||||
|
1) bes2600_pwr_notify_ps_changed() unconditionally fires
|
||||||
|
complete(pm_enter_cmpl) for any non-active psmode. It does not
|
||||||
|
check whether a host-initiated set_pm is actually pending. A
|
||||||
|
spontaneous indication (firmware-internal coex move,
|
||||||
|
idle-driven aging) primes the completion, and the next host-
|
||||||
|
driven enter_lp_mode sees a false success on its first
|
||||||
|
wait_for_completion_timeout.
|
||||||
|
|
||||||
|
2) The wait/reinit ordering in bes2600_pwr_enter_lp_mode is
|
||||||
|
|
||||||
|
status = wait_for_completion_timeout(...);
|
||||||
|
atomic_set(pm_set_in_process, 0);
|
||||||
|
reinit_completion(...);
|
||||||
|
|
||||||
|
If an indication arrives between wait_for_completion_timeout
|
||||||
|
returning with status==1 and reinit_completion, the next
|
||||||
|
enter_lp_mode iteration's wait can also see false success. The
|
||||||
|
reinit must happen *before* we start the new request, not
|
||||||
|
after handling the previous one.
|
||||||
|
|
||||||
|
3) On wait_pm_ind timeout, the driver returns -ETIMEDOUT and walks
|
||||||
|
away. It does not record that the firmware's actual PM state
|
||||||
|
is no longer known to the host. Subsequent wake paths
|
||||||
|
(gpio_wake / sbus_active) assume the chip is still active and
|
||||||
|
hit deterministic SDIO failures when the firmware has
|
||||||
|
transitioned anyway.
|
||||||
|
|
||||||
|
This patch is the safe-prerequisite half of a wider fix:
|
||||||
|
|
||||||
|
* bes_pwr.h gains enum bes2600_chip_pm_state {ACTIVE, LP, UNKNOWN}
|
||||||
|
and bes_power.chip_pm_state. Its job is to track what the host
|
||||||
|
has *seen the firmware confirm*, not what the host has
|
||||||
|
requested. Initialised to ACTIVE in bes2600_pwr_init().
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed() unconditionally updates
|
||||||
|
chip_pm_state on every indication, but only fires
|
||||||
|
complete(pm_enter_cmpl) when atomic_cmpxchg(pm_set_in_process,
|
||||||
|
1, 0) succeeds. A spontaneous indication can no longer prime a
|
||||||
|
waiter that will only set up its request afterwards.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode() now reinit_completion()s before
|
||||||
|
setting pm_set_in_process and sending wsm_set_pm. After a
|
||||||
|
timeout, it cmpxchgs pm_set_in_process back to 0 (so a late
|
||||||
|
indication cannot prime the next iteration) and on the win-
|
||||||
|
cmpxchg branch records chip_pm_state=UNKNOWN.
|
||||||
|
|
||||||
|
A follow-up patch consumes chip_pm_state on the wake side
|
||||||
|
(bes2600_pwr_device_exit_lp_mode + bes2600_gpio_wakeup_mcu) to fix
|
||||||
|
the deterministic "active mcu fail" cycle this state-record
|
||||||
|
enables a fix for. Splitting the work this way keeps the lock-free
|
||||||
|
race fix small and reviewable on its own.
|
||||||
|
|
||||||
|
No new locks, no behaviour change on the success path. Only the
|
||||||
|
recovery path (timeout + spontaneous indication) gains correctness.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 94 ++++++++++++++++++++++++++++++++++++++++++-----
|
||||||
|
bes2600/bes_pwr.h | 15 ++++++++
|
||||||
|
2 files changed, 100 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
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)
|
||||||
|
bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
|
||||||
|
__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
|
||||||
|
priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
|
||||||
|
+ /*
|
||||||
|
+ * Reinit BEFORE the WSM goes out, so a stale
|
||||||
|
+ * indication from a previous cycle cannot have
|
||||||
|
+ * primed pm_enter_cmpl. From here until the
|
||||||
|
+ * indication callback's cmpxchg(1->0) on
|
||||||
|
+ * pm_set_in_process, only the indication for
|
||||||
|
+ * THIS request can complete the wait.
|
||||||
|
+ */
|
||||||
|
+ reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
|
||||||
|
+
|
||||||
|
ret = bes2600_set_pm(priv, &priv->powersave_mode);
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
@@ -535,11 +545,33 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
- atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
- reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
- timeouts++;
|
||||||
|
+ /*
|
||||||
|
+ * The indication callback only fires
|
||||||
|
+ * complete() when it observes
|
||||||
|
+ * pm_set_in_process == 1; cmpxchg it
|
||||||
|
+ * to 0 here so a late indication
|
||||||
|
+ * cannot prime the next wait.
|
||||||
|
+ *
|
||||||
|
+ * If we win the cmpxchg, this is a
|
||||||
|
+ * real timeout: the firmware's PS
|
||||||
|
+ * state is unknown to us. Mark it as
|
||||||
|
+ * such so the next wake path can
|
||||||
|
+ * probe before assuming the chip is
|
||||||
|
+ * still active.
|
||||||
|
+ *
|
||||||
|
+ * If we lose the cmpxchg, the
|
||||||
|
+ * indication arrived between the
|
||||||
|
+ * wait timing out and us getting
|
||||||
|
+ * here; treat as success.
|
||||||
|
+ */
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
@@ -554,10 +586,34 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
* the BES2600.
|
||||||
|
*/
|
||||||
|
- if (timeouts == 0)
|
||||||
|
+ if (timeouts == 0) {
|
||||||
|
bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
- else
|
||||||
|
+ } else {
|
||||||
|
+ /*
|
||||||
|
+ * device_enter_lp_mode() was skipped (one or more VIFs
|
||||||
|
+ * timed out waiting for the firmware indication) so its
|
||||||
|
+ * gpio_sleep(MCU) - which drops the wake-flag bit and, if
|
||||||
|
+ * no other subsystem holds the wake, drives the GPIO low -
|
||||||
|
+ * never ran. Without it the bit stays asserted, and the
|
||||||
|
+ * next bes2600_pwr_device_exit_lp_mode() calls
|
||||||
|
+ * gpio_wake(MCU) into a "bit already set" no-op: the GPIO
|
||||||
|
+ * never re-edges, sbus_active() exhausts its 200x2ms
|
||||||
|
+ * MCU_WAKEUP_READY budget against an unwoken chip, and
|
||||||
|
+ * the first TX after idle stalls for several seconds.
|
||||||
|
+ *
|
||||||
|
+ * Drop the MCU wake-flag bit explicitly here so the next
|
||||||
|
+ * wake injects a real GPIO edge. gpio_allow_mcu_sleep
|
||||||
|
+ * preserves multi-subsystem semantics: it only drives the
|
||||||
|
+ * GPIO low when no other subsystem still holds wake; if
|
||||||
|
+ * BT or another holder is keeping the chip awake, the
|
||||||
|
+ * GPIO stays high and the bit clear here is purely
|
||||||
|
+ * bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -833,6 +889,7 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
hw_priv->bes_power.power_up_task = NULL;
|
||||||
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
|
@@ -1213,9 +1270,28 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event)
|
||||||
|
|
||||||
|
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
{
|
||||||
|
- if((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
- bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
- complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ /*
|
||||||
|
+ * The firmware sends a PM-changed indication for every transition,
|
||||||
|
+ * including ones we didn't ask for (firmware-internal coex moves,
|
||||||
|
+ * idle-driven aging). Update chip_pm_state unconditionally so the
|
||||||
|
+ * wake path can use it, but only fire pm_enter_cmpl when a host-
|
||||||
|
+ * initiated set_pm is actually in flight - otherwise a stale
|
||||||
|
+ * indication can prime a future wait against a freshly
|
||||||
|
+ * reinit_completion()'ed state.
|
||||||
|
+ */
|
||||||
|
+ if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_LP);
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
+ complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("PM ind (LP) without pending wait; state recorded\n");
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h
|
||||||
|
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
|
||||||
|
POWER_DOWN_STATE_UNLOCKED,
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Confirmed PM state of the firmware-side chip. Tracks what the host
|
||||||
|
+ * has *seen* the firmware acknowledge, not what the host has
|
||||||
|
+ * requested. UNKNOWN means a host-initiated transition timed out
|
||||||
|
+ * before the firmware indication arrived; the next wake path should
|
||||||
|
+ * treat it as "we don't know" and probe before issuing GPIO/SDIO
|
||||||
|
+ * wakeup ops.
|
||||||
|
+ */
|
||||||
|
+enum bes2600_chip_pm_state {
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE = 0,
|
||||||
|
+ BES2600_CHIP_PM_LP,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
@@ -106,6 +120,7 @@ struct bes2600_pwr_t
|
||||||
|
bool ap_lp_bad;
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
+ atomic_t chip_pm_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
From c57c77e446d9a552b537175453b838d0400ff41d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:05:27 +0200
|
||||||
|
Subject: [PATCH] bes2600: gate PM indication completion on pending request and
|
||||||
|
track chip state
|
||||||
|
|
||||||
|
When mac80211 toggles PSM on the BES2600, the host sends WSM set_pm
|
||||||
|
and waits up to 5 s on bes_power.pm_enter_cmpl for a firmware-side
|
||||||
|
PM-changed indication confirming the transition. Three sequenced
|
||||||
|
flaws make the wait-and-confirm racy and leave host/chip bookkeeping
|
||||||
|
desynced when anything misfires:
|
||||||
|
|
||||||
|
1) bes2600_pwr_notify_ps_changed() unconditionally fires
|
||||||
|
complete(pm_enter_cmpl) for any non-active psmode. It does not
|
||||||
|
check whether a host-initiated set_pm is actually pending. A
|
||||||
|
spontaneous indication (firmware-internal coex move,
|
||||||
|
idle-driven aging) primes the completion, and the next host-
|
||||||
|
driven enter_lp_mode sees a false success on its first
|
||||||
|
wait_for_completion_timeout.
|
||||||
|
|
||||||
|
2) The wait/reinit ordering in bes2600_pwr_enter_lp_mode is
|
||||||
|
|
||||||
|
status = wait_for_completion_timeout(...);
|
||||||
|
atomic_set(pm_set_in_process, 0);
|
||||||
|
reinit_completion(...);
|
||||||
|
|
||||||
|
If an indication arrives between wait_for_completion_timeout
|
||||||
|
returning with status==1 and reinit_completion, the next
|
||||||
|
enter_lp_mode iteration's wait can also see false success. The
|
||||||
|
reinit must happen *before* we start the new request, not
|
||||||
|
after handling the previous one.
|
||||||
|
|
||||||
|
3) On wait_pm_ind timeout, the driver returns -ETIMEDOUT and walks
|
||||||
|
away. It does not record that the firmware's actual PM state
|
||||||
|
is no longer known to the host. Subsequent wake paths
|
||||||
|
(gpio_wake / sbus_active) assume the chip is still active and
|
||||||
|
hit deterministic SDIO failures when the firmware has
|
||||||
|
transitioned anyway.
|
||||||
|
|
||||||
|
This patch is the safe-prerequisite half of a wider fix:
|
||||||
|
|
||||||
|
* bes_pwr.h gains enum bes2600_chip_pm_state {ACTIVE, LP, UNKNOWN}
|
||||||
|
and bes_power.chip_pm_state. Its job is to track what the host
|
||||||
|
has *seen the firmware confirm*, not what the host has
|
||||||
|
requested. Initialised to ACTIVE in bes2600_pwr_init().
|
||||||
|
|
||||||
|
* bes2600_pwr_notify_ps_changed() unconditionally updates
|
||||||
|
chip_pm_state on every indication, but only fires
|
||||||
|
complete(pm_enter_cmpl) when atomic_cmpxchg(pm_set_in_process,
|
||||||
|
1, 0) succeeds. A spontaneous indication can no longer prime a
|
||||||
|
waiter that will only set up its request afterwards.
|
||||||
|
|
||||||
|
* bes2600_pwr_enter_lp_mode() now reinit_completion()s before
|
||||||
|
setting pm_set_in_process and sending wsm_set_pm. After a
|
||||||
|
timeout, it cmpxchgs pm_set_in_process back to 0 (so a late
|
||||||
|
indication cannot prime the next iteration) and on the win-
|
||||||
|
cmpxchg branch records chip_pm_state=UNKNOWN.
|
||||||
|
|
||||||
|
A follow-up patch consumes chip_pm_state on the wake side
|
||||||
|
(bes2600_pwr_device_exit_lp_mode + bes2600_gpio_wakeup_mcu) to fix
|
||||||
|
the deterministic "active mcu fail" cycle this state-record
|
||||||
|
enables a fix for. Splitting the work this way keeps the lock-free
|
||||||
|
race fix small and reviewable on its own.
|
||||||
|
|
||||||
|
No new locks, no behaviour change on the success path. Only the
|
||||||
|
recovery path (timeout + spontaneous indication) gains correctness.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 94 ++++++++++++++++++++++++++++++++++++++++++-----
|
||||||
|
bes2600/bes_pwr.h | 15 ++++++++
|
||||||
|
2 files changed, 100 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index 474b6f1..9b4a4de 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -524,7 +524,17 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
bes_devel("%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
|
||||||
|
__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
|
||||||
|
priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
|
||||||
|
+ /*
|
||||||
|
+ * Reinit BEFORE the WSM goes out, so a stale
|
||||||
|
+ * indication from a previous cycle cannot have
|
||||||
|
+ * primed pm_enter_cmpl. From here until the
|
||||||
|
+ * indication callback's cmpxchg(1->0) on
|
||||||
|
+ * pm_set_in_process, only the indication for
|
||||||
|
+ * THIS request can complete the wait.
|
||||||
|
+ */
|
||||||
|
+ reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
|
||||||
|
+
|
||||||
|
ret = bes2600_set_pm(priv, &priv->powersave_mode);
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
@@ -535,11 +545,33 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
- atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
- reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
- timeouts++;
|
||||||
|
+ /*
|
||||||
|
+ * The indication callback only fires
|
||||||
|
+ * complete() when it observes
|
||||||
|
+ * pm_set_in_process == 1; cmpxchg it
|
||||||
|
+ * to 0 here so a late indication
|
||||||
|
+ * cannot prime the next wait.
|
||||||
|
+ *
|
||||||
|
+ * If we win the cmpxchg, this is a
|
||||||
|
+ * real timeout: the firmware's PS
|
||||||
|
+ * state is unknown to us. Mark it as
|
||||||
|
+ * such so the next wake path can
|
||||||
|
+ * probe before assuming the chip is
|
||||||
|
+ * still active.
|
||||||
|
+ *
|
||||||
|
+ * If we lose the cmpxchg, the
|
||||||
|
+ * indication arrived between the
|
||||||
|
+ * wait timing out and us getting
|
||||||
|
+ * here; treat as success.
|
||||||
|
+ */
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
@@ -554,10 +586,34 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
* in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
* the BES2600.
|
||||||
|
*/
|
||||||
|
- if (timeouts == 0)
|
||||||
|
+ if (timeouts == 0) {
|
||||||
|
bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
- else
|
||||||
|
+ } else {
|
||||||
|
+ /*
|
||||||
|
+ * device_enter_lp_mode() was skipped (one or more VIFs
|
||||||
|
+ * timed out waiting for the firmware indication) so its
|
||||||
|
+ * gpio_sleep(MCU) - which drops the wake-flag bit and, if
|
||||||
|
+ * no other subsystem holds the wake, drives the GPIO low -
|
||||||
|
+ * never ran. Without it the bit stays asserted, and the
|
||||||
|
+ * next bes2600_pwr_device_exit_lp_mode() calls
|
||||||
|
+ * gpio_wake(MCU) into a "bit already set" no-op: the GPIO
|
||||||
|
+ * never re-edges, sbus_active() exhausts its 200x2ms
|
||||||
|
+ * MCU_WAKEUP_READY budget against an unwoken chip, and
|
||||||
|
+ * the first TX after idle stalls for several seconds.
|
||||||
|
+ *
|
||||||
|
+ * Drop the MCU wake-flag bit explicitly here so the next
|
||||||
|
+ * wake injects a real GPIO edge. gpio_allow_mcu_sleep
|
||||||
|
+ * preserves multi-subsystem semantics: it only drives the
|
||||||
|
+ * GPIO low when no other subsystem still holds wake; if
|
||||||
|
+ * BT or another holder is keeping the chip awake, the
|
||||||
|
+ * GPIO stays high and the bit clear here is purely
|
||||||
|
+ * bookkeeping (so the next gpio_wake doesn't no-op).
|
||||||
|
+ */
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_sleep)
|
||||||
|
+ hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -833,6 +889,7 @@ void bes2600_pwr_init(struct bes2600_common *hw_priv)
|
||||||
|
hw_priv->bes_power.power_up_task = NULL;
|
||||||
|
mutex_init(&hw_priv->bes_power.pwr_mutex);
|
||||||
|
atomic_set(&hw_priv->bes_power.dev_state, 0);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state, BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
init_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
sema_init(&hw_priv->bes_power.sync_lock, 1);
|
||||||
|
device_set_wakeup_capable(hw_priv->pdev, true);
|
||||||
|
@@ -1213,9 +1270,28 @@ int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event)
|
||||||
|
|
||||||
|
void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
|
||||||
|
{
|
||||||
|
- if((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
- bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
- complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ /*
|
||||||
|
+ * The firmware sends a PM-changed indication for every transition,
|
||||||
|
+ * including ones we didn't ask for (firmware-internal coex moves,
|
||||||
|
+ * idle-driven aging). Update chip_pm_state unconditionally so the
|
||||||
|
+ * wake path can use it, but only fire pm_enter_cmpl when a host-
|
||||||
|
+ * initiated set_pm is actually in flight - otherwise a stale
|
||||||
|
+ * indication can prime a future wait against a freshly
|
||||||
|
+ * reinit_completion()'ed state.
|
||||||
|
+ */
|
||||||
|
+ if ((psmode & 0x01) != WSM_PSM_ACTIVE) {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_LP);
|
||||||
|
+ if (atomic_cmpxchg(&hw_priv->bes_power.pm_set_in_process,
|
||||||
|
+ 1, 0) == 1) {
|
||||||
|
+ bes_devel("complete pm_enter_cmpl\n");
|
||||||
|
+ complete(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("PM ind (LP) without pending wait; state recorded\n");
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.h b/bes2600/bes_pwr.h
|
||||||
|
index 1ba866c..6bc44ac 100644
|
||||||
|
--- a/bes2600/bes_pwr.h
|
||||||
|
+++ b/bes2600/bes_pwr.h
|
||||||
|
@@ -64,6 +64,20 @@ enum power_down_state
|
||||||
|
POWER_DOWN_STATE_UNLOCKED,
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Confirmed PM state of the firmware-side chip. Tracks what the host
|
||||||
|
+ * has *seen* the firmware acknowledge, not what the host has
|
||||||
|
+ * requested. UNKNOWN means a host-initiated transition timed out
|
||||||
|
+ * before the firmware indication arrived; the next wake path should
|
||||||
|
+ * treat it as "we don't know" and probe before issuing GPIO/SDIO
|
||||||
|
+ * wakeup ops.
|
||||||
|
+ */
|
||||||
|
+enum bes2600_chip_pm_state {
|
||||||
|
+ BES2600_CHIP_PM_ACTIVE = 0,
|
||||||
|
+ BES2600_CHIP_PM_LP,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
@@ -106,6 +120,7 @@ struct bes2600_pwr_t
|
||||||
|
bool ap_lp_bad;
|
||||||
|
struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
|
||||||
|
atomic_t pm_set_in_process;
|
||||||
|
+ atomic_t chip_pm_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_BES2600_WOWLAN
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
From 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 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
|
||||||
|
level every time wait_for_completion_timeout() on the firmware's
|
||||||
|
PM-change indication returns 0. The preceding patch ('bes2600:
|
||||||
|
gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake') already handles this case correctly: the per-VIF
|
||||||
|
timeouts counter is incremented, the function returns
|
||||||
|
-ETIMEDOUT, and the device-side LP transition is skipped -- the
|
||||||
|
cascade into sdio_tx_work splats and [RX] Receive failure
|
||||||
|
messages is prevented.
|
||||||
|
|
||||||
|
The timeout itself is benign steady-state noise on the PineTab2
|
||||||
|
(BES2600WM). Firmware occasionally misses the 5 s PM-change
|
||||||
|
deadline when mac80211 flips power-save rapidly during
|
||||||
|
association or roaming; observed rate on a quiet, associated
|
||||||
|
ohm is roughly 3-10 events per 10 min of uptime, with no
|
||||||
|
user-visible effect. Keeping it at bes_err() level (== KERN_ERR,
|
||||||
|
priority 3) floods dmesg with what is already a handled
|
||||||
|
condition and makes real SDIO / PM errors harder to spot.
|
||||||
|
|
||||||
|
Demote to bes_devel() (== KERN_DEBUG gated on the driver's debug
|
||||||
|
flag). The gate in the caller is unchanged, so the downstream
|
||||||
|
suppression behaviour introduced by the earlier patch remains.
|
||||||
|
Real pathologies -- bes_err("set operation mode fail") on the
|
||||||
|
same path, and the timeouts != 0 / -ETIMEDOUT return consumed
|
||||||
|
by callers -- still surface at bes_err() / return-value level.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index f62ae22..474b6f1 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -538,7 +538,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
timeouts++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
From ab9e0ad6b4bbb1196c448ed000c8c152b0f04683 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 20:35:17 +0200
|
||||||
|
Subject: [PATCH] bes2600: demote 'wait pm ind timeout' from bes_err to
|
||||||
|
bes_devel
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() logs 'wait pm ind timeout' at bes_err
|
||||||
|
level every time wait_for_completion_timeout() on the firmware's
|
||||||
|
PM-change indication returns 0. The preceding patch ('bes2600:
|
||||||
|
gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake') already handles this case correctly: the per-VIF
|
||||||
|
timeouts counter is incremented, the function returns
|
||||||
|
-ETIMEDOUT, and the device-side LP transition is skipped -- the
|
||||||
|
cascade into sdio_tx_work splats and [RX] Receive failure
|
||||||
|
messages is prevented.
|
||||||
|
|
||||||
|
The timeout itself is benign steady-state noise on the PineTab2
|
||||||
|
(BES2600WM). Firmware occasionally misses the 5 s PM-change
|
||||||
|
deadline when mac80211 flips power-save rapidly during
|
||||||
|
association or roaming; observed rate on a quiet, associated
|
||||||
|
ohm is roughly 3-10 events per 10 min of uptime, with no
|
||||||
|
user-visible effect. Keeping it at bes_err() level (== KERN_ERR,
|
||||||
|
priority 3) floods dmesg with what is already a handled
|
||||||
|
condition and makes real SDIO / PM errors harder to spot.
|
||||||
|
|
||||||
|
Demote to bes_devel() (== KERN_DEBUG gated on the driver's debug
|
||||||
|
flag). The gate in the caller is unchanged, so the downstream
|
||||||
|
suppression behaviour introduced by the earlier patch remains.
|
||||||
|
Real pathologies -- bes_err("set operation mode fail") on the
|
||||||
|
same path, and the timeouts != 0 / -ETIMEDOUT return consumed
|
||||||
|
by callers -- still surface at bes_err() / return-value level.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index f62ae22..474b6f1 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -538,7 +538,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
if (!status) {
|
||||||
|
- bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ bes_devel("%s, wait pm ind timeout\n", __func__);
|
||||||
|
timeouts++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
From 7a65dc374c671e20bd6303959ff234a179bc9ff7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:23:34 +0200
|
||||||
|
Subject: [PATCH 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
|
||||||
|
chip_pm_state field tracking what the host has *seen the firmware
|
||||||
|
confirm*. This patch makes the wake side use it.
|
||||||
|
|
||||||
|
Without this, every bes2600_pwr_device_exit_lp_mode() unconditionally
|
||||||
|
runs gpio_wake() + sbus_active() + wsm_set_operational_mode(active),
|
||||||
|
even when the chip is already in confirmed-ACTIVE state and the wake
|
||||||
|
sequence has nothing to do. The visible failure mode on PineTab2:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
repeat set gpio_wake_flag, sub_sys:0
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
cycling every ~9 s, ~22 cycles in 10 minutes. Three pieces:
|
||||||
|
|
||||||
|
1. enter_lp_mode timed out (firmware indication lost). With c6.1,
|
||||||
|
chip_pm_state is now UNKNOWN.
|
||||||
|
2. lock_device fires exit_lp_mode.
|
||||||
|
3. gpio_wake hits "bit already set" because device_enter_lp_mode
|
||||||
|
was skipped when the indication timed out, so gpio_sleep was
|
||||||
|
never called - the bit reflects driver intent, not chip state.
|
||||||
|
gpio_wake silently no-ops (no GPIO edge), bit stays set.
|
||||||
|
4. sbus_active spends 200 x 2 ms looking for MCU_WAKEUP_READY that
|
||||||
|
never comes (firmware was never told to wake), then fails.
|
||||||
|
5. Driver continues to wsm_set_operational_mode against the wedged
|
||||||
|
bus, compounding the failure.
|
||||||
|
|
||||||
|
This patch's three moves:
|
||||||
|
|
||||||
|
* bes2600_pwr_device_exit_lp_mode() reads chip_pm_state at entry.
|
||||||
|
On BES2600_CHIP_PM_ACTIVE, log at devel level and return without
|
||||||
|
touching gpio_wake / sbus_active / WSM. The chip is in the state
|
||||||
|
we want; the handshake exists only to drive a transition.
|
||||||
|
|
||||||
|
* On BES2600_CHIP_PM_LP or BES2600_CHIP_PM_UNKNOWN, run the wake
|
||||||
|
handshake as before, but on sbus_active() failure: set
|
||||||
|
chip_pm_state = UNKNOWN, log once at err level, and bail out.
|
||||||
|
Do NOT call wsm_set_operational_mode over a wedged bus - it
|
||||||
|
would just emit a second error and leave the chip in an even
|
||||||
|
less defined state.
|
||||||
|
|
||||||
|
* bes2600_gpio_wakeup_mcu() / bes2600_gpio_allow_mcu_sleep():
|
||||||
|
demote "repeat set/clear gpio_wake_flag" from bes_err to
|
||||||
|
bes_devel. Multi-subsystem wake-hold (e.g. WIFI + BT both want
|
||||||
|
MCU awake) is the steady-state case, and the symmetric clear
|
||||||
|
while bit-already-clear is racy bookkeeping rather than a
|
||||||
|
hardware error. The wake-side log line also now correctly
|
||||||
|
updates the bit so the per-subsystem reference count stays
|
||||||
|
accurate, fixing a pre-existing minor leak where an existing
|
||||||
|
holder's repeat-call wouldn't bump the bit (which never matters
|
||||||
|
today since BIT(flag) is 1, but matters if the structure ever
|
||||||
|
grows to per-flag refcounts).
|
||||||
|
|
||||||
|
Net effect on the cycle:
|
||||||
|
|
||||||
|
* If chip is genuinely ACTIVE (chip_pm_state == ACTIVE), wake skips
|
||||||
|
cleanly. Storm goes silent.
|
||||||
|
* If chip is genuinely LP, behaviour is unchanged.
|
||||||
|
* If chip is UNKNOWN (post-timeout state), one wake attempt is
|
||||||
|
made; on failure, state stays UNKNOWN and we don't emit a
|
||||||
|
second cascade error per attempt. Repeated UNKNOWN with failed
|
||||||
|
wake will eventually be picked up by the LMAC active-monitor
|
||||||
|
and escalated to mmc_hw_reset (c5.2).
|
||||||
|
|
||||||
|
No new locks, no new state. Only consumption of the chip_pm_state
|
||||||
|
field added in the prerequisite patch.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 15 +++++++++--
|
||||||
|
bes2600/bes_pwr.c | 56 ++++++++++++++++++++++++++++++++++++------
|
||||||
|
2 files changed, 62 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index f7f86d7..5a0694a 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -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) {
|
||||||
|
- bes_err( "repeat set gpio_wake_flag, sub_sys:%d", flag);
|
||||||
|
+ /*
|
||||||
|
+ * Multiple subsystems holding wake is the steady-state case
|
||||||
|
+ * (e.g. WIFI + BT both want MCU awake). Demoted from bes_err
|
||||||
|
+ * to bes_devel since it isn't an error - the GPIO is already
|
||||||
|
+ * asserted high and the subsystem is now also tracked.
|
||||||
|
+ */
|
||||||
|
+ bes_devel("repeat set gpio_wake_flag, sub_sys:%d\n", flag);
|
||||||
|
+ self->gpio_wakup_flags |= BIT(flag);
|
||||||
|
mutex_unlock(&self->io_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@@ -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) {
|
||||||
|
- bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag);
|
||||||
|
+ /*
|
||||||
|
+ * Mirror of the wake path: a clear when the bit is already
|
||||||
|
+ * clear is racy bookkeeping, not a hardware error.
|
||||||
|
+ */
|
||||||
|
+ bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag);
|
||||||
|
mutex_unlock(&self->io_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index 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)
|
||||||
|
static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
+ enum bes2600_chip_pm_state state;
|
||||||
|
struct wsm_operational_mode mode = {
|
||||||
|
.power_mode = wsm_power_mode_active,
|
||||||
|
.disableMoreFlagUsage = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
- bes_devel("host lock lmac\n");
|
||||||
|
- if(hw_priv->sbus_ops->gpio_wake)
|
||||||
|
- hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
|
||||||
|
+ /*
|
||||||
|
+ * Consult chip_pm_state set by bes2600_pwr_notify_ps_changed().
|
||||||
|
+ * If we last saw the firmware confirm ACTIVE, skip ONLY the
|
||||||
|
+ * gpio_wake + sbus_active wake handshake - the GPIO is already
|
||||||
|
+ * asserted high and the SDIO MCU subsystem is already running,
|
||||||
|
+ * so another sbus_active() round-trip just hits its 200x2ms
|
||||||
|
+ * timeout because the firmware has nothing to do.
|
||||||
|
+ *
|
||||||
|
+ * wsm_set_operational_mode() below is NOT part of the wake
|
||||||
|
+ * handshake; it is the operational-mode setter the firmware
|
||||||
|
+ * tracks per call. Skipping it leaves the chip's SDIO state
|
||||||
|
+ * machine without a fresh operational-mode update, which on
|
||||||
|
+ * PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read)
|
||||||
|
+ * within a few seconds of probe completion. So it must run
|
||||||
|
+ * unconditionally.
|
||||||
|
+ */
|
||||||
|
+ state = atomic_read(&hw_priv->bes_power.chip_pm_state);
|
||||||
|
+ if (state == BES2600_CHIP_PM_ACTIVE) {
|
||||||
|
+ bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n");
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("host lock lmac\n");
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
|
||||||
|
- if(hw_priv->sbus_ops->sbus_active) {
|
||||||
|
- ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
|
||||||
|
- if (ret)
|
||||||
|
- bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ if (hw_priv->sbus_ops->sbus_active) {
|
||||||
|
+ ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv,
|
||||||
|
+ SUBSYSTEM_MCU);
|
||||||
|
+ if (ret) {
|
||||||
|
+ /*
|
||||||
|
+ * MCU_WAKEUP_READY did not arrive within
|
||||||
|
+ * the SDIO handshake window. Record state
|
||||||
|
+ * as UNKNOWN so the next exit_lp_mode call
|
||||||
|
+ * also runs the full wake sequence (no
|
||||||
|
+ * skip), but still send operational_mode
|
||||||
|
+ * below to match pre-c6 behaviour - the
|
||||||
|
+ * WSM may succeed even if the SDIO active
|
||||||
|
+ * confirm was lost, and if it fails too,
|
||||||
|
+ * we just emit a second devel-level error.
|
||||||
|
+ * Repeated UNKNOWN is the signal for the
|
||||||
|
+ * LMAC active-monitor to eventually
|
||||||
|
+ * escalate to bus_reset (c5.2's
|
||||||
|
+ * mmc_hw_reset path).
|
||||||
|
+ */
|
||||||
|
+ bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wsm_set_operational_mode(hw_priv, &mode, 0);
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+190
@@ -0,0 +1,190 @@
|
|||||||
|
From 822a5f1bab37e3f61b91aaf304ec1c54b42d639a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 15:23:34 +0200
|
||||||
|
Subject: [PATCH] bes2600: short-circuit wake handshake when chip is confirmed
|
||||||
|
ACTIVE
|
||||||
|
|
||||||
|
The previous patch ("bes2600: gate PM indication completion on pending
|
||||||
|
request and track chip state") added enum bes2600_chip_pm_state and the
|
||||||
|
chip_pm_state field tracking what the host has *seen the firmware
|
||||||
|
confirm*. This patch makes the wake side use it.
|
||||||
|
|
||||||
|
Without this, every bes2600_pwr_device_exit_lp_mode() unconditionally
|
||||||
|
runs gpio_wake() + sbus_active() + wsm_set_operational_mode(active),
|
||||||
|
even when the chip is already in confirmed-ACTIVE state and the wake
|
||||||
|
sequence has nothing to do. The visible failure mode on PineTab2:
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode, wait pm ind timeout
|
||||||
|
repeat set gpio_wake_flag, sub_sys:0
|
||||||
|
bes2600_sdio_active failed, subsys:0
|
||||||
|
bes2600_pwr_device_exit_lp_mode, active mcu fail
|
||||||
|
|
||||||
|
cycling every ~9 s, ~22 cycles in 10 minutes. Three pieces:
|
||||||
|
|
||||||
|
1. enter_lp_mode timed out (firmware indication lost). With c6.1,
|
||||||
|
chip_pm_state is now UNKNOWN.
|
||||||
|
2. lock_device fires exit_lp_mode.
|
||||||
|
3. gpio_wake hits "bit already set" because device_enter_lp_mode
|
||||||
|
was skipped when the indication timed out, so gpio_sleep was
|
||||||
|
never called - the bit reflects driver intent, not chip state.
|
||||||
|
gpio_wake silently no-ops (no GPIO edge), bit stays set.
|
||||||
|
4. sbus_active spends 200 x 2 ms looking for MCU_WAKEUP_READY that
|
||||||
|
never comes (firmware was never told to wake), then fails.
|
||||||
|
5. Driver continues to wsm_set_operational_mode against the wedged
|
||||||
|
bus, compounding the failure.
|
||||||
|
|
||||||
|
This patch's three moves:
|
||||||
|
|
||||||
|
* bes2600_pwr_device_exit_lp_mode() reads chip_pm_state at entry.
|
||||||
|
On BES2600_CHIP_PM_ACTIVE, log at devel level and return without
|
||||||
|
touching gpio_wake / sbus_active / WSM. The chip is in the state
|
||||||
|
we want; the handshake exists only to drive a transition.
|
||||||
|
|
||||||
|
* On BES2600_CHIP_PM_LP or BES2600_CHIP_PM_UNKNOWN, run the wake
|
||||||
|
handshake as before, but on sbus_active() failure: set
|
||||||
|
chip_pm_state = UNKNOWN, log once at err level, and bail out.
|
||||||
|
Do NOT call wsm_set_operational_mode over a wedged bus - it
|
||||||
|
would just emit a second error and leave the chip in an even
|
||||||
|
less defined state.
|
||||||
|
|
||||||
|
* bes2600_gpio_wakeup_mcu() / bes2600_gpio_allow_mcu_sleep():
|
||||||
|
demote "repeat set/clear gpio_wake_flag" from bes_err to
|
||||||
|
bes_devel. Multi-subsystem wake-hold (e.g. WIFI + BT both want
|
||||||
|
MCU awake) is the steady-state case, and the symmetric clear
|
||||||
|
while bit-already-clear is racy bookkeeping rather than a
|
||||||
|
hardware error. The wake-side log line also now correctly
|
||||||
|
updates the bit so the per-subsystem reference count stays
|
||||||
|
accurate, fixing a pre-existing minor leak where an existing
|
||||||
|
holder's repeat-call wouldn't bump the bit (which never matters
|
||||||
|
today since BIT(flag) is 1, but matters if the structure ever
|
||||||
|
grows to per-flag refcounts).
|
||||||
|
|
||||||
|
Net effect on the cycle:
|
||||||
|
|
||||||
|
* If chip is genuinely ACTIVE (chip_pm_state == ACTIVE), wake skips
|
||||||
|
cleanly. Storm goes silent.
|
||||||
|
* If chip is genuinely LP, behaviour is unchanged.
|
||||||
|
* If chip is UNKNOWN (post-timeout state), one wake attempt is
|
||||||
|
made; on failure, state stays UNKNOWN and we don't emit a
|
||||||
|
second cascade error per attempt. Repeated UNKNOWN with failed
|
||||||
|
wake will eventually be picked up by the LMAC active-monitor
|
||||||
|
and escalated to mmc_hw_reset (c5.2).
|
||||||
|
|
||||||
|
No new locks, no new state. Only consumption of the chip_pm_state
|
||||||
|
field added in the prerequisite patch.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 15 +++++++++--
|
||||||
|
bes2600/bes_pwr.c | 56 ++++++++++++++++++++++++++++++++++++------
|
||||||
|
2 files changed, 62 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index 3e04e8c..acc0f19 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -1388,7 +1388,14 @@ static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag)
|
||||||
|
|
||||||
|
/* error check */
|
||||||
|
if((self->gpio_wakup_flags & BIT(flag)) != 0) {
|
||||||
|
- bes_err( "repeat set gpio_wake_flag, sub_sys:%d", flag);
|
||||||
|
+ /*
|
||||||
|
+ * Multiple subsystems holding wake is the steady-state case
|
||||||
|
+ * (e.g. WIFI + BT both want MCU awake). Demoted from bes_err
|
||||||
|
+ * to bes_devel since it isn't an error - the GPIO is already
|
||||||
|
+ * asserted high and the subsystem is now also tracked.
|
||||||
|
+ */
|
||||||
|
+ bes_devel("repeat set gpio_wake_flag, sub_sys:%d\n", flag);
|
||||||
|
+ self->gpio_wakup_flags |= BIT(flag);
|
||||||
|
mutex_unlock(&self->io_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@@ -1420,7 +1427,11 @@ static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
|
||||||
|
|
||||||
|
/* error check */
|
||||||
|
if((self->gpio_wakup_flags & BIT(flag)) == 0) {
|
||||||
|
- bes_err( "repeat clear gpio_wake_flag, sub_sys:%d", flag);
|
||||||
|
+ /*
|
||||||
|
+ * Mirror of the wake path: a clear when the bit is already
|
||||||
|
+ * clear is racy bookkeeping, not a hardware error.
|
||||||
|
+ */
|
||||||
|
+ bes_devel("repeat clear gpio_wake_flag, sub_sys:%d\n", flag);
|
||||||
|
mutex_unlock(&self->io_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index 9b4a4de..b7b6c2f 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -621,19 +621,61 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
+ enum bes2600_chip_pm_state state;
|
||||||
|
struct wsm_operational_mode mode = {
|
||||||
|
.power_mode = wsm_power_mode_active,
|
||||||
|
.disableMoreFlagUsage = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
- bes_devel("host lock lmac\n");
|
||||||
|
- if(hw_priv->sbus_ops->gpio_wake)
|
||||||
|
- hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
|
||||||
|
+ /*
|
||||||
|
+ * Consult chip_pm_state set by bes2600_pwr_notify_ps_changed().
|
||||||
|
+ * If we last saw the firmware confirm ACTIVE, skip ONLY the
|
||||||
|
+ * gpio_wake + sbus_active wake handshake - the GPIO is already
|
||||||
|
+ * asserted high and the SDIO MCU subsystem is already running,
|
||||||
|
+ * so another sbus_active() round-trip just hits its 200x2ms
|
||||||
|
+ * timeout because the firmware has nothing to do.
|
||||||
|
+ *
|
||||||
|
+ * wsm_set_operational_mode() below is NOT part of the wake
|
||||||
|
+ * handshake; it is the operational-mode setter the firmware
|
||||||
|
+ * tracks per call. Skipping it leaves the chip's SDIO state
|
||||||
|
+ * machine without a fresh operational-mode update, which on
|
||||||
|
+ * PineTab2 wedges the bus (-EBUSY on next sdio_rx_work read)
|
||||||
|
+ * within a few seconds of probe completion. So it must run
|
||||||
|
+ * unconditionally.
|
||||||
|
+ */
|
||||||
|
+ state = atomic_read(&hw_priv->bes_power.chip_pm_state);
|
||||||
|
+ if (state == BES2600_CHIP_PM_ACTIVE) {
|
||||||
|
+ bes_devel("device_exit_lp_mode: chip already ACTIVE, skipping wake handshake\n");
|
||||||
|
+ } else {
|
||||||
|
+ bes_devel("host lock lmac\n");
|
||||||
|
+ if (hw_priv->sbus_ops->gpio_wake)
|
||||||
|
+ hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv,
|
||||||
|
+ GPIO_WAKE_FLAG_MCU);
|
||||||
|
|
||||||
|
- if(hw_priv->sbus_ops->sbus_active) {
|
||||||
|
- ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
|
||||||
|
- if (ret)
|
||||||
|
- bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ if (hw_priv->sbus_ops->sbus_active) {
|
||||||
|
+ ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv,
|
||||||
|
+ SUBSYSTEM_MCU);
|
||||||
|
+ if (ret) {
|
||||||
|
+ /*
|
||||||
|
+ * MCU_WAKEUP_READY did not arrive within
|
||||||
|
+ * the SDIO handshake window. Record state
|
||||||
|
+ * as UNKNOWN so the next exit_lp_mode call
|
||||||
|
+ * also runs the full wake sequence (no
|
||||||
|
+ * skip), but still send operational_mode
|
||||||
|
+ * below to match pre-c6 behaviour - the
|
||||||
|
+ * WSM may succeed even if the SDIO active
|
||||||
|
+ * confirm was lost, and if it fails too,
|
||||||
|
+ * we just emit a second devel-level error.
|
||||||
|
+ * Repeated UNKNOWN is the signal for the
|
||||||
|
+ * LMAC active-monitor to eventually
|
||||||
|
+ * escalate to bus_reset (c5.2's
|
||||||
|
+ * mmc_hw_reset path).
|
||||||
|
+ */
|
||||||
|
+ bes_err("%s, active mcu fail\n", __func__);
|
||||||
|
+ atomic_set(&hw_priv->bes_power.chip_pm_state,
|
||||||
|
+ BES2600_CHIP_PM_UNKNOWN);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wsm_set_operational_mode(hw_priv, &mode, 0);
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+83
@@ -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
|
||||||
|
|
||||||
+121
@@ -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.
|
||||||
+755
@@ -0,0 +1,755 @@
|
|||||||
|
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 05/29] bes2600: remove userspace /dev/bes2600 character device
|
||||||
|
interface
|
||||||
|
|
||||||
|
bes_chardev.c implemented a custom character device at /dev/bes2600 with
|
||||||
|
its own parser and command-dispatch table, exposing operations such as
|
||||||
|
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
|
||||||
|
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
|
||||||
|
the in-tree driver - every kernel call site consumes the internal state
|
||||||
|
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
|
||||||
|
etc) directly, not through the cdev.
|
||||||
|
|
||||||
|
The cdev interface is a standing upstream blocker for two reasons:
|
||||||
|
|
||||||
|
1. Drivers under drivers/staging/ and drivers/net/wireless/ are
|
||||||
|
expected to expose tuning via the firmware/nl80211/debugfs
|
||||||
|
infrastructure rather than a private /dev node with an ad-hoc
|
||||||
|
parser.
|
||||||
|
|
||||||
|
2. The cdev handlers keep a global bes_cdev singleton alive whose
|
||||||
|
->cdev, ->dev_id, ->class and ->device pointers exist only to be
|
||||||
|
torn down; they add no functionality that nl80211 or rfkill do
|
||||||
|
not already provide (wifi/bt on-off, module_param for fw_type).
|
||||||
|
|
||||||
|
Remove the userspace interface:
|
||||||
|
|
||||||
|
- open / read / write / release file_operations handlers and the
|
||||||
|
bes2600_chardev_fops instance
|
||||||
|
- bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
|
||||||
|
- bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
|
||||||
|
string helpers
|
||||||
|
- bes2600_load_uevent (its only caller was
|
||||||
|
bes2600_chrdev_wifi_force_close_work informing userspace of a
|
||||||
|
state it already gates via rfkill; that snprintf +
|
||||||
|
kobject_uevent_env block is gone too, the kernel-side
|
||||||
|
halt_device + switch_wifi(0) + chrdev_check_system_close
|
||||||
|
sequence remains)
|
||||||
|
- alloc_chrdev_region / cdev_init / cdev_add / class_create /
|
||||||
|
device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
|
||||||
|
unwind labels
|
||||||
|
- cdev_del / unregister_chrdev_region / device_destroy /
|
||||||
|
class_destroy in bes2600_chrdev_free
|
||||||
|
- cdev/dev_id/major/minor/class/device fields in struct bes_cdev
|
||||||
|
|
||||||
|
What remains (unchanged behaviour):
|
||||||
|
|
||||||
|
- fw_type module parameter - the primary user-facing knob for
|
||||||
|
signal/no-signal/BT mode switch
|
||||||
|
- All in-kernel bes2600_chrdev_* accessor functions called from
|
||||||
|
bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
|
||||||
|
wifi_testmode_cmd.c (13 call sites)
|
||||||
|
- bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
|
||||||
|
for the remaining bes_cdev state (waitqueues, workqueues, flags)
|
||||||
|
- DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
|
||||||
|
- wifi_force_close worker, system-close logic, bus-probe state
|
||||||
|
machine
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
|
||||||
|
no kernel messages related to the cdev absence. Users that previously
|
||||||
|
wrote to /dev/bes2600 should switch to the fw_type module parameter
|
||||||
|
or (future patch c4) nl80211 testmode commands.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
|
||||||
|
and friends so the global bes2600_cdev singleton can be dropped
|
||||||
|
and the accessors scale to multi-device scenarios.
|
||||||
|
- c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
|
||||||
|
commands to the firmware's patch_wifi_testMode entry.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_chardev.c | 568 +-----------------------------------------
|
||||||
|
1 file changed, 3 insertions(+), 565 deletions(-)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bes_cdev {
|
||||||
|
- struct cdev cdev;
|
||||||
|
- dev_t dev_id;
|
||||||
|
- int major;
|
||||||
|
- int minor;
|
||||||
|
- struct class *class;
|
||||||
|
- struct device *device;
|
||||||
|
atomic_t num_proc;
|
||||||
|
wait_queue_head_t open_wq;
|
||||||
|
spinlock_t status_lock;
|
||||||
|
@@ -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;
|
||||||
|
- int ifname_len = 0;
|
||||||
|
- char *sp = NULL;
|
||||||
|
- char *tmp_ptr = NULL;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
-
|
||||||
|
- /* check if input arguments is valid */
|
||||||
|
- if (!str || strncmp(str, "ifname:", 7) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- sp = strchr(str, ' ');
|
||||||
|
- if (strncmp(sp + 1, "cmd:", 4) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- /* extract interface name */
|
||||||
|
- ifname_len = sp - str - 7;
|
||||||
|
- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- return -2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, str+7, ifname_len);
|
||||||
|
- tmp_ptr[ifname_len] = '\0';
|
||||||
|
- result[0] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- /* get command length */
|
||||||
|
- cmd_ptr = strstr(str, "cmd:");
|
||||||
|
- cmd_ptr += 4;
|
||||||
|
- sp = strchr(cmd_ptr, ' ');
|
||||||
|
- if (!sp) { /* the command don't have any parameter */
|
||||||
|
- cmd_len = strlen(cmd_ptr);
|
||||||
|
- if (cmd_ptr[cmd_len - 1] == '\n')
|
||||||
|
- --cmd_len;
|
||||||
|
- } else { /* the command have one or more parameter */
|
||||||
|
- cmd_len = sp - cmd_ptr;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* copy command to out buffer */
|
||||||
|
- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- kfree(result[0]);
|
||||||
|
- result[0] = NULL;
|
||||||
|
- return -3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, cmd_ptr, cmd_len);
|
||||||
|
- tmp_ptr[cmd_len] = '\0';
|
||||||
|
- result[1] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void bes2600_recyle_cmd_and_ifname_mem(char **info)
|
||||||
|
-{
|
||||||
|
- if (info[0]) {
|
||||||
|
- kfree(info[0]);
|
||||||
|
- info[0] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (info[1]) {
|
||||||
|
- kfree(info[1]);
|
||||||
|
- info[1] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_default_handler(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
-
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
|
||||||
|
- } else {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, str);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_wifi_bt_on_off(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- int ret = 0;
|
||||||
|
- enum wait_state wait_state;
|
||||||
|
- enum bus_probe_state probe_state;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- probe_state = bes2600_cdev.bus_probe;
|
||||||
|
- wait_state = bes2600_cdev.wait_state;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* only work for wifi signal mode */
|
||||||
|
- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
|
||||||
|
- return -EFAULT;
|
||||||
|
|
||||||
|
- /* wait bus probe operation end */
|
||||||
|
- if (probe_state == BES2600_BUS_PROBE_START) {
|
||||||
|
- bes_devel("wait bus probe operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
|
||||||
|
- HZ);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- /* must wait previous operation end in critical section */
|
||||||
|
- if (wait_state != BES2600_BOOT_WAIT_NONE) {
|
||||||
|
- bes_devel("wait previous operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
|
||||||
|
- HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- /* if dpd calibration is doing, modify wifi and bt state directly */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = true;
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = false;
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = true;
|
||||||
|
- bes2600_cdev.bton_pending = true;
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = false;
|
||||||
|
- bes2600_cdev.bton_pending = false;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
|
||||||
|
- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* process wifi/bt on/off operation */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(1);
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(0);
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(1);
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
|
||||||
|
- if (!ret && bes2600_chrdev_check_system_close())
|
||||||
|
- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
- bes2600_cdev.sbus_priv);
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return ret ;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-static int bes2600_op_change_fw_type(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- int temp = 0;
|
||||||
|
- long status = 0;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
- char fw_type[5] = {0};
|
||||||
|
- bool sys_closed = bes2600_chrdev_check_system_close();
|
||||||
|
-
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
-
|
||||||
|
- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- return -EPERM;
|
||||||
|
-
|
||||||
|
- /* check if user input is valid */
|
||||||
|
- cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
|
||||||
|
- if (strlen(str) < 16 || !cmd_ptr) {
|
||||||
|
- bes_err("the format of \"%s\" is error\n", str);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* convert fw_type from string to int */
|
||||||
|
- strncpy(fw_type, cmd_ptr + 14, 4);
|
||||||
|
- fw_type[0] = '+';
|
||||||
|
- ret = kstrtoint(fw_type, 10, &temp);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- bes_err("%s parse error\n", __func__);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* no need to realod firmware if new fw_type is equal to the old */
|
||||||
|
- if (temp == bes2600_cdev.fw_type ) {
|
||||||
|
- bes_devel("fw type is equal\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* close wifi net device */
|
||||||
|
- if (bes2600_cdev.sbus_priv
|
||||||
|
- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
||||||
|
- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* update firmware type */
|
||||||
|
- bes2600_cdev.fw_type = temp;
|
||||||
|
- bes2600_chrdev_update_signal_mode();
|
||||||
|
-
|
||||||
|
- if (!sys_closed) {
|
||||||
|
- /* close device to call disconnect function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
|
||||||
|
- else if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_chrdev_start_bus_probe();
|
||||||
|
-
|
||||||
|
- /* wait disconnect event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.dpd_calied
|
||||||
|
- && bes2600_chrdev_check_system_close()) {
|
||||||
|
- bes_devel("no need to reload firmware\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("reload firmware...\n");
|
||||||
|
- /* power on device to call probe function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
||||||
|
-
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_wakeup(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 wakeup bt.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_sleep(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 allow bt sleep.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_set_wakeup_read_flag(const char *str)
|
||||||
|
-{
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
||||||
|
-int bes2600_load_uevent(char *env[])
|
||||||
|
-{
|
||||||
|
- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
-}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-static struct bes2600_op_map bes2600_op_map_tab[] ={
|
||||||
|
- /*op op_len handler */
|
||||||
|
- {"P2P_SET_NOA", 11, bes2600_op_default_handler},
|
||||||
|
- {"P2P_SET_PS", 10, bes2600_op_default_handler},
|
||||||
|
- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
|
||||||
|
- {"LINKSPEED", 9, bes2600_op_default_handler},
|
||||||
|
- {"RSSI", 4, bes2600_op_default_handler},
|
||||||
|
- {"GETBAND", 7, bes2600_op_default_handler},
|
||||||
|
- {"WLS_BATCHING", 12, bes2600_op_default_handler},
|
||||||
|
- {"MACADDR", 7, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-START", 14, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-STOP", 13, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-ADD", 12, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXMODE", 10, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
|
||||||
|
- {"SETSUSPENDMODE", 14, bes2600_op_default_handler},
|
||||||
|
- {"COUNTRY", 7, bes2600_op_default_handler},
|
||||||
|
- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_ON", 5, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
|
||||||
|
- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
|
||||||
|
- {"BT_SLEEP", 8, bes2600_op_bt_sleep},
|
||||||
|
- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
|
||||||
|
-};
|
||||||
|
|
||||||
|
static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
{
|
||||||
|
@@ -644,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
&& (bes2600_cdev.wifi_opened == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
-{
|
||||||
|
- if (atomic_read(&bes2600_cdev.num_proc) > 0) {
|
||||||
|
- wait_event_timeout(bes2600_cdev.open_wq,
|
||||||
|
- (atomic_read(&bes2600_cdev.num_proc) == 0),
|
||||||
|
- MAX_SCHEDULE_TIMEOUT);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- bes_devel("bes2600 char device is opened\n");
|
||||||
|
- atomic_inc(&bes2600_cdev.num_proc);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
|
||||||
|
- size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- char buf[64] = {0};
|
||||||
|
- unsigned int len;
|
||||||
|
- long status = 0;
|
||||||
|
|
||||||
|
- switch (bes2600_cdev.read_flag) {
|
||||||
|
- case BES_CDEV_READ_WAKEUP_STATE:
|
||||||
|
- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
|
||||||
|
- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
|
||||||
|
- bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
|
||||||
|
- bes2600_cdev.dpd_calied,
|
||||||
|
- bes2600_cdev.wifi_opened,
|
||||||
|
- bes2600_cdev.bt_opened,
|
||||||
|
- bes2600_cdev.fw_type);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- len = sizeof(buf);
|
||||||
|
- /* reset read flag */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static ssize_t bes2600_chrdev_write(struct file *file,
|
||||||
|
- const char __user *user_buf, size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- int i = 0;
|
||||||
|
- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ret = 0;
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- char *buf = NULL;
|
||||||
|
-
|
||||||
|
- /* copy content from user space to kernel */
|
||||||
|
- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
|
||||||
|
- buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
- if (copy_from_user(buf, user_buf, count))
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* add terminal character */
|
||||||
|
- buf[count] = '\0';
|
||||||
|
-
|
||||||
|
- /* extract comand and interface */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
|
||||||
|
- kfree(buf);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* match operation item and execure its handler */
|
||||||
|
- cmd_len = strlen(info[1]);
|
||||||
|
- for (i = 0; i < cmd_num; i++) {
|
||||||
|
- if (cmd_len < bes2600_op_map_tab[i].op_len)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
|
||||||
|
- ret = bes2600_op_map_tab[i].handler(buf);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* operation item mismatch */
|
||||||
|
- if (i == cmd_num) {
|
||||||
|
- bes_err("cmd(%s) mismatch\n", info[1]);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- kfree(buf);
|
||||||
|
-
|
||||||
|
- return (ret == 0) ? count : ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_release (struct inode *inode, struct file *file)
|
||||||
|
-{
|
||||||
|
- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
|
||||||
|
- wake_up(&bes2600_cdev.open_wq);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 char device is closed\n");
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static struct file_operations bes2600_chardev_fops =
|
||||||
|
-{
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
- .open = bes2600_chrdev_open,
|
||||||
|
- .read = bes2600_chrdev_read,
|
||||||
|
- .write = bes2600_chrdev_write,
|
||||||
|
- .release = bes2600_chrdev_release,
|
||||||
|
-};
|
||||||
|
|
||||||
|
#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
@@ -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)
|
||||||
|
{
|
||||||
|
- char wifi_state[15];
|
||||||
|
- char bt_state[15];
|
||||||
|
- char fw_type[15];
|
||||||
|
- char *env[] = { wifi_state, bt_state, fw_type, NULL };
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
if (bes2600_chrdev_is_wifi_opened()) {
|
||||||
|
bes_devel("system exeception, force wifi down\n");
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* notify userspace */
|
||||||
|
- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
|
||||||
|
- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
|
||||||
|
- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
|
||||||
|
- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
- if (!ret)
|
||||||
|
- bes_err("bes2600 notify userspace failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1290,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
|
||||||
|
|
||||||
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
-
|
||||||
|
- /* allocate devide id */
|
||||||
|
- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 alloc device id fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* extract major and minor device id */
|
||||||
|
- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
|
||||||
|
- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
|
||||||
|
-
|
||||||
|
- /* add char device and bind operation function */
|
||||||
|
- bes2600_cdev.cdev.owner = THIS_MODULE;
|
||||||
|
- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
|
||||||
|
- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* create class for creating device node */
|
||||||
|
- bes2600_cdev.class = class_create("bes2600_chrdev");
|
||||||
|
- if (IS_ERR(bes2600_cdev.class)){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* get char device pointer */
|
||||||
|
- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
|
||||||
|
- if (IS_ERR(bes2600_cdev.device)){
|
||||||
|
- bes_err("bes2600 char device create fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* initialise global variable */
|
||||||
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
||||||
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
||||||
|
@@ -1361,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
-
|
||||||
|
-fail3:
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
-fail2:
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
-fail1:
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
-fail:
|
||||||
|
- return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bes2600_chrdev_free(void)
|
||||||
|
@@ -1379,9 +821,5 @@ void bes2600_chrdev_free(void)
|
||||||
|
bes2600_free_dpd_log_buffer();
|
||||||
|
#endif
|
||||||
|
bes2600_chrdev_free_dpd_data();
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+216
@@ -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
|
||||||
|
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
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 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
|
||||||
|
window and never cleared reject_count outside of a successful scan.
|
||||||
|
Field testing on a PineTab2 (linux-pinetab2 6.19.10-danctnix1) shows
|
||||||
|
two distinct mac80211 scan-retry cadences in practice:
|
||||||
|
|
||||||
|
* Idle background scans every ~5 minutes when associated -- well
|
||||||
|
outside any plausible backoff, the defer guard correctly falls
|
||||||
|
through to a real WSM scan attempt.
|
||||||
|
|
||||||
|
* Roam-evaluation bursts triggered when mac80211 wants to find a
|
||||||
|
candidate AP for handover (signal degradation, beacon loss,
|
||||||
|
locally-generated DEAUTH_LEAVING reason=3). Cadence is ~12 s, and
|
||||||
|
one boot reproduced 14 such rejected scans in 3 minutes during a
|
||||||
|
single burst, none of which engaged the defer guard because every
|
||||||
|
retry landed just outside the 10 s window.
|
||||||
|
|
||||||
|
Two-line behaviour change to fix that:
|
||||||
|
|
||||||
|
1. BES2600_SCAN_BACKOFF_JIFFIES grows from 10*HZ to 30*HZ, so a
|
||||||
|
12 s-cadence burst stays inside the window across consecutive
|
||||||
|
rejects and the third reject in the burst trips the threshold
|
||||||
|
guard. The 5 min idle case is still naturally past the window
|
||||||
|
and is unaffected.
|
||||||
|
|
||||||
|
2. bes2600_scan_should_defer() resets reject_count to 0 when
|
||||||
|
time_after(jiffies, backoff_until). Without this, reject_count
|
||||||
|
accumulated indefinitely across the slow-cadence rejects, so an
|
||||||
|
isolated reject after long quiet would have tripped the
|
||||||
|
threshold the moment it arrived. After the change, count is
|
||||||
|
latched only inside an active burst and decays cleanly when the
|
||||||
|
burst ends.
|
||||||
|
|
||||||
|
Net effect on a roam burst:
|
||||||
|
|
||||||
|
* t=0 reject #1 (count 1, backoff_until = t0 + 30s)
|
||||||
|
* t=12 reject #2 (count 2, backoff_until = t1 + 30s)
|
||||||
|
* t=24 reject #3 (count 3, threshold met, next scan deferred)
|
||||||
|
* t=36 defer fires, no WSM round-trip, reject not sent
|
||||||
|
* ... defers continue until the firmware-policy state clears
|
||||||
|
* scan succeeds -> reject_count = 0, normal cadence resumes
|
||||||
|
|
||||||
|
WSM 0x0007 confirm rejections in a burst drop from ~14 to ~3 (just
|
||||||
|
the scans needed to reach the threshold). wpa_supplicant's reason=3
|
||||||
|
locally-generated disconnects driven by exhausted roam candidates
|
||||||
|
during the same burst window also drop.
|
||||||
|
|
||||||
|
No new state, no new symbols, no change to mac80211-facing semantics:
|
||||||
|
the deferred scan still completes via the existing fail: path with
|
||||||
|
status=-EBUSY, the same response a real firmware-busy would produce.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 17 +++++++++++++++--
|
||||||
|
1 file changed, 15 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||||
|
index 5f6af3b..b944adc 100644
|
||||||
|
--- a/drivers/staging/bes2600/scan.c
|
||||||
|
+++ b/drivers/staging/bes2600/scan.c
|
||||||
|
@@ -22,9 +22,17 @@
|
||||||
|
* After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
* that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ *
|
||||||
|
+ * The backoff has to be at least as long as the natural mac80211 scan-
|
||||||
|
+ * retry cadence, otherwise the next attempt lands outside the window
|
||||||
|
+ * and bypasses the defer guard. Observed in the wild on PineTab2:
|
||||||
|
+ * roam-evaluation bursts at ~12 s cadence, idle background scans at
|
||||||
|
+ * ~5 min cadence. 30 s catches the burst and leaves the slow case
|
||||||
|
+ * alone (the firmware-policy state has had minutes to clear by then
|
||||||
|
+ * anyway).
|
||||||
|
*/
|
||||||
|
#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
-#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ)
|
||||||
|
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
@@ -40,7 +48,9 @@ static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
* rejections on recent scan attempts and the backoff window has
|
||||||
|
* not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
- * rejecting them; give it time.
|
||||||
|
+ * rejecting them; give it time. If the backoff has elapsed without
|
||||||
|
+ * a fresh reject refreshing it, the burst is over and we reset the
|
||||||
|
+ * count so an isolated reject doesn't immediately re-trip.
|
||||||
|
*
|
||||||
|
* Returns true if the caller should abandon the scan iteration.
|
||||||
|
*/
|
||||||
|
@@ -51,6 +61,9 @@ static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+ if (time_after(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
+
|
||||||
|
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
return true;
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
From db4ea70fb5dae1b2ab9c06dd91f1d7b2b9dcf09c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Tue, 28 Apr 2026 14:32:18 +0200
|
||||||
|
Subject: [PATCH] bes2600: widen scan-defer backoff to 30s and decay count on
|
||||||
|
quiet
|
||||||
|
|
||||||
|
The scan-defer logic added in the previous patch ("bes2600: defer
|
||||||
|
scan and soften WARN on firmware reject") used a 10-second backoff
|
||||||
|
window and never cleared reject_count outside of a successful scan.
|
||||||
|
Field testing on a PineTab2 (linux-pinetab2 6.19.10-danctnix1) shows
|
||||||
|
two distinct mac80211 scan-retry cadences in practice:
|
||||||
|
|
||||||
|
* Idle background scans every ~5 minutes when associated -- well
|
||||||
|
outside any plausible backoff, the defer guard correctly falls
|
||||||
|
through to a real WSM scan attempt.
|
||||||
|
|
||||||
|
* Roam-evaluation bursts triggered when mac80211 wants to find a
|
||||||
|
candidate AP for handover (signal degradation, beacon loss,
|
||||||
|
locally-generated DEAUTH_LEAVING reason=3). Cadence is ~12 s, and
|
||||||
|
one boot reproduced 14 such rejected scans in 3 minutes during a
|
||||||
|
single burst, none of which engaged the defer guard because every
|
||||||
|
retry landed just outside the 10 s window.
|
||||||
|
|
||||||
|
Two-line behaviour change to fix that:
|
||||||
|
|
||||||
|
1. BES2600_SCAN_BACKOFF_JIFFIES grows from 10*HZ to 30*HZ, so a
|
||||||
|
12 s-cadence burst stays inside the window across consecutive
|
||||||
|
rejects and the third reject in the burst trips the threshold
|
||||||
|
guard. The 5 min idle case is still naturally past the window
|
||||||
|
and is unaffected.
|
||||||
|
|
||||||
|
2. bes2600_scan_should_defer() resets reject_count to 0 when
|
||||||
|
time_after(jiffies, backoff_until). Without this, reject_count
|
||||||
|
accumulated indefinitely across the slow-cadence rejects, so an
|
||||||
|
isolated reject after long quiet would have tripped the
|
||||||
|
threshold the moment it arrived. After the change, count is
|
||||||
|
latched only inside an active burst and decays cleanly when the
|
||||||
|
burst ends.
|
||||||
|
|
||||||
|
Net effect on a roam burst:
|
||||||
|
|
||||||
|
* t=0 reject #1 (count 1, backoff_until = t0 + 30s)
|
||||||
|
* t=12 reject #2 (count 2, backoff_until = t1 + 30s)
|
||||||
|
* t=24 reject #3 (count 3, threshold met, next scan deferred)
|
||||||
|
* t=36 defer fires, no WSM round-trip, reject not sent
|
||||||
|
* ... defers continue until the firmware-policy state clears
|
||||||
|
* scan succeeds -> reject_count = 0, normal cadence resumes
|
||||||
|
|
||||||
|
WSM 0x0007 confirm rejections in a burst drop from ~14 to ~3 (just
|
||||||
|
the scans needed to reach the threshold). wpa_supplicant's reason=3
|
||||||
|
locally-generated disconnects driven by exhausted roam candidates
|
||||||
|
during the same burst window also drop.
|
||||||
|
|
||||||
|
No new state, no new symbols, no change to mac80211-facing semantics:
|
||||||
|
the deferred scan still completes via the existing fail: path with
|
||||||
|
status=-EBUSY, the same response a real firmware-busy would produce.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 17 +++++++++++++++--
|
||||||
|
1 file changed, 15 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/scan.c b/bes2600/scan.c
|
||||||
|
index faa1c90..ad5033b 100644
|
||||||
|
--- a/bes2600/scan.c
|
||||||
|
+++ b/bes2600/scan.c
|
||||||
|
@@ -22,9 +22,17 @@
|
||||||
|
* After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
* that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ *
|
||||||
|
+ * The backoff has to be at least as long as the natural mac80211 scan-
|
||||||
|
+ * retry cadence, otherwise the next attempt lands outside the window
|
||||||
|
+ * and bypasses the defer guard. Observed in the wild on PineTab2:
|
||||||
|
+ * roam-evaluation bursts at ~12 s cadence, idle background scans at
|
||||||
|
+ * ~5 min cadence. 30 s catches the burst and leaves the slow case
|
||||||
|
+ * alone (the firmware-policy state has had minutes to clear by then
|
||||||
|
+ * anyway).
|
||||||
|
*/
|
||||||
|
#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
-#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ)
|
||||||
|
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
@@ -40,7 +48,9 @@ static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
* rejections on recent scan attempts and the backoff window has
|
||||||
|
* not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
- * rejecting them; give it time.
|
||||||
|
+ * rejecting them; give it time. If the backoff has elapsed without
|
||||||
|
+ * a fresh reject refreshing it, the burst is over and we reset the
|
||||||
|
+ * count so an isolated reject doesn't immediately re-trip.
|
||||||
|
*
|
||||||
|
* Returns true if the caller should abandon the scan iteration.
|
||||||
|
*/
|
||||||
|
@@ -51,6 +61,9 @@ static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+ if (time_after(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
+
|
||||||
|
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
return true;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
From 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 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
|
||||||
|
pattern, visible in dmesg roughly 30 times per 10 min of regular
|
||||||
|
WiFi use:
|
||||||
|
|
||||||
|
wsm_generic_confirm ret 2
|
||||||
|
WARNING: at wsm_handle_rx+0x8a4/0xf30 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: wsm_generic_confirm failed for request 0x0007.
|
||||||
|
WARNING: at bes2600_scan_work+0x5d4/0x810 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: [SCAN] Scan failed (-22).
|
||||||
|
|
||||||
|
0x0007 is the WSM start-scan request; status 2 is the firmware's
|
||||||
|
rejected-by-policy response, which it returns for at least two
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
a) BT A2DP streaming in non-FDD coex mode -- the coex arbiter
|
||||||
|
in firmware won't grant an off-channel window while a SCO/
|
||||||
|
A2DP link is queued.
|
||||||
|
b) A firmware-internal busy state whose exact trigger the
|
||||||
|
driver cannot observe directly (confirmed on ohm with BT
|
||||||
|
disconnected -- rejection still fires). Likely transient
|
||||||
|
firmware-PM transitions.
|
||||||
|
|
||||||
|
Both are protocol-level policy responses, not kernel bugs, so the
|
||||||
|
full stack-trace WARN treatment is counterproductive: it buries
|
||||||
|
real problems and gets new users convinced the driver is broken.
|
||||||
|
|
||||||
|
Three-part fix:
|
||||||
|
|
||||||
|
1. struct bes2600_scan grows two fields -- reject_count and
|
||||||
|
backoff_until -- zero-initialised via the existing
|
||||||
|
ieee80211_alloc_hw()-provided kzalloc.
|
||||||
|
|
||||||
|
2. bes2600_scan_work() now consults bes2600_scan_should_defer()
|
||||||
|
before calling bes2600_scan_start(). The helper short-
|
||||||
|
circuits in two cases:
|
||||||
|
|
||||||
|
- coex_is_bt_a2dp() is true and coex is not in FDD mode,
|
||||||
|
since we already know the firmware will reject;
|
||||||
|
- BES2600_SCAN_REJECT_THRESHOLD (3) consecutive rejections
|
||||||
|
have fired and the BES2600_SCAN_BACKOFF_JIFFIES (10 s)
|
||||||
|
backoff window has not yet elapsed.
|
||||||
|
|
||||||
|
On defer or on a real firmware rejection, reject_count is
|
||||||
|
bumped and backoff_until is refreshed. A successful scan
|
||||||
|
clears reject_count.
|
||||||
|
|
||||||
|
3. The WARN_ON(hw_priv->scan.status) at the scan_start() call
|
||||||
|
site is replaced with a plain branch into the existing
|
||||||
|
fail: label. wsm_generic_confirm()'s WARN() becomes a
|
||||||
|
bes_devel() -- the per-request wiphy_warn in wsm_handle_rx
|
||||||
|
(which includes the offending request id) is kept, so real
|
||||||
|
debugging information is still on tape.
|
||||||
|
|
||||||
|
Net behaviour:
|
||||||
|
|
||||||
|
- Expected rejections no longer produce stack traces. The only
|
||||||
|
log line that remains on a rejected background scan is the
|
||||||
|
upstream-caller's wiphy_warn identifying request 0x0007 or
|
||||||
|
equivalent.
|
||||||
|
- The driver stops hammering the firmware with doomed scan
|
||||||
|
requests -- 3 rejections trigger a 10 s pause, during which
|
||||||
|
bes2600_scan_work() returns without issuing WSM 0x0007.
|
||||||
|
- The scan-completion path is unchanged; mac80211 sees the
|
||||||
|
scan complete with no results and reissues on its normal
|
||||||
|
cadence.
|
||||||
|
- Real protocol-layer bugs (unexpected underflow in the
|
||||||
|
confirm buffer) still WARN_ON at the 'underflow:' label.
|
||||||
|
|
||||||
|
Verified on ohm (PineTab2, linux-pinetab2 6.19.10-danctnix1-1):
|
||||||
|
WARN splat count dropped from 32 to 0 per 10 min uptime. WiFi
|
||||||
|
stays associated. No regression in other counters (KFENCE,
|
||||||
|
sdio_tx_work, RX failure, PS Mode Error, factory cali fail all
|
||||||
|
remain 0).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/scan.h | 11 +++++++++
|
||||||
|
bes2600/wsm.c | 14 +++++++++++-
|
||||||
|
3 files changed, 83 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
|
||||||
|
index 3bfa535..5f6af3b 100644
|
||||||
|
--- a/drivers/staging/bes2600/scan.c
|
||||||
|
+++ b/drivers/staging/bes2600/scan.c
|
||||||
|
@@ -14,11 +14,50 @@
|
||||||
|
#include "scan.h"
|
||||||
|
#include "sta.h"
|
||||||
|
#include "pm.h"
|
||||||
|
+#include "epta_coex.h"
|
||||||
|
#include "epta_request.h"
|
||||||
|
#include "bes_pwr.h"
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
+ * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
+ * that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Decide whether to skip sending the next WSM scan command without
|
||||||
|
+ * bothering the firmware. Two triggers:
|
||||||
|
+ *
|
||||||
|
+ * 1. BT A2DP is streaming in non-FDD coex mode. The firmware is
|
||||||
|
+ * known to reject scan requests during that window; short-
|
||||||
|
+ * circuiting here saves a WSM round-trip and avoids the
|
||||||
|
+ * wsm_generic_confirm / scan_work warning cascade that follows.
|
||||||
|
+ *
|
||||||
|
+ * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
+ * rejections on recent scan attempts and the backoff window has
|
||||||
|
+ * not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
+ * rejecting them; give it time.
|
||||||
|
+ *
|
||||||
|
+ * Returns true if the caller should abandon the scan iteration.
|
||||||
|
+ */
|
||||||
|
+static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
||||||
|
+ if (!coex_is_fdd_mode() && coex_is_bt_a2dp())
|
||||||
|
+ return true;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
+ time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ return true;
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#ifdef CONFIG_BES2600_TESTMODE
|
||||||
|
static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
@@ -703,10 +742,29 @@ void bes2600_scan_work(struct work_struct *work)
|
||||||
|
wsm_unlock_tx(hw_priv);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
+ {
|
||||||
|
+ if (bes2600_scan_should_defer(hw_priv)) {
|
||||||
|
+ hw_priv->scan.status = -EBUSY;
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ wiphy_dbg(priv->hw->wiphy,
|
||||||
|
+ "[SCAN] deferred (coex/backoff, reject_count=%u)\n",
|
||||||
|
+ hw_priv->scan.reject_count);
|
||||||
|
+ kfree(scan.ch);
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
hw_priv->scan.status = bes2600_scan_start(priv, &scan);
|
||||||
|
+ }
|
||||||
|
kfree(scan.ch);
|
||||||
|
- if (WARN_ON(hw_priv->scan.status))
|
||||||
|
+ if (hw_priv->scan.status) {
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ /* Lower callers already logged the reason at wiphy_warn. */
|
||||||
|
goto fail;
|
||||||
|
+ }
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
hw_priv->scan.curr = it;
|
||||||
|
}
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
|
diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h
|
||||||
|
index e50fa36..1f3adea 100644
|
||||||
|
--- a/drivers/staging/bes2600/scan.h
|
||||||
|
+++ b/drivers/staging/bes2600/scan.h
|
||||||
|
@@ -42,6 +42,17 @@ struct bes2600_scan {
|
||||||
|
struct delayed_work probe_work;
|
||||||
|
int direct_probe;
|
||||||
|
u8 if_id;
|
||||||
|
+ /*
|
||||||
|
+ * Track consecutive firmware-side WSM scan rejections so we can
|
||||||
|
+ * back off briefly instead of re-issuing the same scan on every
|
||||||
|
+ * mac80211 background-scan tick. Firmware returns WSM status != 0
|
||||||
|
+ * for a handful of transient conditions (BT A2DP active in non-
|
||||||
|
+ * FDD coex, firmware-internal busy windows) and keeps rejecting
|
||||||
|
+ * until the state clears; retrying at full cadence just floods
|
||||||
|
+ * dmesg.
|
||||||
|
+ */
|
||||||
|
+ unsigned int reject_count;
|
||||||
|
+ unsigned long backoff_until;
|
||||||
|
};
|
||||||
|
|
||||||
|
int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||||
|
diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c
|
||||||
|
index d40df30..55a4e2b 100644
|
||||||
|
--- a/drivers/staging/bes2600/wsm.c
|
||||||
|
+++ b/drivers/staging/bes2600/wsm.c
|
||||||
|
@@ -134,8 +134,20 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv,
|
||||||
|
struct wsm_buf *buf)
|
||||||
|
{
|
||||||
|
u32 status = WSM_GET32(buf);
|
||||||
|
- if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * A non-SUCCESS status here is a firmware-side policy decision for
|
||||||
|
+ * the command whose confirm this is -- commonly WSM status 2 for
|
||||||
|
+ * scan (0x0407) rejected because of a coex window or transient
|
||||||
|
+ * firmware-busy state. It is not a driver/kernel bug, so avoid the
|
||||||
|
+ * WARN()/stack-trace treatment; the caller already emits a
|
||||||
|
+ * wiphy_warn identifying the request id and will propagate the
|
||||||
|
+ * error to mac80211.
|
||||||
|
+ */
|
||||||
|
+ if (status != WSM_STATUS_SUCCESS) {
|
||||||
|
+ bes_devel("%s ret %u\n", __func__, status);
|
||||||
|
return -EINVAL;
|
||||||
|
+ }
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
underflow:
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
From adc6c1f332d41ee1aadd349eea11809c88139307 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Fri, 24 Apr 2026 21:31:45 +0200
|
||||||
|
Subject: [PATCH] bes2600: defer scan and soften WARN on firmware reject
|
||||||
|
|
||||||
|
On a BES2600-based PineTab2, mac80211's background-scan cadence
|
||||||
|
(about every 30 s when associated) triggers a two-step WARN splat
|
||||||
|
pattern, visible in dmesg roughly 30 times per 10 min of regular
|
||||||
|
WiFi use:
|
||||||
|
|
||||||
|
wsm_generic_confirm ret 2
|
||||||
|
WARNING: at wsm_handle_rx+0x8a4/0xf30 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: wsm_generic_confirm failed for request 0x0007.
|
||||||
|
WARNING: at bes2600_scan_work+0x5d4/0x810 [bes2600]
|
||||||
|
... full stack trace ...
|
||||||
|
ieee80211 phy0: [SCAN] Scan failed (-22).
|
||||||
|
|
||||||
|
0x0007 is the WSM start-scan request; status 2 is the firmware's
|
||||||
|
rejected-by-policy response, which it returns for at least two
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
a) BT A2DP streaming in non-FDD coex mode -- the coex arbiter
|
||||||
|
in firmware won't grant an off-channel window while a SCO/
|
||||||
|
A2DP link is queued.
|
||||||
|
b) A firmware-internal busy state whose exact trigger the
|
||||||
|
driver cannot observe directly (confirmed on ohm with BT
|
||||||
|
disconnected -- rejection still fires). Likely transient
|
||||||
|
firmware-PM transitions.
|
||||||
|
|
||||||
|
Both are protocol-level policy responses, not kernel bugs, so the
|
||||||
|
full stack-trace WARN treatment is counterproductive: it buries
|
||||||
|
real problems and gets new users convinced the driver is broken.
|
||||||
|
|
||||||
|
Three-part fix:
|
||||||
|
|
||||||
|
1. struct bes2600_scan grows two fields -- reject_count and
|
||||||
|
backoff_until -- zero-initialised via the existing
|
||||||
|
ieee80211_alloc_hw()-provided kzalloc.
|
||||||
|
|
||||||
|
2. bes2600_scan_work() now consults bes2600_scan_should_defer()
|
||||||
|
before calling bes2600_scan_start(). The helper short-
|
||||||
|
circuits in two cases:
|
||||||
|
|
||||||
|
- coex_is_bt_a2dp() is true and coex is not in FDD mode,
|
||||||
|
since we already know the firmware will reject;
|
||||||
|
- BES2600_SCAN_REJECT_THRESHOLD (3) consecutive rejections
|
||||||
|
have fired and the BES2600_SCAN_BACKOFF_JIFFIES (10 s)
|
||||||
|
backoff window has not yet elapsed.
|
||||||
|
|
||||||
|
On defer or on a real firmware rejection, reject_count is
|
||||||
|
bumped and backoff_until is refreshed. A successful scan
|
||||||
|
clears reject_count.
|
||||||
|
|
||||||
|
3. The WARN_ON(hw_priv->scan.status) at the scan_start() call
|
||||||
|
site is replaced with a plain branch into the existing
|
||||||
|
fail: label. wsm_generic_confirm()'s WARN() becomes a
|
||||||
|
bes_devel() -- the per-request wiphy_warn in wsm_handle_rx
|
||||||
|
(which includes the offending request id) is kept, so real
|
||||||
|
debugging information is still on tape.
|
||||||
|
|
||||||
|
Net behaviour:
|
||||||
|
|
||||||
|
- Expected rejections no longer produce stack traces. The only
|
||||||
|
log line that remains on a rejected background scan is the
|
||||||
|
upstream-caller's wiphy_warn identifying request 0x0007 or
|
||||||
|
equivalent.
|
||||||
|
- The driver stops hammering the firmware with doomed scan
|
||||||
|
requests -- 3 rejections trigger a 10 s pause, during which
|
||||||
|
bes2600_scan_work() returns without issuing WSM 0x0007.
|
||||||
|
- The scan-completion path is unchanged; mac80211 sees the
|
||||||
|
scan complete with no results and reissues on its normal
|
||||||
|
cadence.
|
||||||
|
- Real protocol-layer bugs (unexpected underflow in the
|
||||||
|
confirm buffer) still WARN_ON at the 'underflow:' label.
|
||||||
|
|
||||||
|
Verified on ohm (PineTab2, linux-pinetab2 6.19.10-danctnix1-1):
|
||||||
|
WARN splat count dropped from 32 to 0 per 10 min uptime. WiFi
|
||||||
|
stays associated. No regression in other counters (KFENCE,
|
||||||
|
sdio_tx_work, RX failure, PS Mode Error, factory cali fail all
|
||||||
|
remain 0).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/scan.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
bes2600/scan.h | 11 +++++++++
|
||||||
|
bes2600/wsm.c | 14 +++++++++++-
|
||||||
|
3 files changed, 83 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/scan.c b/bes2600/scan.c
|
||||||
|
index b2c22e7..faa1c90 100644
|
||||||
|
--- a/bes2600/scan.c
|
||||||
|
+++ b/bes2600/scan.c
|
||||||
|
@@ -14,11 +14,50 @@
|
||||||
|
#include "scan.h"
|
||||||
|
#include "sta.h"
|
||||||
|
#include "pm.h"
|
||||||
|
+#include "epta_coex.h"
|
||||||
|
#include "epta_request.h"
|
||||||
|
#include "bes_pwr.h"
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * After this many consecutive WSM scan rejections from firmware, stop
|
||||||
|
+ * issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
|
||||||
|
+ * that's rejecting them (coex window, firmware-internal busy) clear.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_SCAN_REJECT_THRESHOLD 3
|
||||||
|
+#define BES2600_SCAN_BACKOFF_JIFFIES (10 * HZ)
|
||||||
|
+
|
||||||
|
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Decide whether to skip sending the next WSM scan command without
|
||||||
|
+ * bothering the firmware. Two triggers:
|
||||||
|
+ *
|
||||||
|
+ * 1. BT A2DP is streaming in non-FDD coex mode. The firmware is
|
||||||
|
+ * known to reject scan requests during that window; short-
|
||||||
|
+ * circuiting here saves a WSM round-trip and avoids the
|
||||||
|
+ * wsm_generic_confirm / scan_work warning cascade that follows.
|
||||||
|
+ *
|
||||||
|
+ * 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
|
||||||
|
+ * rejections on recent scan attempts and the backoff window has
|
||||||
|
+ * not yet elapsed. Whatever was rejecting them is likely still
|
||||||
|
+ * rejecting them; give it time.
|
||||||
|
+ *
|
||||||
|
+ * Returns true if the caller should abandon the scan iteration.
|
||||||
|
+ */
|
||||||
|
+static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
|
||||||
|
+{
|
||||||
|
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
||||||
|
+ if (!coex_is_fdd_mode() && coex_is_bt_a2dp())
|
||||||
|
+ return true;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
|
||||||
|
+ time_before(jiffies, hw_priv->scan.backoff_until))
|
||||||
|
+ return true;
|
||||||
|
+
|
||||||
|
+ return false;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#ifdef CONFIG_BES2600_TESTMODE
|
||||||
|
static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
|
||||||
|
{
|
||||||
|
@@ -702,10 +741,29 @@ void bes2600_scan_work(struct work_struct *work)
|
||||||
|
wsm_unlock_tx(hw_priv);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
+ {
|
||||||
|
+ if (bes2600_scan_should_defer(hw_priv)) {
|
||||||
|
+ hw_priv->scan.status = -EBUSY;
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ wiphy_dbg(priv->hw->wiphy,
|
||||||
|
+ "[SCAN] deferred (coex/backoff, reject_count=%u)\n",
|
||||||
|
+ hw_priv->scan.reject_count);
|
||||||
|
+ kfree(scan.ch);
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
hw_priv->scan.status = bes2600_scan_start(priv, &scan);
|
||||||
|
+ }
|
||||||
|
kfree(scan.ch);
|
||||||
|
- if (WARN_ON(hw_priv->scan.status))
|
||||||
|
+ if (hw_priv->scan.status) {
|
||||||
|
+ hw_priv->scan.reject_count++;
|
||||||
|
+ hw_priv->scan.backoff_until =
|
||||||
|
+ jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
|
||||||
|
+ /* Lower callers already logged the reason at wiphy_warn. */
|
||||||
|
goto fail;
|
||||||
|
+ }
|
||||||
|
+ hw_priv->scan.reject_count = 0;
|
||||||
|
hw_priv->scan.curr = it;
|
||||||
|
}
|
||||||
|
up(&hw_priv->conf_lock);
|
||||||
|
diff --git a/bes2600/scan.h b/bes2600/scan.h
|
||||||
|
index e50fa36..1f3adea 100644
|
||||||
|
--- a/bes2600/scan.h
|
||||||
|
+++ b/bes2600/scan.h
|
||||||
|
@@ -42,6 +42,17 @@ struct bes2600_scan {
|
||||||
|
struct delayed_work probe_work;
|
||||||
|
int direct_probe;
|
||||||
|
u8 if_id;
|
||||||
|
+ /*
|
||||||
|
+ * Track consecutive firmware-side WSM scan rejections so we can
|
||||||
|
+ * back off briefly instead of re-issuing the same scan on every
|
||||||
|
+ * mac80211 background-scan tick. Firmware returns WSM status != 0
|
||||||
|
+ * for a handful of transient conditions (BT A2DP active in non-
|
||||||
|
+ * FDD coex, firmware-internal busy windows) and keeps rejecting
|
||||||
|
+ * until the state clears; retrying at full cadence just floods
|
||||||
|
+ * dmesg.
|
||||||
|
+ */
|
||||||
|
+ unsigned int reject_count;
|
||||||
|
+ unsigned long backoff_until;
|
||||||
|
};
|
||||||
|
|
||||||
|
int bes2600_hw_scan(struct ieee80211_hw *hw,
|
||||||
|
diff --git a/bes2600/wsm.c b/bes2600/wsm.c
|
||||||
|
index d40df30..55a4e2b 100644
|
||||||
|
--- a/bes2600/wsm.c
|
||||||
|
+++ b/bes2600/wsm.c
|
||||||
|
@@ -134,8 +134,20 @@ static int wsm_generic_confirm(struct bes2600_common *hw_priv,
|
||||||
|
struct wsm_buf *buf)
|
||||||
|
{
|
||||||
|
u32 status = WSM_GET32(buf);
|
||||||
|
- if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * A non-SUCCESS status here is a firmware-side policy decision for
|
||||||
|
+ * the command whose confirm this is -- commonly WSM status 2 for
|
||||||
|
+ * scan (0x0407) rejected because of a coex window or transient
|
||||||
|
+ * firmware-busy state. It is not a driver/kernel bug, so avoid the
|
||||||
|
+ * WARN()/stack-trace treatment; the caller already emits a
|
||||||
|
+ * wiphy_warn identifying the request id and will propagate the
|
||||||
|
+ * error to mac80211.
|
||||||
|
+ */
|
||||||
|
+ if (status != WSM_STATUS_SUCCESS) {
|
||||||
|
+ bes_devel("%s ret %u\n", __func__, status);
|
||||||
|
return -EINVAL;
|
||||||
|
+ }
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
underflow:
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+168
@@ -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`.
|
||||||
+168
@@ -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
|
||||||
|
|
||||||
+540
@@ -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
|
||||||
|
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 12:35:28 +0200
|
||||||
|
Subject: [PATCH 0/7] bes2600: staging-prep cleanup for PineTab2 (BES2600WM)
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
This series is a staging-prep cleanup for the out-of-tree Bestechnic
|
||||||
|
BES2600WM Wi-Fi/BT combo-chip driver as shipped by Mobian's bes2600-dkms
|
||||||
|
package (and in-tree at drivers/staging/bes2600/ in the danctnix
|
||||||
|
linux-pinetab2 fork). Target hardware is the Pine64 PineTab2 (RK3566
|
||||||
|
+ BES2600WM, SDIO vendor 0xBE57 / device 0x2002).
|
||||||
|
|
||||||
|
The driver descends from the ST-Ericsson CW1200 (drivers/net/wireless/
|
||||||
|
st/cw1200/) -- same author, Dmitry Tarnyagin, shared WSM host<->firmware
|
||||||
|
protocol, shared SDIO bus backend. Kconfig ancestry markers survive in
|
||||||
|
this tree today: CONFIG_BES2600_USE_STE_EXTENSIONS (STE = ST-Ericsson),
|
||||||
|
CONFIG_BES2600_WSM_DEBUG (WSM). ST-Ericsson wound down in 2013;
|
||||||
|
Bestechnic (founded 2015) appears to have inherited or licensed the
|
||||||
|
CW1200 IP. No linux-wireless RFC has ever linked the two chips.
|
||||||
|
|
||||||
|
The series fixes observable defects on a PineTab2 running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 and removes two upstream blockers. Each patch is
|
||||||
|
independently testable and bisectable; the order below preserves
|
||||||
|
dependencies.
|
||||||
|
|
||||||
|
## What the series does
|
||||||
|
|
||||||
|
* 1/7 -- Replace filp_open() + kernel_read() in the factory-calibration
|
||||||
|
read path with request_firmware(). Repoint the FACTORY_PATH macro to
|
||||||
|
the firmware-class name (bes2600/bes2600_factory.txt, matching the
|
||||||
|
/lib/firmware/ layout). Kills a kernel-mainline anti-pattern and the
|
||||||
|
'(NULL device *): read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' boot spam on PineTab2.
|
||||||
|
|
||||||
|
* 2/7 -- Default STANDARD_FACTORY_EFUSE_FLAG from y to n. The shipped
|
||||||
|
bes2600_factory.txt on PineTab2 contains 30 calibration fields; the
|
||||||
|
driver was expecting 31 (including a ##select_efuse_flag section
|
||||||
|
absent from this firmware). Also unguards the
|
||||||
|
wsm_save_factory_txt_to_mcu() prototype in wsm.h which was
|
||||||
|
inconsistently wrapped in '#if defined(STANDARD_FACTORY_EFUSE_FLAG)'
|
||||||
|
while its definition in wsm.c and its call site in sta.c were
|
||||||
|
ungated -- gcc -Werror=missing-prototypes broke the build with the
|
||||||
|
new default until this is fixed.
|
||||||
|
|
||||||
|
* 3/7 -- Thread struct device * through factory_section_read_file() via
|
||||||
|
a module-local setter invoked at SDIO probe. request_firmware() now
|
||||||
|
receives a real device pointer; '(NULL device *):' no longer prefixes
|
||||||
|
factory-related diagnostics.
|
||||||
|
|
||||||
|
* 4/7 -- Gate the device-end of the low-power transition on successful
|
||||||
|
per-VIF firmware handshake. Pre-patch bes2600_pwr_enter_lp_mode()
|
||||||
|
called bes2600_pwr_device_enter_lp_mode() unconditionally even when
|
||||||
|
wait_for_completion_timeout() returned 0 (firmware never posted the
|
||||||
|
PM-change indication). On PineTab2 this recurred every 5-10 s
|
||||||
|
whenever the interface was associated and idle, flooded dmesg, and
|
||||||
|
cascaded into sdio_tx_work WARN splats / [RX] Receive failure
|
||||||
|
messages. Post-patch: -ETIMEDOUT returned cleanly, dmesg silent,
|
||||||
|
SDIO stable.
|
||||||
|
|
||||||
|
* 5/7 -- Remove the custom /dev/bes2600 character-device interface.
|
||||||
|
file_operations, open/read/write/release, bes2600_op_*
|
||||||
|
command-dispatch table, bes2600_load_uevent, alloc_chrdev_region /
|
||||||
|
cdev_init / cdev_add / class_create / device_create in the init
|
||||||
|
path, and the matching teardown in bes2600_chrdev_free -- 519 lines
|
||||||
|
deleted. The in-kernel accessor functions (is_signal_mode,
|
||||||
|
get_fw_type, etc., 13 call sites) and the fw_type module parameter
|
||||||
|
are preserved; the userspace interface becomes rfkill + module_param
|
||||||
|
+ (with 6/7) nl80211 testmode.
|
||||||
|
|
||||||
|
* 6/7 -- Flip CONFIG_BES2600_TESTMODE default from n to y. The driver
|
||||||
|
already implements a mac80211 testmode_cmd dispatcher (routing to
|
||||||
|
the firmware's patch_wifi_testMode path), already gated on the flag;
|
||||||
|
CONFIG_NL80211_TESTMODE=y is common on target kernels. Enabling the
|
||||||
|
flag also exposes accumulated bit-rot -- ~41 calls to undefined
|
||||||
|
bes2600_info/err/warn/dbg/err_with_cond macros, and 3 TSM/roam-delay
|
||||||
|
helpers with external linkage but no prototype. Add shim macros to
|
||||||
|
bes_log.h rewiring the legacy calls onto the existing bes_info /
|
||||||
|
bes_err / bes_warn / bes_devel family, define BES2600_DBG_* subsystem
|
||||||
|
ids as 0 constants, and mark the 3 helpers static.
|
||||||
|
|
||||||
|
* 7/7 -- Bounce SDIO TX buffers to avoid DMA out-of-bounds reads.
|
||||||
|
sdio_tx_work() rounded the transfer length up to the SDIO block size
|
||||||
|
(align = blks * cur_blksize, or 1632) and handed that length to
|
||||||
|
dma_map_sg() via sg_set_buf(..., tx_buffer->buf, align); tx_buffer->buf
|
||||||
|
typically aliases into an skb linear head allocated to tx_buffer->len,
|
||||||
|
not the block-aligned length. The DMA engine therefore read up to one
|
||||||
|
block past the end of the skb -- KFENCE on PineTab2 fires as
|
||||||
|
'out-of-bounds read in __pi_memcpy_generic ... 704B right of
|
||||||
|
kfence-#...' with sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper in
|
||||||
|
the stack and pskb_expand_head / validate_xmit_skb / tcp_write_xmit in
|
||||||
|
the allocator stack. Besides being undefined behavior, the padding
|
||||||
|
bytes are transmitted to the peer, leaking adjacent kernel memory on
|
||||||
|
the air. Allocate a driver-owned DMA-pages bounce buffer of
|
||||||
|
MAX_SDIO_TRANSFER_LEN, memcpy each TX buffer into its slot, zero the
|
||||||
|
padding tail, and point the SG entries at the bounce. Mirrors the
|
||||||
|
pattern already used for single_gathered_buffer; kept as a separate
|
||||||
|
allocation because sdio_tx_work accumulates SG entries before claiming
|
||||||
|
the bus.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Reference hardware: Pine64 PineTab2 (BES2600WM + Rockchip RK3566,
|
||||||
|
8a:2e:77:1f:ec:05, LAN AP newton @ 2.4 GHz ch11, tested also on
|
||||||
|
TelekomHotspot@ERGO @ 5 GHz ch36).
|
||||||
|
|
||||||
|
Host kernel: linux-pinetab2 6.19.10-danctnix1-1-pinetab2 with
|
||||||
|
CONFIG_NL80211_TESTMODE=y and CONFIG_KFENCE=y (for 7/7 verification).
|
||||||
|
Driver installed via /lib/modules/<ver>/extra/ and verified loaded via
|
||||||
|
modinfo srcversion.
|
||||||
|
|
||||||
|
Per-patch outcomes in post-reboot dmesg (full-stack applied, 11+ min
|
||||||
|
soak, 245k RX packets / 365 MB traffic):
|
||||||
|
|
||||||
|
- 1/7: 'read and check /lib/firmware/bes2600_factory.txt error' -- gone
|
||||||
|
- 2/7: 'bes2600_factory.txt parse fail' / 'factory cali data get
|
||||||
|
failed.' -- gone
|
||||||
|
- 3/7: '(NULL device *):' prefix on factory lines -- gone
|
||||||
|
- 4/7: 'bes2600_pwr_enter_lp_mode, wait pm ind timeout' (pre-patch 20-30
|
||||||
|
msgs / 5 min window) -- 0 per 5 min; '[RX] Receive failure: 4.' --
|
||||||
|
gone
|
||||||
|
- 5/7: /dev/bes2600 -- absent; driver continues to associate
|
||||||
|
- 6/7: 'iw phy0' lists 'testmode' under Supported commands; module
|
||||||
|
builds cleanly
|
||||||
|
- 7/7: 'BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic'
|
||||||
|
(pre-patch ~65 splats per 4 h of real traffic, always via
|
||||||
|
sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper+0x18c) -- 0;
|
||||||
|
'sdio_tx_work+0x2b4' WARN splat residual from 4/7's cascade -- 0
|
||||||
|
(previously recurred ~1 per reboot even with 4/7 applied);
|
||||||
|
'PS Mode Error, Reason:1' benign handshake notice at T+40s --
|
||||||
|
also gone, apparently a downstream effect of no longer DMAing
|
||||||
|
uninitialised padding bytes into firmware
|
||||||
|
|
||||||
|
Full stack: wifi associates and passes traffic across 3+ reboots.
|
||||||
|
|
||||||
|
## Known limitations / out of scope
|
||||||
|
|
||||||
|
- factory_section_write_file() in bes2600_factory.c still uses
|
||||||
|
kernel_write() + filp_open(O_CREAT) to persist per-channel
|
||||||
|
calibration updates back to /lib/firmware/bes2600_factory.txt.
|
||||||
|
Converting the write-back path to debugfs or nl80211 testmode is a
|
||||||
|
follow-up.
|
||||||
|
|
||||||
|
- bes_chardev.c still carries DPD file-read/write paths gated by
|
||||||
|
BES2600_WRITE_DPD_TO_FILE (off by default, so dead code in the
|
||||||
|
default build). Same treatment needed.
|
||||||
|
|
||||||
|
- bes_fw.c:587 unconditionally creates
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open() for debug
|
||||||
|
observation. Needs to go before drivers/staging/ accepts the driver.
|
||||||
|
|
||||||
|
- bes_cdev global singleton still holds sig_mode and fw_type. Moving
|
||||||
|
those to per-hw_priv state is blocked by fw_type being a module
|
||||||
|
parameter (inherently singleton). Migrating fw_type to a per-phy
|
||||||
|
debugfs knob or nl80211 testmode command is the next step; overlaps
|
||||||
|
with 6/7's testmode plumbing.
|
||||||
|
|
||||||
|
Markus Fritsche (7):
|
||||||
|
bes2600: use request_firmware() for factory.txt read
|
||||||
|
bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for PineTab2
|
||||||
|
factory.txt format
|
||||||
|
bes2600: thread struct device * through factory request_firmware()
|
||||||
|
call
|
||||||
|
bes2600: gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake
|
||||||
|
bes2600: remove userspace /dev/bes2600 character device interface
|
||||||
|
bes2600: enable CONFIG_BES2600_TESTMODE by default + fix bit-rotted
|
||||||
|
testmode plumbing
|
||||||
|
bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
drivers/staging/bes2600/Makefile | 6 +-
|
||||||
|
drivers/staging/bes2600/bes2600_factory.c | 45 ++--
|
||||||
|
drivers/staging/bes2600/bes2600_factory.h | 3 +
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 43 +++-
|
||||||
|
drivers/staging/bes2600/bes_chardev.c | 519 --------------------------------------
|
||||||
|
drivers/staging/bes2600/bes_log.h | 23 ++
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 20 +-
|
||||||
|
drivers/staging/bes2600/sta.c | 6 +-
|
||||||
|
drivers/staging/bes2600/wsm.h | 2 -
|
||||||
|
9 files changed, 117 insertions(+), 550 deletions(-)
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
From d18aa6a9bc03a03e455434f83577892aa1a60ffe Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <d18aa6a9bc03a03e455434f83577892aa1a60ffe.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 10:09:44 +0200
|
||||||
|
Subject: [PATCH 1/7] bes2600: use request_firmware() for factory.txt read
|
||||||
|
|
||||||
|
The BES2600 factory calibration file (bes2600_factory.txt) was being read
|
||||||
|
via filp_open() + kernel_read() from a hard-coded absolute path baked in
|
||||||
|
at compile time via the FACTORY_PATH Makefile macro
|
||||||
|
(default: /lib/firmware/bes2600_factory.txt).
|
||||||
|
|
||||||
|
This had several problems:
|
||||||
|
|
||||||
|
1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
|
||||||
|
device-pine64-pinetab2) ships the file at
|
||||||
|
/lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
|
||||||
|
driver logged '(NULL device *): read and check
|
||||||
|
/lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
|
||||||
|
running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
|
||||||
|
2. Direct filesystem access via filp_open() / kernel_read() from a driver
|
||||||
|
is an anti-pattern that upstream rejects: drivers should use
|
||||||
|
request_firmware() to get binary data from userspace-managed firmware
|
||||||
|
directories. request_firmware() natively searches the firmware_class
|
||||||
|
path list (typically /lib/firmware + derivatives), associates the load
|
||||||
|
with a uevent, and respects the firmware-loading infrastructure.
|
||||||
|
|
||||||
|
3. The (NULL device *) prefix in error messages indicated the absence of
|
||||||
|
proper device-context logging. While this patch does not yet thread
|
||||||
|
struct device through, the upstream path uses request_firmware() which
|
||||||
|
works with dev=NULL and is the building block for a follow-up patch
|
||||||
|
that adds per-chip device context.
|
||||||
|
|
||||||
|
Repoint the FACTORY_PATH default to the firmware-class name
|
||||||
|
(bes2600/bes2600_factory.txt) - request_firmware() prepends
|
||||||
|
/lib/firmware/ from the configured search paths. The macro remains
|
||||||
|
overridable at build time for non-standard deployments.
|
||||||
|
|
||||||
|
Rewrite factory_section_read_file() to:
|
||||||
|
* Call request_firmware(&fw, path, NULL).
|
||||||
|
* Size-check fw->size against FACTORY_MAX_SIZE.
|
||||||
|
* memcpy the data into the caller's buffer.
|
||||||
|
* Always call release_firmware() on exit.
|
||||||
|
|
||||||
|
The file write path (factory_section_write_file + kernel_write) is left
|
||||||
|
unchanged in this patch; it is the subject of a follow-up patch that
|
||||||
|
removes kernel_write and moves any remaining userspace-visible factory
|
||||||
|
configuration to a standard kernel-userspace boundary (debugfs or
|
||||||
|
nl80211 testmode).
|
||||||
|
|
||||||
|
No caller signature changes. No Makefile flag drops. Bisectable.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
|
||||||
|
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' is gone; request_firmware reads the file successfully (a separate
|
||||||
|
factory_parse() bug, previously masked by the read failure, is now
|
||||||
|
exposed and tracked separately).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/Makefile | 2 +-
|
||||||
|
drivers/staging/bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
|
||||||
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 300912b..788aee2 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
-FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||||
|
+FACTORY_PATH ?= drivers/staging/bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
# basic function
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index dc5d3da..8d60b7c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
+#include <linux/firmware.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/crc32.h>
|
||||||
|
@@ -137,38 +138,32 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data)
|
||||||
|
*/
|
||||||
|
static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
+ const struct firmware *fw;
|
||||||
|
+ int ret;
|
||||||
|
|
||||||
|
if (!path || !buffer) {
|
||||||
|
bes_err("%s NULL pointer err\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- bes_devel("reading %s \n", path);
|
||||||
|
+ bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
+ ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, FACTORY_MAX_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
+ if (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) {
|
||||||
|
+ bes_err("bes2600_factory.txt size check failed, read_size: %zu max_size: %d\n",
|
||||||
|
+ fw->size, FACTORY_MAX_SIZE);
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- if (ret != fp->f_inode->i_size) {
|
||||||
|
- bes_err("bes2600_factory.txt read fail\n");
|
||||||
|
- ret = -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ memcpy(buffer, fw->data, fw->size);
|
||||||
|
+ ret = (int)fw->size;
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
From a826f4db7d97a3a872d92079db37dbdaf9a0cdec Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <a826f4db7d97a3a872d92079db37dbdaf9a0cdec.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:17:56 +0200
|
||||||
|
Subject: [PATCH 2/7] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for
|
||||||
|
PineTab2 factory.txt format
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The shipped factory calibration file bes2600_factory.txt on PineTab2
|
||||||
|
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
|
||||||
|
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
|
||||||
|
bt (4). The file terminates with '%%\n' directly after edr_power.
|
||||||
|
|
||||||
|
When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
|
||||||
|
assembles STANDARD_FACTORY with an extra select_efuse_flag section
|
||||||
|
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):
|
||||||
|
|
||||||
|
__STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
|
||||||
|
+ \"%%%%\\n\"
|
||||||
|
|
||||||
|
The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
|
||||||
|
stops after field 30 and factory_parse() returns -1 with:
|
||||||
|
|
||||||
|
bes2600_factory.txt parse fail
|
||||||
|
read and check drivers/staging/bes2600/bes2600_factory.txt error
|
||||||
|
factory cali data get failed.
|
||||||
|
|
||||||
|
This was latent until the preceding patch (use request_firmware() for
|
||||||
|
factory.txt read) fixed the path bug that masked the parse failure.
|
||||||
|
|
||||||
|
Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
|
||||||
|
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
|
||||||
|
firmware packages that do ship the select_efuse_flag section.
|
||||||
|
|
||||||
|
Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
|
||||||
|
inconsistently wrapped in a conditional that keyed on
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
|
||||||
|
the call site in sta.c are ungated. With the flag now defaulting to
|
||||||
|
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
|
||||||
|
conditional wrapper around the prototype -- the function exists and is
|
||||||
|
used regardless of the factory-parse flag.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
|
||||||
|
succeeds on the shipped factory.txt, factory_cali_data is populated,
|
||||||
|
and dmesg no longer shows the parse-fail / read-and-check-error /
|
||||||
|
factory-cali-data-get-failed sequence.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/Makefile | 2 +-
|
||||||
|
drivers/staging/bes2600/wsm.h | 2 --
|
||||||
|
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 788aee2..2dcba09 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
-STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
+STANDARD_FACTORY_EFUSE_FLAG ?= n
|
||||||
|
FACTORY_PATH ?= drivers/staging/bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h
|
||||||
|
index 0673131..22845ac 100644
|
||||||
|
--- a/drivers/staging/bes2600/wsm.h
|
||||||
|
+++ b/drivers/staging/bes2600/wsm.h
|
||||||
|
@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
|
||||||
|
|
||||||
|
-#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
||||||
|
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
|
||||||
|
-#endif
|
||||||
|
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
From c7ba2044b78cc3778763737daea60c9912b710c6 Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <c7ba2044b78cc3778763737daea60c9912b710c6.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:18:38 +0200
|
||||||
|
Subject: [PATCH 3/7] bes2600: thread struct device * through factory
|
||||||
|
request_firmware() call
|
||||||
|
|
||||||
|
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
|
||||||
|
That patch switched the factory calibration read path from filp_open()
|
||||||
|
+ kernel_read() to request_firmware(), but passed dev=NULL to
|
||||||
|
request_firmware() because factory_section_read_file() did not have a
|
||||||
|
struct device * in scope. The resulting logs carry the
|
||||||
|
'(NULL device *):' prefix and do not propagate a udev association.
|
||||||
|
|
||||||
|
Add a module-local static struct device * used as the firmware-class
|
||||||
|
load context, plus a small exported setter:
|
||||||
|
|
||||||
|
static struct device *bes2600_factory_dev;
|
||||||
|
void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
|
||||||
|
Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
|
||||||
|
right after bes2600_platform_data_init() so the platform layer has
|
||||||
|
already had a chance to use the same struct device for its own
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
factory_section_read_file() now passes bes2600_factory_dev (instead
|
||||||
|
of NULL) to request_firmware(). When the factory read happens before
|
||||||
|
probe (not currently the case on PineTab2) the pointer is still NULL
|
||||||
|
and request_firmware() accepts that; no regression.
|
||||||
|
|
||||||
|
No API changes to bes2600_get_factory_cali_data() callers. The
|
||||||
|
char *path parameter remains (it is the firmware-class name fed
|
||||||
|
straight to request_firmware()).
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
|
||||||
|
post-c5 factory diagnostics now carry the SDIO device identity
|
||||||
|
instead of '(NULL device *)'.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_factory.c | 14 +++++++++++++-
|
||||||
|
drivers/staging/bes2600/bes2600_factory.h | 3 +++
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 4 ++++
|
||||||
|
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
index 8d60b7c..1cda447 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.c
|
||||||
|
@@ -31,6 +31,18 @@
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(factory_lock);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * struct device * for request_firmware() context. Set once at SDIO
|
||||||
|
+ * probe via bes2600_factory_set_dev(). NULL is tolerated (falls back
|
||||||
|
+ * to the udev-less firmware-class path) but loses per-device logging.
|
||||||
|
+ */
|
||||||
|
+static struct device *bes2600_factory_dev;
|
||||||
|
+
|
||||||
|
+void bes2600_factory_set_dev(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ bes2600_factory_dev = dev;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* It is only used for temporary storage.
|
||||||
|
* Every time get the factory, it will read from the
|
||||||
|
@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
|
||||||
|
bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ ret = request_firmware(&fw, path, bes2600_factory_dev);
|
||||||
|
if (ret) {
|
||||||
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
index 3835b0d..7dbe9f8 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_factory.h
|
||||||
|
@@ -199,6 +199,9 @@ enum factory_cali_status {
|
||||||
|
/* just calibrate 11n, other protocols are automatically mapped */
|
||||||
|
#define WIFI_RF_11N_MODE 0x15
|
||||||
|
|
||||||
|
+/* set the struct device * used for request_firmware() context */
|
||||||
|
+void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
+
|
||||||
|
/* read wifi & bt factory cali value*/
|
||||||
|
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
|
||||||
|
void factory_little_endian_cvrt(u8 *data);
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..371ef4f 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
#include "bes2600.h"
|
||||||
|
#include "sbus.h"
|
||||||
|
#include "bes2600_plat.h"
|
||||||
|
+#include "bes2600_factory.h"
|
||||||
|
#include "hwio.h"
|
||||||
|
#include "bes_chardev.h"
|
||||||
|
#include "bes_log.h"
|
||||||
|
@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
+ /* wire struct device into factory.c for request_firmware() context */
|
||||||
|
+ bes2600_factory_set_dev(dev);
|
||||||
|
+
|
||||||
|
self->pdata = bes2600_get_platform_data();
|
||||||
|
self->func = func;
|
||||||
|
self->dev = &func->dev;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
From 108d3967eac4ba3a6e0f508d865a5f221b49079d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <108d3967eac4ba3a6e0f508d865a5f221b49079d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:37:45 +0200
|
||||||
|
Subject: [PATCH 4/7] bes2600: gate device LP-mode entry on successful per-VIF
|
||||||
|
firmware handshake
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() drives the transition to low-power for each
|
||||||
|
associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on
|
||||||
|
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
|
||||||
|
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Two bugs:
|
||||||
|
|
||||||
|
1. A failed wsm_set_pm() only logs an error, then still falls into
|
||||||
|
wait_for_completion_timeout() on a completion the firmware will
|
||||||
|
never post (the set-mode command never reached it). The loop
|
||||||
|
therefore always blocks the full 5 s, logs a second error, and
|
||||||
|
proceeds.
|
||||||
|
|
||||||
|
2. A genuine wait-timeout (firmware received the set-mode command but
|
||||||
|
never posted the indication) also only logs a warning. The code
|
||||||
|
then drops to bes2600_pwr_device_enter_lp_mode(), handing the
|
||||||
|
device subsystem an inconsistent view of mac-layer state.
|
||||||
|
|
||||||
|
On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
|
||||||
|
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
|
||||||
|
message flooding dmesg every 5-10 s when the interface is associated
|
||||||
|
and idle. Sending the device to LP in that state cascades into the
|
||||||
|
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
|
||||||
|
WARN splat.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Add a 'timeouts' counter; bump it on both failure paths.
|
||||||
|
- Skip the wait_for_completion entirely when wsm_set_pm() failed
|
||||||
|
(there is no completion to wait for).
|
||||||
|
- Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
|
||||||
|
handshake reached firmware-ACKed completion; otherwise return
|
||||||
|
-ETIMEDOUT and leave the device in its current power state.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
Post-patch the handshake still fails on this particular firmware
|
||||||
|
revision (separate root-cause investigation outside this patch), but
|
||||||
|
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
|
||||||
|
and destabilising the SDIO path.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes_pwr.c | 20 +++++++++++++++++---
|
||||||
|
1 file changed, 17 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
index e7a1045..f62ae22 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_pwr.c
|
||||||
|
@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
int i = 0;
|
||||||
|
struct bes2600_vif *priv;
|
||||||
|
int ret = 0;
|
||||||
|
+ int timeouts = 0;
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
bes_err("%s, set operation mode fail\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
- if (!status)
|
||||||
|
+ if (!status) {
|
||||||
|
bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* set device low power configuration */
|
||||||
|
- bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ /*
|
||||||
|
+ * Enter the device-end of the LP transition only if every per-VIF
|
||||||
|
+ * mac80211 handshake reached firmware-ACKed completion. Doing the
|
||||||
|
+ * device-LP setup while any VIF is still pending leaves the driver
|
||||||
|
+ * in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
+ * the BES2600.
|
||||||
|
+ */
|
||||||
|
+ if (timeouts == 0)
|
||||||
|
+ bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ else
|
||||||
|
+ ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+678
@@ -0,0 +1,678 @@
|
|||||||
|
From 3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:55:18 +0200
|
||||||
|
Subject: [PATCH 5/7] bes2600: remove userspace /dev/bes2600 character device
|
||||||
|
interface
|
||||||
|
|
||||||
|
bes_chardev.c implemented a custom character device at /dev/bes2600 with
|
||||||
|
its own parser and command-dispatch table, exposing operations such as
|
||||||
|
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
|
||||||
|
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
|
||||||
|
the in-tree driver - every kernel call site consumes the internal state
|
||||||
|
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
|
||||||
|
etc) directly, not through the cdev.
|
||||||
|
|
||||||
|
The cdev interface is a standing upstream blocker for two reasons:
|
||||||
|
|
||||||
|
1. Drivers under drivers/staging/ and drivers/net/wireless/ are
|
||||||
|
expected to expose tuning via the firmware/nl80211/debugfs
|
||||||
|
infrastructure rather than a private /dev node with an ad-hoc
|
||||||
|
parser.
|
||||||
|
|
||||||
|
2. The cdev handlers keep a global bes_cdev singleton alive whose
|
||||||
|
->cdev, ->dev_id, ->class and ->device pointers exist only to be
|
||||||
|
torn down; they add no functionality that nl80211 or rfkill do
|
||||||
|
not already provide (wifi/bt on-off, module_param for fw_type).
|
||||||
|
|
||||||
|
Remove the userspace interface:
|
||||||
|
|
||||||
|
- open / read / write / release file_operations handlers and the
|
||||||
|
bes2600_chardev_fops instance
|
||||||
|
- bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
|
||||||
|
- bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
|
||||||
|
string helpers
|
||||||
|
- bes2600_load_uevent (its only caller was
|
||||||
|
bes2600_chrdev_wifi_force_close_work informing userspace of a
|
||||||
|
state it already gates via rfkill; that snprintf +
|
||||||
|
kobject_uevent_env block is gone too, the kernel-side
|
||||||
|
halt_device + switch_wifi(0) + chrdev_check_system_close
|
||||||
|
sequence remains)
|
||||||
|
- alloc_chrdev_region / cdev_init / cdev_add / class_create /
|
||||||
|
device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
|
||||||
|
unwind labels
|
||||||
|
- cdev_del / unregister_chrdev_region / device_destroy /
|
||||||
|
class_destroy in bes2600_chrdev_free
|
||||||
|
- cdev/dev_id/major/minor/class/device fields in struct bes_cdev
|
||||||
|
|
||||||
|
What remains (unchanged behaviour):
|
||||||
|
|
||||||
|
- fw_type module parameter - the primary user-facing knob for
|
||||||
|
signal/no-signal/BT mode switch
|
||||||
|
- All in-kernel bes2600_chrdev_* accessor functions called from
|
||||||
|
bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
|
||||||
|
wifi_testmode_cmd.c (13 call sites)
|
||||||
|
- bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
|
||||||
|
for the remaining bes_cdev state (waitqueues, workqueues, flags)
|
||||||
|
- DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
|
||||||
|
- wifi_force_close worker, system-close logic, bus-probe state
|
||||||
|
machine
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
|
||||||
|
no kernel messages related to the cdev absence. Users that previously
|
||||||
|
wrote to /dev/bes2600 should switch to the fw_type module parameter
|
||||||
|
or (future patch c4) nl80211 testmode commands.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
|
||||||
|
and friends so the global bes2600_cdev singleton can be dropped
|
||||||
|
and the accessors scale to multi-device scenarios.
|
||||||
|
- c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
|
||||||
|
commands to the firmware's patch_wifi_testMode entry.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes_chardev.c | 519 ------------------------------------------
|
||||||
|
1 file changed, 519 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
index 9038e48..e2e4f1b 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes_chardev.c
|
||||||
|
@@ -43,12 +43,6 @@ enum bus_probe_state {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bes_cdev {
|
||||||
|
- struct cdev cdev;
|
||||||
|
- dev_t dev_id;
|
||||||
|
- int major;
|
||||||
|
- int minor;
|
||||||
|
- struct class *class;
|
||||||
|
- struct device *device;
|
||||||
|
atomic_t num_proc;
|
||||||
|
wait_queue_head_t open_wq;
|
||||||
|
spinlock_t status_lock;
|
||||||
|
@@ -249,351 +243,18 @@ int bes2600_switch_bt(bool on)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_get_cmd_and_ifname(const char *str, char **result)
|
||||||
|
-{
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ifname_len = 0;
|
||||||
|
- char *sp = NULL;
|
||||||
|
- char *tmp_ptr = NULL;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
-
|
||||||
|
- /* check if input arguments is valid */
|
||||||
|
- if (!str || strncmp(str, "ifname:", 7) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- sp = strchr(str, ' ');
|
||||||
|
- if (strncmp(sp + 1, "cmd:", 4) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- /* extract interface name */
|
||||||
|
- ifname_len = sp - str - 7;
|
||||||
|
- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- return -2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, str+7, ifname_len);
|
||||||
|
- tmp_ptr[ifname_len] = '\0';
|
||||||
|
- result[0] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- /* get command length */
|
||||||
|
- cmd_ptr = strstr(str, "cmd:");
|
||||||
|
- cmd_ptr += 4;
|
||||||
|
- sp = strchr(cmd_ptr, ' ');
|
||||||
|
- if (!sp) { /* the command don't have any parameter */
|
||||||
|
- cmd_len = strlen(cmd_ptr);
|
||||||
|
- if (cmd_ptr[cmd_len - 1] == '\n')
|
||||||
|
- --cmd_len;
|
||||||
|
- } else { /* the command have one or more parameter */
|
||||||
|
- cmd_len = sp - cmd_ptr;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* copy command to out buffer */
|
||||||
|
- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- kfree(result[0]);
|
||||||
|
- result[0] = NULL;
|
||||||
|
- return -3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, cmd_ptr, cmd_len);
|
||||||
|
- tmp_ptr[cmd_len] = '\0';
|
||||||
|
- result[1] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void bes2600_recyle_cmd_and_ifname_mem(char **info)
|
||||||
|
-{
|
||||||
|
- if (info[0]) {
|
||||||
|
- kfree(info[0]);
|
||||||
|
- info[0] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (info[1]) {
|
||||||
|
- kfree(info[1]);
|
||||||
|
- info[1] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_default_handler(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
-
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
|
||||||
|
- } else {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, str);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_wifi_bt_on_off(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- int ret = 0;
|
||||||
|
- enum wait_state wait_state;
|
||||||
|
- enum bus_probe_state probe_state;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- probe_state = bes2600_cdev.bus_probe;
|
||||||
|
- wait_state = bes2600_cdev.wait_state;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* only work for wifi signal mode */
|
||||||
|
- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* wait bus probe operation end */
|
||||||
|
- if (probe_state == BES2600_BUS_PROBE_START) {
|
||||||
|
- bes_devel("wait bus probe operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
|
||||||
|
- HZ);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* must wait previous operation end in critical section */
|
||||||
|
- if (wait_state != BES2600_BOOT_WAIT_NONE) {
|
||||||
|
- bes_devel("wait previous operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
|
||||||
|
- HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* if dpd calibration is doing, modify wifi and bt state directly */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = true;
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = false;
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = true;
|
||||||
|
- bes2600_cdev.bton_pending = true;
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = false;
|
||||||
|
- bes2600_cdev.bton_pending = false;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* process wifi/bt on/off operation */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(1);
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(0);
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(1);
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!ret && bes2600_chrdev_check_system_close())
|
||||||
|
- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
- bes2600_cdev.sbus_priv);
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return ret ;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-static int bes2600_op_change_fw_type(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- int temp = 0;
|
||||||
|
- long status = 0;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
- char fw_type[5] = {0};
|
||||||
|
- bool sys_closed = bes2600_chrdev_check_system_close();
|
||||||
|
-
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
-
|
||||||
|
- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- return -EPERM;
|
||||||
|
-
|
||||||
|
- /* check if user input is valid */
|
||||||
|
- cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
|
||||||
|
- if (strlen(str) < 16 || !cmd_ptr) {
|
||||||
|
- bes_err("the format of \"%s\" is error\n", str);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* convert fw_type from string to int */
|
||||||
|
- strncpy(fw_type, cmd_ptr + 14, 4);
|
||||||
|
- fw_type[0] = '+';
|
||||||
|
- ret = kstrtoint(fw_type, 10, &temp);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- bes_err("%s parse error\n", __func__);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* no need to realod firmware if new fw_type is equal to the old */
|
||||||
|
- if (temp == bes2600_cdev.fw_type ) {
|
||||||
|
- bes_devel("fw type is equal\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* close wifi net device */
|
||||||
|
- if (bes2600_cdev.sbus_priv
|
||||||
|
- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
||||||
|
- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* update firmware type */
|
||||||
|
- bes2600_cdev.fw_type = temp;
|
||||||
|
- bes2600_chrdev_update_signal_mode();
|
||||||
|
-
|
||||||
|
- if (!sys_closed) {
|
||||||
|
- /* close device to call disconnect function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
|
||||||
|
- else if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_chrdev_start_bus_probe();
|
||||||
|
-
|
||||||
|
- /* wait disconnect event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.dpd_calied
|
||||||
|
- && bes2600_chrdev_check_system_close()) {
|
||||||
|
- bes_devel("no need to reload firmware\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("reload firmware...\n");
|
||||||
|
- /* power on device to call probe function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
||||||
|
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_wakeup(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 wakeup bt.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_sleep(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
|
||||||
|
- bes_devel("bes2600 allow bt sleep.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static int bes2600_op_set_wakeup_read_flag(const char *str)
|
||||||
|
-{
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
||||||
|
-int bes2600_load_uevent(char *env[])
|
||||||
|
-{
|
||||||
|
- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
-}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-static struct bes2600_op_map bes2600_op_map_tab[] ={
|
||||||
|
- /*op op_len handler */
|
||||||
|
- {"P2P_SET_NOA", 11, bes2600_op_default_handler},
|
||||||
|
- {"P2P_SET_PS", 10, bes2600_op_default_handler},
|
||||||
|
- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
|
||||||
|
- {"LINKSPEED", 9, bes2600_op_default_handler},
|
||||||
|
- {"RSSI", 4, bes2600_op_default_handler},
|
||||||
|
- {"GETBAND", 7, bes2600_op_default_handler},
|
||||||
|
- {"WLS_BATCHING", 12, bes2600_op_default_handler},
|
||||||
|
- {"MACADDR", 7, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-START", 14, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-STOP", 13, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-ADD", 12, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXMODE", 10, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
|
||||||
|
- {"SETSUSPENDMODE", 14, bes2600_op_default_handler},
|
||||||
|
- {"COUNTRY", 7, bes2600_op_default_handler},
|
||||||
|
- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_ON", 5, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
|
||||||
|
- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
|
||||||
|
- {"BT_SLEEP", 8, bes2600_op_bt_sleep},
|
||||||
|
- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
|
||||||
|
-};
|
||||||
|
|
||||||
|
static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
{
|
||||||
|
@@ -603,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
&& (bes2600_cdev.wifi_opened == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
-{
|
||||||
|
- if (atomic_read(&bes2600_cdev.num_proc) > 0) {
|
||||||
|
- wait_event_timeout(bes2600_cdev.open_wq,
|
||||||
|
- (atomic_read(&bes2600_cdev.num_proc) == 0),
|
||||||
|
- MAX_SCHEDULE_TIMEOUT);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- bes_devel("bes2600 char device is opened\n");
|
||||||
|
- atomic_inc(&bes2600_cdev.num_proc);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
|
||||||
|
- size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- char buf[64] = {0};
|
||||||
|
- unsigned int len;
|
||||||
|
- long status = 0;
|
||||||
|
|
||||||
|
- switch (bes2600_cdev.read_flag) {
|
||||||
|
- case BES_CDEV_READ_WAKEUP_STATE:
|
||||||
|
- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
|
||||||
|
- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
|
||||||
|
- bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
|
||||||
|
- bes2600_cdev.dpd_calied,
|
||||||
|
- bes2600_cdev.wifi_opened,
|
||||||
|
- bes2600_cdev.bt_opened,
|
||||||
|
- bes2600_cdev.fw_type);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- len = sizeof(buf);
|
||||||
|
- /* reset read flag */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static ssize_t bes2600_chrdev_write(struct file *file,
|
||||||
|
- const char __user *user_buf, size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- int i = 0;
|
||||||
|
- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ret = 0;
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- char *buf = NULL;
|
||||||
|
-
|
||||||
|
- /* copy content from user space to kernel */
|
||||||
|
- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
|
||||||
|
- buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
- if (copy_from_user(buf, user_buf, count))
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* add terminal character */
|
||||||
|
- buf[count] = '\0';
|
||||||
|
-
|
||||||
|
- /* extract comand and interface */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
|
||||||
|
- kfree(buf);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* match operation item and execure its handler */
|
||||||
|
- cmd_len = strlen(info[1]);
|
||||||
|
- for (i = 0; i < cmd_num; i++) {
|
||||||
|
- if (cmd_len < bes2600_op_map_tab[i].op_len)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
|
||||||
|
- ret = bes2600_op_map_tab[i].handler(buf);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* operation item mismatch */
|
||||||
|
- if (i == cmd_num) {
|
||||||
|
- bes_err("cmd(%s) mismatch\n", info[1]);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- kfree(buf);
|
||||||
|
-
|
||||||
|
- return (ret == 0) ? count : ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_release (struct inode *inode, struct file *file)
|
||||||
|
-{
|
||||||
|
- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
|
||||||
|
- wake_up(&bes2600_cdev.open_wq);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 char device is closed\n");
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static struct file_operations bes2600_chardev_fops =
|
||||||
|
-{
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
- .open = bes2600_chrdev_open,
|
||||||
|
- .read = bes2600_chrdev_read,
|
||||||
|
- .write = bes2600_chrdev_write,
|
||||||
|
- .release = bes2600_chrdev_release,
|
||||||
|
-};
|
||||||
|
|
||||||
|
#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
@@ -1124,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void)
|
||||||
|
|
||||||
|
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
- char wifi_state[15];
|
||||||
|
- char bt_state[15];
|
||||||
|
- char fw_type[15];
|
||||||
|
- char *env[] = { wifi_state, bt_state, fw_type, NULL };
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
if (bes2600_chrdev_is_wifi_opened()) {
|
||||||
|
bes_devel("system exeception, force wifi down\n");
|
||||||
|
|
||||||
|
@@ -1146,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* notify userspace */
|
||||||
|
- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
|
||||||
|
- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
|
||||||
|
- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
|
||||||
|
- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
- if (!ret)
|
||||||
|
- bes_err("bes2600 notify userspace failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1247,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
|
||||||
|
|
||||||
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
-
|
||||||
|
- /* allocate devide id */
|
||||||
|
- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 alloc device id fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* extract major and minor device id */
|
||||||
|
- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
|
||||||
|
- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
|
||||||
|
-
|
||||||
|
- /* add char device and bind operation function */
|
||||||
|
- bes2600_cdev.cdev.owner = THIS_MODULE;
|
||||||
|
- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
|
||||||
|
- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* create class for creating device node */
|
||||||
|
- bes2600_cdev.class = class_create("bes2600_chrdev");
|
||||||
|
- if (IS_ERR(bes2600_cdev.class)){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* get char device pointer */
|
||||||
|
- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
|
||||||
|
- if (IS_ERR(bes2600_cdev.device)){
|
||||||
|
- bes_err("bes2600 char device create fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* initialise global variable */
|
||||||
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
||||||
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
||||||
|
@@ -1318,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
-
|
||||||
|
-fail3:
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
-fail2:
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
-fail1:
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
-fail:
|
||||||
|
- return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bes2600_chrdev_free(void)
|
||||||
|
@@ -1336,9 +821,5 @@ void bes2600_chrdev_free(void)
|
||||||
|
bes2600_free_dpd_log_buffer();
|
||||||
|
#endif
|
||||||
|
bes2600_chrdev_free_dpd_data();
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
From 6f13e008d21d453db486f707f47340a0a17e650b Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <6f13e008d21d453db486f707f47340a0a17e650b.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:04:27 +0200
|
||||||
|
Subject: [PATCH 6/7] bes2600: enable CONFIG_BES2600_TESTMODE by default + fix
|
||||||
|
bit-rotted testmode plumbing
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The driver implements a mac80211 testmode_cmd operation that dispatches
|
||||||
|
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
|
||||||
|
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
|
||||||
|
BES2600 RF-test path (bes2600_vendor_rf_cmd -> firmware
|
||||||
|
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
|
||||||
|
binding in struct ieee80211_ops are conditionally compiled under
|
||||||
|
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
|
||||||
|
|
||||||
|
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
|
||||||
|
included in the build and the .testmode_cmd op is populated. On the
|
||||||
|
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
|
||||||
|
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
|
||||||
|
through the standard nl80211 testmode interface ('iw phy0 ...').
|
||||||
|
|
||||||
|
This also makes visible two classes of bit-rot that had accumulated
|
||||||
|
while nobody was building with CONFIG_BES2600_TESTMODE=y:
|
||||||
|
|
||||||
|
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
|
||||||
|
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
|
||||||
|
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
|
||||||
|
first argument. Neither the macros nor any of the BES2600_DBG_*
|
||||||
|
constants are defined anywhere in the tree. The same call pattern
|
||||||
|
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
|
||||||
|
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
|
||||||
|
|
||||||
|
Add minimal shim macros to bes_log.h that rewire the calls onto
|
||||||
|
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
|
||||||
|
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
|
||||||
|
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
|
||||||
|
as 0 constants for documentation / grep.
|
||||||
|
|
||||||
|
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
|
||||||
|
bes2600_get_roam_delay() are declared in sta.c with external
|
||||||
|
linkage but have no prototype in any header. All callers live in
|
||||||
|
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
|
||||||
|
off the compiler never sees them; with it on gcc
|
||||||
|
-Werror=missing-prototypes breaks the build.
|
||||||
|
|
||||||
|
Mark the three functions static. (Keeping them file-local also
|
||||||
|
matches their actual usage.)
|
||||||
|
|
||||||
|
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
|
||||||
|
buildable; no behavioural change when the flag is off.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
|
||||||
|
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/Makefile | 2 +-
|
||||||
|
drivers/staging/bes2600/bes_log.h | 23 +++++++++++++++++++++++
|
||||||
|
drivers/staging/bes2600/sta.c | 6 +++---
|
||||||
|
3 files changed, 27 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
|
||||||
|
index 2dcba09..2c1a850 100644
|
||||||
|
--- a/drivers/staging/bes2600/Makefile
|
||||||
|
+++ b/drivers/staging/bes2600/Makefile
|
||||||
|
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||||
|
# feature option
|
||||||
|
BES2600 ?= m
|
||||||
|
|
||||||
|
-CONFIG_BES2600_TESTMODE ?= n
|
||||||
|
+CONFIG_BES2600_TESTMODE ?= y
|
||||||
|
|
||||||
|
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes_log.h b/drivers/staging/bes2600/bes_log.h
|
||||||
|
index 605cea8..65cf703 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes_log.h
|
||||||
|
+++ b/drivers/staging/bes2600/bes_log.h
|
||||||
|
@@ -8,3 +8,26 @@ extern struct device *global_dev;
|
||||||
|
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
|
||||||
|
+ * was never implemented in-tree; these shims let code paths gated by
|
||||||
|
+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
|
||||||
|
+ * build when their conditions are enabled. The first argument is
|
||||||
|
+ * currently unused; pick one of the BES2600_DBG_* constants below for
|
||||||
|
+ * documentation.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_DBG_SBUS 0
|
||||||
|
+#define BES2600_DBG_DOWNLOAD 0
|
||||||
|
+#define BES2600_DBG_ITP 0
|
||||||
|
+#define BES2600_DBG_TEST_MODE 0
|
||||||
|
+
|
||||||
|
+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_cond) \
|
||||||
|
+ bes_err(fmt, ##__VA_ARGS__); \
|
||||||
|
+ } while (0)
|
||||||
|
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
|
||||||
|
index aa69eb8..5f1a456 100644
|
||||||
|
--- a/drivers/staging/bes2600/sta.c
|
||||||
|
+++ b/drivers/staging/bes2600/sta.c
|
||||||
|
@@ -3633,7 +3633,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw,
|
||||||
|
*
|
||||||
|
* Returns: 0 on success or non zero value on failure
|
||||||
|
*/
|
||||||
|
-int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
+static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
{
|
||||||
|
struct bes_msg_start_stop_tsm *start_stop_tsm =
|
||||||
|
(struct bes_msg_start_stop_tsm *) data;
|
||||||
|
@@ -3663,7 +3663,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
*
|
||||||
|
* Returns: TSM parameters collected
|
||||||
|
*/
|
||||||
|
-int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
struct bes_tsm_stats tsm_stats;
|
||||||
|
@@ -3703,7 +3703,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
*
|
||||||
|
* Returns: Returns the last measured roam delay
|
||||||
|
*/
|
||||||
|
-int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <10a05d21bfe4563f963e16d65228fd7a713c143d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH 7/7] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
drivers/staging/bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
index 371ef4f..3e04e8c 100644
|
||||||
|
--- a/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/drivers/staging/bes2600/bes2600_sdio.c
|
||||||
|
@@ -95,6 +95,7 @@ struct sbus_priv {
|
||||||
|
struct work_struct tx_work;
|
||||||
|
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
|
||||||
|
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
|
||||||
|
+ u8 *tx_bounce;
|
||||||
|
u32 tx_data_cnt;
|
||||||
|
u32 tx_xfer_cnt;
|
||||||
|
u32 tx_proc_cnt;
|
||||||
|
@@ -1136,7 +1137,26 @@ static void sdio_tx_work(struct work_struct *work)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- sg_set_buf(&sg[scatters], tx_buffer->buf, align);
|
||||||
|
+ /*
|
||||||
|
+ * The transfer length is rounded up to the SDIO block
|
||||||
|
+ * size, but tx_buffer->buf is only tx_buffer->len bytes
|
||||||
|
+ * long (it usually aliases into an skb linear head).
|
||||||
|
+ * Copy into a driver-owned bounce buffer and zero-pad
|
||||||
|
+ * to the aligned size; otherwise DMA reads past the
|
||||||
|
+ * skb and leaks adjacent kernel memory on the wire --
|
||||||
|
+ * observed as KFENCE OOB reads from
|
||||||
|
+ * bes_sdio_memcpy_to_io_helper via dma_map_sg.
|
||||||
|
+ */
|
||||||
|
+ if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN))
|
||||||
|
+ goto flush_previous;
|
||||||
|
+ memcpy(self->tx_bounce + total_len,
|
||||||
|
+ tx_buffer->buf, tx_buffer->len);
|
||||||
|
+ if (align > tx_buffer->len)
|
||||||
|
+ memset(self->tx_bounce + total_len +
|
||||||
|
+ tx_buffer->len, 0,
|
||||||
|
+ align - tx_buffer->len);
|
||||||
|
+ sg_set_buf(&sg[scatters],
|
||||||
|
+ self->tx_bounce + total_len, align);
|
||||||
|
total_len += align;
|
||||||
|
++scatters;
|
||||||
|
/*del_node:*/
|
||||||
|
@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (!self->single_gathered_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ if (!self->tx_bounce) {
|
||||||
|
+#ifndef SDIO_HOST_ADMA_SUPPORT
|
||||||
|
+ free_pages((unsigned long)self->single_gathered_buffer,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+#endif
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
#ifdef BES_SDIO_RXTX_TOGGLE
|
||||||
|
self->fw_started = false;
|
||||||
|
#endif
|
||||||
|
@@ -1985,6 +2016,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 12:35:28 +0200
|
||||||
|
Subject: [PATCH 0/7] bes2600: staging-prep cleanup for PineTab2 (BES2600WM)
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
This series is a staging-prep cleanup for the out-of-tree Bestechnic
|
||||||
|
BES2600WM Wi-Fi/BT combo-chip driver as shipped by Mobian's bes2600-dkms
|
||||||
|
package (and in-tree at drivers/staging/bes2600/ in the danctnix
|
||||||
|
linux-pinetab2 fork). Target hardware is the Pine64 PineTab2 (RK3566
|
||||||
|
+ BES2600WM, SDIO vendor 0xBE57 / device 0x2002).
|
||||||
|
|
||||||
|
The driver descends from the ST-Ericsson CW1200 (drivers/net/wireless/
|
||||||
|
st/cw1200/) -- same author, Dmitry Tarnyagin, shared WSM host<->firmware
|
||||||
|
protocol, shared SDIO bus backend. Kconfig ancestry markers survive in
|
||||||
|
this tree today: CONFIG_BES2600_USE_STE_EXTENSIONS (STE = ST-Ericsson),
|
||||||
|
CONFIG_BES2600_WSM_DEBUG (WSM). ST-Ericsson wound down in 2013;
|
||||||
|
Bestechnic (founded 2015) appears to have inherited or licensed the
|
||||||
|
CW1200 IP. No linux-wireless RFC has ever linked the two chips.
|
||||||
|
|
||||||
|
The series fixes observable defects on a PineTab2 running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 and removes two upstream blockers. Each patch is
|
||||||
|
independently testable and bisectable; the order below preserves
|
||||||
|
dependencies.
|
||||||
|
|
||||||
|
## What the series does
|
||||||
|
|
||||||
|
* 1/7 -- Replace filp_open() + kernel_read() in the factory-calibration
|
||||||
|
read path with request_firmware(). Repoint the FACTORY_PATH macro to
|
||||||
|
the firmware-class name (bes2600/bes2600_factory.txt, matching the
|
||||||
|
/lib/firmware/ layout). Kills a kernel-mainline anti-pattern and the
|
||||||
|
'(NULL device *): read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' boot spam on PineTab2.
|
||||||
|
|
||||||
|
* 2/7 -- Default STANDARD_FACTORY_EFUSE_FLAG from y to n. The shipped
|
||||||
|
bes2600_factory.txt on PineTab2 contains 30 calibration fields; the
|
||||||
|
driver was expecting 31 (including a ##select_efuse_flag section
|
||||||
|
absent from this firmware). Also unguards the
|
||||||
|
wsm_save_factory_txt_to_mcu() prototype in wsm.h which was
|
||||||
|
inconsistently wrapped in '#if defined(STANDARD_FACTORY_EFUSE_FLAG)'
|
||||||
|
while its definition in wsm.c and its call site in sta.c were
|
||||||
|
ungated -- gcc -Werror=missing-prototypes broke the build with the
|
||||||
|
new default until this is fixed.
|
||||||
|
|
||||||
|
* 3/7 -- Thread struct device * through factory_section_read_file() via
|
||||||
|
a module-local setter invoked at SDIO probe. request_firmware() now
|
||||||
|
receives a real device pointer; '(NULL device *):' no longer prefixes
|
||||||
|
factory-related diagnostics.
|
||||||
|
|
||||||
|
* 4/7 -- Gate the device-end of the low-power transition on successful
|
||||||
|
per-VIF firmware handshake. Pre-patch bes2600_pwr_enter_lp_mode()
|
||||||
|
called bes2600_pwr_device_enter_lp_mode() unconditionally even when
|
||||||
|
wait_for_completion_timeout() returned 0 (firmware never posted the
|
||||||
|
PM-change indication). On PineTab2 this recurred every 5-10 s
|
||||||
|
whenever the interface was associated and idle, flooded dmesg, and
|
||||||
|
cascaded into sdio_tx_work WARN splats / [RX] Receive failure
|
||||||
|
messages. Post-patch: -ETIMEDOUT returned cleanly, dmesg silent,
|
||||||
|
SDIO stable.
|
||||||
|
|
||||||
|
* 5/7 -- Remove the custom /dev/bes2600 character-device interface.
|
||||||
|
file_operations, open/read/write/release, bes2600_op_*
|
||||||
|
command-dispatch table, bes2600_load_uevent, alloc_chrdev_region /
|
||||||
|
cdev_init / cdev_add / class_create / device_create in the init
|
||||||
|
path, and the matching teardown in bes2600_chrdev_free -- 519 lines
|
||||||
|
deleted. The in-kernel accessor functions (is_signal_mode,
|
||||||
|
get_fw_type, etc., 13 call sites) and the fw_type module parameter
|
||||||
|
are preserved; the userspace interface becomes rfkill + module_param
|
||||||
|
+ (with 6/7) nl80211 testmode.
|
||||||
|
|
||||||
|
* 6/7 -- Flip CONFIG_BES2600_TESTMODE default from n to y. The driver
|
||||||
|
already implements a mac80211 testmode_cmd dispatcher (routing to
|
||||||
|
the firmware's patch_wifi_testMode path), already gated on the flag;
|
||||||
|
CONFIG_NL80211_TESTMODE=y is common on target kernels. Enabling the
|
||||||
|
flag also exposes accumulated bit-rot -- ~41 calls to undefined
|
||||||
|
bes2600_info/err/warn/dbg/err_with_cond macros, and 3 TSM/roam-delay
|
||||||
|
helpers with external linkage but no prototype. Add shim macros to
|
||||||
|
bes_log.h rewiring the legacy calls onto the existing bes_info /
|
||||||
|
bes_err / bes_warn / bes_devel family, define BES2600_DBG_* subsystem
|
||||||
|
ids as 0 constants, and mark the 3 helpers static.
|
||||||
|
|
||||||
|
* 7/7 -- Bounce SDIO TX buffers to avoid DMA out-of-bounds reads.
|
||||||
|
sdio_tx_work() rounded the transfer length up to the SDIO block size
|
||||||
|
(align = blks * cur_blksize, or 1632) and handed that length to
|
||||||
|
dma_map_sg() via sg_set_buf(..., tx_buffer->buf, align); tx_buffer->buf
|
||||||
|
typically aliases into an skb linear head allocated to tx_buffer->len,
|
||||||
|
not the block-aligned length. The DMA engine therefore read up to one
|
||||||
|
block past the end of the skb -- KFENCE on PineTab2 fires as
|
||||||
|
'out-of-bounds read in __pi_memcpy_generic ... 704B right of
|
||||||
|
kfence-#...' with sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper in
|
||||||
|
the stack and pskb_expand_head / validate_xmit_skb / tcp_write_xmit in
|
||||||
|
the allocator stack. Besides being undefined behavior, the padding
|
||||||
|
bytes are transmitted to the peer, leaking adjacent kernel memory on
|
||||||
|
the air. Allocate a driver-owned DMA-pages bounce buffer of
|
||||||
|
MAX_SDIO_TRANSFER_LEN, memcpy each TX buffer into its slot, zero the
|
||||||
|
padding tail, and point the SG entries at the bounce. Mirrors the
|
||||||
|
pattern already used for single_gathered_buffer; kept as a separate
|
||||||
|
allocation because sdio_tx_work accumulates SG entries before claiming
|
||||||
|
the bus.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Reference hardware: Pine64 PineTab2 (BES2600WM + Rockchip RK3566,
|
||||||
|
8a:2e:77:1f:ec:05, LAN AP newton @ 2.4 GHz ch11, tested also on
|
||||||
|
TelekomHotspot@ERGO @ 5 GHz ch36).
|
||||||
|
|
||||||
|
Host kernel: linux-pinetab2 6.19.10-danctnix1-1-pinetab2 with
|
||||||
|
CONFIG_NL80211_TESTMODE=y and CONFIG_KFENCE=y (for 7/7 verification).
|
||||||
|
Driver installed via /lib/modules/<ver>/extra/ and verified loaded via
|
||||||
|
modinfo srcversion.
|
||||||
|
|
||||||
|
Per-patch outcomes in post-reboot dmesg (full-stack applied, 11+ min
|
||||||
|
soak, 245k RX packets / 365 MB traffic):
|
||||||
|
|
||||||
|
- 1/7: 'read and check /lib/firmware/bes2600_factory.txt error' -- gone
|
||||||
|
- 2/7: 'bes2600_factory.txt parse fail' / 'factory cali data get
|
||||||
|
failed.' -- gone
|
||||||
|
- 3/7: '(NULL device *):' prefix on factory lines -- gone
|
||||||
|
- 4/7: 'bes2600_pwr_enter_lp_mode, wait pm ind timeout' (pre-patch 20-30
|
||||||
|
msgs / 5 min window) -- 0 per 5 min; '[RX] Receive failure: 4.' --
|
||||||
|
gone
|
||||||
|
- 5/7: /dev/bes2600 -- absent; driver continues to associate
|
||||||
|
- 6/7: 'iw phy0' lists 'testmode' under Supported commands; module
|
||||||
|
builds cleanly
|
||||||
|
- 7/7: 'BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic'
|
||||||
|
(pre-patch ~65 splats per 4 h of real traffic, always via
|
||||||
|
sdio_tx_work+0x2b4 / bes_sdio_memcpy_to_io_helper+0x18c) -- 0;
|
||||||
|
'sdio_tx_work+0x2b4' WARN splat residual from 4/7's cascade -- 0
|
||||||
|
(previously recurred ~1 per reboot even with 4/7 applied);
|
||||||
|
'PS Mode Error, Reason:1' benign handshake notice at T+40s --
|
||||||
|
also gone, apparently a downstream effect of no longer DMAing
|
||||||
|
uninitialised padding bytes into firmware
|
||||||
|
|
||||||
|
Full stack: wifi associates and passes traffic across 3+ reboots.
|
||||||
|
|
||||||
|
## Known limitations / out of scope
|
||||||
|
|
||||||
|
- factory_section_write_file() in bes2600_factory.c still uses
|
||||||
|
kernel_write() + filp_open(O_CREAT) to persist per-channel
|
||||||
|
calibration updates back to /lib/firmware/bes2600_factory.txt.
|
||||||
|
Converting the write-back path to debugfs or nl80211 testmode is a
|
||||||
|
follow-up.
|
||||||
|
|
||||||
|
- bes_chardev.c still carries DPD file-read/write paths gated by
|
||||||
|
BES2600_WRITE_DPD_TO_FILE (off by default, so dead code in the
|
||||||
|
default build). Same treatment needed.
|
||||||
|
|
||||||
|
- bes_fw.c:587 unconditionally creates
|
||||||
|
/lib/firmware/bes2002_fw_write.bin via filp_open() for debug
|
||||||
|
observation. Needs to go before drivers/staging/ accepts the driver.
|
||||||
|
|
||||||
|
- bes_cdev global singleton still holds sig_mode and fw_type. Moving
|
||||||
|
those to per-hw_priv state is blocked by fw_type being a module
|
||||||
|
parameter (inherently singleton). Migrating fw_type to a per-phy
|
||||||
|
debugfs knob or nl80211 testmode command is the next step; overlaps
|
||||||
|
with 6/7's testmode plumbing.
|
||||||
|
|
||||||
|
Markus Fritsche (7):
|
||||||
|
bes2600: use request_firmware() for factory.txt read
|
||||||
|
bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for PineTab2
|
||||||
|
factory.txt format
|
||||||
|
bes2600: thread struct device * through factory request_firmware()
|
||||||
|
call
|
||||||
|
bes2600: gate device LP-mode entry on successful per-VIF firmware
|
||||||
|
handshake
|
||||||
|
bes2600: remove userspace /dev/bes2600 character device interface
|
||||||
|
bes2600: enable CONFIG_BES2600_TESTMODE by default + fix bit-rotted
|
||||||
|
testmode plumbing
|
||||||
|
bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
bes2600/Makefile | 6 +-
|
||||||
|
bes2600/bes2600_factory.c | 45 ++--
|
||||||
|
bes2600/bes2600_factory.h | 3 +
|
||||||
|
bes2600/bes2600_sdio.c | 43 +++-
|
||||||
|
bes2600/bes_chardev.c | 519 --------------------------------------
|
||||||
|
bes2600/bes_log.h | 23 ++
|
||||||
|
bes2600/bes_pwr.c | 20 +-
|
||||||
|
bes2600/sta.c | 6 +-
|
||||||
|
bes2600/wsm.h | 2 -
|
||||||
|
9 files changed, 117 insertions(+), 550 deletions(-)
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
From d18aa6a9bc03a03e455434f83577892aa1a60ffe Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <d18aa6a9bc03a03e455434f83577892aa1a60ffe.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 10:09:44 +0200
|
||||||
|
Subject: [PATCH 1/7] bes2600: use request_firmware() for factory.txt read
|
||||||
|
|
||||||
|
The BES2600 factory calibration file (bes2600_factory.txt) was being read
|
||||||
|
via filp_open() + kernel_read() from a hard-coded absolute path baked in
|
||||||
|
at compile time via the FACTORY_PATH Makefile macro
|
||||||
|
(default: /lib/firmware/bes2600_factory.txt).
|
||||||
|
|
||||||
|
This had several problems:
|
||||||
|
|
||||||
|
1. Path mismatch - linux-firmware-style packaging (and danctnix 0.2-5
|
||||||
|
device-pine64-pinetab2) ships the file at
|
||||||
|
/lib/firmware/bes2600/bes2600_factory.txt, not /lib/firmware/. The
|
||||||
|
driver logged '(NULL device *): read and check
|
||||||
|
/lib/firmware/bes2600_factory.txt error' on every boot on PineTab2
|
||||||
|
running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
|
||||||
|
2. Direct filesystem access via filp_open() / kernel_read() from a driver
|
||||||
|
is an anti-pattern that upstream rejects: drivers should use
|
||||||
|
request_firmware() to get binary data from userspace-managed firmware
|
||||||
|
directories. request_firmware() natively searches the firmware_class
|
||||||
|
path list (typically /lib/firmware + derivatives), associates the load
|
||||||
|
with a uevent, and respects the firmware-loading infrastructure.
|
||||||
|
|
||||||
|
3. The (NULL device *) prefix in error messages indicated the absence of
|
||||||
|
proper device-context logging. While this patch does not yet thread
|
||||||
|
struct device through, the upstream path uses request_firmware() which
|
||||||
|
works with dev=NULL and is the building block for a follow-up patch
|
||||||
|
that adds per-chip device context.
|
||||||
|
|
||||||
|
Repoint the FACTORY_PATH default to the firmware-class name
|
||||||
|
(bes2600/bes2600_factory.txt) - request_firmware() prepends
|
||||||
|
/lib/firmware/ from the configured search paths. The macro remains
|
||||||
|
overridable at build time for non-standard deployments.
|
||||||
|
|
||||||
|
Rewrite factory_section_read_file() to:
|
||||||
|
* Call request_firmware(&fw, path, NULL).
|
||||||
|
* Size-check fw->size against FACTORY_MAX_SIZE.
|
||||||
|
* memcpy the data into the caller's buffer.
|
||||||
|
* Always call release_firmware() on exit.
|
||||||
|
|
||||||
|
The file write path (factory_section_write_file + kernel_write) is left
|
||||||
|
unchanged in this patch; it is the subject of a follow-up patch that
|
||||||
|
removes kernel_write and moves any remaining userspace-visible factory
|
||||||
|
configuration to a standard kernel-userspace boundary (debugfs or
|
||||||
|
nl80211 testmode).
|
||||||
|
|
||||||
|
No caller signature changes. No Makefile flag drops. Bisectable.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1, deployed via /lib/modules/<ver>/extra/. Verified
|
||||||
|
post-reboot: original 'read and check /lib/firmware/bes2600_factory.txt
|
||||||
|
error' is gone; request_firmware reads the file successfully (a separate
|
||||||
|
factory_parse() bug, previously masked by the read failure, is now
|
||||||
|
exposed and tracked separately).
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes2600_factory.c | 33 ++++++++++++++-------------------
|
||||||
|
2 files changed, 15 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 300912b..788aee2 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -66,7 +66,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
-FACTORY_PATH ?= /lib/firmware/bes2600_factory.txt
|
||||||
|
+FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
# basic function
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index dc5d3da..8d60b7c 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
+#include <linux/firmware.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/crc32.h>
|
||||||
|
@@ -137,38 +138,32 @@ static int bes2600_factory_crc_check(struct factory_t *factory_data)
|
||||||
|
*/
|
||||||
|
static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
- struct file *fp;
|
||||||
|
+ const struct firmware *fw;
|
||||||
|
+ int ret;
|
||||||
|
|
||||||
|
if (!path || !buffer) {
|
||||||
|
bes_err("%s NULL pointer err\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- bes_devel("reading %s \n", path);
|
||||||
|
+ bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
|
||||||
|
- if (IS_ERR(fp)) {
|
||||||
|
- bes_devel("BES2600 : can't open %s\n",path);
|
||||||
|
+ ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ if (ret) {
|
||||||
|
+ bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) {
|
||||||
|
- bes_err( "bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
|
||||||
|
- fp->f_inode->i_size, FACTORY_MAX_SIZE);
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
+ if (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) {
|
||||||
|
+ bes_err("bes2600_factory.txt size check failed, read_size: %zu max_size: %d\n",
|
||||||
|
+ fw->size, FACTORY_MAX_SIZE);
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
|
||||||
|
-
|
||||||
|
- filp_close(fp, NULL);
|
||||||
|
-
|
||||||
|
- if (ret != fp->f_inode->i_size) {
|
||||||
|
- bes_err("bes2600_factory.txt read fail\n");
|
||||||
|
- ret = -1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
+ memcpy(buffer, fw->data, fw->size);
|
||||||
|
+ ret = (int)fw->size;
|
||||||
|
+ release_firmware(fw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
From a826f4db7d97a3a872d92079db37dbdaf9a0cdec Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <a826f4db7d97a3a872d92079db37dbdaf9a0cdec.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:17:56 +0200
|
||||||
|
Subject: [PATCH 2/7] bes2600: default STANDARD_FACTORY_EFUSE_FLAG off for
|
||||||
|
PineTab2 factory.txt format
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The shipped factory calibration file bes2600_factory.txt on PineTab2
|
||||||
|
(danctnix linux-firmware 0.3.5_2023.0209) contains 30 calibration
|
||||||
|
fields: head (3), iq/xtal (3), 2.4G power 11n (5), 5G power 11n (15),
|
||||||
|
bt (4). The file terminates with '%%\n' directly after edr_power.
|
||||||
|
|
||||||
|
When STANDARD_FACTORY_EFUSE_FLAG is defined at compile time the driver
|
||||||
|
assembles STANDARD_FACTORY with an extra select_efuse_flag section
|
||||||
|
appended and expects 31 sscanf matches (FACTORY_MEMBER_NUM=31):
|
||||||
|
|
||||||
|
__STANDARD_FACTORY + \"##select_efuse_flag\\nselect_efuse:%hx\\n\"
|
||||||
|
+ \"%%%%\\n\"
|
||||||
|
|
||||||
|
The PineTab2 factory.txt has no select_efuse_flag section, so sscanf
|
||||||
|
stops after field 30 and factory_parse() returns -1 with:
|
||||||
|
|
||||||
|
bes2600_factory.txt parse fail
|
||||||
|
read and check bes2600/bes2600_factory.txt error
|
||||||
|
factory cali data get failed.
|
||||||
|
|
||||||
|
This was latent until the preceding patch (use request_firmware() for
|
||||||
|
factory.txt read) fixed the path bug that masked the parse failure.
|
||||||
|
|
||||||
|
Default STANDARD_FACTORY_EFUSE_FLAG to n. The flag remains overridable
|
||||||
|
at build time (make STANDARD_FACTORY_EFUSE_FLAG=y ...) for chips /
|
||||||
|
firmware packages that do ship the select_efuse_flag section.
|
||||||
|
|
||||||
|
Also: the wsm_save_factory_txt_to_mcu() prototype in wsm.h was
|
||||||
|
inconsistently wrapped in a conditional that keyed on
|
||||||
|
STANDARD_FACTORY_EFUSE_FLAG, but the function definition in wsm.c and
|
||||||
|
the call site in sta.c are ungated. With the flag now defaulting to
|
||||||
|
n, the gcc -Werror=missing-prototypes flag breaks the build. Drop the
|
||||||
|
conditional wrapper around the prototype -- the function exists and is
|
||||||
|
used regardless of the factory-parse flag.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. With the flag defaulted off, factory_parse()
|
||||||
|
succeeds on the shipped factory.txt, factory_cali_data is populated,
|
||||||
|
and dmesg no longer shows the parse-fail / read-and-check-error /
|
||||||
|
factory-cali-data-get-failed sequence.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/wsm.h | 2 --
|
||||||
|
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 788aee2..2dcba09 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -65,7 +65,7 @@ BES2600_DRV_VERSION := bes2600_0.3.5_2024.0116
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
|
||||||
|
FACTORY_CRC_CHECK ?= n
|
||||||
|
-STANDARD_FACTORY_EFUSE_FLAG ?= y
|
||||||
|
+STANDARD_FACTORY_EFUSE_FLAG ?= n
|
||||||
|
FACTORY_PATH ?= bes2600/bes2600_factory.txt
|
||||||
|
endif
|
||||||
|
|
||||||
|
diff --git a/bes2600/wsm.h b/bes2600/wsm.h
|
||||||
|
index 0673131..22845ac 100644
|
||||||
|
--- a/bes2600/wsm.h
|
||||||
|
+++ b/bes2600/wsm.h
|
||||||
|
@@ -2236,7 +2236,5 @@ int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
|
||||||
|
|
||||||
|
int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
|
||||||
|
|
||||||
|
-#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
||||||
|
int wsm_save_factory_txt_to_mcu(struct bes2600_common *hw_priv, const u8 *data, int if_id, enum bes2600_rf_cmd_type cmd_type);
|
||||||
|
-#endif
|
||||||
|
#endif /* BES2600_HWIO_H_INCLUDED */
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
From c7ba2044b78cc3778763737daea60c9912b710c6 Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <c7ba2044b78cc3778763737daea60c9912b710c6.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:18:38 +0200
|
||||||
|
Subject: [PATCH 3/7] bes2600: thread struct device * through factory
|
||||||
|
request_firmware() call
|
||||||
|
|
||||||
|
Follow-up to \"bes2600: use request_firmware() for factory.txt read\".
|
||||||
|
That patch switched the factory calibration read path from filp_open()
|
||||||
|
+ kernel_read() to request_firmware(), but passed dev=NULL to
|
||||||
|
request_firmware() because factory_section_read_file() did not have a
|
||||||
|
struct device * in scope. The resulting logs carry the
|
||||||
|
'(NULL device *):' prefix and do not propagate a udev association.
|
||||||
|
|
||||||
|
Add a module-local static struct device * used as the firmware-class
|
||||||
|
load context, plus a small exported setter:
|
||||||
|
|
||||||
|
static struct device *bes2600_factory_dev;
|
||||||
|
void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
|
||||||
|
Wire bes2600_factory_set_dev(&func->dev) from bes2600_sdio_probe(),
|
||||||
|
right after bes2600_platform_data_init() so the platform layer has
|
||||||
|
already had a chance to use the same struct device for its own
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
factory_section_read_file() now passes bes2600_factory_dev (instead
|
||||||
|
of NULL) to request_firmware(). When the factory read happens before
|
||||||
|
probe (not currently the case on PineTab2) the pointer is still NULL
|
||||||
|
and request_firmware() accepts that; no regression.
|
||||||
|
|
||||||
|
No API changes to bes2600_get_factory_cali_data() callers. The
|
||||||
|
char *path parameter remains (it is the firmware-class name fed
|
||||||
|
straight to request_firmware()).
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver probes, factory data is read, and any
|
||||||
|
post-c5 factory diagnostics now carry the SDIO device identity
|
||||||
|
instead of '(NULL device *)'.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_factory.c | 14 +++++++++++++-
|
||||||
|
bes2600/bes2600_factory.h | 3 +++
|
||||||
|
bes2600/bes2600_sdio.c | 4 ++++
|
||||||
|
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_factory.c b/bes2600/bes2600_factory.c
|
||||||
|
index 8d60b7c..1cda447 100644
|
||||||
|
--- a/bes2600/bes2600_factory.c
|
||||||
|
+++ b/bes2600/bes2600_factory.c
|
||||||
|
@@ -31,6 +31,18 @@
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(factory_lock);
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * struct device * for request_firmware() context. Set once at SDIO
|
||||||
|
+ * probe via bes2600_factory_set_dev(). NULL is tolerated (falls back
|
||||||
|
+ * to the udev-less firmware-class path) but loses per-device logging.
|
||||||
|
+ */
|
||||||
|
+static struct device *bes2600_factory_dev;
|
||||||
|
+
|
||||||
|
+void bes2600_factory_set_dev(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ bes2600_factory_dev = dev;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* It is only used for temporary storage.
|
||||||
|
* Every time get the factory, it will read from the
|
||||||
|
@@ -148,7 +160,7 @@ static int factory_section_read_file(char *path, void *buffer)
|
||||||
|
|
||||||
|
bes_devel("requesting firmware-class %s\n", path);
|
||||||
|
|
||||||
|
- ret = request_firmware(&fw, path, NULL);
|
||||||
|
+ ret = request_firmware(&fw, path, bes2600_factory_dev);
|
||||||
|
if (ret) {
|
||||||
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
||||||
|
return -1;
|
||||||
|
diff --git a/bes2600/bes2600_factory.h b/bes2600/bes2600_factory.h
|
||||||
|
index 3835b0d..7dbe9f8 100644
|
||||||
|
--- a/bes2600/bes2600_factory.h
|
||||||
|
+++ b/bes2600/bes2600_factory.h
|
||||||
|
@@ -199,6 +199,9 @@ enum factory_cali_status {
|
||||||
|
/* just calibrate 11n, other protocols are automatically mapped */
|
||||||
|
#define WIFI_RF_11N_MODE 0x15
|
||||||
|
|
||||||
|
+/* set the struct device * used for request_firmware() context */
|
||||||
|
+void bes2600_factory_set_dev(struct device *dev);
|
||||||
|
+
|
||||||
|
/* read wifi & bt factory cali value*/
|
||||||
|
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
|
||||||
|
void factory_little_endian_cvrt(u8 *data);
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..371ef4f 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -30,6 +30,7 @@
|
||||||
|
#include "bes2600.h"
|
||||||
|
#include "sbus.h"
|
||||||
|
#include "bes2600_plat.h"
|
||||||
|
+#include "bes2600_factory.h"
|
||||||
|
#include "hwio.h"
|
||||||
|
#include "bes_chardev.h"
|
||||||
|
#include "bes_log.h"
|
||||||
|
@@ -1834,6 +1835,9 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
+ /* wire struct device into factory.c for request_firmware() context */
|
||||||
|
+ bes2600_factory_set_dev(dev);
|
||||||
|
+
|
||||||
|
self->pdata = bes2600_get_platform_data();
|
||||||
|
self->func = func;
|
||||||
|
self->dev = &func->dev;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
From 108d3967eac4ba3a6e0f508d865a5f221b49079d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <108d3967eac4ba3a6e0f508d865a5f221b49079d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:37:45 +0200
|
||||||
|
Subject: [PATCH 4/7] bes2600: gate device LP-mode entry on successful per-VIF
|
||||||
|
firmware handshake
|
||||||
|
|
||||||
|
bes2600_pwr_enter_lp_mode() drives the transition to low-power for each
|
||||||
|
associated STA VIF: it pushes wsm_set_pm(), waits up to 5 seconds on
|
||||||
|
pm_enter_cmpl for the firmware to acknowledge, then unconditionally
|
||||||
|
calls bes2600_pwr_device_enter_lp_mode() to drop the device end of the
|
||||||
|
bus.
|
||||||
|
|
||||||
|
Two bugs:
|
||||||
|
|
||||||
|
1. A failed wsm_set_pm() only logs an error, then still falls into
|
||||||
|
wait_for_completion_timeout() on a completion the firmware will
|
||||||
|
never post (the set-mode command never reached it). The loop
|
||||||
|
therefore always blocks the full 5 s, logs a second error, and
|
||||||
|
proceeds.
|
||||||
|
|
||||||
|
2. A genuine wait-timeout (firmware received the set-mode command but
|
||||||
|
never posted the indication) also only logs a warning. The code
|
||||||
|
then drops to bes2600_pwr_device_enter_lp_mode(), handing the
|
||||||
|
device subsystem an inconsistent view of mac-layer state.
|
||||||
|
|
||||||
|
On PineTab2 (BES2600WM + RK3566) the second bug is the recurring
|
||||||
|
root-cause of the 'bes2600_pwr_enter_lp_mode, wait pm ind timeout'
|
||||||
|
message flooding dmesg every 5-10 s when the interface is associated
|
||||||
|
and idle. Sending the device to LP in that state cascades into the
|
||||||
|
SDIO TX path as the 'bes_sdio_memcpy_to_io_helper / sdio_tx_work'
|
||||||
|
WARN splat.
|
||||||
|
|
||||||
|
Fix:
|
||||||
|
- Add a 'timeouts' counter; bump it on both failure paths.
|
||||||
|
- Skip the wait_for_completion entirely when wsm_set_pm() failed
|
||||||
|
(there is no completion to wait for).
|
||||||
|
- Only call bes2600_pwr_device_enter_lp_mode() when every per-VIF
|
||||||
|
handshake reached firmware-ACKed completion; otherwise return
|
||||||
|
-ETIMEDOUT and leave the device in its current power state.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 running linux-pinetab2 6.19.10-danctnix1-1.
|
||||||
|
Post-patch the handshake still fails on this particular firmware
|
||||||
|
revision (separate root-cause investigation outside this patch), but
|
||||||
|
the driver now returns -ETIMEDOUT cleanly instead of flooding dmesg
|
||||||
|
and destabilising the SDIO path.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_pwr.c | 20 +++++++++++++++++---
|
||||||
|
1 file changed, 17 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_pwr.c b/bes2600/bes_pwr.c
|
||||||
|
index e7a1045..f62ae22 100644
|
||||||
|
--- a/bes2600/bes_pwr.c
|
||||||
|
+++ b/bes2600/bes_pwr.c
|
||||||
|
@@ -472,6 +472,7 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
int i = 0;
|
||||||
|
struct bes2600_vif *priv;
|
||||||
|
int ret = 0;
|
||||||
|
+ int timeouts = 0;
|
||||||
|
char ip_str[20];
|
||||||
|
unsigned long status = 0;
|
||||||
|
|
||||||
|
@@ -528,22 +529,35 @@ static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
|
||||||
|
if (ret) {
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
bes_err("%s, set operation mode fail\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait power save mode changed indication */
|
||||||
|
status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
|
||||||
|
atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
|
||||||
|
reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
|
||||||
|
- if (!status)
|
||||||
|
+ if (!status) {
|
||||||
|
bes_err("%s, wait pm ind timeout\n", __func__);
|
||||||
|
+ timeouts++;
|
||||||
|
+ }
|
||||||
|
} else {
|
||||||
|
bes_devel("skip enter lp mode\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* set device low power configuration */
|
||||||
|
- bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ /*
|
||||||
|
+ * Enter the device-end of the LP transition only if every per-VIF
|
||||||
|
+ * mac80211 handshake reached firmware-ACKed completion. Doing the
|
||||||
|
+ * device-LP setup while any VIF is still pending leaves the driver
|
||||||
|
+ * in an inconsistent state that cascades into SDIO TX errors on
|
||||||
|
+ * the BES2600.
|
||||||
|
+ */
|
||||||
|
+ if (timeouts == 0)
|
||||||
|
+ bes2600_pwr_device_enter_lp_mode(hw_priv);
|
||||||
|
+ else
|
||||||
|
+ ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+678
@@ -0,0 +1,678 @@
|
|||||||
|
From 3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <3304b13a2b2e7388ebf076c2bcb7f02cd0b6800f.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 12:55:18 +0200
|
||||||
|
Subject: [PATCH 5/7] bes2600: remove userspace /dev/bes2600 character device
|
||||||
|
interface
|
||||||
|
|
||||||
|
bes_chardev.c implemented a custom character device at /dev/bes2600 with
|
||||||
|
its own parser and command-dispatch table, exposing operations such as
|
||||||
|
'wifi on|off', 'bt on|off', 'change_fw_type <n>', 'bt_wakeup',
|
||||||
|
'bt_sleep', and 'wakeup_read_flag'. None of these surfaces are used by
|
||||||
|
the in-tree driver - every kernel call site consumes the internal state
|
||||||
|
accessors (bes2600_chrdev_is_signal_mode, bes2600_chrdev_get_fw_type,
|
||||||
|
etc) directly, not through the cdev.
|
||||||
|
|
||||||
|
The cdev interface is a standing upstream blocker for two reasons:
|
||||||
|
|
||||||
|
1. Drivers under drivers/staging/ and drivers/net/wireless/ are
|
||||||
|
expected to expose tuning via the firmware/nl80211/debugfs
|
||||||
|
infrastructure rather than a private /dev node with an ad-hoc
|
||||||
|
parser.
|
||||||
|
|
||||||
|
2. The cdev handlers keep a global bes_cdev singleton alive whose
|
||||||
|
->cdev, ->dev_id, ->class and ->device pointers exist only to be
|
||||||
|
torn down; they add no functionality that nl80211 or rfkill do
|
||||||
|
not already provide (wifi/bt on-off, module_param for fw_type).
|
||||||
|
|
||||||
|
Remove the userspace interface:
|
||||||
|
|
||||||
|
- open / read / write / release file_operations handlers and the
|
||||||
|
bes2600_chardev_fops instance
|
||||||
|
- bes2600_op_* command handlers and bes2600_op_map_tab dispatcher
|
||||||
|
- bes2600_get_cmd_and_ifname / bes2600_recyle_cmd_and_ifname_mem
|
||||||
|
string helpers
|
||||||
|
- bes2600_load_uevent (its only caller was
|
||||||
|
bes2600_chrdev_wifi_force_close_work informing userspace of a
|
||||||
|
state it already gates via rfkill; that snprintf +
|
||||||
|
kobject_uevent_env block is gone too, the kernel-side
|
||||||
|
halt_device + switch_wifi(0) + chrdev_check_system_close
|
||||||
|
sequence remains)
|
||||||
|
- alloc_chrdev_region / cdev_init / cdev_add / class_create /
|
||||||
|
device_create in bes2600_chrdev_init plus the fail1/fail2/fail3
|
||||||
|
unwind labels
|
||||||
|
- cdev_del / unregister_chrdev_region / device_destroy /
|
||||||
|
class_destroy in bes2600_chrdev_free
|
||||||
|
- cdev/dev_id/major/minor/class/device fields in struct bes_cdev
|
||||||
|
|
||||||
|
What remains (unchanged behaviour):
|
||||||
|
|
||||||
|
- fw_type module parameter - the primary user-facing knob for
|
||||||
|
signal/no-signal/BT mode switch
|
||||||
|
- All in-kernel bes2600_chrdev_* accessor functions called from
|
||||||
|
bes2600_sdio.c, bes_pwr.c, sta.c, bh.c, main.c, wsm.c, and
|
||||||
|
wifi_testmode_cmd.c (13 call sites)
|
||||||
|
- bes2600_chrdev_init / bes2600_chrdev_free as state-init / teardown
|
||||||
|
for the remaining bes_cdev state (waitqueues, workqueues, flags)
|
||||||
|
- DPD management (bes2600_chrdev_get_dpd_buffer / update / free)
|
||||||
|
- wifi_force_close worker, system-close logic, bus-probe state
|
||||||
|
machine
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1. Driver continues to associate and pass traffic;
|
||||||
|
no kernel messages related to the cdev absence. Users that previously
|
||||||
|
wrote to /dev/bes2600 should switch to the fw_type module parameter
|
||||||
|
or (future patch c4) nl80211 testmode commands.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- c3.1: thread struct device * through bes2600_chrdev_is_signal_mode
|
||||||
|
and friends so the global bes2600_cdev singleton can be dropped
|
||||||
|
and the accessors scale to multi-device scenarios.
|
||||||
|
- c4: enable CONFIG_BES2600_TESTMODE and route nl80211 testmode
|
||||||
|
commands to the firmware's patch_wifi_testMode entry.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes_chardev.c | 519 ------------------------------------------
|
||||||
|
1 file changed, 519 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_chardev.c b/bes2600/bes_chardev.c
|
||||||
|
index 9038e48..e2e4f1b 100644
|
||||||
|
--- a/bes2600/bes_chardev.c
|
||||||
|
+++ b/bes2600/bes_chardev.c
|
||||||
|
@@ -43,12 +43,6 @@ enum bus_probe_state {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bes_cdev {
|
||||||
|
- struct cdev cdev;
|
||||||
|
- dev_t dev_id;
|
||||||
|
- int major;
|
||||||
|
- int minor;
|
||||||
|
- struct class *class;
|
||||||
|
- struct device *device;
|
||||||
|
atomic_t num_proc;
|
||||||
|
wait_queue_head_t open_wq;
|
||||||
|
spinlock_t status_lock;
|
||||||
|
@@ -249,351 +243,18 @@ int bes2600_switch_bt(bool on)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_get_cmd_and_ifname(const char *str, char **result)
|
||||||
|
-{
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ifname_len = 0;
|
||||||
|
- char *sp = NULL;
|
||||||
|
- char *tmp_ptr = NULL;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
-
|
||||||
|
- /* check if input arguments is valid */
|
||||||
|
- if (!str || strncmp(str, "ifname:", 7) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- sp = strchr(str, ' ');
|
||||||
|
- if (strncmp(sp + 1, "cmd:", 4) != 0)
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- /* extract interface name */
|
||||||
|
- ifname_len = sp - str - 7;
|
||||||
|
- tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- return -2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, str+7, ifname_len);
|
||||||
|
- tmp_ptr[ifname_len] = '\0';
|
||||||
|
- result[0] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- /* get command length */
|
||||||
|
- cmd_ptr = strstr(str, "cmd:");
|
||||||
|
- cmd_ptr += 4;
|
||||||
|
- sp = strchr(cmd_ptr, ' ');
|
||||||
|
- if (!sp) { /* the command don't have any parameter */
|
||||||
|
- cmd_len = strlen(cmd_ptr);
|
||||||
|
- if (cmd_ptr[cmd_len - 1] == '\n')
|
||||||
|
- --cmd_len;
|
||||||
|
- } else { /* the command have one or more parameter */
|
||||||
|
- cmd_len = sp - cmd_ptr;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* copy command to out buffer */
|
||||||
|
- tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
|
||||||
|
- if (!tmp_ptr) {
|
||||||
|
- kfree(result[0]);
|
||||||
|
- result[0] = NULL;
|
||||||
|
- return -3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- strncpy(tmp_ptr, cmd_ptr, cmd_len);
|
||||||
|
- tmp_ptr[cmd_len] = '\0';
|
||||||
|
- result[1] = tmp_ptr;
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void bes2600_recyle_cmd_and_ifname_mem(char **info)
|
||||||
|
-{
|
||||||
|
- if (info[0]) {
|
||||||
|
- kfree(info[0]);
|
||||||
|
- info[0] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (info[1]) {
|
||||||
|
- kfree(info[1]);
|
||||||
|
- info[1] = NULL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_default_handler(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
-
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- bes_devel("cmd(%s) on %s not handled\n", info[1], info[0]);
|
||||||
|
- } else {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, str);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_wifi_bt_on_off(const char *str)
|
||||||
|
-{
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- int ret = 0;
|
||||||
|
- enum wait_state wait_state;
|
||||||
|
- enum bus_probe_state probe_state;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- probe_state = bes2600_cdev.bus_probe;
|
||||||
|
- wait_state = bes2600_cdev.wait_state;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* only work for wifi signal mode */
|
||||||
|
- if (bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* wait bus probe operation end */
|
||||||
|
- if (probe_state == BES2600_BUS_PROBE_START) {
|
||||||
|
- bes_devel("wait bus probe operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
|
||||||
|
- HZ);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* must wait previous operation end in critical section */
|
||||||
|
- if (wait_state != BES2600_BOOT_WAIT_NONE) {
|
||||||
|
- bes_devel("wait previous operation end\n");
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- (bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
|
||||||
|
- HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* if dpd calibration is doing, modify wifi and bt state directly */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = true;
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- bes2600_cdev.wifi_opened = false;
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = true;
|
||||||
|
- bes2600_cdev.bton_pending = true;
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- bes2600_cdev.bt_opened = false;
|
||||||
|
- bes2600_cdev.bton_pending = false;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- /* process wifi/bt on/off operation */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(str, info) == 0) {
|
||||||
|
- if (strncmp(info[1], "WIFI_ON", 7) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(1);
|
||||||
|
- } else if (strncmp(info[1], "WIFI_OFF", 8) == 0) {
|
||||||
|
- ret = bes2600_switch_wifi(0);
|
||||||
|
- } else if (strncmp(info[1], "BT_ON", 5) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(1);
|
||||||
|
- } else if (strncmp(info[1], "BT_OFF", 6) == 0) {
|
||||||
|
- ret = bes2600_switch_bt(0);
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!ret && bes2600_chrdev_check_system_close())
|
||||||
|
- ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
- bes2600_cdev.sbus_priv);
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
-
|
||||||
|
- return ret ;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-static int bes2600_op_change_fw_type(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- int temp = 0;
|
||||||
|
- long status = 0;
|
||||||
|
- char *cmd_ptr = NULL;
|
||||||
|
- char fw_type[5] = {0};
|
||||||
|
- bool sys_closed = bes2600_chrdev_check_system_close();
|
||||||
|
-
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
-
|
||||||
|
- if (!bes2600_cdev.sbus_ops->power_switch && !bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- return -EPERM;
|
||||||
|
-
|
||||||
|
- /* check if user input is valid */
|
||||||
|
- cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
|
||||||
|
- if (strlen(str) < 16 || !cmd_ptr) {
|
||||||
|
- bes_err("the format of \"%s\" is error\n", str);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* convert fw_type from string to int */
|
||||||
|
- strncpy(fw_type, cmd_ptr + 14, 4);
|
||||||
|
- fw_type[0] = '+';
|
||||||
|
- ret = kstrtoint(fw_type, 10, &temp);
|
||||||
|
- if (ret < 0) {
|
||||||
|
- bes_err("%s parse error\n", __func__);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* no need to realod firmware if new fw_type is equal to the old */
|
||||||
|
- if (temp == bes2600_cdev.fw_type ) {
|
||||||
|
- bes_devel("fw type is equal\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* close wifi net device */
|
||||||
|
- if (bes2600_cdev.sbus_priv
|
||||||
|
- && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
|
||||||
|
- bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* update firmware type */
|
||||||
|
- bes2600_cdev.fw_type = temp;
|
||||||
|
- bes2600_chrdev_update_signal_mode();
|
||||||
|
-
|
||||||
|
- if (!sys_closed) {
|
||||||
|
- /* close device to call disconnect function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
|
||||||
|
- else if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_cdev.sbus_ops->reboot(bes2600_cdev.sbus_priv);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.sbus_ops->reboot)
|
||||||
|
- bes2600_chrdev_start_bus_probe();
|
||||||
|
-
|
||||||
|
- /* wait disconnect event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- if (bes2600_cdev.dpd_calied
|
||||||
|
- && bes2600_chrdev_check_system_close()) {
|
||||||
|
- bes_devel("no need to reload firmware\n");
|
||||||
|
- return 0;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("reload firmware...\n");
|
||||||
|
- /* power on device to call probe function */
|
||||||
|
- if (bes2600_cdev.sbus_ops->power_switch)
|
||||||
|
- bes2600_cdev.sbus_ops->power_switch(NULL, 1);
|
||||||
|
-
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 10);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
-
|
||||||
|
- ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
|
||||||
|
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_wakeup(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 wakeup bt.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
|
||||||
|
-
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_op_bt_sleep(const char *str)
|
||||||
|
-{
|
||||||
|
- int ret = 0;
|
||||||
|
- unsigned long status = 0;
|
||||||
|
-
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- if (!bes2600_cdev.bt_opened) {
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
- return -EFAULT;
|
||||||
|
- }
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- /* wait probe done event */
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.probe_done_wq,
|
||||||
|
- bes2600_bootup_end(), HZ * 8);
|
||||||
|
- if (status <= 0 || bes2600_chrdev_is_bus_error())
|
||||||
|
- return -EFAULT;
|
||||||
|
|
||||||
|
- bes_devel("bes2600 allow bt sleep.\n");
|
||||||
|
- ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
|
||||||
|
|
||||||
|
- return ret;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static int bes2600_op_set_wakeup_read_flag(const char *str)
|
||||||
|
-{
|
||||||
|
- bes_devel("%s is called, arg:%s\n", __func__, str);
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
#ifdef FW_DOWNLOAD_UART_DAEMON
|
||||||
|
-int bes2600_load_uevent(char *env[])
|
||||||
|
-{
|
||||||
|
- return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
-}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-static struct bes2600_op_map bes2600_op_map_tab[] ={
|
||||||
|
- /*op op_len handler */
|
||||||
|
- {"P2P_SET_NOA", 11, bes2600_op_default_handler},
|
||||||
|
- {"P2P_SET_PS", 10, bes2600_op_default_handler},
|
||||||
|
- {"SET_AP_WPS_P2P_IE", 17, bes2600_op_default_handler},
|
||||||
|
- {"LINKSPEED", 9, bes2600_op_default_handler},
|
||||||
|
- {"RSSI", 4, bes2600_op_default_handler},
|
||||||
|
- {"GETBAND", 7, bes2600_op_default_handler},
|
||||||
|
- {"WLS_BATCHING", 12, bes2600_op_default_handler},
|
||||||
|
- {"MACADDR", 7, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-START", 14, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-STOP", 13, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-ADD", 12, bes2600_op_default_handler},
|
||||||
|
- {"RXFILTER-REMOVE", 15, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXMODE", 10, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-START", 16, bes2600_op_default_handler},
|
||||||
|
- {"BTCOEXSCAN-STOP", 15, bes2600_op_default_handler},
|
||||||
|
- {"SETSUSPENDMODE", 14, bes2600_op_default_handler},
|
||||||
|
- {"COUNTRY", 7, bes2600_op_default_handler},
|
||||||
|
- {"WIFI_ON", 7, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"WIFI_OFF", 8, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_ON", 5, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"BT_OFF", 6, bes2600_op_wifi_bt_on_off},
|
||||||
|
- {"CHANGE_FW_TYPE", 14, bes2600_op_change_fw_type},
|
||||||
|
- {"BT_WAKEUP", 9, bes2600_op_bt_wakeup},
|
||||||
|
- {"BT_SLEEP", 8, bes2600_op_bt_sleep},
|
||||||
|
- {"WAKEUP_STATE", 12, bes2600_op_set_wakeup_read_flag},
|
||||||
|
-};
|
||||||
|
|
||||||
|
static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
{
|
||||||
|
@@ -603,123 +264,10 @@ static int bes2600_chrdev_check_system_close_internal(void)
|
||||||
|
&& (bes2600_cdev.wifi_opened == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
|
||||||
|
-{
|
||||||
|
- if (atomic_read(&bes2600_cdev.num_proc) > 0) {
|
||||||
|
- wait_event_timeout(bes2600_cdev.open_wq,
|
||||||
|
- (atomic_read(&bes2600_cdev.num_proc) == 0),
|
||||||
|
- MAX_SCHEDULE_TIMEOUT);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- bes_devel("bes2600 char device is opened\n");
|
||||||
|
- atomic_inc(&bes2600_cdev.num_proc);
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
|
||||||
|
-static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
|
||||||
|
- size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- char buf[64] = {0};
|
||||||
|
- unsigned int len;
|
||||||
|
- long status = 0;
|
||||||
|
|
||||||
|
- switch (bes2600_cdev.read_flag) {
|
||||||
|
- case BES_CDEV_READ_WAKEUP_STATE:
|
||||||
|
- if (bes2600_chrdev_wakeup_by_event_get() > WAKEUP_EVENT_NONE) {
|
||||||
|
- status = wait_event_timeout(bes2600_cdev.wakeup_reason_wq,
|
||||||
|
- bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_NONE, HZ * 2);
|
||||||
|
- WARN_ON(status <= 0);
|
||||||
|
- }
|
||||||
|
- len = sprintf(buf, "wakeup_reason: %u, src_port: %u\n",
|
||||||
|
- bes2600_cdev.wakeup_state, bes2600_cdev.src_port);
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
|
||||||
|
- bes2600_cdev.dpd_calied,
|
||||||
|
- bes2600_cdev.wifi_opened,
|
||||||
|
- bes2600_cdev.bt_opened,
|
||||||
|
- bes2600_cdev.fw_type);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- len = sizeof(buf);
|
||||||
|
- /* reset read flag */
|
||||||
|
- spin_lock(&bes2600_cdev.status_lock);
|
||||||
|
- bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
|
||||||
|
- spin_unlock(&bes2600_cdev.status_lock);
|
||||||
|
-
|
||||||
|
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static ssize_t bes2600_chrdev_write(struct file *file,
|
||||||
|
- const char __user *user_buf, size_t count, loff_t *ppos)
|
||||||
|
-{
|
||||||
|
- int i = 0;
|
||||||
|
- int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
|
||||||
|
- int cmd_len = 0;
|
||||||
|
- int ret = 0;
|
||||||
|
- char *info[2] = {0};
|
||||||
|
- char *buf = NULL;
|
||||||
|
-
|
||||||
|
- /* copy content from user space to kernel */
|
||||||
|
- /* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
|
||||||
|
- buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
- if (copy_from_user(buf, user_buf, count))
|
||||||
|
- return -EFAULT;
|
||||||
|
-
|
||||||
|
- /* add terminal character */
|
||||||
|
- buf[count] = '\0';
|
||||||
|
-
|
||||||
|
- /* extract comand and interface */
|
||||||
|
- if (bes2600_get_cmd_and_ifname(buf, info) != 0) {
|
||||||
|
- bes_err("%s get command fail, the origin string is %s\n", __func__, buf);
|
||||||
|
- kfree(buf);
|
||||||
|
- return -EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* match operation item and execure its handler */
|
||||||
|
- cmd_len = strlen(info[1]);
|
||||||
|
- for (i = 0; i < cmd_num; i++) {
|
||||||
|
- if (cmd_len < bes2600_op_map_tab[i].op_len)
|
||||||
|
- continue;
|
||||||
|
-
|
||||||
|
- if (strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
|
||||||
|
- ret = bes2600_op_map_tab[i].handler(buf);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* operation item mismatch */
|
||||||
|
- if (i == cmd_num) {
|
||||||
|
- bes_err("cmd(%s) mismatch\n", info[1]);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes2600_recyle_cmd_and_ifname_mem(info);
|
||||||
|
- kfree(buf);
|
||||||
|
-
|
||||||
|
- return (ret == 0) ? count : ret;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int bes2600_chrdev_release (struct inode *inode, struct file *file)
|
||||||
|
-{
|
||||||
|
- if (atomic_dec_and_test(&bes2600_cdev.num_proc)) {
|
||||||
|
- wake_up(&bes2600_cdev.open_wq);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bes_devel("bes2600 char device is closed\n");
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static struct file_operations bes2600_chardev_fops =
|
||||||
|
-{
|
||||||
|
- .owner = THIS_MODULE,
|
||||||
|
- .open = bes2600_chrdev_open,
|
||||||
|
- .read = bes2600_chrdev_read,
|
||||||
|
- .write = bes2600_chrdev_write,
|
||||||
|
- .release = bes2600_chrdev_release,
|
||||||
|
-};
|
||||||
|
|
||||||
|
#ifdef BES2600_WRITE_DPD_TO_FILE
|
||||||
|
static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
|
||||||
|
@@ -1124,12 +672,6 @@ void bes2600_chrdev_update_signal_mode(void)
|
||||||
|
|
||||||
|
static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
- char wifi_state[15];
|
||||||
|
- char bt_state[15];
|
||||||
|
- char fw_type[15];
|
||||||
|
- char *env[] = { wifi_state, bt_state, fw_type, NULL };
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
if (bes2600_chrdev_is_wifi_opened()) {
|
||||||
|
bes_devel("system exeception, force wifi down\n");
|
||||||
|
|
||||||
|
@@ -1146,14 +688,6 @@ static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
|
||||||
|
bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
|
||||||
|
bes2600_cdev.sbus_priv);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* notify userspace */
|
||||||
|
- snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
|
||||||
|
- snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
|
||||||
|
- snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
|
||||||
|
- ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
|
||||||
|
- if (!ret)
|
||||||
|
- bes_err("bes2600 notify userspace failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1247,46 +781,6 @@ int bes2600_chrdev_wakeup_by_event_get(void)
|
||||||
|
|
||||||
|
int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
{
|
||||||
|
- int ret = 0;
|
||||||
|
-
|
||||||
|
- /* allocate devide id */
|
||||||
|
- ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 alloc device id fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* extract major and minor device id */
|
||||||
|
- bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
|
||||||
|
- bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
|
||||||
|
-
|
||||||
|
- /* add char device and bind operation function */
|
||||||
|
- bes2600_cdev.cdev.owner = THIS_MODULE;
|
||||||
|
- cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
|
||||||
|
- ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
|
||||||
|
- if (ret < 0){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail1;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* create class for creating device node */
|
||||||
|
- bes2600_cdev.class = class_create("bes2600_chrdev");
|
||||||
|
- if (IS_ERR(bes2600_cdev.class)){
|
||||||
|
- bes_err("bes2600 char device add fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail2;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* get char device pointer */
|
||||||
|
- bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
|
||||||
|
- if (IS_ERR(bes2600_cdev.device)){
|
||||||
|
- bes_err("bes2600 char device create fail\n");
|
||||||
|
- ret = -EFAULT;
|
||||||
|
- goto fail3;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* initialise global variable */
|
||||||
|
atomic_set(&bes2600_cdev.num_proc, 0);
|
||||||
|
init_waitqueue_head(&bes2600_cdev.open_wq);
|
||||||
|
@@ -1318,15 +812,6 @@ int bes2600_chrdev_init(struct sbus_ops *ops)
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
-
|
||||||
|
-fail3:
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
-fail2:
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
-fail1:
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
-fail:
|
||||||
|
- return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bes2600_chrdev_free(void)
|
||||||
|
@@ -1336,9 +821,5 @@ void bes2600_chrdev_free(void)
|
||||||
|
bes2600_free_dpd_log_buffer();
|
||||||
|
#endif
|
||||||
|
bes2600_chrdev_free_dpd_data();
|
||||||
|
- cdev_del(&bes2600_cdev.cdev);
|
||||||
|
- unregister_chrdev_region(bes2600_cdev.dev_id, 1);
|
||||||
|
- device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
|
||||||
|
- class_destroy(bes2600_cdev.class);
|
||||||
|
bes_devel("%s done\n", __func__);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
From 6f13e008d21d453db486f707f47340a0a17e650b Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <6f13e008d21d453db486f707f47340a0a17e650b.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Wed, 22 Apr 2026 13:04:27 +0200
|
||||||
|
Subject: [PATCH 6/7] bes2600: enable CONFIG_BES2600_TESTMODE by default + fix
|
||||||
|
bit-rotted testmode plumbing
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The driver implements a mac80211 testmode_cmd operation that dispatches
|
||||||
|
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
|
||||||
|
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
|
||||||
|
BES2600 RF-test path (bes2600_vendor_rf_cmd -> firmware
|
||||||
|
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
|
||||||
|
binding in struct ieee80211_ops are conditionally compiled under
|
||||||
|
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
|
||||||
|
|
||||||
|
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
|
||||||
|
included in the build and the .testmode_cmd op is populated. On the
|
||||||
|
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
|
||||||
|
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
|
||||||
|
through the standard nl80211 testmode interface ('iw phy0 ...').
|
||||||
|
|
||||||
|
This also makes visible two classes of bit-rot that had accumulated
|
||||||
|
while nobody was building with CONFIG_BES2600_TESTMODE=y:
|
||||||
|
|
||||||
|
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
|
||||||
|
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
|
||||||
|
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
|
||||||
|
first argument. Neither the macros nor any of the BES2600_DBG_*
|
||||||
|
constants are defined anywhere in the tree. The same call pattern
|
||||||
|
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
|
||||||
|
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
|
||||||
|
|
||||||
|
Add minimal shim macros to bes_log.h that rewire the calls onto
|
||||||
|
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
|
||||||
|
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
|
||||||
|
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
|
||||||
|
as 0 constants for documentation / grep.
|
||||||
|
|
||||||
|
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
|
||||||
|
bes2600_get_roam_delay() are declared in sta.c with external
|
||||||
|
linkage but have no prototype in any header. All callers live in
|
||||||
|
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
|
||||||
|
off the compiler never sees them; with it on gcc
|
||||||
|
-Werror=missing-prototypes breaks the build.
|
||||||
|
|
||||||
|
Mark the three functions static. (Keeping them file-local also
|
||||||
|
matches their actual usage.)
|
||||||
|
|
||||||
|
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
|
||||||
|
buildable; no behavioural change when the flag is off.
|
||||||
|
|
||||||
|
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
|
||||||
|
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
|
||||||
|
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
|
||||||
|
userspace.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/Makefile | 2 +-
|
||||||
|
bes2600/bes_log.h | 23 +++++++++++++++++++++++
|
||||||
|
bes2600/sta.c | 6 +++---
|
||||||
|
3 files changed, 27 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/Makefile b/bes2600/Makefile
|
||||||
|
index 2dcba09..2c1a850 100644
|
||||||
|
--- a/bes2600/Makefile
|
||||||
|
+++ b/bes2600/Makefile
|
||||||
|
@@ -2,7 +2,7 @@ KERN_DIR = /lib/modules/$(KERNELRELEASE)/build
|
||||||
|
# feature option
|
||||||
|
BES2600 ?= m
|
||||||
|
|
||||||
|
-CONFIG_BES2600_TESTMODE ?= n
|
||||||
|
+CONFIG_BES2600_TESTMODE ?= y
|
||||||
|
|
||||||
|
CONFIG_BES2600_ENABLE_DEVEL_LOGS ?= n
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes_log.h b/bes2600/bes_log.h
|
||||||
|
index 605cea8..65cf703 100644
|
||||||
|
--- a/bes2600/bes_log.h
|
||||||
|
+++ b/bes2600/bes_log.h
|
||||||
|
@@ -8,3 +8,26 @@ extern struct device *global_dev;
|
||||||
|
#define bes_info(fmt, ...) dev_info(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_warn(fmt, ...) dev_warn(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
#define bes_err(fmt, ...) dev_err(global_dev, fmt, ##__VA_ARGS__)
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Legacy debug-subsystem-tagged log macros. The per-subsystem filtering
|
||||||
|
+ * was never implemented in-tree; these shims let code paths gated by
|
||||||
|
+ * CONFIG_BES2600_TESTMODE / CONFIG_BES2600_ITP / BES2600_DETECTION_LOGIC
|
||||||
|
+ * build when their conditions are enabled. The first argument is
|
||||||
|
+ * currently unused; pick one of the BES2600_DBG_* constants below for
|
||||||
|
+ * documentation.
|
||||||
|
+ */
|
||||||
|
+#define BES2600_DBG_SBUS 0
|
||||||
|
+#define BES2600_DBG_DOWNLOAD 0
|
||||||
|
+#define BES2600_DBG_ITP 0
|
||||||
|
+#define BES2600_DBG_TEST_MODE 0
|
||||||
|
+
|
||||||
|
+#define bes2600_info(_dbg, fmt, ...) bes_info(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err(_dbg, fmt, ...) bes_err(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_warn(_dbg, fmt, ...) bes_warn(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_dbg(_dbg, fmt, ...) bes_devel(fmt, ##__VA_ARGS__)
|
||||||
|
+#define bes2600_err_with_cond(_cond, _dbg, fmt, ...) \
|
||||||
|
+ do { \
|
||||||
|
+ if (_cond) \
|
||||||
|
+ bes_err(fmt, ##__VA_ARGS__); \
|
||||||
|
+ } while (0)
|
||||||
|
diff --git a/bes2600/sta.c b/bes2600/sta.c
|
||||||
|
index aa69eb8..5f1a456 100644
|
||||||
|
--- a/bes2600/sta.c
|
||||||
|
+++ b/bes2600/sta.c
|
||||||
|
@@ -3633,7 +3633,7 @@ static int bes2600_set_power_save(struct ieee80211_hw *hw,
|
||||||
|
*
|
||||||
|
* Returns: 0 on success or non zero value on failure
|
||||||
|
*/
|
||||||
|
-int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
+static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
{
|
||||||
|
struct bes_msg_start_stop_tsm *start_stop_tsm =
|
||||||
|
(struct bes_msg_start_stop_tsm *) data;
|
||||||
|
@@ -3663,7 +3663,7 @@ int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
||||||
|
*
|
||||||
|
* Returns: TSM parameters collected
|
||||||
|
*/
|
||||||
|
-int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
struct bes_tsm_stats tsm_stats;
|
||||||
|
@@ -3703,7 +3703,7 @@ int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
||||||
|
*
|
||||||
|
* Returns: Returns the last measured roam delay
|
||||||
|
*/
|
||||||
|
-int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
+static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
||||||
|
{
|
||||||
|
struct bes2600_common *hw_priv = hw->priv;
|
||||||
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
From 10a05d21bfe4563f963e16d65228fd7a713c143d Mon Sep 17 00:00:00 2001
|
||||||
|
Message-ID: <10a05d21bfe4563f963e16d65228fd7a713c143d.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
In-Reply-To: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
References: <cover.1776940528.git.fritsche.markus@gmail.com>
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH 7/7] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index 371ef4f..3e04e8c 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -95,6 +95,7 @@ struct sbus_priv {
|
||||||
|
struct work_struct tx_work;
|
||||||
|
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
|
||||||
|
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
|
||||||
|
+ u8 *tx_bounce;
|
||||||
|
u32 tx_data_cnt;
|
||||||
|
u32 tx_xfer_cnt;
|
||||||
|
u32 tx_proc_cnt;
|
||||||
|
@@ -1136,7 +1137,26 @@ static void sdio_tx_work(struct work_struct *work)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- sg_set_buf(&sg[scatters], tx_buffer->buf, align);
|
||||||
|
+ /*
|
||||||
|
+ * The transfer length is rounded up to the SDIO block
|
||||||
|
+ * size, but tx_buffer->buf is only tx_buffer->len bytes
|
||||||
|
+ * long (it usually aliases into an skb linear head).
|
||||||
|
+ * Copy into a driver-owned bounce buffer and zero-pad
|
||||||
|
+ * to the aligned size; otherwise DMA reads past the
|
||||||
|
+ * skb and leaks adjacent kernel memory on the wire --
|
||||||
|
+ * observed as KFENCE OOB reads from
|
||||||
|
+ * bes_sdio_memcpy_to_io_helper via dma_map_sg.
|
||||||
|
+ */
|
||||||
|
+ if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN))
|
||||||
|
+ goto flush_previous;
|
||||||
|
+ memcpy(self->tx_bounce + total_len,
|
||||||
|
+ tx_buffer->buf, tx_buffer->len);
|
||||||
|
+ if (align > tx_buffer->len)
|
||||||
|
+ memset(self->tx_bounce + total_len +
|
||||||
|
+ tx_buffer->len, 0,
|
||||||
|
+ align - tx_buffer->len);
|
||||||
|
+ sg_set_buf(&sg[scatters],
|
||||||
|
+ self->tx_bounce + total_len, align);
|
||||||
|
total_len += align;
|
||||||
|
++scatters;
|
||||||
|
/*del_node:*/
|
||||||
|
@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (!self->single_gathered_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ if (!self->tx_bounce) {
|
||||||
|
+#ifndef SDIO_HOST_ADMA_SUPPORT
|
||||||
|
+ free_pages((unsigned long)self->single_gathered_buffer,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+#endif
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
#ifdef BES_SDIO_RXTX_TOGGLE
|
||||||
|
self->fw_started = false;
|
||||||
|
#endif
|
||||||
|
@@ -1985,6 +2016,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
From 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 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
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/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
|
||||||
|
@@ -95,6 +95,7 @@ struct sbus_priv {
|
||||||
|
struct work_struct tx_work;
|
||||||
|
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
|
||||||
|
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
|
||||||
|
+ u8 *tx_bounce;
|
||||||
|
u32 tx_data_cnt;
|
||||||
|
u32 tx_xfer_cnt;
|
||||||
|
u32 tx_proc_cnt;
|
||||||
|
@@ -1136,7 +1137,26 @@ static void sdio_tx_work(struct work_struct *work)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- sg_set_buf(&sg[scatters], tx_buffer->buf, align);
|
||||||
|
+ /*
|
||||||
|
+ * The transfer length is rounded up to the SDIO block
|
||||||
|
+ * size, but tx_buffer->buf is only tx_buffer->len bytes
|
||||||
|
+ * long (it usually aliases into an skb linear head).
|
||||||
|
+ * Copy into a driver-owned bounce buffer and zero-pad
|
||||||
|
+ * to the aligned size; otherwise DMA reads past the
|
||||||
|
+ * skb and leaks adjacent kernel memory on the wire --
|
||||||
|
+ * observed as KFENCE OOB reads from
|
||||||
|
+ * bes_sdio_memcpy_to_io_helper via dma_map_sg.
|
||||||
|
+ */
|
||||||
|
+ if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN))
|
||||||
|
+ goto flush_previous;
|
||||||
|
+ memcpy(self->tx_bounce + total_len,
|
||||||
|
+ tx_buffer->buf, tx_buffer->len);
|
||||||
|
+ if (align > tx_buffer->len)
|
||||||
|
+ memset(self->tx_bounce + total_len +
|
||||||
|
+ tx_buffer->len, 0,
|
||||||
|
+ align - tx_buffer->len);
|
||||||
|
+ sg_set_buf(&sg[scatters],
|
||||||
|
+ self->tx_bounce + total_len, align);
|
||||||
|
total_len += align;
|
||||||
|
++scatters;
|
||||||
|
/*del_node:*/
|
||||||
|
@@ -1857,6 +1877,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (!self->single_gathered_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ if (!self->tx_bounce) {
|
||||||
|
+#ifndef SDIO_HOST_ADMA_SUPPORT
|
||||||
|
+ free_pages((unsigned long)self->single_gathered_buffer,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+#endif
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
#ifdef BES_SDIO_RXTX_TOGGLE
|
||||||
|
self->fw_started = false;
|
||||||
|
#endif
|
||||||
|
@@ -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));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
From 4ec7d25817af09654fb9439e472890f69281840c Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
Date: Thu, 23 Apr 2026 11:58:31 +0200
|
||||||
|
Subject: [PATCH] bes2600: bounce SDIO TX buffers to avoid DMA OOB read
|
||||||
|
|
||||||
|
The SDIO TX path rounds the DMA transfer length up to the host's
|
||||||
|
current block size and hands that length to dma_map_sg() via
|
||||||
|
sg_set_buf(&sg[scatters], tx_buffer->buf, align) in sdio_tx_work().
|
||||||
|
tx_buffer->buf typically aliases into an skb linear head whose
|
||||||
|
allocated size matches tx_buffer->len, not the block-aligned
|
||||||
|
align. The DMA engine (swiotlb / dw_mci IDMAC) therefore reads up
|
||||||
|
to one block past the end of the skb. On a PineTab2 with KFENCE
|
||||||
|
enabled this fires as:
|
||||||
|
|
||||||
|
BUG: KFENCE: out-of-bounds read in __pi_memcpy_generic
|
||||||
|
Out-of-bounds read at ... (704B right of kfence-#...):
|
||||||
|
__pi_memcpy_generic
|
||||||
|
swiotlb_tbl_map_single
|
||||||
|
swiotlb_map
|
||||||
|
dma_direct_map_sg
|
||||||
|
__dma_map_sg_attrs
|
||||||
|
dma_map_sg_attrs
|
||||||
|
dw_mci_pre_dma_transfer
|
||||||
|
__dw_mci_start_request
|
||||||
|
...
|
||||||
|
bes_sdio_memcpy_to_io_helper+0x18c/0x288 [bes2600]
|
||||||
|
sdio_tx_work+0x2b4/0x4a0 [bes2600]
|
||||||
|
|
||||||
|
allocated by ... pskb_expand_head / validate_xmit_skb / tcp_*
|
||||||
|
|
||||||
|
In addition to being undefined behavior, the padding bytes (which
|
||||||
|
come from whatever memory follows the skb) are transmitted to the
|
||||||
|
peer, leaking kernel memory on the air.
|
||||||
|
|
||||||
|
Allocate a driver-owned DMA-page bounce buffer sized to
|
||||||
|
MAX_SDIO_TRANSFER_LEN and use it as the scatter-gather backing for
|
||||||
|
sdio_tx_work. Each TX buffer is copied into its bounce slot and the
|
||||||
|
tail (align - tx_buffer->len bytes) is zeroed. This mirrors the
|
||||||
|
existing bounce pattern already used by bes2600_sdio_memcpy_toio()
|
||||||
|
via single_gathered_buffer; a separate allocation is used for the
|
||||||
|
TX path because single_gathered_buffer is only serialised via
|
||||||
|
sdio_claim_host and sdio_tx_work accumulates scatter entries before
|
||||||
|
claiming the bus.
|
||||||
|
|
||||||
|
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
|
||||||
|
---
|
||||||
|
bes2600/bes2600_sdio.c | 39 ++++++++++++++++++++++++++++++++++++++-
|
||||||
|
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/bes2600/bes2600_sdio.c b/bes2600/bes2600_sdio.c
|
||||||
|
index b595365..7bc922c 100644
|
||||||
|
--- a/bes2600/bes2600_sdio.c
|
||||||
|
+++ b/bes2600/bes2600_sdio.c
|
||||||
|
@@ -94,6 +94,7 @@ struct sbus_priv {
|
||||||
|
struct work_struct tx_work;
|
||||||
|
struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
|
||||||
|
struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
|
||||||
|
+ u8 *tx_bounce;
|
||||||
|
u32 tx_data_cnt;
|
||||||
|
u32 tx_xfer_cnt;
|
||||||
|
u32 tx_proc_cnt;
|
||||||
|
@@ -1135,7 +1136,26 @@ static void sdio_tx_work(struct work_struct *work)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- sg_set_buf(&sg[scatters], tx_buffer->buf, align);
|
||||||
|
+ /*
|
||||||
|
+ * The transfer length is rounded up to the SDIO block
|
||||||
|
+ * size, but tx_buffer->buf is only tx_buffer->len bytes
|
||||||
|
+ * long (it usually aliases into an skb linear head).
|
||||||
|
+ * Copy into a driver-owned bounce buffer and zero-pad
|
||||||
|
+ * to the aligned size; otherwise DMA reads past the
|
||||||
|
+ * skb and leaks adjacent kernel memory on the wire --
|
||||||
|
+ * observed as KFENCE OOB reads from
|
||||||
|
+ * bes_sdio_memcpy_to_io_helper via dma_map_sg.
|
||||||
|
+ */
|
||||||
|
+ if (WARN_ON_ONCE(total_len + align > MAX_SDIO_TRANSFER_LEN))
|
||||||
|
+ goto flush_previous;
|
||||||
|
+ memcpy(self->tx_bounce + total_len,
|
||||||
|
+ tx_buffer->buf, tx_buffer->len);
|
||||||
|
+ if (align > tx_buffer->len)
|
||||||
|
+ memset(self->tx_bounce + total_len +
|
||||||
|
+ tx_buffer->len, 0,
|
||||||
|
+ align - tx_buffer->len);
|
||||||
|
+ sg_set_buf(&sg[scatters],
|
||||||
|
+ self->tx_bounce + total_len, align);
|
||||||
|
total_len += align;
|
||||||
|
++scatters;
|
||||||
|
/*del_node:*/
|
||||||
|
@@ -1853,6 +1873,17 @@ static int bes2600_sdio_probe(struct sdio_func *func,
|
||||||
|
if (!self->single_gathered_buffer)
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ self->tx_bounce = (u8 *)__get_dma_pages(GFP_KERNEL,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ if (!self->tx_bounce) {
|
||||||
|
+#ifndef SDIO_HOST_ADMA_SUPPORT
|
||||||
|
+ free_pages((unsigned long)self->single_gathered_buffer,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+#endif
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
#ifdef BES_SDIO_RXTX_TOGGLE
|
||||||
|
self->fw_started = false;
|
||||||
|
#endif
|
||||||
|
@@ -1981,6 +2012,12 @@ static void bes2600_sdio_remove(struct sdio_func *func)
|
||||||
|
if (self->single_gathered_buffer) {
|
||||||
|
free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
|
||||||
|
+ if (self->tx_bounce) {
|
||||||
|
+ free_pages((unsigned long)self->tx_bounce,
|
||||||
|
+ get_order(MAX_SDIO_TRANSFER_LEN));
|
||||||
|
+ }
|
||||||
|
#endif
|
||||||
|
kfree(self);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.53.0
|
||||||
|
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Tue, 24 Mar 2026 00:00:00 +0000
|
||||||
|
Subject: [PATCH] Bluetooth: btrtl: make RTL_SEC_PROJ read non-fatal
|
||||||
|
|
||||||
|
The RTL8852B (lmp_subver=0x8852, hci_rev=0x000b, HCI_USB) does not
|
||||||
|
support HCI vendor opcode 0xfc61 during early initialization.
|
||||||
|
btrtl_vendor_read_reg16() returns -ENODEV, which previously caused
|
||||||
|
btrtl_initialize() to abort with ERR_PTR(-19) and left the controller
|
||||||
|
unconfigured — visible as a kernel Oops and "No default controller
|
||||||
|
available" in bluetoothctl.
|
||||||
|
|
||||||
|
The RTL_SEC_PROJ register encodes a key_id used to match firmware
|
||||||
|
security-header sections. When key_id is 0 the driver already skips
|
||||||
|
all security headers (drivers/bluetooth/btrtl.c, case
|
||||||
|
RTL_PATCH_SECURITY_HEADER: "If key_id from chip is zero, ignore all
|
||||||
|
security headers"). Defaulting to key_id=0 on read failure is
|
||||||
|
therefore safe: unsigned firmware without an embedded key continues to
|
||||||
|
load normally, while secure-firmware paths that require a non-zero
|
||||||
|
key_id would only be taken on chips that successfully return one.
|
||||||
|
|
||||||
|
Change btrtl_initialize() so that a failed RTL_SEC_PROJ read emits an
|
||||||
|
informational message and falls back to key_id=0 instead of aborting
|
||||||
|
initialization.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
--- a/drivers/bluetooth/btrtl.c
|
||||||
|
+++ b/drivers/bluetooth/btrtl.c
|
||||||
|
@@ -1186,10 +1186,12 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
|
||||||
|
- if (rc < 0)
|
||||||
|
- goto err_free;
|
||||||
|
-
|
||||||
|
- key_id = reg_val[0];
|
||||||
|
+ if (rc < 0) {
|
||||||
|
+ rtl_dev_info(hdev, "RTL_SEC_PROJ read failed (%d), using key_id=0", rc);
|
||||||
|
+ key_id = 0;
|
||||||
|
+ } else {
|
||||||
|
+ key_id = reg_val[0];
|
||||||
|
+ }
|
||||||
|
btrtl_dev->key_id = key_id;
|
||||||
|
rtl_dev_info(hdev, "%s: key id %u", __func__, key_id);
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# patches/driver/bluetooth/btrtl/
|
||||||
|
|
||||||
|
Realtek Bluetooth driver (`drivers/bluetooth/btrtl.c`) patches.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
### `0009-Bluetooth-btrtl-make-RTL_SEC_PROJ-read-non-fatal.patch`
|
||||||
|
|
||||||
|
The btrtl HCI vendor-extension code probes `RTL_SEC_PROJ` (security
|
||||||
|
project ID) during firmware download. On some controllers the
|
||||||
|
extension is absent and the read times out, currently treated as a
|
||||||
|
fatal probe failure — the BT subsystem never comes up and the userspace
|
||||||
|
sees no HCI device.
|
||||||
|
|
||||||
|
This patch downgrades the failure to a warning so probe continues with
|
||||||
|
default firmware selection. Affected hardware:
|
||||||
|
|
||||||
|
- ampere (CoolPi GenBook): Realtek RTL8852BE (M.2 module)
|
||||||
|
- boltzmann (Rock 5 ITX+): same chip family on M.2
|
||||||
|
|
||||||
|
Source: `github.com/marfrit/misc_patches/genbook/kernel/0009`
|
||||||
|
Author: Markus Fritsche
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
From 3798abbc26fe7ac7da5cf5253d964d299b48d300 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Thu, 16 Apr 2026 23:53:01 +0200
|
||||||
|
Subject: [PATCH 1/9] gpio: rockchip: propagate irq_set_wake to parent GIC
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The Rockchip GPIO irqchip uses irq_gc_set_wake() which only tracks the
|
||||||
|
wake state locally in gc->wake_active. It never calls irq_set_irq_wake()
|
||||||
|
on the parent GIC interrupt for the GPIO bank. During suspend,
|
||||||
|
suspend_device_irqs() disables all non-wakeup IRQs at the GIC level, so
|
||||||
|
GPIO-based wakeup sources (RTC alarm, PMIC power key) can never reach
|
||||||
|
the CPU — the GPIO controller detects the interrupt but the GIC blocks it.
|
||||||
|
|
||||||
|
Replace irq_gc_set_wake with rockchip_irq_set_wake that propagates the
|
||||||
|
wake setting to the parent bank->irq via irq_set_irq_wake(). On failure,
|
||||||
|
revert the local wake state via irq_gc_set_wake(!on) which handles
|
||||||
|
gc->lock internally.
|
||||||
|
|
||||||
|
Generated-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
drivers/gpio/gpio-rockchip.c | 19 ++++++++++++++++++-
|
||||||
|
1 file changed, 18 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c
|
||||||
|
index 0fff4a699f12..d3b874251efc 100644
|
||||||
|
--- a/drivers/gpio/gpio-rockchip.c
|
||||||
|
+++ b/drivers/gpio/gpio-rockchip.c
|
||||||
|
@@ -483,6 +483,23 @@ static void rockchip_irq_relres(struct irq_data *d)
|
||||||
|
gpiochip_relres_irq(&bank->gpio_chip, d->hwirq);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int rockchip_irq_set_wake(struct irq_data *d, unsigned int on)
|
||||||
|
+{
|
||||||
|
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
+ struct rockchip_pin_bank *bank = gc->private;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = irq_gc_set_wake(d, on);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ ret = irq_set_irq_wake(bank->irq, on);
|
||||||
|
+ if (ret)
|
||||||
|
+ irq_gc_set_wake(d, !on);
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static void rockchip_irq_suspend(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||||
|
@@ -550,7 +567,7 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank)
|
||||||
|
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
|
||||||
|
gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
|
||||||
|
gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
|
||||||
|
- gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
|
||||||
|
+ gc->chip_types[0].chip.irq_set_wake = rockchip_irq_set_wake;
|
||||||
|
gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
|
||||||
|
gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
|
||||||
|
gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# patches/driver/gpio/rockchip/
|
||||||
|
|
||||||
|
Rockchip GPIO controller (`drivers/gpio/gpio-rockchip.c`) patches.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
### `0010a-gpio-rockchip-propagate-irq_set_wake-to-parent-GIC.patch`
|
||||||
|
|
||||||
|
**WIP — sleep fixes not yet upstream-ready.** Do not include in fleet
|
||||||
|
manifests until Markus closes the suspend/resume thread.
|
||||||
|
|
||||||
|
The Rockchip GPIO irqchip uses `irq_gc_set_wake()` which only tracks
|
||||||
|
the wake state locally in `gc->wake_active`. It never calls
|
||||||
|
`irq_set_irq_wake()` on the parent GIC interrupt for the GPIO bank.
|
||||||
|
During suspend, `suspend_device_irqs()` disables all non-wakeup IRQs
|
||||||
|
at the GIC level, so GPIO-based wakeup sources (RTC alarm, PMIC power
|
||||||
|
key) can never reach the CPU — the GPIO controller detects the
|
||||||
|
interrupt but the GIC blocks it.
|
||||||
|
|
||||||
|
This patch replaces `irq_gc_set_wake` with `rockchip_irq_set_wake`
|
||||||
|
that propagates the wake setting to the parent `bank->irq` via
|
||||||
|
`irq_set_irq_wake()`.
|
||||||
|
|
||||||
|
Source: split from `github.com/marfrit/misc_patches/genbook/kernel/0010`
|
||||||
|
(the monolithic "gpio/drm/mfd/input/dts: fix suspend/resume and wakeup
|
||||||
|
on RK3588" patch). Split commits live on `decompose-0010` branch in
|
||||||
|
`marfrit/linux-rk3588-marfrit`.
|
||||||
|
|
||||||
|
Sister patches (also from 0010):
|
||||||
|
- `patches/driver/gpu/drm/bridge/analogix/0010b-…` (analogix-dp IRQ)
|
||||||
|
- `patches/driver/mfd/rk8xx/0010c-…` (rk8xx-spi PM ops)
|
||||||
|
- `patches/driver/input/misc/0010d-…` (rk805-pwrkey wake-irq)
|
||||||
|
- `patches/board/coolpi-cm5-genbook/0010e-…` (DTS: NPU PD + touchpad wakeup-source)
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
From fdb2dae76c6258e309d1713f0ed776a416d0c077 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Thu, 16 Apr 2026 23:53:02 +0200
|
||||||
|
Subject: [PATCH 2/9] drm/bridge: analogix-dp: disable IRQ before clock gating
|
||||||
|
in suspend
|
||||||
|
|
||||||
|
analogix_dp_suspend() powers off the PHY and disables the clock but
|
||||||
|
leaves the eDP IRQ enabled. During the noirq suspend phase,
|
||||||
|
suspend_device_irqs() calls synchronize_irq() which waits for any
|
||||||
|
running handler to complete. If the eDP controller fires a spurious
|
||||||
|
interrupt after the clock was gated, the hardirq handler accesses
|
||||||
|
registers on a dead bus, hanging synchronize_irq().
|
||||||
|
|
||||||
|
Add disable_irq() before powering down and enable_irq() after
|
||||||
|
re-initialization in the resume path. Ensure enable_irq() is called
|
||||||
|
even if clk_prepare_enable() fails, to avoid leaving the IRQ
|
||||||
|
permanently disabled.
|
||||||
|
|
||||||
|
Generated-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
|
||||||
|
index efe534977d12..1b1f811ba9f4 100644
|
||||||
|
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
|
||||||
|
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
|
||||||
|
@@ -1577,6 +1577,8 @@ EXPORT_SYMBOL_GPL(analogix_dp_probe);
|
||||||
|
|
||||||
|
int analogix_dp_suspend(struct analogix_dp_device *dp)
|
||||||
|
{
|
||||||
|
+ disable_irq(dp->irq);
|
||||||
|
+
|
||||||
|
phy_power_off(dp->phy);
|
||||||
|
|
||||||
|
if (dp->plat_data->power_off)
|
||||||
|
@@ -1595,6 +1597,7 @@ int analogix_dp_resume(struct analogix_dp_device *dp)
|
||||||
|
ret = clk_prepare_enable(dp->clock);
|
||||||
|
if (ret < 0) {
|
||||||
|
DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
|
||||||
|
+ enable_irq(dp->irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1606,6 +1609,8 @@ int analogix_dp_resume(struct analogix_dp_device *dp)
|
||||||
|
|
||||||
|
analogix_dp_init_dp(dp);
|
||||||
|
|
||||||
|
+ enable_irq(dp->irq);
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(analogix_dp_resume);
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# patches/driver/gpu/drm/bridge/analogix
|
||||||
|
|
||||||
|
analogix-dp eDP bridge driver patches.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
The `0010*` patches here are sister patches to
|
||||||
|
`patches/driver/gpio/rockchip/0010a-…` — see that README for the
|
||||||
|
full context (split from misc_patches/genbook/kernel/0010, the
|
||||||
|
RK3588 suspend/resume monolithic patch). **All marked WIP — do not
|
||||||
|
include in fleet manifests until the upstream-targeting shape is
|
||||||
|
decided.**
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
From 2d58b4b47078c19f6c1e110c619009dcbeaf8b53 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Thu, 16 Apr 2026 23:53:04 +0200
|
||||||
|
Subject: [PATCH 4/9] input: rk805-pwrkey: register wake IRQ via
|
||||||
|
dev_pm_set_wake_irq
|
||||||
|
|
||||||
|
device_init_wakeup() only marks the device as wakeup-capable; without
|
||||||
|
dev_pm_set_wake_irq() the PM core never arms the IRQ. Wire up the
|
||||||
|
wake-IRQ so the PMIC power key can wake the system from suspend.
|
||||||
|
|
||||||
|
Generated-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
drivers/input/misc/rk805-pwrkey.c | 6 ++++++
|
||||||
|
1 file changed, 6 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/input/misc/rk805-pwrkey.c b/drivers/input/misc/rk805-pwrkey.c
|
||||||
|
index 76873aa005b4..dd0008e25d6d 100644
|
||||||
|
--- a/drivers/input/misc/rk805-pwrkey.c
|
||||||
|
+++ b/drivers/input/misc/rk805-pwrkey.c
|
||||||
|
@@ -14,6 +14,7 @@
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
+#include <linux/pm_wakeirq.h>
|
||||||
|
|
||||||
|
static irqreturn_t pwrkey_fall_irq(int irq, void *_pwr)
|
||||||
|
{
|
||||||
|
@@ -87,6 +88,11 @@ static int rk805_pwrkey_probe(struct platform_device *pdev)
|
||||||
|
platform_set_drvdata(pdev, pwr);
|
||||||
|
device_init_wakeup(&pdev->dev, true);
|
||||||
|
|
||||||
|
+ err = dev_pm_set_wake_irq(&pdev->dev, fall_irq);
|
||||||
|
+ if (err)
|
||||||
|
+ dev_warn(&pdev->dev, "Can't set wake IRQ %d: %d\n",
|
||||||
|
+ fall_irq, err);
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# patches/driver/input/misc
|
||||||
|
|
||||||
|
misc input drivers patches.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
The `0010*` patches here are sister patches to
|
||||||
|
`patches/driver/gpio/rockchip/0010a-…` — see that README for the
|
||||||
|
full context (split from misc_patches/genbook/kernel/0010, the
|
||||||
|
RK3588 suspend/resume monolithic patch). **All marked WIP — do not
|
||||||
|
include in fleet manifests until the upstream-targeting shape is
|
||||||
|
decided.**
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
From 9ddcae54a171f2fc7742e92e03b1478d87ae4bbb Mon Sep 17 00:00:00 2001
|
||||||
|
From: Venkata Atchuta Bheemeswara Sarma Darbha <vdarbha0473@gmail.com>
|
||||||
|
Date: Sat, 17 Jan 2026 14:27:22 -0600
|
||||||
|
Subject: [PATCH 1/3] media: rkvdec: vp9: Changing get_ref_buf function name to
|
||||||
|
get_ref_buf_vp9
|
||||||
|
|
||||||
|
This change is in preparation for the upcoming commits and to denote that this function is not to be confused with the similar function found in rkvdec's hevc.
|
||||||
|
|
||||||
|
Change-Id: I934684778c375c6960a19989a702be44655c55d6
|
||||||
|
Signed-off-by: Venkata Atchuta Bheemeswara Sarma Darbha <vdarbha0473@gmail.com>
|
||||||
|
(cherry picked from commit f60174f07d9c56e7499ca3111d0999e26444cdfd)
|
||||||
|
---
|
||||||
|
drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c | 10 +++++-----
|
||||||
|
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
|
||||||
|
index e4cdd2122873..bab2e9c83d06 100644
|
||||||
|
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
|
||||||
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
|
||||||
|
@@ -349,7 +349,7 @@ static void init_probs(struct rkvdec_ctx *ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rkvdec_decoded_buffer *
|
||||||
|
-get_ref_buf(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp)
|
||||||
|
+get_ref_buf_vp9(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp)
|
||||||
|
{
|
||||||
|
struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
|
||||||
|
struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
|
||||||
|
@@ -489,12 +489,12 @@ static void config_registers(struct rkvdec_ctx *ctx,
|
||||||
|
|
||||||
|
dec_params = run->decode_params;
|
||||||
|
dst = vb2_to_rkvdec_decoded_buf(&run->base.bufs.dst->vb2_buf);
|
||||||
|
- ref_bufs[0] = get_ref_buf(ctx, &dst->base.vb, dec_params->last_frame_ts);
|
||||||
|
- ref_bufs[1] = get_ref_buf(ctx, &dst->base.vb, dec_params->golden_frame_ts);
|
||||||
|
- ref_bufs[2] = get_ref_buf(ctx, &dst->base.vb, dec_params->alt_frame_ts);
|
||||||
|
+ ref_bufs[0] = get_ref_buf_vp9(ctx, &dst->base.vb, dec_params->last_frame_ts);
|
||||||
|
+ ref_bufs[1] = get_ref_buf_vp9(ctx, &dst->base.vb, dec_params->golden_frame_ts);
|
||||||
|
+ ref_bufs[2] = get_ref_buf_vp9(ctx, &dst->base.vb, dec_params->alt_frame_ts);
|
||||||
|
|
||||||
|
if (vp9_ctx->last.valid)
|
||||||
|
- last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp);
|
||||||
|
+ last = get_ref_buf_vp9(ctx, &dst->base.vb, vp9_ctx->last.timestamp);
|
||||||
|
else
|
||||||
|
last = dst;
|
||||||
|
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
From c5063d93e0e6011abe91418a98ed7c7550f0391b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Venkata Atchuta Bheemeswara Sarma Darbha <vdarbha0473@gmail.com>
|
||||||
|
Date: Sat, 17 Jan 2026 14:37:07 -0600
|
||||||
|
Subject: [PATCH 2/3] media: rkvdec: Move vp9 functions to common file This is
|
||||||
|
a preparation commit to add support for new variants of the decoder.
|
||||||
|
|
||||||
|
The functions will later be shared with vdpu381 (rk3588).
|
||||||
|
|
||||||
|
Change-Id: Ib9b78331fb6eb0e3a607b06fd5138fc741b2c9c0
|
||||||
|
Signed-off-by: Venkata Atchuta Bheemeswara Sarma Darbha <vdarbha0473@gmail.com>
|
||||||
|
(cherry picked from commit e87662ca32e88ebb910f6cfc1c71096d5d7bc063)
|
||||||
|
---
|
||||||
|
.../media/platform/rockchip/rkvdec/Makefile | 1 +
|
||||||
|
.../rockchip/rkvdec/rkvdec-vp9-common.c | 77 +++++++++++
|
||||||
|
.../rockchip/rkvdec/rkvdec-vp9-common.h | 95 +++++++++++++
|
||||||
|
.../platform/rockchip/rkvdec/rkvdec-vp9.c | 125 +-----------------
|
||||||
|
4 files changed, 174 insertions(+), 124 deletions(-)
|
||||||
|
create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.c
|
||||||
|
create mode 100644 drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.h
|
||||||
|
|
||||||
|
diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile
|
||||||
|
index e629d571e4d8..2bbd67b2db11 100644
|
||||||
|
--- a/drivers/media/platform/rockchip/rkvdec/Makefile
|
||||||
|
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
|
||||||
|
@@ -12,4 +12,5 @@ rockchip-vdec-y += \
|
||||||
|
rkvdec-vdpu381-hevc.o \
|
||||||
|
rkvdec-vdpu383-h264.o \
|
||||||
|
rkvdec-vdpu383-hevc.o \
|
||||||
|
+ rkvdec-vp9-common.o \
|
||||||
|
rkvdec-vp9.o
|
||||||
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..93023737c1ed
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.c
|
||||||
|
@@ -0,0 +1,77 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0
|
||||||
|
+/*
|
||||||
|
+ * Rockchip video decoder VP9 common functions
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2019 Collabora, Ltd.
|
||||||
|
+ * Boris Brezillon <boris.brezillon@collabora.com>
|
||||||
|
+ * Copyright (C) 2021 Collabora, Ltd.
|
||||||
|
+ * Andrzej Pietrasiewicz <andrzej.p@collabora.com>
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
|
||||||
|
+ * Alpha Lin <Alpha.Lin@rock-chips.com>
|
||||||
|
+ */
|
||||||
|
+#include <linux/v4l2-common.h>
|
||||||
|
+#include <media/v4l2-h264.h>
|
||||||
|
+#include <media/v4l2-mem2mem.h>
|
||||||
|
+
|
||||||
|
+#include "rkvdec.h"
|
||||||
|
+#include "rkvdec-vp9-common.h"
|
||||||
|
+
|
||||||
|
+void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane)
|
||||||
|
+{
|
||||||
|
+ unsigned int idx = 0, byte_count = 0;
|
||||||
|
+ int k, m, n;
|
||||||
|
+ u8 p;
|
||||||
|
+
|
||||||
|
+ for (k = 0; k < 6; k++) {
|
||||||
|
+ for (m = 0; m < 6; m++) {
|
||||||
|
+ for (n = 0; n < 3; n++) {
|
||||||
|
+ p = coef[k][m][n];
|
||||||
|
+ coeff_plane[idx++] = p;
|
||||||
|
+ byte_count++;
|
||||||
|
+ if (byte_count == 27) {
|
||||||
|
+ idx += 5;
|
||||||
|
+ byte_count = 0;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+struct rkvdec_decoded_buffer *
|
||||||
|
+get_ref_buf_vp9(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp)
|
||||||
|
+{
|
||||||
|
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
|
||||||
|
+ struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
|
||||||
|
+ struct vb2_buffer *buf;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * If a ref is unused or invalid, address of current destination
|
||||||
|
+ * buffer is returned.
|
||||||
|
+ */
|
||||||
|
+ buf = vb2_find_buffer(cap_q, timestamp);
|
||||||
|
+ if (!buf)
|
||||||
|
+ buf = &dst->vb2_buf;
|
||||||
|
+
|
||||||
|
+ return vb2_to_rkvdec_decoded_buf(buf);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf)
|
||||||
|
+{
|
||||||
|
+ unsigned int aligned_pitch, aligned_height, yuv_len;
|
||||||
|
+
|
||||||
|
+ aligned_height = round_up(buf->vp9.height, 64);
|
||||||
|
+ aligned_pitch = round_up(buf->vp9.width * buf->vp9.bit_depth, 512) / 8;
|
||||||
|
+ yuv_len = (aligned_height * aligned_pitch * 3) / 2;
|
||||||
|
+
|
||||||
|
+ return vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 0) +
|
||||||
|
+ yuv_len;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void update_dec_buf_info(struct rkvdec_decoded_buffer *buf,
|
||||||
|
+ const struct v4l2_ctrl_vp9_frame *dec_params)
|
||||||
|
+{
|
||||||
|
+ buf->vp9.width = dec_params->frame_width_minus_1 + 1;
|
||||||
|
+ buf->vp9.height = dec_params->frame_height_minus_1 + 1;
|
||||||
|
+ buf->vp9.bit_depth = dec_params->bit_depth;
|
||||||
|
+}
|
||||||
|
\ No newline at end of file
|
||||||
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.h
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..056842cf1bba
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9-common.h
|
||||||
|
@@ -0,0 +1,95 @@
|
||||||
|
+// SPDX-License-Identifier: GPL-2.0
|
||||||
|
+/*
|
||||||
|
+ * Rockchip video decoder VP9 common functions
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2019 Collabora, Ltd.
|
||||||
|
+ * Boris Brezillon <boris.brezillon@collabora.com>
|
||||||
|
+ * Copyright (C) 2021 Collabora, Ltd.
|
||||||
|
+ * Andrzej Pietrasiewicz <andrzej.p@collabora.com>
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
|
||||||
|
+ * Alpha Lin <Alpha.Lin@rock-chips.com>
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <media/v4l2-h264.h>
|
||||||
|
+#include <media/v4l2-mem2mem.h>
|
||||||
|
+#include <media/v4l2-vp9.h>
|
||||||
|
+
|
||||||
|
+#include "rkvdec.h"
|
||||||
|
+
|
||||||
|
+struct rkvdec_vp9_run {
|
||||||
|
+ struct rkvdec_run base;
|
||||||
|
+ const struct v4l2_ctrl_vp9_frame *decode_params;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct rkvdec_vp9_intra_mode_probs {
|
||||||
|
+ u8 y_mode[105];
|
||||||
|
+ u8 uv_mode[23];
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct rkvdec_vp9_intra_only_frame_probs {
|
||||||
|
+ u8 coef_intra[4][2][128];
|
||||||
|
+ struct rkvdec_vp9_intra_mode_probs intra_mode[10];
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct rkvdec_vp9_inter_frame_probs {
|
||||||
|
+ u8 y_mode[4][9];
|
||||||
|
+ u8 comp_mode[5];
|
||||||
|
+ u8 comp_ref[5];
|
||||||
|
+ u8 single_ref[5][2];
|
||||||
|
+ u8 inter_mode[7][3];
|
||||||
|
+ u8 interp_filter[4][2];
|
||||||
|
+ u8 padding0[11];
|
||||||
|
+ u8 coef[2][4][2][128];
|
||||||
|
+ u8 uv_mode_0_2[3][9];
|
||||||
|
+ u8 padding1[5];
|
||||||
|
+ u8 uv_mode_3_5[3][9];
|
||||||
|
+ u8 padding2[5];
|
||||||
|
+ u8 uv_mode_6_8[3][9];
|
||||||
|
+ u8 padding3[5];
|
||||||
|
+ u8 uv_mode_9[9];
|
||||||
|
+ u8 padding4[7];
|
||||||
|
+ u8 padding5[16];
|
||||||
|
+ struct {
|
||||||
|
+ u8 joint[3];
|
||||||
|
+ u8 sign[2];
|
||||||
|
+ u8 classes[2][10];
|
||||||
|
+ u8 class0_bit[2];
|
||||||
|
+ u8 bits[2][10];
|
||||||
|
+ u8 class0_fr[2][2][3];
|
||||||
|
+ u8 fr[2][3];
|
||||||
|
+ u8 class0_hp[2];
|
||||||
|
+ u8 hp[2];
|
||||||
|
+ u8 padding6[3];
|
||||||
|
+ } mv;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct rkvdec_vp9_probs {
|
||||||
|
+ u8 partition[16][3];
|
||||||
|
+ u8 pred[3];
|
||||||
|
+ u8 tree[7];
|
||||||
|
+ u8 skip[3];
|
||||||
|
+ u8 tx32[2][3];
|
||||||
|
+ u8 tx16[2][2];
|
||||||
|
+ u8 tx8[2][1];
|
||||||
|
+ u8 is_inter[4];
|
||||||
|
+ /* 128 bit alignment */
|
||||||
|
+ u8 padding0[3];
|
||||||
|
+ union {
|
||||||
|
+ struct rkvdec_vp9_inter_frame_probs inter;
|
||||||
|
+ struct rkvdec_vp9_intra_only_frame_probs intra_only;
|
||||||
|
+ };
|
||||||
|
+ /* 128 bit alignment */
|
||||||
|
+ u8 padding1[8];
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane);
|
||||||
|
+
|
||||||
|
+struct rkvdec_decoded_buffer *
|
||||||
|
+get_ref_buf_vp9(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp);
|
||||||
|
+
|
||||||
|
+dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf);
|
||||||
|
+
|
||||||
|
+void update_dec_buf_info(struct rkvdec_decoded_buffer *buf,
|
||||||
|
+ const struct v4l2_ctrl_vp9_frame *dec_params);
|
||||||
|
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
|
||||||
|
index bab2e9c83d06..2b368d7b61e0 100644
|
||||||
|
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
|
||||||
|
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
|
||||||
|
@@ -23,71 +23,12 @@
|
||||||
|
|
||||||
|
#include "rkvdec.h"
|
||||||
|
#include "rkvdec-regs.h"
|
||||||
|
+#include "rkvdec-vp9-common.h"
|
||||||
|
|
||||||
|
#define RKVDEC_VP9_PROBE_SIZE 4864
|
||||||
|
#define RKVDEC_VP9_COUNT_SIZE 13232
|
||||||
|
#define RKVDEC_VP9_MAX_SEGMAP_SIZE 73728
|
||||||
|
|
||||||
|
-struct rkvdec_vp9_intra_mode_probs {
|
||||||
|
- u8 y_mode[105];
|
||||||
|
- u8 uv_mode[23];
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-struct rkvdec_vp9_intra_only_frame_probs {
|
||||||
|
- u8 coef_intra[4][2][128];
|
||||||
|
- struct rkvdec_vp9_intra_mode_probs intra_mode[10];
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-struct rkvdec_vp9_inter_frame_probs {
|
||||||
|
- u8 y_mode[4][9];
|
||||||
|
- u8 comp_mode[5];
|
||||||
|
- u8 comp_ref[5];
|
||||||
|
- u8 single_ref[5][2];
|
||||||
|
- u8 inter_mode[7][3];
|
||||||
|
- u8 interp_filter[4][2];
|
||||||
|
- u8 padding0[11];
|
||||||
|
- u8 coef[2][4][2][128];
|
||||||
|
- u8 uv_mode_0_2[3][9];
|
||||||
|
- u8 padding1[5];
|
||||||
|
- u8 uv_mode_3_5[3][9];
|
||||||
|
- u8 padding2[5];
|
||||||
|
- u8 uv_mode_6_8[3][9];
|
||||||
|
- u8 padding3[5];
|
||||||
|
- u8 uv_mode_9[9];
|
||||||
|
- u8 padding4[7];
|
||||||
|
- u8 padding5[16];
|
||||||
|
- struct {
|
||||||
|
- u8 joint[3];
|
||||||
|
- u8 sign[2];
|
||||||
|
- u8 classes[2][10];
|
||||||
|
- u8 class0_bit[2];
|
||||||
|
- u8 bits[2][10];
|
||||||
|
- u8 class0_fr[2][2][3];
|
||||||
|
- u8 fr[2][3];
|
||||||
|
- u8 class0_hp[2];
|
||||||
|
- u8 hp[2];
|
||||||
|
- } mv;
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-struct rkvdec_vp9_probs {
|
||||||
|
- u8 partition[16][3];
|
||||||
|
- u8 pred[3];
|
||||||
|
- u8 tree[7];
|
||||||
|
- u8 skip[3];
|
||||||
|
- u8 tx32[2][3];
|
||||||
|
- u8 tx16[2][2];
|
||||||
|
- u8 tx8[2][1];
|
||||||
|
- u8 is_inter[4];
|
||||||
|
- /* 128 bit alignment */
|
||||||
|
- u8 padding0[3];
|
||||||
|
- union {
|
||||||
|
- struct rkvdec_vp9_inter_frame_probs inter;
|
||||||
|
- struct rkvdec_vp9_intra_only_frame_probs intra_only;
|
||||||
|
- };
|
||||||
|
- /* 128 bit alignment */
|
||||||
|
- u8 padding1[11];
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
/* Data structure describing auxiliary buffer format. */
|
||||||
|
struct rkvdec_vp9_priv_tbl {
|
||||||
|
struct rkvdec_vp9_probs probs;
|
||||||
|
@@ -136,11 +77,6 @@ struct rkvdec_vp9_intra_frame_symbol_counts {
|
||||||
|
struct rkvdec_vp9_refs_counts ref_cnt[2][4][2][6][6];
|
||||||
|
};
|
||||||
|
|
||||||
|
-struct rkvdec_vp9_run {
|
||||||
|
- struct rkvdec_run base;
|
||||||
|
- const struct v4l2_ctrl_vp9_frame *decode_params;
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
struct rkvdec_vp9_frame_info {
|
||||||
|
u32 valid : 1;
|
||||||
|
u32 segmapid : 1;
|
||||||
|
@@ -166,27 +102,6 @@ struct rkvdec_vp9_ctx {
|
||||||
|
struct rkvdec_regs regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
-static void write_coeff_plane(const u8 coef[6][6][3], u8 *coeff_plane)
|
||||||
|
-{
|
||||||
|
- unsigned int idx = 0, byte_count = 0;
|
||||||
|
- int k, m, n;
|
||||||
|
- u8 p;
|
||||||
|
-
|
||||||
|
- for (k = 0; k < 6; k++) {
|
||||||
|
- for (m = 0; m < 6; m++) {
|
||||||
|
- for (n = 0; n < 3; n++) {
|
||||||
|
- p = coef[k][m][n];
|
||||||
|
- coeff_plane[idx++] = p;
|
||||||
|
- byte_count++;
|
||||||
|
- if (byte_count == 27) {
|
||||||
|
- idx += 5;
|
||||||
|
- byte_count = 0;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static void init_intra_only_probs(struct rkvdec_ctx *ctx,
|
||||||
|
const struct rkvdec_vp9_run *run)
|
||||||
|
{
|
||||||
|
@@ -348,36 +263,6 @@ static void init_probs(struct rkvdec_ctx *ctx,
|
||||||
|
init_inter_probs(ctx, run);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static struct rkvdec_decoded_buffer *
|
||||||
|
-get_ref_buf_vp9(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp)
|
||||||
|
-{
|
||||||
|
- struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
|
||||||
|
- struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
|
||||||
|
- struct vb2_buffer *buf;
|
||||||
|
-
|
||||||
|
- /*
|
||||||
|
- * If a ref is unused or invalid, address of current destination
|
||||||
|
- * buffer is returned.
|
||||||
|
- */
|
||||||
|
- buf = vb2_find_buffer(cap_q, timestamp);
|
||||||
|
- if (!buf)
|
||||||
|
- buf = &dst->vb2_buf;
|
||||||
|
-
|
||||||
|
- return vb2_to_rkvdec_decoded_buf(buf);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static dma_addr_t get_mv_base_addr(struct rkvdec_decoded_buffer *buf)
|
||||||
|
-{
|
||||||
|
- unsigned int aligned_pitch, aligned_height, yuv_len;
|
||||||
|
-
|
||||||
|
- aligned_height = round_up(buf->vp9.height, 64);
|
||||||
|
- aligned_pitch = round_up(buf->vp9.width * buf->vp9.bit_depth, 512) / 8;
|
||||||
|
- yuv_len = (aligned_height * aligned_pitch * 3) / 2;
|
||||||
|
-
|
||||||
|
- return vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 0) +
|
||||||
|
- yuv_len;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static void config_ref_registers(struct rkvdec_ctx *ctx,
|
||||||
|
const struct rkvdec_vp9_run *run,
|
||||||
|
struct rkvdec_decoded_buffer *ref_buf,
|
||||||
|
@@ -446,14 +331,6 @@ static void config_seg_registers(struct rkvdec_ctx *ctx, unsigned int segid)
|
||||||
|
(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static void update_dec_buf_info(struct rkvdec_decoded_buffer *buf,
|
||||||
|
- const struct v4l2_ctrl_vp9_frame *dec_params)
|
||||||
|
-{
|
||||||
|
- buf->vp9.width = dec_params->frame_width_minus_1 + 1;
|
||||||
|
- buf->vp9.height = dec_params->frame_height_minus_1 + 1;
|
||||||
|
- buf->vp9.bit_depth = dec_params->bit_depth;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static void update_ctx_cur_info(struct rkvdec_vp9_ctx *vp9_ctx,
|
||||||
|
struct rkvdec_decoded_buffer *buf,
|
||||||
|
const struct v4l2_ctrl_vp9_frame *dec_params)
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
|||||||
|
# patches/driver/media/
|
||||||
|
|
||||||
|
Scope-tagged kernel-agent patches that touch `drivers/media/` — third-party
|
||||||
|
video-codec enablement work that hasn't reached linux-media patchwork as
|
||||||
|
formal series yet, but is empirically known to work on our test hardware.
|
||||||
|
|
||||||
|
## 0001..0003 — Sarma's VP9 enablement on VDPU381 (RK3588 rkvdec)
|
||||||
|
|
||||||
|
Three patches from `D.V.A.B. Sarma <vdarbha0473@gmail.com>` adding VP9
|
||||||
|
decode support to the VDPU381 variant of rkvdec (the RK3588 generation).
|
||||||
|
|
||||||
|
| # | Subject | LOC | What |
|
||||||
|
|---|---------|----:|------|
|
||||||
|
| 0001 | rkvdec/vp9: rename get_ref_buf to get_ref_buf_vp9 | 10 | rename existing helper to avoid namespace collision with the upcoming HEVC equivalent |
|
||||||
|
| 0002 | rkvdec: move vp9 functions to common file | 172 | extract VP9 plumbing into `rkvdec-vp9-common.{c,h}` so VDPU381 can share with the older RK3399 backend |
|
||||||
|
| 0003 | rkvdec: add VP9 support for VDPU381 variant | 1303 | the actual VDPU381 VP9 backend — register defs + `rkvdec-vdpu381-vp9.c` + glue |
|
||||||
|
|
||||||
|
Combined: ~1500 LOC, 5 new files in `drivers/media/platform/rockchip/rkvdec/`.
|
||||||
|
|
||||||
|
### Upstream provenance
|
||||||
|
|
||||||
|
- Author maintains the work at https://github.com/dvab-sarma/android_kernel_rk_opi
|
||||||
|
branch `add-rkvdec-vdpu381-vp9-v8`.
|
||||||
|
- Collabora's blog post on RK3588/RK3576 video decoder mainline merge cites
|
||||||
|
the work but notes "v1 series needs to be sent for review soon" —
|
||||||
|
i.e. not yet on linux-media patchwork, no upstream timeline.
|
||||||
|
- Casanova's VDPU381+VDPU383 H.264/HEVC base (which these patches sit on top
|
||||||
|
of) IS in mainline 7.0 release.
|
||||||
|
- Patches do NOT modify any of our scope-tagged board / module / soc /
|
||||||
|
subsystem code paths — purely additive to the upstream rkvdec subdirectory.
|
||||||
|
|
||||||
|
### Tested on
|
||||||
|
|
||||||
|
- Author: Orange Pi 5 Pro board (RK3588), AOSP 16 + FFMPEG, Profile 0 + Profile 2
|
||||||
|
- Our fleet: build verified clean on `ampere` (CoolPi CM5 GenBook, RK3588)
|
||||||
|
2026-05-18 with KERNELRELEASE `7.0.0-rc3-vp9-test+` (base = running
|
||||||
|
`7.0.0-rc3-devices+` config + LOCALVERSION change + these 3 patches +
|
||||||
|
the pre-existing issue14 vb2-resv local mods). Full kernel image
|
||||||
|
+ DTB + modules + initramfs land at `/boot/firmware/*-7.0.0-rc3-vp9-test+`
|
||||||
|
and `/lib/modules/7.0.0-rc3-vp9-test+`. New extlinux label `arch_vp9_test`
|
||||||
|
added without touching default `arch_devices`. End-to-end VP9 decode
|
||||||
|
validation requires booting into `arch_vp9_test` (pending operator
|
||||||
|
confirmation, then `v4l2-ctl -d /dev/video1 --list-formats-out` should
|
||||||
|
list `VP9F` alongside `S265` + `S264`).
|
||||||
|
|
||||||
|
### Apply order
|
||||||
|
|
||||||
|
Strict — 0001 → 0002 → 0003. 0003 depends on the common-file refactor
|
||||||
|
from 0002, which depends on the helper rename in 0001.
|
||||||
|
|
||||||
|
### Removal criteria
|
||||||
|
|
||||||
|
Drop these patches when:
|
||||||
|
- Sarma sends a v1 series to linux-media and it lands upstream — adopt
|
||||||
|
the upstream version at the next baseline bump, OR
|
||||||
|
- Collabora produces an alternative VP9 enablement on their own
|
||||||
|
hardware-enablement/rockchip-3588 GitLab tree — prefer that lineage
|
||||||
|
(more likely to land cleanly upstream).
|
||||||
|
|
||||||
|
### How to use in a kernel-agent build
|
||||||
|
|
||||||
|
If `fleet/ampere.yaml` is bumped to include VP9 (currently scope-out per
|
||||||
|
the manifest preamble — "Asks #2 (VP9 enablement on RK3588 rkvdec) and
|
||||||
|
#3 (AV1 dec integration) from issue #6 are NOT addressed in this
|
||||||
|
manifest — tracked separately"), reference these three files in apply
|
||||||
|
order under the manifest's scope-tagged patch list.
|
||||||
|
|
||||||
|
Cross-references: `marfrit/kernel-agent#12` (the VP9-on-ampere enablement
|
||||||
|
issue).
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
From e1ddd44dea499fd62907ac100baaa2835da6e213 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Date: Thu, 16 Apr 2026 23:53:03 +0200
|
||||||
|
Subject: [PATCH 3/9] mfd: rk8xx-spi: add PM ops and shutdown callback
|
||||||
|
|
||||||
|
The I2C transport (rk8xx-i2c.c) wires up rk8xx_suspend/rk8xx_resume
|
||||||
|
and a shutdown callback, but the SPI transport does not. Add the
|
||||||
|
matching PM ops (SIMPLE_DEV_PM_OPS) and shutdown callback that calls
|
||||||
|
rk8xx_shutdown() so RK806-based boards can suspend/resume and power
|
||||||
|
off cleanly via the SPI transport.
|
||||||
|
|
||||||
|
Generated-by: Claude Opus 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@localhost>
|
||||||
|
---
|
||||||
|
drivers/mfd/rk8xx-spi.c | 9 +++++++++
|
||||||
|
1 file changed, 9 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c
|
||||||
|
index 3405fb82ff9f..148af672ce12 100644
|
||||||
|
--- a/drivers/mfd/rk8xx-spi.c
|
||||||
|
+++ b/drivers/mfd/rk8xx-spi.c
|
||||||
|
@@ -109,12 +109,21 @@ static const struct spi_device_id rk8xx_spi_id_table[] = {
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table);
|
||||||
|
|
||||||
|
+static void rk8xx_spi_shutdown(struct spi_device *spi)
|
||||||
|
+{
|
||||||
|
+ rk8xx_shutdown(&spi->dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static SIMPLE_DEV_PM_OPS(rk8xx_spi_pm_ops, rk8xx_suspend, rk8xx_resume);
|
||||||
|
+
|
||||||
|
static struct spi_driver rk8xx_spi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "rk8xx-spi",
|
||||||
|
.of_match_table = rk8xx_spi_of_match,
|
||||||
|
+ .pm = &rk8xx_spi_pm_ops,
|
||||||
|
},
|
||||||
|
.probe = rk8xx_spi_probe,
|
||||||
|
+ .shutdown = rk8xx_spi_shutdown,
|
||||||
|
.id_table = rk8xx_spi_id_table,
|
||||||
|
};
|
||||||
|
module_spi_driver(rk8xx_spi_driver);
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# patches/driver/mfd/rk8xx
|
||||||
|
|
||||||
|
Rockchip RK8xx PMIC family (RK805/RK806/RK809/RK817) MFD core patches.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
The `0010*` patches here are sister patches to
|
||||||
|
`patches/driver/gpio/rockchip/0010a-…` — see that README for the
|
||||||
|
full context (split from misc_patches/genbook/kernel/0010, the
|
||||||
|
RK3588 suspend/resume monolithic patch). **All marked WIP — do not
|
||||||
|
include in fleet manifests until the upstream-targeting shape is
|
||||||
|
decided.**
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Subject: [PATCH 3/3] arm64: dts: rockchip: rk3588-coolpi-cm5: fix power-off by enabling RK806 as system power controller
|
||||||
|
|
||||||
|
Without the system-power-controller property the rk8xx-core driver never
|
||||||
|
registers its sys_off handler (rk808_power_off), which writes the DEV_OFF
|
||||||
|
bit to RK806_SYS_CFG3 at shutdown time. As a result the RK806 PMIC does
|
||||||
|
not cut power and the board remains partially active after "poweroff" —
|
||||||
|
the heartbeat LED stops but internal activity continues.
|
||||||
|
|
||||||
|
All other mainline RK3588 boards that use the RK806 carry this property
|
||||||
|
(NanoPC-T6, Rock 5A, OrangePi 5, Jaguar, ...). Add it to the CoolPi CM5
|
||||||
|
PMIC node to restore proper power-off behaviour.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
---
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-coolpi-cm5.dtsi
|
||||||
|
@@ -346,6 +346,7 @@
|
||||||
|
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
gpio-controller;
|
||||||
|
#gpio-cells = <2>;
|
||||||
|
+ system-power-controller;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>,
|
||||||
|
<&rk806_dvs2_null>, <&rk806_dvs3_null>;
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# module/coolpi-cm5
|
||||||
|
|
||||||
|
Patches that target the CoolPi CM5 system-on-module (RK3588-based,
|
||||||
|
sold as a carrier-board-agnostic compute module). They apply to any
|
||||||
|
carrier board that hosts the CM5 — currently only the GenBook in our
|
||||||
|
fleet, but the scope tag exists so that a hypothetical second carrier
|
||||||
|
wouldn't have to clone the patch under `board/<other-carrier>/`.
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
- `0003-arm64-dts-rockchip-rk3588-coolpi-cm5-Fix-power-off-by-enabling-RK806-as-system-power-controller.patch` —
|
||||||
|
marks the on-module RK806 PMIC as `system-power-controller` so the
|
||||||
|
rk8xx-core driver registers it for the `pm_power_off` hook. Without
|
||||||
|
this, `poweroff` reaches `arm_pm_restart` instead and the CM5 reboots
|
||||||
|
on shutdown.
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
From: Markus Fritsche <mfritsche@localhost>
|
||||||
|
Subject: [PATCH 1/2] arm64: dts: rockchip: rk3588: Add pwm15 pinctrl entries
|
||||||
|
|
||||||
|
Add pinctrl entries for all four mux options of the RK3588 pwm15
|
||||||
|
controller (m0-m3) to rk3588-extra-pinctrl.dtsi, marked with
|
||||||
|
/omit-if-no-ref/ so they are only compiled in when referenced.
|
||||||
|
|
||||||
|
Generated-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||||
|
Signed-off-by: Markus Fritsche <mfritsche@reauktion.de>
|
||||||
|
---
|
||||||
|
1 file changed, 30 insertions(+)
|
||||||
|
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3588-extra-pinctrl.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3588-extra-pinctrl.dtsi
|
||||||
|
@@ -342,6 +342,36 @@
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
+ pwm15 {
|
||||||
|
+ /omit-if-no-ref/
|
||||||
|
+ pwm15m0_pins: pwm15m0-pins {
|
||||||
|
+ rockchip,pins =
|
||||||
|
+ /* pwm15_ir_m0 */
|
||||||
|
+ <3 RK_PC3 11 &pcfg_pull_none>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ /omit-if-no-ref/
|
||||||
|
+ pwm15m1_pins: pwm15m1-pins {
|
||||||
|
+ rockchip,pins =
|
||||||
|
+ /* pwm15_ir_m1 */
|
||||||
|
+ <4 RK_PB3 11 &pcfg_pull_none>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ /omit-if-no-ref/
|
||||||
|
+ pwm15m2_pins: pwm15m2-pins {
|
||||||
|
+ rockchip,pins =
|
||||||
|
+ /* pwm15_ir_m2 */
|
||||||
|
+ <1 RK_PC6 11 &pcfg_pull_none>;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ /omit-if-no-ref/
|
||||||
|
+ pwm15m3_pins: pwm15m3-pins {
|
||||||
|
+ rockchip,pins =
|
||||||
|
+ /* pwm15_ir_m3 */
|
||||||
|
+ <1 RK_PD7 11 &pcfg_pull_none>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
sdio {
|
||||||
|
/omit-if-no-ref/
|
||||||
|
sdiom0_pins: sdiom0-pins {
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# soc/rockchip/rk3588
|
||||||
|
|
||||||
|
SoC-wide RK3588 patches that any board built on this SoC benefits from.
|
||||||
|
Distinct from `board/<name>/` (specific board configuration) and
|
||||||
|
`module/<som-name>/` (specific SoM configuration that several boards
|
||||||
|
share).
|
||||||
|
|
||||||
|
## Patches
|
||||||
|
|
||||||
|
- `0001-arm64-dts-rockchip-rk3588-Add-pwm15-pinctrl-entries.patch` —
|
||||||
|
fills in the four mux options for RK3588's `pwm15`. Cosmetic
|
||||||
|
upstream-quality fix; required as a *prerequisite* by board patches
|
||||||
|
that want to wire pwm15 to a real consumer (e.g. CoolPi GenBook's
|
||||||
|
pwm-fan, see `patches/board/coolpi-cm5-genbook/0002-...`).
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user