T1: port 8812AU phydm I/Q calibration#66
Merged
Conversation
Closes the IQK side of the T1 canary divergence — upstream
`phy_iq_calibrate_8812a` runs at init + on band switch; devourer just
held `_needIQK = false` and never called IQK. Without it, BB 0x8b0 /
RF[A/B] 0x00 / 0xc10 / 0xccc / 0xcd4 stay at their BB-init seed.
Port covers:
- `_phy_iq_calibrate_8812a` end-to-end — backup MAC/BB/RF/AFE state,
run `_iqk_tx_8812a`, restore state, replay 0xcb8/0xeb8.
- `_iqk_tx_8812a` (~480 LOC) — TX-tone calibration loop, RX-tone
calibration loop, fill IQC, with ±4 averaging convergence over
up to 10 cal iterations per path. Drops the VDF (>80MHz) branch.
- `_iqk_backup_*` / `_iqk_restore_*` helpers (MAC/BB, RF, AFE).
- `_iqk_configure_mac_8812a` — MAC scratch state for IQK duration.
- `_iqk_tx_fill_iqc_8812a` / `_iqk_rx_fill_iqc_8812a` — final
coefficient write to 0xc10/0xe10 (RX) and 0xccc/0xcd4/0xecc/0xed4
(TX), or default 0x200/0x0 for paths that didn't converge.
Helpers intentionally omitted (out-of-scope or not reachable from
devourer's monitor-mode init path): FW-offload IQK, per-channel
`iqk_matrix_reg_setting[]` cache, VDF/VHT-160, LC calibration, DPK.
**Opt-in via env, not default-on.** Full matrix at ch36 surfaced a
regression: with IQK enabled at init, 8812AU TX at 5GHz stops
reaching kernel receivers (8812-dev → 8821/8814-ker drops from
6500/6500 to 0/6500). Devourer-vs-kernel canary at ch6 IS clean
(BB 0x8b0 = 0x642 matches kernel byte-for-byte) and on-air TX still
works at 2.4GHz, but the 5GHz regression blocks default-on. Root
cause not yet isolated — likely a register that survives the
IQK MAC/BB/RF/AFE backup-restore cycle but disturbs the 5G TX
chain in a way that doesn't surface in kernel-side use. Code lands
behind env flags so the canary-parity work is preserved + the IQK
plumbing is available for root-cause investigation; defaults match
master (no behaviour change):
- DEVOURER_ENABLE_IQK=1: enable the existing `_needIQK` trigger
surface (init + band switch).
- DEVOURER_FORCE_IQK=1: run IQK on every channel-set (implies
enable; for canary-diff workflow against kernel).
Wiring (gated by `iqk_enabled` env check in
`phy_SwChnlAndSetBwMode8812`):
- `HalModule::rtl8812au_hal_init` arms `_needIQK = true` post
RF-config via new `RadioManagementModule::ArmIQKOnNextChannelSet`.
- `phy_SwBand8812` arms `_needIQK = true` whenever band transitions.
- Channel-set callback runs `_iqk.Calibrate()` only when the env
is set.
Verification:
- DEVOURER_ENABLE_IQK=1 at ch6: BB 0x8b0 = 0x00000642 matches
kernel canary byte-for-byte (was 0x00000618 pre-port).
- Default (no env): identical canary + matrix to PR #65 baseline.
- Full matrix ch6 + ch100 unchanged from PR #65.
- IQK convergence log: TX A_done=1 B_done=1, RX A_done=1 B_done=1
with 0-1 retries observed on RTL8812AU.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause of the 5GHz TX regression flagged in the parent commit.
Upstream's `_iqk_tx_8812a` reads BB 0xd00/0xd40 IQK output via:
TX_IQC_temp[i][0] = odm_get_bb_reg(dm, R_0xd00, 0x07ff0000) << 21;
`odm_get_bb_reg(dm, addr, mask)` **right-shifts by the mask offset**
before returning — so the 11-bit IQK value at bits 16:26 of 0xd00
comes back at bits 0:10 (range 0..0x7FF). The `<< 21` then encodes
it as a signed 11-bit value in the top 11 bits of an int (bits 21:31),
which the dx/dy convergence test later sign-extends back via `>> 21`.
My port skipped the shift-down step:
TX_IQC_temp[i][0] = (rtw_read32(0xd00) & 0x07ff0000) << 21;
`& 0x07ff0000` leaves the value at bits 16:26, then `<< 21` tries
to shift to bits 37:47 — UB in 32-bit int, in practice yields 0.
All eight TX/RX read sites had the same bug. Result: every IQK
measurement was silently zero; the ±4 convergence test trivially
"succeeded" with both samples == 0; the IQK fill wrote X=0 (instead
of either the calibrated value or the default X=0x200) to
0xccc/0xcd4/0xecc/0xed4 + 0xc10/0xe10. On 2.4GHz this happened to
be tolerable but at 5GHz the invalid IQ correction killed devourer's
TX path on the 2T2R 8812AU — matrix at ch36 went from 6500/6500 to
0/6500 on 8812-dev → kernel-RX cells (parent commit's regression
note).
Fix: insert the missing `>> 16` so the mask extract matches
`odm_get_bb_reg`'s contract. The net shift is now `<< 5`, equivalent
to upstream's `>> 16` then `<< 21`.
The env-gating from the parent commit is left in place pending
hardware confirmation that the fix closes the regression. Once
verified, the trigger can flip back to default-on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mask-extract bug fixed in the previous commit (b4b1038) was the root cause of the 5GHz TX regression that had forced IQK behind `DEVOURER_ENABLE_IQK=1` env opt-in. Re-verified post-fix on RTL8812AU hardware: ch36 8812-dev → 8821-ker (IQK on) = 6534/6500 ✓ ch36 8821-ker → 8812-dev (IQK on) = 100/444 ✓ (Pre-fix had been 0/6500 ✗ for the dev→ker direction.) The dev-dev cell at ch36 remains 0/6500 — pre-existing on master, bisect against 5b7fc12 produced identical numbers; not introduced by this PR. Flips the trigger condition back to plain `_needIQK` so init + band switches run IQK by default. Kept `DEVOURER_DISABLE_IQK=1` as an emergency escape hatch and `DEVOURER_FORCE_IQK=1` for canary-diff workflow (runs IQK on every channel-set, not just band changes). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
josephnef
added a commit
that referenced
this pull request
Jun 2, 2026
…DPK outputs) (#67) ## 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 = 0` → `phy_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 - [x] 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. - [x] Single-cell at ch36: `8812-dev → 8821-ker = 6528/6500 ✓` (was `0/6500 ✗` pre-fix). - [x] Partial matrix at ch36: `8812-dev → 8814-ker = 6182/6500 ✓` (was `0/6500 ✗` pre-fix). - [x] Full matrix at ch6: `22/24 ✓` identical to PR #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](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <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.
Summary
Port of upstream phydm's IQK (
phy_iq_calibrate_8812a→_iqk_tx_8812a) for RTL8812AU. Closes the IQK side of T1 canary divergence vsaircrack-ng/88XXau:0x00000642matches kernel byte-for-byte (was0x00000618pre-port — IQK side effect we'd been mis-attributing as static init drift).0xc10,0xccc,0xcd4,0xe10,0xecc,0xed4) now get calibrated values; previously held BB-init seeds.Wiring
HalModule::rtl8812au_hal_initarms_needIQK = truepost-RF-config via newRadioManagementModule::ArmIQKOnNextChannelSet.phy_SwBand8812arms_needIQK = trueon band transitions (2.4G ↔ 5G).phy_SwChnlAndSetBwMode8812calls_iqk.Calibrate()when_needIQKis set.DEVOURER_FORCE_IQK=1— run IQK on every channel-set (canary-diff workflow).DEVOURER_DISABLE_IQK=1— emergency escape hatch.Scope
In:
_phy_iq_calibrate_8812aend-to-end (backup → run → restore),_iqk_tx_8812aTX-tone + RX-tone calibration loops with ±4 averaging convergence over up to 10 cal iterations per path,_iqk_backup_*/_iqk_restore_*for MAC/BB/RF/AFE state,_iqk_configure_mac_8812a,_iqk_tx_fill_iqc_8812a/_iqk_rx_fill_iqc_8812a.Out (not reachable from devourer's monitor-mode init): FW-offload IQK (no H2C mailbox), per-channel
iqk_matrix_reg_setting[]cache, VDF/VHT-160, LC calibration, DPK (_phy_dp_calibrate_8812a, separate ~700 LOC; that's where BB 0xc90 and residual RF[A/B] 0x00 divergence come from).Bug found during validation: 5GHz TX regression
First matrix run with IQK enabled surfaced a regression — 8812AU TX at ch36 dropped from
6500/6500 ✓to0/6500 ✗. Root cause was a subtle porting bug in commitb4b1038:Upstream uses
odm_get_bb_reg(dm, 0xd00, 0x07ff0000) << 21to read the 11-bit IQK output.odm_get_bb_regright-shifts by the mask offset (16) before returning — devourer'sphy_query_bb_regmatches this contract, but my naive port used(rtw_read32(0xd00) & 0x07ff0000) << 21which leaves the value at bits 16:26, then shifts to positions 37:47 — UB in 32-bit int, in practice yields 0. All 8 TX/RX read sites had the bug → every IQK sample was 0 → convergence "succeeded" with both samples == 0 → FillIQC wrote X=0 instead of either calibrated or default X=0x200 → 5GHz TX broken.Fix: insert the missing
>> 16to matchodm_get_bb_reg's shift-down semantics. The lesson is now captured in kaeru (Realtek phydm porting trap — odm_get_bb_reg returns shifted-down value).Test plan
Full matrix in VM mode across 3 channels with IQK default-on:
8812-dev → 8821-ker = 6440/6500 ✓,8812-dev → 8814-ker = 5870/6500 ✓(vs0/6500pre-fix). All other cells match master baseline. Pre-existing failures unchanged (8814 TX-gate, 5G dev-dev gap, 5G UNII-1 8814 RX).DEVOURER_DUMP_CANARY=1at ch6:BB 0x8b0 = 0x642matches kernel byte-for-byte.Commits
6027fe9Initial IQK port (env-gated due to regression).b4b1038Fix mask-extract bug — root cause of 5GHz TX regression.af97973Flip IQK trigger back to default-on after fix verified.🤖 Generated with Claude Code