Skip to content

Adapter capabilities API + extended out-of-band 5 GHz tuning#215

Merged
josephnef merged 2 commits into
masterfrom
adapter-caps-extended-freq
Jul 8, 2026
Merged

Adapter capabilities API + extended out-of-band 5 GHz tuning#215
josephnef merged 2 commits into
masterfrom
adapter-caps-extended-freq

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

What

Two coupled additions requested for dependent apps (OpenIPC-FPV tooling, link managers, test harnesses) — modeled on the vendor rtl88x2bu monitor_chan_override procfs hack, but as a first-class library API.

A. Adapter capabilities API

New src/AdapterCaps.h aggregates what the opened radio is and can do:

  • identity — chip name, generation, variant, transport, SYS_CFG2 chip-id
  • chains — TX/RX count (EFUSE RF-type on Jaguar1, per-ChipVariant on Jaguar2/3)
  • composed TxCaps + TxPowerCaps (single source of truth)
  • bandwidth set (bitmask; Jaguar3 adds 5/10 MHz below 20/40/80)
  • per-band tunable + characterized frequency spans
  • feature flags — per-packet TX power (Jaguar2-only), narrowband (Jaguar3), fast retune, per-chain RSSI

Surfaced via IRtlDevice::GetAdapterCaps() — static, thread-safe, callable before Init, mirroring the existing GetTxCaps/GetTxPowerCaps precedent. GetActiveRxPaths() is the live companion: a best-effort per-chain-RSSI estimate of which antennas actually carry signal (RxPathActivityAccumulator + classify_active_paths in RxQuality.h, fed from all three RX loops). Demos emit the static caps as the adapter.caps JSONL event via shared examples/common/caps_event.h; rxdemo emits adapter.rxpaths for the live hint.

B. Extended out-of-band 5 GHz tuning

The synthesizer tunes past the UNII channels (~5080–6165 MHz, chan up to 253, freq = 5000 + 5·chan). ChannelFreq.h lifts the 5895 MHz cap; the per-generation channel-programming buckets (fc / AGC / RF18 / SCO / VCO band, IQK/TXGAPK calibration bands) are opened at both edges so ch 15–35 and >177 tune instead of falling through. TX power for uncharacterized channels falls back to the nearest characterized channel's index (never the near-zero index 0), with a one-shot diagnostic. No regulatory enforcement — the caller owns compliance, as in monitor mode generally.

Validation (on-air, all three generations)

  • tests/adapter_caps_probe.sh — correct caps on 8812AU, 8814AU (4T4R), 8822C, 8821C (1T1R), 8822E, 8822B
  • tests/extended_freq_smoke.sh — TX+RX confirmed at ch180 = 5900 MHz (75 MHz past the ch165 edge): 8812AU→8822C and 8822B→8822E, matching in-band control hit counts
  • 8814AU adapter.rxpaths = 4/4 chains active, balanced RSSI
  • ctest 13/13, incl. the new adapter_caps_derive selftest

OFF/unbuilt-chip configs are unaffected (additive API, no existing-event changes).

🤖 Generated with Claude Code

josephnef and others added 2 commits July 8, 2026 07:51
Two coupled additions for dependent apps (OpenIPC-FPV tooling, link
managers, test harnesses):

AdapterCaps API. New src/AdapterCaps.h aggregates what the opened radio
IS and can do — chip identity (name/generation/variant/transport/id),
TX/RX chain counts, the composed TxCaps + TxPowerCaps, the supported
channel-width set, per-band tunable + characterized frequency spans, and
per-family feature flags (per-packet TX power, narrowband, fast retune,
per-chain RSSI). Surfaced via IRtlDevice::GetAdapterCaps() (static,
thread-safe, callable pre-Init), overridden per generation. GetActiveRxPaths()
is the live companion — a best-effort per-chain-RSSI estimate of which
antennas actually carry signal (RxPathActivityAccumulator +
classify_active_paths in RxQuality.h, fed from all three RX loops). The
demos emit the static caps as the adapter.caps JSONL event via the shared
examples/common/caps_event.h; rxdemo emits adapter.rxpaths for the live hint.

