Skip to content

T1: fix 5G TX-AGC over-power + expand canary set (path-B mirror, IQK/DPK outputs)#67

Merged
josephnef merged 1 commit into
masterfrom
t1-5g-txagc-fix
Jun 2, 2026
Merged

T1: fix 5G TX-AGC over-power + expand canary set (path-B mirror, IQK/DPK outputs)#67
josephnef merged 1 commit into
masterfrom
t1-5g-txagc-fix

Conversation

@josephnef
Copy link
Copy Markdown
Collaborator

Summary

Two related fixes uncovered by expanding the T1 canary set to 5G channels and the path-B TX-AGC mirror. Brings devourer's 5GHz TX power output byte-for-byte in line with the upstream aircrack-ng/88XXau USB build.

Findings

Pre-fix, at ch36 + ch100, devourer wrote ~6 power-index steps higher than kernel across the full TX-AGC table:

Reg @ ch36 Pre-fix Post-fix / Kernel
BB 0xc24 (OFDM 6/9/12/18) 0x1E1E1E1E 0x16161616
BB 0xc28 (OFDM 24/36/48/54) 0x181A1E1E 0x16161616
BB 0xc3c (VHT1SS 0..3) 0x2E303232 0x16161616
path-B mirror 0xe20-0xe40 same divergence match path-A

Two root causes:

1. CONFIG_TXPWR_BY_RATE_EN=n on upstream USB build

Upstream aircrack-ng/88XXau ships with CONFIG_TXPWR_BY_RATE_EN = n (Makefile line 48). This sets RegEnableTxPowerByRate = 0phy_is_tx_power_by_rate_needed() returns FALSE → PHY_GetTxPowerByRate() short-circuits to always return 0. Upstream's USB driver never applies the PG-table per-rate offsets; all TX power = base + boost (=2).

PR #64's LoadTxPowerByRate + headroom-cap formula was effectively a no-op at 2.4G (because the cap zeroed by_rate when base + boost > limit for FCC's 2.4G OFDM caps), so the canary matched — masking the bug. At 5G the headroom is positive, by_rate gets applied as +6 → devourer overshoots uniformly.

Fix: default-off the by-rate layer to match upstream's USB build. The EFUSE PG-table parse + headroom-cap formula are preserved behind DEVOURER_ENABLE_TXPWR_BY_RATE=1 for deployments that mirror upstream's CONFIG_TXPWR_BY_RATE_EN=y (Windows / some Android variants).

2. PowerTracking8812a init-ordering bug

PowerTracking8812a::Init() captures default_ofdm_index BEFORE phy_SetBBSwingByBand_8812A runs the per-band BB-swing write. For 5G dongles with EFUSE-driven swing-down (our 8812AU writes 0x16A = -3 dB per EEPROM_TX_BBSWING_5G_8812), this leaves default_ofdm_index = 24 (matching the post-BB-init 0x200) while the actual base after band switch is index 18. The first pwrtrk tick then computes final = 24 + abs_swing_idx instead of 18 + abs_swing_idx — six steps too high.

Fix: refresh default_ofdm_index = LookupSwingIndexFromBb() from inside ClearState(), which phy_SetBBSwingByBand_8812A already calls post-write. Init()-time snapshot remains as the cold-init seed; every band switch reseeds.

Canary expansion

