From 50bf6954c6025cc31322b3df8552575dcda8dfd3 Mon Sep 17 00:00:00 2001 From: Joseph Albert Nefario Date: Fri, 22 May 2026 14:36:23 +0300 Subject: [PATCH] RTL8814AU: port PHY_SetTxPowerIndex_8814A (BB 0x1998 packed write) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8814AU uses a fundamentally different TX-power-table register layout than 8812: a single packed DWord write to BB register 0x1998 per (path, rate), where the value encodes RF-path, rate code, and power index. The 8812 path writes to per-rate per-byte registers (rTxAGC_A_CCK11_CCK1_JAguar, rTxAGC_A_Ofdm18_Ofdm6_JAguar, etc.) that don't exist on 8814; those writes scribbled random bits and stalled the chip's BB on each one, which is why the full TX-power loop took ~6 minutes to complete on 8814 — and why an earlier workaround skipped it entirely on 8814 in monitor mode. This change ports PHY_SetTxPowerIndex_8814A from upstream hal/rtl8814a/rtl8814a_phycfg.c:743 verbatim: txagc_table_wd[31:24] = PowerIndex txagc_table_wd[15:8] = RFPath txagc_table_wd[7:0] = MRateToHwRate(Rate) txagc_table_wd |= 0x00801000 (TXAGC table-write enable + addr) phy_set_bb_reg(0x1998, bMaskDWord, txagc_table_wd); if (Rate == MGN_1M) phy_set_bb_reg(...); // first time turns on table Branched at the top of PHY_SetTxPowerIndex_8812A on CHIP_8814A. The existing 8812 register fanout below stays untouched. Also extends phy_set_tx_power_level_by_path to cover HT_MCS16_MCS23 and VHT_3SSMCS0_3SSMCS9 for 8814 — upstream PHY_SetTxPowerLevel8814 iterates all sections so the TXAGC table is fully populated even though the USB-2 link can't sustain 3-SS rates. The previous "skip TX power on 8814 in monitor mode" workaround is removed; gating the loop with DEVOURER_SKIP_TXPWR=1 remains as an escape hatch. Verified on CF-938AC (0bda:8813, channel 6): - Init completes in ~5s (vs the prior 6-minute stall). - 8814 RX still works end-to-end (10+ packets received in demo window). Note: this does NOT fix end-to-end 8814 TX. bulk OUT EP 0x02 still times out (rc=-7) post-init for an independent reason — chip-init puts the USB controller in a state where EP 0x02 doesn't drain, even though TX power is now correctly programmed. That gate is tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/RadioManagementModule.cpp | 69 +++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/src/RadioManagementModule.cpp b/src/RadioManagementModule.cpp index df2adbb..f5a6ccb 100644 --- a/src/RadioManagementModule.cpp +++ b/src/RadioManagementModule.cpp @@ -2,6 +2,10 @@ #include "Hal8812PhyReg.h" #include "registry_priv.h" +extern "C" { +#include "ieee80211_radiotap.h" +} + #include #include #include @@ -237,19 +241,18 @@ void RadioManagementModule::phy_SwChnlAndSetBwMode8812() { phy_PostSetBwMode8812(); _setChannelBw = false; } - /* For 8814AU specifically, TX power setup iterates 4 RF paths × - * ~8 rate sections, totaling ~370 PHY_SetTxPowerIndex calls and - * roughly 20–30 seconds of control transfers. The values are only - * consumed by the TX path, so monitor-mode RX gains nothing from - * doing the work. Set DEVOURER_FORCE_TXPWR=1 to enable the full - * loop (required if you actually want to TX from an 8814 build — - * TX path is not yet end-to-end validated). */ - if (_eepromManager->version_id.ICType != CHIP_8814A || - std::getenv("DEVOURER_FORCE_TXPWR")) { - PHY_SetTxPowerLevel8812(_currentChannel); + /* 8814A uses a packed single-DWord write to BB 0x1998 per (path,rate) + * instead of the 8812's per-rate per-byte register fanout (see the + * CHIP_8814A branch at the top of PHY_SetTxPowerIndex_8812A). The + * earlier "skip TX power on 8814" workaround was a symptom of the + * 8812 register layout being wrong for 8814 — every write hit the + * wrong bits and the chip's BB stalled on each one. Setting + * DEVOURER_SKIP_TXPWR=1 keeps the old skip behaviour as an escape + * hatch (e.g. for RX-only experiments). */ + if (std::getenv("DEVOURER_SKIP_TXPWR")) { + _logger->info("DEVOURER_SKIP_TXPWR=1 — skipping TX power setup"); } else { - _logger->info("8814A: skipping TX power setup (monitor-mode RX-only; " - "set DEVOURER_FORCE_TXPWR=1 to enable)"); + PHY_SetTxPowerLevel8812(_currentChannel); } _needIQK = false; @@ -1145,6 +1148,15 @@ void RadioManagementModule::phy_set_tx_power_level_by_path(uint8_t channel, phy_set_tx_power_index_by_rate_section(path, channel, RATE_SECTION::VHT_2SSMCS0_2SSMCS9); } + /* 8814A 3-stream rate sections — must be programmed so the chip's TXAGC + * table is fully populated even though the USB-2 link can't sustain 3-SS + * data rates. Upstream PHY_SetTxPowerLevel8814 iterates all sections. */ + if (_eepromManager->version_id.ICType == CHIP_8814A) { + phy_set_tx_power_index_by_rate_section(path, channel, + RATE_SECTION::HT_MCS16_MCS23); + phy_set_tx_power_index_by_rate_section(path, channel, + RATE_SECTION::VHT_3SSMCS0_3SSMCS9); + } } const static std::vector mgn_rates_cck = { @@ -1229,11 +1241,38 @@ void RadioManagementModule::PHY_SetTxPowerIndex_8812A(uint32_t powerIndex, MGN_RATE rate) { _logger->debug("PHY_SetTxPowerIndex {} {} {}", powerIndex, (int)rfPath, rate); + + /* 8814A: per-rate per-path power index is programmed via a single packed + * BB-register write at 0x1998. Port of PHY_SetTxPowerIndex_8814A from + * upstream hal/rtl8814a/rtl8814a_phycfg.c:743. + * + * txagc_table_wd[31:24] = PowerIndex + * txagc_table_wd[15:8] = RFPath + * txagc_table_wd[7:0] = MRateToHwRate(Rate) + * txagc_table_wd |= 0x00801000 (TXAGC table-write enable + addr) + * + * The 8812 per-rate fanout below uses register addresses (rTxAGC_A_CCK_* + * etc.) that don't exist on 8814 — using it on 8814 scribbles random bits + * and stalls the BB; that's what the earlier "skip TX power for monitor + * mode" workaround was masking. */ + if (_eepromManager->version_id.ICType == CHIP_8814A) { + uint32_t txagc_table_wd = + 0x00801000u | + (static_cast(rfPath) << 8) | + static_cast(MRateToHwRate(static_cast(rate))) | + (powerIndex << 24); + _device.phy_set_bb_reg(0x1998, bMaskDWord, txagc_table_wd); + if (rate == MGN_1M) { + /* Upstream comment: "first time to turn on the txagc table". */ + _device.phy_set_bb_reg(0x1998, bMaskDWord, txagc_table_wd); + } + return; + } + /* The per-rate register table below only encodes paths A/B (8812-family). * 8814AU paths C/D use a different per-path register layout (the rTxAGC_C_ - * and rTxAGC_D_ symbol family in Hal8814PhyReg.h) not yet wired here. For - * now, silently skip C/D so the bring-up trace isn't flooded with errors; - * follow-up Phase-4 work will extend the table. */ + * and rTxAGC_D_ symbol family in Hal8814PhyReg.h) — that's handled by the + * CHIP_8814A branch above. */ if (static_cast(rfPath) >= RF_PATH_C) { return; }