Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
478 changes: 168 additions & 310 deletions CLAUDE.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ option(DEVOURER_JAGUAR3_8822C "RTL8812CU / 8822CU (Jaguar3, rtl8822c)"
option(DEVOURER_JAGUAR3_8822E "RTL8812EU / 8822EU (Jaguar3, rtl8822e)" ON)
# Kestrel — the Wi-Fi 6 / 802.11ax generation (Realtek "G6 phl" architecture,
# vendor trees reference/rtl8852bu + reference/rtl8852cu): monitor RX, TX
# injection and channel/BW on-air validated on both chips (see src/kestrel/
# and the CLAUDE.md Kestrel section).
# injection and channel/BW on-air validated on both chips (see
# src/kestrel/CLAUDE.md).
option(DEVOURER_KESTREL_8852B "RTL8852BU / 8832BU (Kestrel 11ax, G6)" ON)
option(DEVOURER_KESTREL_8852C "RTL8852CU / 8832CU (Kestrel 11ax, G6)" ON)
# PCIe transport (vfio-pci). Linux-only. The transport implements the HalMAC-
Expand Down
6 changes: 3 additions & 3 deletions docs/8852c-quirks.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ The Kestrel **C8852C** die (WiFi-6 / 802.11ax, `35bc:0101` on the reference
adapters, die-id `0x52`) as devourer drives it today. Each entry states what the
chip does, what devourer does about it, the residual cost if any, and the
reproducer that proves it. The 8852C shares one HAL with the C8852B; most of its
divergences are the `_V1` register-bank moves called out in the top-level
`CLAUDE.md`. This file is for the behavioural quirks that aren't a simple bank
remap.
divergences are the `_V1` register-bank moves called out in
`src/kestrel/CLAUDE.md`. This file is for the behavioural quirks that aren't a
simple bank remap.

## The frame-free NHM absolute noise floor is 2.4 GHz-only (firmware DIG)

Expand Down
49 changes: 49 additions & 0 deletions src/jaguar1/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# src/jaguar1/ — Jaguar1 (11ac gen1) working context

Deep per-generation facts for this subtree, loaded alongside the root
CLAUDE.md. Chips: RTL8812AU (2T2R reference), RTL8811AU (1T1R cut, rides the
8812 path), RTL8814AU (4T4R RF / 3-SS baseband), RTL8821AU (1T1R + BT).

## HAL layout

`RtlJaguarDevice` (orchestrator), `HalModule` (bring-up, power seq, table
apply — most chip-family divergence, `_8812A`/`_8814A`/`_8821A` suffixed
methods), `RadioManagementModule` (channel/BW/TX power, up to 4 RF paths),
`EepromManager` (EFUSE + `cut_version`/`rfe_type` for `CheckPositive`),
`FirmwareManager` (8814 = 3081-DDMA path, 8812/8821 = page-write),
`FrameParser` (RX parsing + `SET_TX_DESC_*_8812` descriptors).

## Chip facts

- **8814AU**: host-pushed TX requires the on-chip 3081 MCU booted during FW
download; a failed FW-boot poll means dead TX while RX still works.
- **5/10 MHz narrowband** (8812AU/8811AU + 8814AU): both dies share the
Jaguar2 `0x8ac` ADC/DAC clock-divider block, so the re-clock trick works
even though the vendor never wired it — TX+RX bench-characterized. The two
dies use different field encodings of the same register: the 8812A divides
via `[9:8]`/`[21:20]`, the 8814A via the 8822B's
`[9:8]+[16]`/`[21:20]+[28]`. The 8821A is excluded (dividing its DAC clock
starves the 1T1R TX DMA/USB path) and falls back to 20 MHz. See
`docs/narrowband.md`.
- `DEVOURER_RX_PATHS` (RX-chain mask) writes `0x808` byte 0 via
`SetRxPathMask`; sticky across `SetMonitorChannel` because IQK
saves/restores `0x808`.
- Thermal (RF 0x42 meter): Jaguar1 has no hard thermal TX shutdown — a
rising delta is the early warning, hence bucketed status rather than a
fake temperature. On the 8814 the EFUSE baseline is read at the 8812
offset, so absolute delta may be off; the trend is valid.
- The 8812A/8821A have no LA capture block (the 8814A does).
- The 8821A's VHT-LDPC RX is broken (field-reported; HT-LDPC fine); the
8814A decodes LDPC but reports no per-frame flag (`ldpc_rx_flag=0`,
`RxAtrib.ldpc` reads 0).

## Per-packet TX power

- **8814A**: the 3-bit descriptor `TXPWR_OFSET` LUT at the 8822B position
(dword5 [30:28]); session default `SetTxPacketPowerStep` on
`RtlJaguarDevice`.
- **8812AU/8821AU**: no descriptor field exists — per-rate selection via
radiotap is their only per-packet lever. The compensating fast lever is
`FastSetTxPowerOffsetQdb` (BB-swing TxScale `0xc1c/0xe1c`: global
per-burst, 1–4 writes, 0.5 dB steps, −12..+2 dB, folded through the 8812A
thermal tracker; on-air-validated on the 8812AU).
41 changes: 41 additions & 0 deletions src/jaguar2/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# src/jaguar2/ — Jaguar2 (11ac gen2) working context

Deep per-generation facts for this subtree, loaded alongside the root
CLAUDE.md. Chips: RTL8822BU / RTL8812BU (chip-id `0x0a`) and RTL8811CU /
RTL8821CU (chip 8821C, chip-id `0x09`); the RTL8821CE (PCIe) rides this same
HAL through `PcieTransport`.

## HAL layout

Shared core (`RtlJaguar2Device`, `HalJaguar2`, `HalmacJaguar2Fw`/`MacInit`,
`FrameParserJaguar2.h`) + per-variant strategy interfaces selected by
`ChipVariant`: `Jaguar2PhyTables` → `Phy8822bTables`/`Phy8821cTables`,
`Jaguar2Calibration` → `Halrf8822b`/`Halrf8821c`. Every strategy seam
defaults to `C8822B`, so the 8822B path stays byte-identical when the 8821C
variant is compiled out. A hybrid generation: HalMAC FW download / MAC init /
power sequencing like Jaguar3, phydm `check_positive` register tables like
Jaguar1 (shared `PhyTableLoader`).

## Chip facts

- **5/10 MHz narrowband on both variants**: a baseband ADC/DAC re-clock
packed into BB `0x8ac`; the RF stays in 20 MHz mode; applied as an
end-of-bring-up retune. The 8822B RF synth only re-latches on an RF18
*value edge*, so the narrowband path writes RF18 twice — same-value
rewrites do nothing; see the `set_channel_bw` NB branch and
`docs/narrowband.md`.
- 5 MHz at 5 GHz is CFO-limited: subcarrier spacing shrinks 4× and a
far-offset TX/RX crystal pair syncs bimodally per bring-up — at 2.4 GHz
the same pair is stable (`tests/narrowband_cross_rx.sh` header).
- Per-rate bandwidth-aware efuse TX power clamped to generated `txpwr_lmt`
tables (narrowband folds to the 20 MHz column).
- Golden-init replay (`DEVOURER_REPLAY_WSEQ`, a captured kernel write stream
applied verbatim at the end of Init) is the debugging lever that found the
RF18-edge bug.
- The TXAGC block is write-only, so `GetTxPowerState` reports the software
shadow (`hw_readback=false`).

## Per-packet TX power

The 8822B/8821C descriptor `TXPWR_OFSET` is a hardware LUT
(0/-3/-7/-11/+3/+6 dB); session default via `SetTxPacketPowerStep`.
42 changes: 42 additions & 0 deletions src/jaguar3/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# src/jaguar3/ — Jaguar3 (11ac gen3) working context

Deep per-generation facts for this subtree, loaded alongside the root
CLAUDE.md. Chips: rtl8822c (RTL8812CU/8822CU, chip-id `0x13`) and rtl8822e
(RTL8812EU/8822EU, chip-id `0x17`).

## HAL layout

`RtlJaguar3Device`, `HalJaguar3` (power seq, table apply, 3-wire RF, bf_init,
efuse incl. 8822e OTP burst-mode), `HalmacJaguar3Fw`/`MacInit`,
`RadioManagementJaguar3` (channel/BW/per-path power, the `0x9b0`/`0x9b4`
narrowband dividers, RF18 encoding), strategy interfaces `Jaguar3Calibration`
→ `Halrf8822c`/`Halrf8822e` and `Jaguar3PhyTables` (its own
`PhyTableLoaderJaguar3`, separate from the Jaguar1/2 `PhyTableLoader`).

## Chip facts

- **Coex runtime thread** (`RtlJaguar3Device::coex_runtime_loop`, started in
`InitWrite`): sustained 5 GHz TX needs it — without its ~2 s WiFi-only coex
re-apply + FW heartbeats, the combo chip's coex firmware silences the
antenna. It also drains C2H, so TX-only sessions still see `tx.report`.
- **5/10 MHz narrowband**: the re-clock lives in the `0x9b0`/`0x9b4`
dividers (vs the `0x8ac` block the Jaguar1/2 chips share). 80 MHz works,
incl. a 40-in-80 frame via TX-descriptor DATA_SC. See `docs/narrowband.md`.
- halrf calibration: DACK/IQK/TXGAPK/thermal tracking.
- `DEVOURER_TX_WITH_RX=thread` must be set **before** `InitWrite` — the
bring-up keeps the RX filters open; retrofitting RX later is unreliable.
- The rtl8822e's hardware-bisected constraints (DPDT/pin-mux front end,
single-path 1SS TX, spur channels, LCK, the 2.4 GHz TX kernel-parity
limitation) live in `docs/8822e-quirks.md`.