Added to DEVOURER_DUMP_CANARY + tools/canary_kernel_dump.sh:

  • Path-B TX-AGC mirror: 0xe20-0xe40 (catches 2T2R-specific drifts the previous canary couldn't surface).
  • IQK output regs: 0xc10, 0xc14, 0xe10, 0xe14.
  • DPK output regs: 0xc94, 0xe94 (0xc90 was already there).

Plus a new env-gated diagnostic DEVOURER_LOG_TXPWR=1 — traces base/by_rate/limit/headroom/final per (channel, path, rate, ntx, bw) for future canary-divergence investigations on the per-rate calc.

Test plan

  • Canary diff at ch6/ch36/ch100: TX-AGC table now matches kernel byte-for-byte (full diff in commit message). Remaining canary divergences are runtime dynamic state (thermal meter, beacon counters, IQK/DPK fire-on-init differences) — not init bugs.
  • Single-cell at ch36: 8812-dev → 8821-ker = 6528/6500 ✓ (was 0/6500 ✗ pre-fix).
  • Partial matrix at ch36: 8812-dev → 8814-ker = 6182/6500 ✓ (was 0/6500 ✗ pre-fix).
  • Full matrix at ch6: 22/24 ✓ identical to PR T1: port 8812AU phydm I/Q calibration #66 baseline.
  • Full matrix at ch36 + ch100 with all 3 adapters — deferred; the 8821 dongle was temporarily detached + the 8812 dropped off USB mid-matrix during this PR's test pass (recurring rig flake, unrelated to the code change).

🤖 Generated with Claude Code

…trk fix

Two related fixes to bring devourer's 5GHz TX-AGC output byte-for-byte
in line with the upstream aircrack-ng/88XXau USB build. Surfaced by
expanding the T1 canary set to cover ch36 + ch100 and the path-B AGC
table mirror — at ch36 devourer was writing `BB 0xc24 = 0x1E1E1E1E`
while kernel wrote `0x18181818`; the divergence ran across
0xc24-0xc40 + 0xe24-0xe40, ~6 power-index steps higher than upstream.

## Root causes

(1) **Per-rate TX-power offset enabled by default in devourer, but
upstream's USB driver builds with `CONFIG_TXPWR_BY_RATE_EN = n`**
(`Makefile:48`). This sets `RegEnableTxPowerByRate = 0` →
`phy_is_tx_power_by_rate_needed()` returns FALSE →
`PHY_GetTxPowerByRate()` short-circuits to return 0. Upstream's USB
build NEVER applies the PG-table per-rate offsets; all TX power =
`base + boost (=2)`.

At 2.4G devourer's headroom-cap formula happened to zero out by_rate
(since `base + boost > limit` for FCC's 2.4G OFDM caps) → canary
matched. At 5G headroom is positive, by_rate gets applied as +6 →
devourer overshoots kernel by 6 indices uniformly across 0xc24-0xc40.

Default-off the by-rate layer to match upstream's USB build. The
EFUSE PG-table parse + headroom-cap formula are preserved behind
`DEVOURER_ENABLE_TXPWR_BY_RATE=1` for deployments that mirror
upstream's `CONFIG_TXPWR_BY_RATE_EN=y` (Windows, some Android
variants).

(2) **PowerTracking8812a init-ordering bug**:
`PowerTracking8812a::Init()` captured `default_ofdm_index` BEFORE
`phy_SetBBSwingByBand_8812A` ran the per-band BB-swing write. For
5G dongles with EFUSE-driven swing-down (e.g. our 8812AU at
`0x16A = -3 dB` per `EEPROM_TX_BBSWING_5G_8812`), this left
`default_ofdm_index = 24` (matching the post-BB-init `0x200`) while
the actual base after band switch was index 18 (`0x16A`). The first
pwrtrk tick then computed `final = 24 + abs_swing_idx` instead of
`18 + abs_swing_idx` — six steps too high.

Refresh `default_ofdm_index = LookupSwingIndexFromBb()` from inside
`ClearState()` (which `phy_SetBBSwingByBand_8812A` already calls
post-write). The Init()-time snapshot stays as the cold-init seed;
every band switch + tx_agc change reseeds correctly.

## Canary expansion

Added path-B TX-AGC mirror (0xe20-0xe40), IQK output regs (0xc10,
0xc14, 0xe10, 0xe14), and DPK output regs (0xc94, 0xe94, 0xc90 was
already in the set) to `DEVOURER_DUMP_CANARY` + the kernel-side
companion script. The path-B mirror covers a class of 2T2R-specific
init drifts the previous canary couldn't surface.

Also added `DEVOURER_LOG_TXPWR=1` diagnostic — traces
base/by_rate/limit/headroom/final per (channel, path, rate, ntx, bw)
for canary-divergence chasing on the per-rate calc.

## Verification

Canary diff (8812AU, kernel = aircrack-ng/88XXau v5.6.4.2 in VM):

| Reg | Pre-fix ch36 | Post-fix ch36 | Kernel ch36 |
|---|---|---|---|
| BB 0xc24 | `0x1E1E1E1E` | `0x16161616` ✓ | `0x16161616` |
| BB 0xc28 | `0x181A1E1E` | `0x16161616` ✓ | `0x16161616` |
| BB 0xc3c | `0x2E303232` | `0x16161616` ✓ | `0x16161616` |
| BB 0xc40 | `0x161E2024` | `0x16161616` ✓ | `0x16161616` |
| BB 0xe20-0xe40 | non-homogeneous | matches path-A | matches |

Single-cell + partial matrix (8821AU dongle was temporarily detached
mid-run, full matrix re-runs deferred until rig stabilises):
- `8812-dev → 8821-ker @ ch36 = 6528/6500 ✓` (was `0/6500 ✗` pre-fix)
- `8812-dev → 8814-ker @ ch36 = 6182/6500 ✓` (was `0/6500 ✗` pre-fix)
- `8821-dev → 8814-ker @ ch36 = 6464/7000 ✓` (unchanged)
- Full matrix at ch6: `22/24 ✓` identical to PR #66 baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@josephnef josephnef merged commit e14327c into master Jun 2, 2026
5 checks passed
@josephnef josephnef deleted the t1-5g-txagc-fix branch June 2, 2026 14:47
josephnef added a commit that referenced this pull request Jun 2, 2026
…14) (#69)

## Summary

The 8821AU canary diff (T1 follow-on) caught this bug:
`PowerTracking8812a::TickThermalMeter` was firing on every Jaguar chip
(8812 / 8821 / 8814), but its delta-swing tables + math are
8812-specific. Upstream maintains a parallel `halrf_8821a_ce.c` for 8821
with different per-band tables and 1T1R-specific logic — the 8812 code
produces wrong values on 8821.

## Findings

**8821 ch6 canary divergences (pre-gate):**

| Register | Kernel | Devourer | Notes |
|---|---|---|---|
| BB 0xc1c[31:21] | 0x200 (0 dB) | 0x1C8 (-1 dB) | devourer's wrong
pwrtrk write |
| BB 0x8b0 | 0x18 | 0x42 | AGC-state side effect |

**Post-gate:** both divergences disappear — pwrtrk no longer fires on
8821, BB stays at band-init values matching kernel's not-yet-converged
state.

## Fix

Gate the trigger at both call sites with
`_eepromManager->version_id.ICType == CHIP_8812`:
- `RadioManagementModule::phy_SwChnlAndSetBwMode8812` (per channel-set
tick)
- `HalModule::rtl8812au_hal_init` (cold-init seed)

Matches the same chip-gating pattern already used for IQK (`Iqk8812a`).

8814AU also benefits — pwrtrk was firing on it with the same 8812
tables. No matrix run for 8814 because 8814 TX is gated by issue #36,
but canary correctness improves.

## Future work

8821AU pwrtrk port — separate change. Needs `halrf_8821a_ce.c`
translation + 1T1R-specific table simplification.

## Test plan

- [x] 8821 ch6 canary: BB 0xc1c + BB 0x8b0 divergences gone (verified
post-build).
- [x] Build clean.
- [ ] Full matrix at ch6/ch36/ch100 with all 3 adapters — 8812 dongle
was off USB during this work; verification pending re-plug. Expected:
8821 + 8814 unchanged from PR #67 baseline (which is currently stacked
under this); 8812 unchanged (still runs pwrtrk).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <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