Jaguar2 5/10 MHz narrowband — 8821C validated, 8822B gated#216
Merged
Conversation
Port the vendor narrowband re-clock (config_phydm_switch_bandwidth_8822b/ _8821c: 0x8ac small-BW [7:6] + ADC/DAC clock fields, 0x8c4[30]=0, 0x8c8[31]=1, RF18 kept at the 20 MHz encoding) to the Jaguar2 HAL, plus the three pieces the vendor register recipe alone does not give, each hardware-bisected on the bench: - BB reset after the re-clock (MAC 0x0 BIT16 toggle) so the DAC/DFE relatch at the new sample rate — same lesson the Jaguar3 port hardware-taught. - Narrowband applied as an END-of-bring-up retune (kernel-flow parity): the vendor NB switch only ever runs on an initialized interface; re-clocking mid-bring-up (before IQK / TRX re-assert) leaves the chip deaf. - The config_phydm_switch_band CCK-block trio (5G: 0x808[28]=0, 0x454[7]=1, 0xa80[18]=1) the 8822B path had never ported — a CCK engine left enabled under the NB re-clock breaks the OFDM demod. Also fixed en route: the 8821C RX-DFIR 0xc20[31] conditional was wrong for bw=5/6, and apply_tx_power read raw ChannelWidth 5/6 as HT40 and missed the txpwr_lmt lookup entirely (unclamped 63) — narrowband now folds to the 20 MHz power column, mirroring Jaguar3. 8821C (RTL8811CU/8821CU/8821CE) is fully validated: SDR occupied-bandwidth 16.71/8.55/4.41 MHz (ratios 1.95 / 3.79), cross-generation RX+TX against Jaguar3 at 20 MHz-control parity for both widths, NB survives FastRetune, 2.4 GHz + efuse-power fold path verified. 8822B stays gated (narrowband_ok=false, bw_mask clears kBw5|kBw10): with the same recipe it syncs ~10% of NB frames and airs no NB TX, while the OpenHD kernel module decodes 5000/5000 on the same dongle with a byte-identical firmware blob and identical page-8 BB state (write_reg reverse-bisect). The kernel's own 20->10 MHz delta shows an RF-internal RXBB LPF retune (RF 0x82/0xf2) driven by a runtime FW interaction devourer does not reproduce yet — documented in the NB branch comment; the DEVOURER_NB_ADC/DEVOURER_NB_DAC divider-override knobs remain as the debugging levers for that follow-up. Jaguar1 has no vendor narrowband implementation anywhere (enum-only dead code) — documented out of scope. New tests: tests/jaguar2_narrowband_sdr.sh (differential-PSD occupied-BW, the Jaguar3 script's twin) and tests/narrowband_cross_rx.sh (both-ends re-clocked demod proof across generations, with DLFW-flake retry). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… lever, document 5 MHz CFO limit Systematic comparison against the vendored OpenHD rtl88x2bu (issue #184 pattern — devourer as an incomplete port): - tests/narrowband_cross_rx.sh counted rx.txhit EVENTS, but the demo throttles them (first 10, then every 100th) — cells reading "57 hits" were really ~4700 frames. Count the `hits` field of the last event. Re-validated with true numbers: 8821C 10 MHz at 2100 vs 4700-frame 20 MHz control (the remaining factor is NB frame airtime), 5 MHz at 7000+ per 10 s on 2.4 GHz. - Time-resolved kernel dumps show the working driver CONVERGES RF 0x82/0xf2 (RXBB) to narrowband values over ~3 s after the switch (0x44 -> 0x50 -> 0x52) via its dynamic loop. Forcing those converged values statically from devourer's NB branch does NOT recover 8822B RX (hardware-tested, reverted) — the 8822B gap note now records the refutation; the operative delta is the dynamic mechanism itself or a still-unobserved FW interplay. - 5 MHz at 5 GHz is CFO-limited physics, not a driver bug: quarter clock shrinks subcarrier spacing 4x, and a far-offset TX/RX crystal pair is bimodal per bring-up (9100 hits one run, 0 the next, while a closer-crystal peer catches 10200 from the same TX); the same pair is stable at 2.4 GHz. Documented in the test header + CLAUDE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
usbmon capture of the working kernel's 20->10 MHz switch + the 5 s convergence window: 189 vendor control writes, zero H2C box writes, and only three 3-wire RF writes (RF18 x2 for the switch, RF 0x42 thermal trigger from the 2.1 s watchdog). The switch-time MAC writes are the halmac cfg_bw defaults (0x668 + the 80 MHz MAC-clock trio 0x24/0x55c/ 0x638), spur/CSI block, and DIG — all benign or already matched. Meaning: the kernel's RF 0x82/0xf2 RXBB retune at narrowband is done by the on-chip firmware autonomously, with no host trigger — and the FW does it under the kernel but not under devourer, whose FW never receives the interface-up H2C state (media status / macid / RA) that arms the FW dynamic engine. Since statically forcing the converged RF values does not recover RX, the FW's runtime assist itself is the operative delta. Comment updated to record the closed hypothesis space; porting the interface-up H2C bring-up is the follow-up (same machinery gap as the issue #184 thermal-tracking request). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
josephnef
added a commit
that referenced
this pull request
Jul 8, 2026
…r2 variants) + kernel-parity ports (#219) ## What PR #216 shipped Jaguar2 5/10 MHz narrowband but **gated the 8822B** — it synced ~0.5% of NB frames on RX and aired nothing on TX, while the OpenHD kernel module did full-rate NB on the same dongle with the same firmware. This PR finds and fixes the root cause, and lands the vendor-parity gaps discovered along the way. ## Root cause: RF18 re-latch edge **The RTL8822B RF synthesizer only re-latches its internal channel state on an RF18 (`RF 0x18`) *value edge*** — a same-value rewrite is a no-op. Devourer applies narrowband as an end-of-bring-up re-clock and rewrote RF18 with the *identical* value, leaving the RF latched to its pre-re-clock internals: RX deaf, TX invalid, band-independent. The kernel driver never exposes this because its flow always tunes *through* a channel/band change (an RF18 edge) around any bandwidth switch. This also retroactively explains the earlier "firmware-autonomous RF `0x82`/`0xf2` retune" observation — those are RF-internal LUT outputs refreshed by the RF18 edge, not FW action. **Fix** (`set_channel_bw`, 8822B NB branch): write RF18 once with a substitute channel byte, then the real value, on both RF paths. ## How it was found — golden-init replay Every readable-state hypothesis had already been eliminated by A/B against the vendored kernel module (a 116-register MAC+BB flip and removal of the module's only H2C traffic both left kernel NB working). The latch is invisible to state diffing because it keys on the *write event*, not the value. So: 1. usbmon-capture the working kernel's complete post-DLFW register write stream (3630 writes incl. the 10 MHz switch). 2. Replay it verbatim from devourer via a new `DEVOURER_REPLAY_WSEQ` debug knob (applied at end of `Init`, overriding devourer's own config) → NB unlocks (39100 hits vs ~10) → causal state is host-writable. 3. Delta-debug the stream (~25 automated probes) down to the causal RF18 write pair. Edge pair alone: 23300 hits; same-value rewrite alone: 9. `DEVOURER_REPLAY_WSEQ` is kept as a permanent lever — it converts any "same registers, different behavior" mystery into a mechanical bisection. ## Hardware validation (T3U 8822BU ↔ Jaguar3 8812CU, true frame counts) - ch44 10 MHz RX 7700 / TX 12600; ch44 5 MHz RX 6500; 2.4 GHz ch6 10 MHz RX 7700 - scripted `tests/narrowband_cross_rx.sh`: **20M=3900 / 10M=4000 / 5M=3900** — full control parity, both widths - 20/40/80 smokes + `ctest` pass; the RF18 edge is narrowband-gated (wide paths byte-unchanged) Caps ungated: `narrowband_ok=true` on both Jaguar2 variants, `bw_mask` keeps 5/10. ## Also in this branch (kernel-parity ports found by the same comparison) These were prerequisites/gap-closures uncovered while diffing devourer's init against the kernel's write stream. Each is independently vendor-parity and measured; together they roughly **doubled 8822B 20 MHz RX** (~4700 → ~8100 hits/cell) before the NB fix: - **halrf kfree** — efuse power/PA-bias trim: the PA-bias RF LUT write (RF `0x51/0x52` → `0x3f` via the `0xef[10]` window) + per-channel-group TX gain trim (`RF 0xde/0x65/0x55`). Gotcha ported with it: close the RF LUT window before the `0x51/0x52` reads (the table apply leaves it open, aliasing the reads). - **eFEM CCA table fix** — RFE types 3/5/12/15/16/17/19 (external-FEM boards incl. the bench T3U) need `cca_ifem_ccut_rfe`; devourer hardcoded the plain iFEM column for every board. Plus the 20 MHz DFS-channel CCA adjust. - **phydm spur calibration** — reset the NBI/CSI notch state on every channel set, and PSD-detect + notch the chip's own clock spur on the fixed spur channels (validated engaging at ch6/2440 MHz). - **halmac cfg_mac_clk** — the MAC µs-tick dividers (`0x55c/0x638`); chip reset defaults are wrong for the running clock. - **SoML enable + RxHP band block**, and the **FW GENERAL_INFO/PHYDM_INFO H2C** (`send_fw_general_info`) — the kernel's post-DLFW FW identity handshake. ## Test notes - `DEVOURER_REPLAY_WSEQ=<file>` — golden-init replay (lines: `<addr_hex> <width 1|2|4> <val_hex>`). - `tests/narrowband_cross_rx.sh` / `tests/jaguar2_narrowband_sdr.sh` now cover both Jaguar2 variants. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
PR #215 left 5/10 MHz narrowband as Jaguar3-only. Investigating the in-repo reference drivers showed Jaguar2 has real vendor narrowband support (
config_phydm_switch_bandwidth_8822b/_8821c) — same "RF stays in 20 MHz mode, underclock the baseband" concept as Jaguar3, packed into the BB0x8acclock word (small-BW[7:6]+ ADC/DAC fields,0x8c4[30]=0,0x8c8[31]=1) instead of Jaguar3's0x9b0/0x9b4dividers. Jaguar1 has no vendor narrowband anywhere (enum-only dead code in every rtl8812au tree) — documented out of scope.The three pieces the vendor registers alone don't give (hardware-bisected)
0x0BIT16 toggle (the_iqk_bb_resetmechanism) so the DAC/DFE relatch at the new sample rate; the Jaguar3 port learned the same lesson.iw/OpenHDmonitor_chan_override); re-clocking mid-bring-up before IQK leaves the chip deaf both directions.config_phydm_switch_bandCCK-block trio (5G:0x808[28]=0,0x454[7]=1,0xa80[18]=1) — the 8821C path had it, the 8822B path never ported it; a CCK engine left enabled under the NB re-clock breaks the OFDM demod.Fixed en route: 8821C RX-DFIR
0xc20[31]was wrong for bw 5/6, andapply_tx_powerread raw ChannelWidth 5/6 as HT40 and missed the regulatorytxpwr_lmtlookup entirely (unclamped 63) — narrowband now folds to the 20 MHz power column like Jaguar3.Hardware validation
8821C (RTL8811CU / RTL8821CU / RTL8821CE) — fully working:
ctest13/138822B (RTL8822BU) — gated off (
narrowband_ok=false,bw_maskclears 5/10): with the same recipe it syncs ~10% of NB frames and airs no NB TX, while the OpenHD kernel module decodes 5000/5000 of our Jaguar3 NB frames on the same dongle with a byte-identical firmware blob and identical page-8 BB state (proven by awrite_regreverse-bisect on the working kernel). The kernel's own 20→10 MHz self-diff leaves exactly one unexplained delta: an RF-internal RXBB LPF retune (RF0x82/0xf2) driven by a runtime FW interaction devourer doesn't reproduce yet — documented in the NB branch comment, withDEVOURER_NB_ADC/DEVOURER_NB_DACdivider-override knobs left in as the debugging levers for that follow-up.Tests
tests/jaguar2_narrowband_sdr.sh— differential-PSD occupied-BW check (Jaguar3 script's twin,CHIP=8821cdefault)tests/narrowband_cross_rx.sh— both-ends-re-clocked demod proof across generations (20M control + 10/5 MHz cells, DLFW-flake retry)🤖 Generated with Claude Code