## Per-packet TX power

The descriptor `TXPWR_OFSET_TYPE` is a bank *selector*: types 2/3 pick two
programmable 7-bit-signed power-index offsets in BB `0x1e70[31:16]`
(~1 dB/step, `DEVOURER_TXPKT_STEP_QDB` recalibrates), LRU-managed by
`SetTxPacketPowerOffsetQdb` (`TxPktPwrBanks.h`) — the banks reset *disabled*
at BB-table load, so the descriptor field alone is inert until programmed
(types 0/1 = per-STA BB-RAM by descriptor MACID, left as the 0 dB baseline).
On-air-validated on 8822CU + 8822EU, sticky across
`SetMonitorChannel`/`FastRetune`; the E compresses deep cuts (≈−6 dB floor,
same TSSI reshape as its offset slope).
2 changes: 1 addition & 1 deletion src/jaguar3/RtlJaguar3Device.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* power-on, firmware download, MAC/BB/RF config, IQK and DACK drive RX,
* channel/bandwidth (incl. 5/10 MHz narrowband) and on-air TX. send_packet is
* on-air; sustained continuous TX is kept alive by the coex runtime thread
* (coex_runtime_loop) — see CLAUDE.md. */
* (coex_runtime_loop) — see src/jaguar3/CLAUDE.md. */
class RtlJaguar3Device : public IRtlDevice {
public:
RtlJaguar3Device(RtlAdapter device, Logger_t logger,
Expand Down
116 changes: 116 additions & 0 deletions src/kestrel/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# src/kestrel/ — Kestrel (RTL8852, Wi-Fi 6) working context

Deep per-generation facts for this subtree, loaded alongside the root
CLAUDE.md. Chips: RTL8852BU/8832BU (variant C8852B) and RTL8852CU/8832CU
(C8852C), one HAL serving both dies on all platforms (MSVC included).

## Two-plane architecture

The **mac_ax plane** (power-on / FWDL / efuse / MAC TRX / H2C-C2H / USB /
descriptors) is hand-ported C++ from Realtek's "G6 phl" vendor trees
(`reference/rtl8852bu`, `reference/rtl8852cu` — none of the 11ac
loaders/parsers apply). The **BB/RF plane is Realtek's halbb/halrf C compiled
VERBATIM** for both chips: one chip-agnostic core + per-chip backends under
`hal/halbb/g6/` + `hal/halrf/g6/` (`vendor/` = untouched vendor files;
`shim/` + `hal_headers_le.h` = the minimal PHL surface they compile against;
`kestrel_halbb_glue.c` / `kestrel_halrf_glue.c` = the C entry points devourer
drives; `tools/vendor_hal{bb,rf}_*.sh` re-vendor).

The vendored loaders own the BB/RF/gain tables (`halbb_init_reg`,
`halrf_config_radio` — radio pages reach the fw via the bridge `send_h2c`
into the mac_ax H2C encoder), the per-channel BB config (`halbb_ctrl_bw_ch`),
the RF tune (`halrf_ctl_ch/bw` on the B, `halrf_ctl_band_ch_bw_8852c` on the
C), and the cals: DACK + RX-DCK both dies (wrapped in an RF 0x0/0x1/0x5
save/restore on the B — the vendor relies on later TRX flow to rewrite them),
IQK on the 8852C only. The RFK prologue (NCTL microcode / a-die SI reset /
LCK / RCK / efuse trim + TSSI DE) runs at `phy_bb_rf_init` in the vendor's
`halrf_dm_init` position — that position is load-bearing: run after the tune,
the SI reset deafens the radio both ways. **Evidence-gated cals**: TSSI/DPK
(both dies, via `support_ability`) and IQK on the 8852B degrade TX under the
fixed-dBm power model — gates fall with a TSSI-referenced power model.

## Dispatch and cold boot

Dispatched **PID-first** (`kestrel/KestrelUsbIds.h`), not from the 0x00FC
byte: on AX silicon that register is R_AX_SYS_CHIPINFO (die-id 0x51/0x52; the
8852A's 0x50 collides with the 8822B cold transient). Cold-boot facts (mac_ax
plane, both dies): a USB chip from real cold reads WLMAC_PWR_STE=MAC_ON (its
AFSM auto-powers to enumerate) and power_on must force the MAC off first
(`pwr.c` prologue) or the bootrom never raises H2C_PATH_RDY; `B_AX_ENBT` must
be set before any LTE-space access (arms the LTE indirect interface) and the
GNT arbitration is SW-forced to WiFi (`mac_cfg_gnt_8852b` stance) — a kernel
driver pre-initializing the chip masks all of this, so blacklist rtw89 when
testing.

USB open: Kestrel adapters skip the pre-claim `libusb_reset_device`
unconditionally (`claim_interface_then_reset` guard): `power_on` forces the
MAC off from any retained state, while a USB reset on running firmware drops
the chip to ROM — a stale-handle re-enumeration that can land in the dead
ZeroCD DISK id (`0bda:1a2b`).

## Validated capability surface

On-air-validated: monitor RX (both dies, 2.4/5 GHz; the RX front-end needs
the halbb per-band LNA/TIA gain-error cache, without which 5 GHz is deaf), TX
(mgmt injection — the OpenIPC video path via `streamtx`; legacy/HT/VHT/HE
rates), channel/BW **5/10/20/40/80 MHz on both dies + 160 MHz on the 8852C**
(the 8852B die has no 160 MHz — vendor bw_sup; caps report accordingly).
40 MHz tunes to the block center (primary ±2); 80/160 MHz derive
center/pri_ch from the channel plan (160 = an 8-wide block, center =
block_start+14). **6 GHz TX tops out at 80 MHz**: the 6G 160 MHz TX does not
radiate on the C8852C (the RF synth locks and RX-160 works, but the 6G+160
TX-enable path is un-ported — B210-confirmed 0% duty vs 45% at 6G-80 / 40% at
5G-160; a MAC TXAGC-max / RF-TX-path gap, not a chip limit — the vendor
drives it). 5/10 MHz narrowband is the BB "small BW" field with the RF left
in 20 MHz mode (no ADC re-clock, unlike Jaguar). RX bulk-IN delivery requires
the USB RXAGG engine enabled (`B_AX_RXAGG_EN`). TX airs with the CMAC
EDCCA/CCA gate disabled (`sch_tx_en`, TX path only) — the intended
injection/monitor-link mode. `ReadTsf` reads the per-port MAC TSF;
`StartBeacon` drives the AX HW beacon engine.

**HE ER SU + DCM extended range** (both dies): per-packet via radiotap-HE
FORMAT=EXT_SU or `DEVOURER_TX_RATE=.../ER[/DCM]`; RX classifies the format in
`RxAtrib.ppdu_type` (7=HE_SU, 8=HE_ERSU) — `docs/he-extended-range.md`.

Async packet-C2H (bulk-IN rpkt_type=10) delivery works — routed by
`handle_c2h` on the C2H class/func — so the #236 C2H surface (TWT/F2P
reports) is reachable. Not working: the fw's USR_TX_RPT TX-egress-timestamp
C2H (gated on a full BSS association, not just the registered NO_LINK role).

## TX power

A fixed BB dBm (`halbb_set_txpwr_dbm`, default 20 dBm, `DEVOURER_TX_PWR`
override — whole dBm on this family) with the runtime `SetTxPowerOffsetQdb`
lever. Per-packet radiotap `DBM_TX_POWER`: no WD-descriptor field exists —
`send_packet` honours the radiotap delta by rewriting the fixed-dBm BB target
between frames (2 BB RMWs on value change, free while constant; 0.25 dB
steps, clamped to the 0..23 dBm PA window around the `DEVOURER_TX_PWR` base;
global, so a HW beacon airing between frames follows the last-written level;
a field-less frame restores the `SetTxPowerOffsetQdb` session offset).

## 8852C vs 8852B divergences

Almost every 8852C divergence is a **`_V1` register-bank** move
(USB/HFC/RXAGG/HCI at 0x5xxx/0x1700/0x6000/0x7880 vs the 8852B
0x1xxx/0x8Axx/0x8900/0x8380) plus a different descriptor: FWDL/H2C use a
16-byte `rxd_short_t` (not the 24-byte WD body), data/mgmt TX uses the
32-byte `wd_body_t_v1` (not 24), and the RX-descriptor drv_info unit is
16 bytes (not 8 — `FrameParserKestrel.h`; with the B's unit the C "receives"
frames with correct lengths but unreadable bodies, i.e. looks deaf to any
SA-matching harness). The CBV-cut die loads the `u2_nic` fw image (CAV→u1).
Two 8852C-specific TX pieces the 8852B path lacks: the BB IFFT→TX-chain
routing (`halbb_ctrl_tx_path_tmac_8852c`, the 0xD800..0xD82C "path-com" block
the 8852B replaces with its per-STA CMAC antenna model), and the V1
descriptor's rate word — `USERATE_SEL`/BW/GI/DATARATE live in wd_body dword7,
not wd_info dword0 as on the 8852B (mis-placing it left `f_rate=0` so the
scheduler stalled and the bulk-OUT NAKed). Behavioural quirks beyond the
`_V1` bank moves (e.g. the frame-free NHM absolute floor is 2.4 GHz-only —
the firmware DIG biases the 5 GHz idle-noise measurement):
`docs/8852c-quirks.md`.

## Scope

The capstone is 11ax trigger-based UL + TWT (issue #236 — the v1.19 vendor
fwcmd surface exposes TWT-OFDMA + F2P trigger H2Cs that mainline rtw89
lacks). The 8852A-family (RTL8832AU) is deliberately excluded (frozen 2021
v1.15 vendor drop only).
2 changes: 1 addition & 1 deletion tests/bench_onair.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def main() -> int:
present = {c: v for c, v in present.items() if c.upper() in want}
print(f"# chips: {', '.join(present)} | MCS{args.mcs}/{args.bw}MHz/{args.size}B\n")

# Pre-flight rail-sag guard (CLAUDE.md defence #1): the bus-powered hub rail
# Pre-flight rail-sag guard: the bus-powered hub rail
# can brown out the 5 GHz PA, collapsing on-air power while 2.4 GHz still
# works — making every 5 GHz number untrustworthy. Check the known-good
# control adapter first so a sagging rail is flagged loudly instead of
Expand Down
Loading