Extended tuning. The 5 GHz synthesizer tunes past the UNII channels
(~5080-6165 MHz, chan up to 253, freq = 5000 + 5*chan — the vendor
rtl88x2bu monitor_chan_override range). ChannelFreq.h lifts the 5895 MHz
cap; the per-generation channel-programming buckets (fc/AGC/RF18/SCO/VCO
band, IQK/TXGAPK calibration bands) are opened at both edges so ch 15-35
and >177 tune instead of falling through. TX power for uncharacterized
channels falls back to the nearest characterized channel's index (never
the near-zero index 0), with a one-shot diagnostic. No regulatory
enforcement — the caller owns compliance, as in monitor mode generally.

Validated on-air across all three generations: adapter.caps correct on
8812AU/8814AU/8822C/8821C/8822E/8822B; TX+RX confirmed at ch180 = 5900 MHz
(75 MHz past the ch165 edge) on cross-generation pairs; ctest 13/13 with
the new adapter_caps_derive selftest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The build-mingw job builds a hand-listed set of targets (its demo binaries
aren't mingw targets), and every ctest-registered selftest binary must be
in it or the Test step fails it as Not Run. Add the new AdapterCapsSelftest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@josephnef josephnef merged commit 21994e1 into master Jul 8, 2026
15 checks passed
@josephnef josephnef deleted the adapter-caps-extended-freq branch July 8, 2026 05:01
josephnef added a commit that referenced this pull request Jul 8, 2026
## 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 BB `0x8ac` clock word (small-BW `[7:6]` + ADC/DAC
fields, `0x8c4[30]=0`, `0x8c8[31]=1`) instead of Jaguar3's `0x9b0/0x9b4`
dividers. **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)

1. **BB reset after the re-clock** — MAC `0x0` BIT16 toggle (the
`_iqk_bb_reset` mechanism) so the DAC/DFE relatch at the new sample
rate; the Jaguar3 port learned the same lesson.
2. **NB as an end-of-bring-up retune** — the vendor NB switch only ever
runs on an initialized interface (`iw`/OpenHD `monitor_chan_override`);
re-clocking mid-bring-up before IQK leaves the chip deaf both
directions.
3. **The `config_phydm_switch_band` CCK-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, and
`apply_tx_power` read raw ChannelWidth 5/6 as HT40 **and missed the
regulatory `txpwr_lmt` lookup entirely** (unclamped 63) — narrowband now
folds to the 20 MHz power column like Jaguar3.

## Hardware validation

**8821C (RTL8811CU / RTL8821CU / RTL8821CE) — fully working:**
- SDR occupied bandwidth (c811, differential PSD): **16.71 / 8.55 / 4.41
MHz** → ratios **1.95** (10M) and **3.79** (5M)
- Cross-generation vs Jaguar3 c812, both directions, both widths, at 20
MHz-control parity (10M: 44–58 hits, 5M: 56–106 vs control 57–67)
- NB survives FastRetune (175 hits across sweep dwells — the re-clock
state is bandwidth-keyed and the NB RF18 equals the 20 MHz encoding)
- 2.4 GHz + efuse/regulatory power path verified (134 hits ch6, no flat
override)
- 20/40/80 regression smokes pass on both variants; `ctest` 13/13

**8822B (RTL8822BU) — gated off (`narrowband_ok=false`, `bw_mask` clears
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 a `write_reg` reverse-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 (RF `0x82`/`0xf2`)
driven by a runtime FW interaction devourer doesn't reproduce yet —
documented in the NB branch comment, with
`DEVOURER_NB_ADC`/`DEVOURER_NB_DAC` divider-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=8821c` default)
- `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](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant