{{ message }}
Jaguar2: RTL8822BU (8822B) userspace driver — dual-band RX + on-air TX#157
Merged
Conversation
First milestone of the RTL8822BU (chip 8822B, 2T2R USB, "Jaguar2") port.
Scaffold only: the orchestrator constructs but bring-up is not yet wired (data
paths throw/return so premature use is loud). Bring-up sub-modules (HalMAC
fw/mac-init, phydm tables, halrf IQK, radio management, frame parser) land in
later milestones.
- New src/jaguar2/RtlJaguar2Device.{h,cpp}: IRtlDevice orchestrator stub.
- Promote src/jaguar1/PhyTableLoader.{h,cpp} -> src/ (shared): 8822B uses the
same check_positive phydm table format as Jaguar1, so the walker becomes a
generation-neutral core file (src/ is already on the include path; jaguar1
builds unchanged).
- CMake: DEVOURER_JAGUAR2 option (default ON), no-chip-selected guard, source
group + DEVOURER_HAVE_JAGUAR2 PUBLIC define.
- WiFiDriver factory: guarded Jaguar2 dispatch branch on a placeholder chip-id
(kChipId8822B = 0x0F; real SYS_CFG2 value determined on hardware in M2, so no
real chip is misrouted meanwhile).
- demo/txdemo: RTL8822BU PIDs (0xb812/0xb82c); OEM VIDs via DEVOURER_VID/PID.
- CI: 8822b-only build-config row; -DDEVOURER_JAGUAR2=OFF on the other
single-chip rows and the reject-"no chip" check.
Gate: full build + every single-chip config (incl. -DDEVOURER_JAGUAR2=ON alone)
+ ctest green; no-chip-selected still rejected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Vendor the 8822B phydm table sources (provenance for the generated tables):
hal/phydm/rtl8822b/halhwimg8822b_{bb,mac}.c and
hal/phydm/halrf/rtl8822b/halhwimg8822b_rf.c (from OpenHD/rtl88x2bu).
- tools/extract_8822c_phy_tables.py: add an 8822b entry (the extractor is
format-agnostic — it lifts array bodies). Generate
hal/phydm/rtl8822b/Hal8822b_PhyTables.{c,h}: 16 arrays incl. phy_reg,
agc_tab, radioa/b, mac_reg, phy_reg_pg[+type variants]. These are the OLD
check_positive format, walked by the shared src/PhyTableLoader.
- tools/extract_8822b_fw.py + generated hal/hal8822b_fw.{c,h}: NIC firmware
blob (array_mp_8822b_fw_nic, 161240 bytes), mirroring the 8822c/8822e fw
vendoring; length accessor normalized to _len.
- src/jaguar2/PhyTableLoaderJaguar2.{h,cpp}: thin glue naming the generated
arrays and driving PhyTableLoader::Load (HalJaguar2 supplies the context +
register writer in M4).
- CMake: link the generated tables + fw + glue into the DEVOURER_JAGUAR2 group;
add hal/phydm/rtl8822b to the include path.
Gate: full + jaguar2-only build link the tables/fw; ctest green; the shared
check_positive walker parses the 8822B tables (phy_reg -> 1492 writes, first
(0x800,0x9020d010) matches the raw head; radioa -> 412 writes after cut/rfe
conditional filtering), confirming table-format compatibility.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Read the 8822B SYS_CFG2 (0x00FC) chip-id off hardware: a TP-Link Archer T3U (RTL8822BU) reports 0x50 (SYS_CFG1 0x0c492537, bit27=1 -> 2T2R). Validated the probe against the known 8822C on the same bus (reads 0x13). Replace the M0 placeholder kChipId8822B (0x0F) with 0x50; it does not collide with the Jaguar1 (0x04/05/08/09) or Jaguar3 (0x13/0x17) ids. Verified on hardware: a plugged 8822B now routes to RtlJaguar2Device (whose Init stub throws the "port in progress" message) instead of falling through to the Jaguar1 path and crashing on an incompatible register read. Remaining M2: port HalJaguar2 power_on/off (halmac 8822b pwr_seq) + read_chip_version, and validate power-on on hardware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the HalMAC 8822B power sequence and chip-version read, modeled on src/jaguar3/HalJaguar3. - HalJaguar2 power_on()/power_off(): PwrCfg tables transcribed from halmac_pwr_seq_8822b.c (card_en_flow / card_dis_flow), USB + ALL-interface entries only (SDIO/PCI-only steps dropped), incl. the cut-C 0x10A8..0x10AA writes (production RTL8822BU is C-cut) and a DELAY step. Executor supports WRITE (byte RMW) / POLL / DELAY / END; power_on runs power_off first to reset from a kernel-left active state (same pattern as Jaguar3). - read_chip_version(): decode REG_SYS_CFG1 (0x00F0) — cut / vendor / 2T2R. - RtlJaguar2Device::Init now runs power_on() + read_chip_version() then reports RX-not-yet-implemented, so power-on is exercisable end to end. Build: full + jaguar2-only green; ctest green. Hardware validation on the T3U is pending a replug — the adapter wedged off-bus during chip-id probing (the known Realtek "needs a VBUS power-cycle" state) and hasn't re-enumerated; the power-on path will be confirmed on hardware once it returns. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hardware validation on the Archer T3U (via uhubctl power-cycle,
lkl-wifi-poc/scripts/recover-chip.sh) showed REG_SYS_CFG2 (0x00FC) reads 0x0a in
steady state (and in the demo's post-libusb_reset path), not 0x50 — the 0x50
seen earlier was a cold-boot transient in the brief window right after a VBUS
power-cycle. Dispatch now matches both (is_8822b_chip_id): 0x0a primary, 0x50
cold transient; neither collides with Jaguar1/Jaguar3 ids.
Validated end-to-end: the T3U routes to RtlJaguar2Device, power_on() completes
("card active", both MAC polls satisfied), read_chip_version() decodes 8822B
2T2R. M2 power-on gate met.
NB (for M4): SYS_CFG1's low 16 bits are volatile on 8822B (cut field reads 2 or
3 across reads), so the cut used by the check_positive table walker needs a
stable source (halmac get_chip_info / a de-glitched read) before BB/AGC/RF
tables are applied.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the firmware-download (DLFW) state machine + the 88xx TX/RX descriptor,
adapted from src/jaguar3 (the halmac_88xx DLFW code is byte-identical between the
rtl88x2bu and rtl88x2cu trees, and the DLFW register addresses match, so this is
a faithful sibling with the ChipVariant dispatch removed — 8822B is single-chip):
- HalmacJaguar2Fw.{h,cpp}: download_firmware/start_dlfw/dlfw_to_mem/iddma/
check_fw_chksum/dlfw_end_flow/wlan_cpu_en/pltfm_reset + the send_fw_page
rsvd-page transport (build an 88xx TX descriptor for the chunk, bulk-OUT to
the HIQ reserved page). Boot success = REG_MCUFW_CTRL == 0xC078. Uses the
bundled hal8822b_fw NIC blob.
- HalmacJaguar2Regs.h: the common-88xx DLFW register/bit/fw-header constants,
verified identical to the Jaguar3 set (MCUFW_CTRL 0x80, DDMA_CH0SA 0x1200,
CPU_DMEM_CON 0x1080, FIFOPAGE_CTRL_2 0x0204, ...).
- FrameParserJaguar2.h: the 88xx TX(48)/RX(24) descriptor, _8822B-suffixed
macros + cal_txdesc_chksum_8822b + fill_data_tx_desc_8822b + parse_rx_8822b.
Build: full + jaguar2-only green; ctest green. On-hardware DLFW boot validation
is deferred to M4 — the rsvd-page download needs the pre-DLFW MAC config
(init_system_cfg: DDMA enable, SYS_FUNC_EN) and the queue/page allocation
(_rsvd_boundary), which land with HalmacJaguar2MacInit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the pre-DLFW system config and wire the bring-up through firmware download.
- HalmacJaguar2MacInit.{h,cpp}: pre_init_system_cfg (pinmux/LED/GPIO, BB-RF
off), init_system_cfg (DDMA enable, SYS_FUNC_EN, PHY_REQ_DELAY, disable
boot-from-flash), enable_bb_rf — ported from HalmacJaguar3MacInit (88xx-common
registers). #undef the hal_com_reg.h macros that collide with the scoped
constexpr register names.
- RtlJaguar2Device::Init now orchestrates pre_init_system_cfg -> power_on ->
read_chip_version -> init_system_cfg -> download_default_firmware, matching the
HalJaguar3 order.
- HalmacJaguar2Fw: _dlfw_pkt_size 4096 -> 2048 (rsvd-page staging is
DLFW_RSVDPG_SIZE=2048).
Hardware (Archer T3U): the full sequence runs — pre-init, power-on ("card
active"), chip-version (8822B 2T2R), init_system_cfg all succeed. DLFW then
stalls on the rsvd-page bulk-OUT: the chip accepts exactly 2048 bytes then NAKs
(rc=-7 timeout) regardless of chunk size, i.e. the download-queue TX FIFO isn't
draining. This is an endpoint / FIFO-page-allocation gap (the download_firmware
FIFOPAGE/RQPN values are 8822C-derived; 8822B's TX FIFO page layout differs).
Next: usbmon-diff the DLFW USB sequence vs the kernel rtl88x2bu driver to pin the
endpoint + page config (the planned M3 de-risking).
Build: full + jaguar2-only green; ctest green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…LFW FIFO stall) Port init_trx_cfg + priority_queue_cfg + init_h2c (halmac 88xx; 8822B page numbers verified identical to 8822C: TX_FIFO 262144, HQ/NQ/LQ 64/64/64, rsvd_boundary 1938) and run init_trx_cfg BEFORE firmware download, passing the computed rsvd_boundary to HalmacJaguar2Fw. Hardware (Archer T3U): this FIXES the DLFW rsvd-page bulk-OUT stall — the chip no longer NAKs at 2048 bytes (the download queue now has TX-FIFO pages). Root cause confirmed: download_firmware only re-triggers the existing RQPN, so the queue page allocation must run first (8822C's power-on defaults happened to suffice; 8822B's do not). DLFW now progresses past the FIFO stall to the rsvd-page completion poll, which does not yet latch bcn-valid (packet not landing on the beacon page → IDDMA copies stale data → checksum fail). Faithful-port gap found for next step: halmac send_fwpkt_88xx appends a dummy byte when (chunk + 48) is a multiple of 512 (USB exact-maxpacket-boundary workaround) — devourer's send_fw_page omits it. Next: implement that quirk + verify the beacon/HIQ bulk-OUT endpoint vs a kernel usbmon capture. Build: full + jaguar2-only green; ctest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two 8822B-specific corrections vs the 8822C values previously ported: - init_system_cfg: set ONLY BIT_WL_PLATFORM_RST in REG_CPU_DMEM_CON (not BIT_DDMA_EN), and do NOT write PHY_REQ_DELAY — matching init_system_cfg_8822b (halmac_init_8822b.c). The 8822C DDMA_EN/PHY_REQ_DELAY writes are wrong for 8822B. - init_trx_cfg(set_bcn_boundary): run the page allocation + LLT before DLFW (8822B needs it for the rsvd-page TX FIFO to drain — unlike 8822C) but skip the beacon-boundary writes then (applied later in full MAC init). Hardware (Archer T3U): the DLFW rsvd-page bulk-OUT now DRAINS (no more 2048-byte NAK). Remaining: the rsvd-page beacon-download completion (bcn-valid) still does not latch — narrowed to either the beacon/HIQ bulk-OUT endpoint (devourer uses first_bulk_out_ep()=EP5; may differ from 8822B's beacon pipe) or the send_fwpkt_88xx +1 dummy-byte USB quirk. Confirming needs a working-driver usbmon capture (host rtw88_8822bu also fails DLFW on kernel 6.18; vendor-driver VM currently down). Build: full + jaguar2-only green; ctest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Five genuine bring-up fixes for the RTL8812BU/8822BU firmware download, verified against usbmon goldens (vendor rtl88x2bu at USB high-speed and in-tree rtw88_8822bu at SuperSpeed) on a TP-Link Archer T3U: - pltfm_reset: add the 8822B/8821C clock-sync toggle (SYS_CLK_CTRL+1 BIT6 clear/set bracketing the CPU_DMEM_CON reset), matching pltfm_reset_88xx. Was missing. - _dlfw_pkt_size 2048 -> 4096: 2048 was the free/EMEM rsvd-page size; the normal download uses 4096-byte chunks (4144-byte bulk). - dlfw_to_mem: use w32_set (RMW) on CH0CTRL RESET_CHKSUM_STS, matching HALMAC_REG_W32_SET (preserves residual DDMA bits across segments). - Order: download firmware BEFORE trx/queue config, matching the HalMAC _halmac_init_hal flow and the working jaguar3 bring-up. Running init_trx first over-allocated the FIFOPAGE_INFO queues. - send_fw_page: minimal rsvd-page TX descriptor (TXPKTSIZE + OFFSET + QSEL_BEACON), matching the rtl88x2bu golden. With these, the DMEM segment downloads fully with a valid checksum. Remaining blocker (M3): the first IMEM-segment chunk's bulk-OUT NAKs at 2048/4144 at USB SuperSpeed — the beacon rsvd-page depletes across the segment boundary. Register sequence is byte-identical to both working goldens; the kernel does the same DLFW at SuperSpeed successfully, so the wall is a libusb-vs-kernel bulk interaction at the segment boundary, still under investigation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port send_fwpkt_88xx's USB dummy-byte quirk: when the rsvd-page bulk-OUT frame (48-byte TX desc + payload) is an exact multiple of the USB bulk max-packet size, append one dummy byte so a short packet terminates the transfer. Without it the chip's bulk controller waits for a transfer-end signal that never arrives and the following chunk's bulk-OUT wedges. The firmware DMEM segment's last chunk is 3024+48 = 3072 = 6*512, which hit this exactly — the bulk now goes out as 3073 bytes, matching both the vendor rtl88x2bu and in-tree rtw88_8822bu goldens. Also trims the rsvd-page TX descriptor to the 8822B download set (TXPKTSIZE + OFFSET + QSEL_BEACON), matching those goldens. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The firmware download now completes and the MCU boots (REG_MCUFW_CTRL == 0xC078) on real hardware at USB SuperSpeed (TP-Link Archer T3U), for both the DMEM and IMEM segments. Root cause of the prior wall: for a rsvd-page chunk whose (TX desc + payload) length is an exact multiple of the USB bulk max-packet size, the previous code appended a single trailing dummy byte. That broke the exact-multiple (so a short packet terminated the bulk) but did NOT tell the chip about the extra byte, so the beacon-FIFO write pointer drifted by one and the next segment's first chunk was parsed from a misaligned descriptor — its bcn-valid latch never asserted and the download wedged. Fix ports usb_write_data_not_xmitframe (rtl8822bu_halmac.c) verbatim: insert PACKET_OFFSET_SZ (8) bytes between the descriptor and the payload, set the descriptor OFFSET to desc+8 and PKT_OFFSET=1 so the chip accounts for them, and advance the iddma source past the packet-offset so the DDMA copies the true payload (otherwise its checksum sees the pad and fails). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port the 8822B init_mac_cfg flow (halmac init_mac_cfg_88xx +
init_{protocol,edca,wmac}_cfg_8822b) and init_usb_cfg_88xx (USB RX-DMA
mode + RX aggregation), and run them after the firmware boots. Verified on
the Archer T3U: LLT auto-init completes (rsvd_boundary=1938), the MAC
protocol/edca/wmac registers and USB RX-DMA config apply, and BB/RF is
enabled — all without wedging the chip.
PHY (BB/AGC/RF) table application and RX bring-up land next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Apply the 8822B BB (phy_reg), AGC (agc_tab) and RF (radioa/radiob) phydm tables via the shared check_positive walker, in the rtl8822b_phy.c order: config_phydm_parameter_init PRE (disable OFDM/CCK, 0x808[29:28]=0) -> init_bb_reg -> init_rf_reg -> POST (enable, =0x3). Writers ported from the Jaguar phydm path: - BB/AGC: phydm delay opcodes (0xfe..0xf9) else a full-dword BB register write (phy_set_bb_reg) + 1us settle. - RF: Jaguar 3-wire LSSI write (rA/rB_LSSIWrite_Jaguar 0xC90/0xE90), data = (addr<<20)|(val&0xfffff) masked to 28 bits — confirmed 8822B uses the LSSI 3-wire offset (rtl8822b_phy.c). EFUSE rfe_type read via the standard 88xx OneByteRead + 2-byte extended header decode (EEPROM_RFE_OPTION_8822B = 0xCA). Verified on the Archer T3U: after DLFW boot the BB (2984+21368 words) and RF (10638+9234 words) tables apply with no chip wedge. NB: the efuse rfe byte reads 0xff on this unit and needs a default/fix before RX (the tables are rfe-gated) — handled with channel-set + RX bring-up next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match Hal_ReadRFEType_8822b: a blank EFUSE RFE byte (0xFF) falls back to rfe_type 0. The Archer T3U ships blank; BB/AGC/RF tables now apply with rfe_type=0 (the correct gated block set) instead of 0xff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port config_phydm_switch_channel_8822b + config_phydm_switch_bandwidth_8822b into HalJaguar2::set_channel_bw: RF18 read-modify (band/BW/channel), band AGC-table index (0x958), fc for CFO tracking (0x860), CCK TX filter (0xa24/ 0xa28), RF 0xbe phase-noise, RFE antenna pins (phydm_rfe_ifem, rfe_type 0), RF-path toggle (0x808) + IGI toggle. Adds the Jaguar 3-wire LSSI RF read (rHSSIRead 0x8B0 -> SI/PI readback 0xD08/0xD48) + masked RF write. enable_rx: CR=0x06FF (MACRXEN + full MAC, matching jaguar3) + promiscuous RCR for monitor. RtlJaguar2Device::Init now runs the full chain and enters an async bulk-IN RX loop (parse_rx_8822b -> packet processor). On the Archer T3U the whole chain runs end-to-end without wedging: DLFW boot -> MAC cfg -> BB/AGC/RF tables -> channel ch6 (rf18=0x10d06) -> RX enable -> RX loop. RX currently delivers 0 frames (chip pushes no bulk-IN data) — the RF-deaf tuning (RX-mode / AGC / IQK / bb_reset) is the next step, same gate the jaguar3 port worked through. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port config_phydm_trx_mode_8822b: RF mode table (0xc08/0xe08=0x3231), antenna-path HW-block enable, phydm_config_rx_path_8822b (0x808 RX enable, CCK/MRC/antenna-weighting), and the RF 0xef/0x33/0x3e/0x3f mode-table sync. Called before the channel set. (CCK/OFDM TX-logic-map sub-config is deferred to the TX milestone.) Full 8822B bring-up now runs end-to-end on the T3U without wedge: DLFW -> MAC -> BB/AGC/RF -> trx_mode -> channel -> RX enable -> RX loop. RX still delivers 0 frames: the RF front-end receives nothing, which points to the missing halrf calibration (LCK/IQK) — the LO/RF needs calibration to lock and receive (the same "RX gap is stale IQK" gate the jaguar3 port hit). That is milestone M5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port _phy_lc_calibrate_8822b (halrf_8822b.c) + aac_check_8822b: lock the RF LO tank at the current channel after the channel set. Runs on hardware (RF CHNLBW backup/restore, RTK disable/enable, LCK poll) without wedge. RX still delivers 0 frames after LCK, so the RF-deaf cause is deeper than LO lock — needs the MAC-RX-counter check (to split RF-deaf vs USB RX-DMA delivery) and likely IQK. Continues in M5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Breakthrough on the RX-deaf gate: the BB/RF path works. A BB-counter diagnostic (0xf04/0xf08/0xf14/0xf48) showed the chip receives energy and, once the initial gain (IGI) is raised, cleanly decodes real OFDM frames (CRC-OK > 0). The default IGI from the AGC table is too low with no DIG thread running, so the RX drowned in false alarms (~4000/s); forcing IGI=0x40 drops FA ~7x and yields clean CRC-OK frames. Changes: - enable_rx: interim fixed IGI=0x40 (0xc50/0xe50) until the phydm DIG thread is ported; RCR set to the working kernel value (0xf410400e) + AAP for monitor promiscuity. - init_usb_cfg: RX-DMA aggregation threshold/timeout matched to the kernel (fast flush) instead of the jaguar3 values. Remaining M4/M5 gap: decoded CRC-OK frames still don't reach the bulk-IN endpoint (0 completions; URBs pending, chip pushes no RX data) — an RX-DMA->USB delivery issue distinct from decode, plus DIG for dynamic IGI and IQK for decode quality. Well-characterized for the next iteration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eds IQK) RX-DMA delivery diagnostics localized the remaining first-RX gap: with a sane IGI the BB decodes only 1-2 OFDM frames (CRC-OK) amid a still-high false-alarm rate, and REG_RXPKT_NUM (0x0284[31:24]) reads 0 — the MAC RX FIFO is empty, so no real MPDU is reaching the MAC. That is the classic uncalibrated-RX signature: the decode is too marginal without IQK, not an RX-DMA/aggregation stall (tested agg on/off and multiple thresholds — no effect on delivery). Restore init_usb_cfg's RX aggregation to the verbatim cfg_usb_rx_agg_88xx (agg enabled, SS size5/timeout0xA) and drop the temporary RX-FIFO probes. Next: port halrf IQK (do_iqk_8822b) — the decode-quality calibration that lets real frames resolve; then the phydm DIG thread to replace the interim fixed IGI. Firmware download, MAC/BB/RF bring-up, channel set and LCK all run correctly on the T3U. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8822B reads RF registers via a direct BB shadow window (base 0x2800 path A / 0x2c00 path B + rf_addr<<2), per config_phydm_read_rf_reg_8822b — not the 3-wire LSSI readback (0x8B0->0xD08) the legacy driver PHY_QueryRFReg uses. The 3-wire path returns stale data on 8822B, so every masked RF read-modify-write (rf_set) corrupted the register. This is the read mechanism all phydm/RF code (channel set, and the upcoming IQK) relies on. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New Halrf8822b module — the software IQK ported from halrf_iqk_8822b.c. This commit lands the foundation (compiles, not yet wired): - RF/BB access shims matching phydm (RF read = direct BB window 0x2800/0x2c00; RF write = LSSI 0xC90/0xE90; rf_set_check verify loop). - backup/restore of the IQK MAC(0x520,0x550)/BB(21)/RF(0xdf,0x8f,0x65, 0x0,0x1) register sets. - setup: afe_setting, bb_reset, agc_bnd_int, rfe_setting, rf_setting, configure_macbb. - per-K settings: lok/txk/rxk1/rxk2_setting, set_rf0x8 (2.4G/5G, per path). Next: the nctl (0x1b00 bank) tone-measurement loops (_iqk_iqk_by_path / _iqk_rx_iqk_by_path), the orchestrator (_phy_iq_calibrate_8822b), then wire iqk_trigger after the channel set and validate real frames resolve. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M5: complete the halrf IQK — nctl measurement one-shots (LOK/TXK/RXK), RXK gain-search machine, and the fresh-IQK orchestrator (backup/restore MAC/BB/RF, per-path step engine to iqk_step==7). Ported verbatim from reference/rtl88x2bu halrf_iqk_8822b.c. Per-K result logging surfaces LOK/TXK/RXK pass/fail (currently: LOK converges, TXK maxes retries). M4 RX bring-up, two faithful vendor fixes toward first delivery: - RCR (0x608): the frame-type-accept bits ADF/ACF/AMF (BIT11/12/13) were clear, so WMAC accepted the address (AAP) but dropped every frame type including beacons. Set the vendor monitor value 0x7000282F + ACF -> 0x7000382F (RCR_AAP|APM|AM|AB|APWRMGT|ADF|ACF|AMF|APP_PHYST|MIC|ICV). - coex_wlan_only: 8822B is a WiFi+BT combo; on power-up the antenna switch can be owned by BT, leaving the WL RX front-end deaf. Port ex_hal8822b_wifi_only_hw_config (WL-side controller, gnt_wl=1/gnt_bt=0, antenna mux to WL); run before enable_rx. Debug knobs: DEVOURER_SKIP_IQK, DEVOURER_RX_DEBUG (register dump), dbg_rf_read accessor. Register state now verifies correct (CR/RCR/RXpath/ RF-tune/RF-mode/coex) but RX still delivers 0 frames — remaining blocker needs a working-driver canary to localise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M4 RX: port the two config_phydm_switch_bandwidth_8822b tail steps that were missing from set_channel_bw: - phydm_rxdfirpar_by_bw_8822b (RX digital filter): BW20 = 0x948[29:28]=2, 0x94c[29:28]=2, 0xc20[31]=1, 0xe20[31]=1. The RX DFIR must match the bandwidth or the OFDM demod filter is wrong. - phydm_ccapar_by_rfe_8822b (CCA thresholds): rfe_type 0 (iFEM C-cut) 0x82c/0x830/0x838 from cca_ifem_ccut by 2G/5G x 1R/2R column. DEVOURER_RX_KEEP_CORRUPTED: accept CRC32/ICV-error frames (RCR ACRC32 BIT8 | AICV BIT9). Doubles as a delivery discriminator. Diagnosis (via the gated DEVOURER_RX_DEBUG MAC-RX poll): REG_RXPKT_NUM (0x0284[31:24]) stays 0 with RXDMA idle even with corrupt-accept on — so the BB delivers zero PSDUs to the MAC RX FIFO; the blocker is BB-decode / BB->MAC, not USB delivery. Full RX register chain now verifies faithful to the vendor (CR/RCR/RXpath/RF18/RF-mode/coex/DFIR/CCA/AGC tables) yet MAC gets nothing — needs a working-driver canary to localise further. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the interim fixed initial-gain (0xc50/0xe50 [6:0]) tunable via DEVOURER_IGI=0xNN (default 0x40) for RX bring-up experiments. Sweep result: IGI 0x20/0x24/0x2c/0x40 all leave REG_RXPKT_NUM=0 — decode does not depend on IGI, ruling it out as the RX blocker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add DEVOURER_SKIP_COEX to bypass coex_wlan_only for RX isolation. RX bring-up finding: even a -38 dBm AP (KArt2-Local on ch9, right next to the adapter, confirmed via an independent scan) produces zero RX — RXPKT_NUM stays 0 with or without the coex grant. So this is NOT marginal decode and NOT a dead-channel artifact (ch6 has no local AP; earlier 0-RX runs were on ch6). The RX signal path fundamentally does not deliver demodulated frames to the MAC despite a register config now verified 100% faithful to the vendor across the whole chain (RCR/filtermaps/cut/RFE(phydm_rfe_ifem)/ channel/bandwidth/DFIR/CCA/RF18/RF-mode/coex/IGI). FA counters track IGI, so RF->ADC->detector works; the failure is detector->demod->MAC. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add tests/jaguar2_rx_canary.sh: attaches the T3U to the pinned-kernel VM (devourer-testrig, 5.15) whose 88x2bu_ohd driver (== reference/rtl88x2bu) receives correctly, brings up monitor mode, dumps the WORKING MAC/BB/RF register state via rtw_proc, and returns the DUT to the host (trap cleanup). Add gated debug tooling to RtlJaguar2Device::Init: - DEVOURER_BB_DUMP: dump MAC(0x0-0x7ff)/BB(0x800-0xfff,0x1800-0x1aff)/RF registers in the vendor rtw_proc format for canary diffing. - DEVOURER_BB_PATCH=<file>: apply "0xADDR 0xVAL" overrides before RX to bisect which register(s) are decode-critical. Canary findings so far: kernel RX works at -35 dBm on ch9. Diffed vs devourer: patching all 79 differing BB registers to the kernel golden did NOT restore RX, and SKIP_IQK on ch9 is still 0 — so the blocker is NOT in the BB register file and NOT IQK-RF-corruption. Localized to RF and/or MAC (RF 0x08=0/0x18 residual bits/0x3f, MAR/RXDMA-agg MAC diffs) — bisect continues. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M4 RX works. Root cause found via the VM kernel canary (jaguar2_rx_canary.sh): the RF18 (RF_CHNLBW) register carried spurious bits 8 and 16 (devourer wrote 0x10d09 for ch9/20M vs the kernel's clean 0xc09). The RF radioA table leaves those bits set and set_channel_bw's read-modify-write preserved them; the extra bits detuned the synthesizer so the OFDM demod never locked — energy was detected (FA tracked IGI) but zero PSDUs reached the MAC RX FIFO. Fix: mask RF18 to its valid fields only — channel [7:0], RF-BW-mode [11:10], band-select [18:17] (rf18 &= 0x00060cff). Confirmed by forcing the kernel's RF register state via the canary BB_PATCH tool (RX came alive, 43 frames), then bisected to RF18 alone. Result on ch9 (-35 dBm AP): 33 frames / 32 USB reads, CRC-ok, and the IQK now fully converges (TXK 0/0, RXK gs 0/0) since the front-end is finally tuned correctly. Adds dbg_rf_write for the canary patch tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port a lean phydm DIG: a ~100 ms background thread (HalJaguar2::dig_step) reads the per-window false-alarm count (OFDM 0xf48 + CCK 0xa5c), resets the hold-type FA/CCA counters (0x9a4[17]/0xa2c[15]/0xb58[0]), and nudges IGI (0xc50/0xe50) per phydm phydm_new_igi_by_fa: fa>750 -> +2, fa>500 -> +1, fa<250 -> -2, clamped [0x1c, 0x3e]. Replaces the fixed IGI=0x40 that could not span the sensitivity range (0x40 misses weak APs, 0x20 = FA storm). Thread lifecycle: started after enable_rx, stopped on RX-loop exit / Stop() / dtor. DEVOURER_SKIP_DIG bypasses it; DEVOURER_RX_DEBUG logs IGI/FA. Result: DIG settles IGI to 0x1c with low FA (~135) in a clean band and brought up previously-dead channels (ch7/8/10/11 now RX). Remaining: lower 2.4G sub-band (ch1-6) still 0 even for a -28 dBm AP while upper (ch7-11) works — a separate channel-dependent RF issue (VCO/LCK per sub-band), to be canaried next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the arbitrary first-5-frames cap on the per-frame RX log; emit every frame's len/rate/crc when DEVOURER_RX_DEBUG is set (silent otherwise). Used to confirm CCK vs OFDM decode: on ch9 devourer decodes both (rate 0/1 CCK + rate 4 OFDM), so CCK RX is not broken. Finding: the remaining weak-channel gap is a ~20 dB sensitivity deficit vs the kernel (ch9 -40 dBm works, ch1 -62 dBm all-CCK fails though the kernel RXes it). The OFDM RX AGC gain registers 0x994-0x9a0 read 0xffff.. defaults in devourer vs the kernel's gain tables (0x4440412f/0x2824201c/..); the lean DIG only manages IGI. Full phydm AGC/rx-gain init is the next lever. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire the TX data path (chip accepts frames; RF-radiate still pending): - config_trx_mode: complete phydm_config_tx_path_8822b (CCK 0xa04[31:28], OFDM 0x93c[31:20]/0x940 TX logic map) — deferred at M4, now done. - Refactor the cold bring-up into a shared bring_up() used by Init (RX) and InitWrite (TX); InitWrite adds an optional flat TXAGC (set_tx_power_flat, 0x1d00/0x1d80, DEVOURER_TX_PWR) and readies the chip for monitor inject. - send_packet: radiotap parse (rate/bw/SGI/LDPC/STBC/MCS/VHT) + 8822B TX descriptor + synchronous bulk-OUT. Ported from RtlJaguar3Device::send_packet (shared halmac 88xx descriptor); rate-less frames use the SetTxMode default. - tests/jaguar2_tx_onair.sh: sniff the canonical beacon (SA 57:42:75:05:d6:00) on a host 8812au monitor adapter. Status: WiFiDriverTxDemo runs, InitWrite completes, and the chip accepts every beacon (2796x 148-byte bulk-OUT OK) — so the USB/MAC/descriptor path is correct. But nothing radiates yet (0 on-air), i.e. the RF TX enable is incomplete (analogous to jaguar3's enable_tx_path). RX still works (45 frames) after the shared refactor + TX-path additions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…C dequeue Add gated TX diagnostics (DEVOURER_TX_DEBUG: BB TX-enable counters 0x2de0/ 0x2de4 + CR/TXPAUSE/TXDMA_OFFSET_CHK readback; DEVOURER_NO_DROPDATA test knob) and localize why on-air TX doesn't radiate despite the chip accepting frames: - BB TX-enable counters (0x2de0 OFDM, 0x2de4 CCK) stay 0 during TX -> the BB never keys TX; frames don't reach the BB. - With DROP_DATA_EN (0x20c[9]) default: bulk-OUT succeeds but frames are silently DROPPED (FIFO drains, BB txen=0). With it cleared: bulk-OUT NAKs (rc=-7) as the FIFO fills -> the MAC never DEQUEUES TX frames to the BB. - Not TXPAUSE (0x522=0), not CR (0x06ff MACTX on), not TXAGC (0x1d00 write matches config_phydm_write_txagc_8822b), not RF/PA (RX works => antenna on WL), not CSMA (quiet-channel TX also 0). SDR (B210) confirms zero radiate. So the RF-radiate blocker is the MAC TX-DMA dequeue path (queue/EP routing or the TXDMA OFFSET check the descriptor must satisfy), not RF. Next: usbmon a kernel 8822B TX on the VM to capture a known-good TX descriptor + EP, and diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add DEVOURER_TX_EP override and a faster BB-TX-counter probe. Confirmed across all 3 bulk-OUT endpoints (0x05/0x06/0x08) and 38k frames at gap=0: the BB TX-enable counters (0x2de0/0x2de4) stay 0 — the MAC TX scheduler never dequeues any injected frame to the BB, on any endpoint. TX descriptor field offsets verified against halmac_tx_desc_nic.h (OFFSET 0x00[16:8], TXPKTSIZE 0x00[0:16], QSEL 0x04[8:5], PKT_OFFSET 0x04[24:5]) — all correct. Queue- priority map (REG_TXDMA_PQ_MAP) is set (ported from the working jaguar3). So M6 TX is blocked in the MAC TX-DMA/scheduler dequeue stage (frame reaches the FIFO/queue but is never scheduled to the PHY). Next: usbmon a kernel 8822B TX injection on the VM to capture the known-good TX descriptor bytes + target endpoint + any TX-enable register the kernel sets that devourer misses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the two TX-descriptor fields the vendor update_txdesc (rtl8822bu_xmit.c)
sets that devourer omitted: CHK_EN (0x0C[14]=1) and WHEADER_LEN (0x0C[0:5] =
802.11 header length >> 1; computed in send_packet from the FC: 24 B base, +2
QoS, +6 4-addr). The 8822B TXDMA descriptor-check needs both to process a
frame. Vendor-faithful and required for TX, though not sufficient alone.
Add DEVOURER_TX_REPLAY=<file> ("0xREG WIDTH 0xVAL" ordered, width-aware) to
replay a kernel usbmon-captured write sequence for TX bisect.
TX investigation (usbmon now works — was a sudo/perms issue, not availability):
captured the kernel's full init+TX write stream (host + VM usbmon) and the
byte-granular register golden. Replaying the kernel's ENTIRE MAC state
(0x0-0x7ff, all bytes), its 2 H2C commands, and its monitor-up sequence onto
devourer — none unlock TX (BB TX-enable counters stay 0, FIFO still jams).
Descriptor dword0 + TX endpoint (5) match the kernel. So the RF-radiate gate
is NOT a MAC register value, H2C, or descriptor dword0 — it's a write-triggered
/ sequence-dependent action or a BB/RF-write difference (>0x800), which end-of-
init state-replay cannot reproduce. RX still works.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Root-cause work for M6 TX: the kernel does an FW reserved-page download on interface-up (usbmon: FIFOPAGE_CTRL_2 beacon-head arm + template bulk-OUT + bcn-valid poll) that devourer skipped — a write-triggered step invisible to register-state comparison, and the leading suspect for RX-works/TX-dead. Expose the mechanism: HalmacJaguar2Fw::download_rsvd_page (== halmac dl_rsvd_page_88xx == the existing send_fw_page) + rsvd_boundary accessor. DEVOURER_TX_RSVD downloads a placeholder template to the rsvd boundary (pg=1938) — the bcn-valid latches OK, but a minimal/dummy blob does NOT unlock TX (BB TX-en still 0, 0 on-air). So the FW needs the REAL template set (probe_rsp/null/qos_null per _rtw_hal_set_fw_rsvd_page) and/or the iddma copy the kernel does after the download (usbmon 0x1208). Opt-in so the default TX path is unchanged. RX unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n harness M6 TX investigation. Captured the kernel rtl88x2bu monitor-inject TX descriptor on the wire (scapy inject on a VM-bound T3U + host/VM usbmon): the kernel's dword3 is 0x00000100 (USE_RATE only). devourer had additionally set WHEADER_LEN and CHK_EN (a prior guess) — dword3 0x0000410c — which tells the 8822B MAC to apply header-based (security/QoS) processing to a raw-injected frame. Drop those two fields so the descriptor byte-matches the kernel (dwords 0-7 verified identical: MACID=1, QSEL=0x12, RATE_ID=9, G_ID=0x3f, DISQSELSEQ/BMC/LS). The 0x1C checksum is still computed (kernel fills it even with CHK_EN=0). Add tests/jaguar2_tx_canary.sh: attaches the T3U to the pinned-kernel VM, drives scapy injection so the vendor driver genuinely keys TX, and dumps mac/bb/rf registers + usbmon while transmitting — the injecting-kernel golden (a passive monitor iface never keys TX, so its state has no TX-path activation to diff). Add DEVOURER_TX_QSEL/MACID/RATEID debug knobs (re-fill + re-checksum). TX still not on-air: with the correct descriptor the MAC accepts frames but the BB never keys (tx_phy_ok_cnt 0x2de0 stays 0; frames fill the HQ queue and jam when DROP_DATA_EN is cleared). Ruled out on hardware this session: descriptor, MAC+BB register state (replayed), CCA/EDCCA-ignore, QSEL/queue, TXPAUSE, DROP_DATA, post-cal TRX re-apply, RFE T/R inverter (0xcbc). Gate is the MAC-scheduler->BB grant, a bring-up sequence/latch not visible in static state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M6 TX: capture the kernel rtl88x2bu COMPLETE vendor register-write sequence (cold driver rebind -> monitor-up -> inject) as a full-payload usbmon pcap, and decode the Realtek control-OUT writes (bmRequestType 0x40, bRequest 0x05, wValue=addr, data=value) to an ordered (addr,len,val) list — the diff target for the BB-never-keys-TX gate that static register-state can't surface. Decoded 8109 writes. Key finding at monitor-up: the kernel sends 2 firmware H2C commands via HMEBOX (0x1f0 ext-box then 0x1d0 box0-trigger) — ext=0x11 box0=0x0300034c and ext=0x33 box0=0x0302034c i.e. H2C element 0x4c = PHYDM_H2C_FW_GENERAL_INIT (phydm_interface.h), payloads [03 00 03 11] / [03 02 03 33]. devourer sends ZERO phydm FW H2C at monitor-up (0x1d0/0x1f0 read back 0) — its phydm is a check_positive table-walker port with no FW-offload H2C path. Leading candidate for the gate; next: implement a proper HMEBOX H2C send and test these two commands on-air. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… out state) M6 TX investigation, iteration 2. Two reusable diagnostics + two more gates ruled out on hardware: - DEVOURER_LOG_WRITES (RtlUsbAdapter): log every vendor reg write as "0xADDR N 0xVAL" (tests/decode_wseq.py format) to diff devourer's bring-up write SET against the kernel golden. Captured 3728 devourer writes vs the kernel's 8109; the register-set diff shows devourer never writes several blocks (EDCA AC params 0x500-0x50f, rate/RRSR tables 0x420-0x454, TXAGC 0x1d00-0x1dbc, HMEBOX H2C). - DEVOURER_TX_H2C: send the 2 phydm FW H2C the kernel issues at monitor-up (element 0x4c PHYDM_H2C_FW_GENERAL_INIT) via the proper HMEBOX box protocol (poll REG_HMETFR 0x1cc box0-free -> write EXT0 0x1f0 -> write BOX0 0x1d0). Fixes the earlier flawed register-replay (back-to-back writes clobbered the box before the FW consumed it). RESULTS (hardware, VBUS-cycled clean state): replaying ALL 173 missing kernel register writes (MAC + BB + special, final values) does NOT key the BB (tx_phy 0x2de0 stays 0); the proper H2C FW_GENERAL_INIT send does NOT either. So MAC+BB register STATE and the phydm FW H2C are both ruled out. The TX gate is neither register state nor these H2C — pointing at the firmware itself (devourer bundles the combo _nic FW with active BT-coex; kernel _ohd is wifi-only) or a coex-FW antenna/TDMA silence that needs a runtime C2H-drain/GNT-reassert thread (cf. the Jaguar3 coex_runtime_loop). Next: coex-FW runtime for jaguar2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
M6 TX iteration 2 cont. Implement the halmac post-DLFW FW handshake devourer skipped (hal_halmac.c _send_general_info + phydm_info) — sent via the 32-byte FW-offload H2C PACKET queue (QSEL_H2C_CMD=0x13, TX-descriptor + bulk-OUT to the HIGH bulkout id 0 = EP 0x05), NOT the 8-byte HMEBOX path. This path is invisible to control-write capture, so it was missed until decoding the vendor send flow. - general_info (sub 0x0D): FW_TX_BOUNDARY = rsvd_fw_txbuf_addr(1994) - rsvd_boundary(1938) = 0x38. - phydm_info (sub 0x11): rfe_type/rf_type(2=2T2R)/cut/rx_ant/tx_ant(AB). Header per halmac_fw_offload_h2c_nic.h (CATEGORY 1, CMD_ID 0xFF, SUB_CMD_ID, TOTAL_LEN, SEQ). Gated DEVOURER_TX_FWINFO. RESULT (hardware, sniffer + txen): does NOT unlock TX — 0 on-air frames, BB tx_phy 0x2de0 stays 0. Endpoint verified correct (QSEL_CMD->DMA_HIGH->id0->EP5). Also confirms 0x2de0==0 correlates with 0 on-air, so txen is a valid TX indicator (validates all prior negatives). So BOTH FW-config H2C paths (HMEBOX FW_GENERAL_INIT + queue general_info/phydm_info) are ruled out. Remaining: the rest of the halmac init flow (init_mac_flow H2C, _drv_enable_trx, config_rx_info, init_interface_cfg) may be needed as a set, or the H2C queue (0x244) isn't actually consuming packets (no ack to confirm receipt). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DEVOURER_RF_REPLAY: replay kernel RF-register values via the 3-wire LSSI
("path addr val"). RF writes go through BB LSSI regs so RF state was invisible
to the vendor-reg-write set diff and never replayed. Replaying the 18 differing
path-A RF registers (incl RF 0x08 = 0x9c060, which reads 0 on devourer) does
NOT key the BB (txen still 0). So RF register state is ruled out as well.
Every replayable state (MAC + BB + RF register state, TX descriptor, HMEBOX +
FW-offload H2C) is now ruled out on hardware. The remaining suspects are the
firmware image itself (cf. the 8814 "host TX needs the MCU/FW booted" precedent
— RX works, TX dead) or a write-order/reset latch not captured by final state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rmed same DEVOURER_TX_DRAIN: continuously read+discard bulk-IN during TX to keep the FW C2H buffer drained (the RX demo submits async bulk-IN URBs, but the TX demo never reads bulk-IN — the RX-works/TX-dead asymmetry suggested C2H backpressure). Does NOT key the BB (txen still 0). Ruled out. Also confirmed the FW image is identical: devourer bundles array_mp_8822b_fw_nic (sig 0x8822, v30.20, 161240 B) which is reference/rtl88x2bu's FW == the VM's rtl88x2bu_ohd (OpenHD) FW. And DLFW reaches 0xC078 (full boot). So the FW is not the difference. M6 status: every replayable state and every visible FW-config path is ruled out on hardware — MAC+BB+RF register state, TX descriptor, HMEBOX + FW-offload H2C, CCA/EDCCA, QSEL/queue, RFE, rsvd-page, C2H drain, coex GNT state, FW image/boot. The kernel injects the byte-identical descriptor with matching registers and it transmits; devourer does not. The blocker is a write-order/latch or chip-driving difference not expressible as final register/H2C/state — needs a full ordered write-sequence replay (incl. DLFW bulk) to bisect, or USB-level A/B tracing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a TSF (0x560/0x564) advance check to DEVOURER_TX_DEBUG. Hypothesis: a frozen MAC timing engine would freeze the EDCA backoff so TX never schedules while RX (receive-driven) still works. Result: TSF ticks at the correct rate (~0x1524/ 5ms) — timing engine runs. Ruled out. Also verified via the injection pcap that the kernel's FULL 12-dword TX descriptor matches devourer's exactly, incl dword8=0x00008000 (EN_HWSEQ) — descriptor 100% ruled out (all fields, not just dwords 0-7). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…arity proof) Prompted by "have you matched 100% parity" — I had NOT (RF path B was never diffed/replayed; the ordered replay was layered on top of devourer's own bring-up). Close those: - Diffed + replayed RF PATH B (was a real gap): RF-B 0x08/0x64 unwritten on devourer, mode-table 0x33/0x3e/0x3f differ (config_trx_mode only programs path A). Forcing both paths + full MAC+BB state diff: still txen=0. - DEVOURER_DLFW_ONLY: bring_up stops after DLFW so DEVOURER_TX_REPLAY can reconstruct the ENTIRE post-DLFW init from a clean FW-booted chip (no devourer-bring_up latch interference). Added post-init TSF/CR liveness probe. RESULT: replaying the kernel's COMPLETE post-DLFW write sequence (3266 writes, nothing excluded incl 0x4e0=REG_NULL_PKT_STATUS, in exact order) from DLFW-only yields a FUNCTIONAL MAC (TSF ticking, CR=0x06ff) but STILL txen=0 / 0 on-air. CONCLUSION (sharper than before): register-STATE parity AND full register-WRITE- SEQUENCE parity (from scratch) are BOTH achieved and BOTH insufficient. The unmatched dimension is outside the host register-write interface: the DLFW/FW internal state, or FW-internal BB/MAC writes the running firmware does to arm TX once properly configured (which host register replay cannot reproduce), or interleaved bulk/read/timing. RX production-working; TX gated off by default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d out) Continued parity audit (prompted by "have you matched 100% parity"). Decoded the kernel's H2C-queue packets from the injection pcap and byte-diffed vs devourer's: - H2C DELIVERY VERIFIED: instrumented REG_H2C_PKT_WRITEADDR(0x10D4)/READADDR (0x10D0) around each send — both advance = "FW CONSUMED". So general_info/ phydm_info genuinely reach and are read by the FW (the earlier FWINFO-negative is valid, not a silent drop). - Found 4 real payload divergences and fixed devourer to match the kernel: general_info FW_TX_BOUNDARY 0x38->0x30 (I'd double-counted CPU_INSTR/H2CQ pages); phydm_info rfe_type 0->3, rf_type 2->4(1T1R), rx_ant/tx_ant AB->A. - The kernel operates the Archer T3U (RTL8812BU) SINGLE-PATH-A (1T1R) even though SYS_CFG bit27 reports 2T2R silicon; devourer forced AB. Knobs added: DEVOURER_FORCE_1T1R (config_trx_mode path A + FWINFO ant=A), DEVOURER_RFE=N (BB/RF table rfe variant), DEVOURER_TXBND/DEVOURER_PHYDM. RESULT: with 1T1R + rfe=3 + kernel-exact FWINFO (all FW-consumed), TX still txen=0 / 0 on-air; RX still works in 1T1R (6 frames). So the FW handshake CONTENT is ruled out — the FW receives the correct register state AND the correct, consumed config handshake and still does not arm the MAC->BB TX path. Parity now verified on every host-controllable dimension (register state, full write sequence from scratch, FW handshake delivery+content, FW image) — all matched, TX still dead. Remaining unmatched: DLFW load method (iddma vs kernel MCUFW_CTRL seq) / FW-load timing, and BB state 0x1d00-0x2fff (never dumped). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… out If the running FW samples register state at specific init stages and latches a TX-enable decision, devourer's fast init could catch it mid-config. Add DEVOURER_SLOW=<ms> settle delays between config_trx_mode / channel / LCK / IQK / coex / enable_rx. Test at 200ms: TX still txen=0. Completes the host-controllable parity audit (prompted by "100% parity?"): register STATE, full write SEQUENCE from a clean DLFW (DLFW_ONLY), FW-config H2C handshake (content byte-matched + delivery verified via 0x10D0/0x10D4), FW image, 1T1R operational mode, rfe variant, and init timing — ALL matched, and TX still does not key the BB. So the blocker is not any host-driver difference: the same FW, given identical register state + config + handshake, arms the MAC->BB TX path on the kernel but not on devourer. Residual difference is in FW-internal runtime behavior (unobservable without FW debug) or hardware — the one dimension a userspace driver cannot match by construction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The BB_DUMP only covered 0x800-0xffc + 0x1800-0x1afc, so my "BB parity" had never compared 0x1000-0x17ff, 0x1b00-0x1cff, or 0x1d00-0x2fff. Extend to 0x800-0x2ffc and diff vs the kernel (ch9): - Found real unmatched BB state in 0x1c38/0x1c78/0x1cb8 (per-rate BB table, 0 on devourer vs 0x24201c00/0x302c2800/0x44403c00 on kernel) + 0x1c94/0x1b* cal. Replaying all to kernel values: still txen=0 / 0 on-air. Not the gate. - devourer's TXAGC (0x1d00-0x1d7f) is ALL ZERO — the per-rate TX-power table is never programmed (M7 was pending; only the DEVOURER_TX_PWR debug knob writes 0x1d00/0x1d80, which doesn't cover MCS1's rate group). GENUINE M7 gap. Resolved the txen-validity question: set the FULL TXAGC table (0x1d00-0x1d3c/ 0x1d80-0x1dbc = 0x2d) and sniffed — still 0 on-air. If the BB were transmitting at 0 power, full power would show on-air; it doesn't. So 0x2de0=0 is a VALID indicator: the BB genuinely does not key TX, independent of power. Net: host-observable state parity is now complete (MAC 0x0-0x7ff, BB 0x800- 0x2fff, RF both paths, FW handshake+delivery, write sequence) and the BB still won't key TX. TXAGC-not-programmed is a real M7 todo but not the M6 gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…a/sniffer MAJOR correction. Read the KERNEL's 0x2de0/0x2de4 via rtw_proc read_reg WHILE it was successfully injecting: all 0x0. So 0x2de0 (tx_phy_ok_cnt) is PMAC/MP-only and reads 0 during normal host TX — it is NOT a valid indicator, and every prior "txen=0 => BB won't key" conclusion was built on it. Kernel TXAGC 0x1d00 also reads 0 during TX, so devourer's zero TXAGC matches (not a gap). Replace the TXDBG readout with valid indicators: REG_TXPKT_EMPTY(0x41a), the 0xfb4 activity counter, and the bulk-OUT rc. Kernel bb_reg_dump_ex (0x800-0x1ffc) during injection vs devourer: only 3 diffs (0x1f90/94/98) — devourer's BB state essentially MATCHES the transmitting kernel. CORRECTED PICTURE (all sniffer/jam-verified, not 0x2de0): devourer does NOT radiate (unfiltered tcpdump: no 57:42:75, no MCS). With DROP_DATA_EN cleared its frames JAM after ~43 (fill HQ, bulk NAKs) — so the MAC genuinely does not schedule them to the BB, confirmed independently of 0x2de0. The kernel schedules the byte-identical descriptor with matching register state and transmits. So the gate is the MAC-scheduler->BB grant for the HQ/MGNT queue — a real scheduler/ TXDMA difference, re-opened now that the invalid indicator is discarded. Re-test all prior hypotheses by sniffer/jam, not 0x2de0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nblocks TX THE M6 gate. The 8822B HW checksums ONLY the first 32 bytes of the TX descriptor (fill_txdesc_check_sum_8822b in halmac_common_8822b.c: fixed 8-pair loop, comment "HW calculates only 32byte"). devourer's cal_txdesc_chksum_8822b was ported from the Jaguar3 path and used (pkt_offset + TXDESC_SIZE/8)<<1 = 12 pairs = 48 bytes, so it XORed dwords 8-11 (incl EN_HWSEQ 0x8000) that the HW excludes. The stored checksum therefore never matched the HW's, and the USB MAC silently DROPPED every injected frame at the TXDMA (verified regardless of CHK_EN=0) — which is why the MAC never scheduled frames to the BB and TX looked completely dead across the entire investigation. Fix: XOR exactly the first 8 pairs / 32 bytes, matching the vendor. Verified the on-wire checksum is now correct (devourer desc d7=0xa962, the true 32B XOR). EFFECT (hardware): with the fix + DROP_DATA_EN cleared, TX frames now DRAIN without jamming (2926 bulk-OUT OK / 1 err, vs 43-then-jam before) — the MAC is finally dequeuing frames to the BB. On-air radiation is still 0 (a separate TX-power/RF issue, M7) but the MAC->BB scheduling gate is CLOSED. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8822B is a WiFi+BT combo; the coex firmware can periodically re-grab the antenna for BT, which would explain marginal/intermittent on-air TX (the Jaguar3 combo path needs a coex_runtime_loop or "the coex firmware silences the antenna"). Add _coex_thread that re-applies coex_wlan_only (WL-only HW grant) every ~500 ms, gated DEVOURER_TX_COEX_LOOP, joined in stop_dig. Tested on-air (3 runs) post-checksum-fix: re-asserting the WL-only HW grant does NOT make radiation reliable — so the marginal-radiation cause is elsewhere (RF TX-mode/synth-lock), or the coex FW needs the full C2H-drain+heartbeat, not just the HW re-grant. Kept opt-in as combo-chip infrastructure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instrument do_lck to report whether the RF LO actually locks (poll RF_A 0x18 bit15 clearing). Finding: devourer's LCK TIMES OUT — bit15 stays set, LO not locked (RF_A 0x18=0x18c09). A marginal/unlocked RF LO is a strong candidate for the intermittent on-air TX (~1 frame per 4 runs) that remains after the descriptor-checksum fix closed the MAC-scheduling gate: the frames reach the BB but the RF synth is only occasionally close enough to radiate. Caveat: the vendor _phy_lc_calibrate_8822b also has "LCK time out" handling, so the timeout may be benign or a flaky 3-wire RF read (RX/IQK reads work, so RF read isn't wholly broken). Needs confirmation vs the kernel's LCK behaviour (read RF 0x18 during kernel injection via VM rtw_proc). Concrete next lead for reliable radiation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…inding The LCK-done poll "times out" (RF_A 0x18 bit15 reads 1) but this is a devourer 3-wire-read artifact: the read returns transient status bits (0x18c09) right after the sequence, while a later settled read is 0xc09 — matching the kernel, i.e. the RF LO DOES lock. So LCK is not the marginal-radiation cause (ruled out). Keep a mask of lc_cal to the channel/BW/band bits so the value written into the LCK matches the kernel's clean 0xc09 (correctness; inert for the timeout). Radiation reliability remains open after ruling out: TX power, IQK, coex runtime, RF mode-table, RF register state, and now LCK/LO-lock. The MAC-scheduling gate (descriptor checksum, cbe31c3) is the core M6 fix and is solid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete the RTL8822BU port's TX-power and channel/bandwidth coverage: - 5 GHz RX+TX: stop masking RF18 (RF_CHNLBW) to 0x00060cff, which cleared the 5G band indicator bits (8/16). The vendor never masks RF18 there; the mask left the LC-calibrated VCO tuned for the wrong band, so every 5G read returned 0. RF18 mask is now band-conditional (2.4G only, where it suppresses spurious bits) — matching config_phydm_switch_channel_8822b and _phy_lc_calibrate_8822b in reference/rtl88x2bu. - 40/80 MHz: set_channel_bw tunes RF/AGC to the wide channel's CENTRAL channel (not the primary) per the vendor primary_ch_idx offset math, and drives the config_phydm_switch_bandwidth_8822b register recipe (0x8ac masks, RF18 BW bits, BW80 rfe-conditional extra writes). - Per-rate TX power: apply_tx_power programs 0x1d00/0x1d80 TXAGC from the EFUSE power-by-rate calibration (bandwidth-aware base + BW/Nss diff), clamped to the regulatory txpwr_lmt table. The efuse logical map is read once and cached (_efuse_map) so cold init pays a single physical walk. - Regulatory limits: tools/extract_8822b_txpwr_lmt.py generates hal/phydm/rtl8822b/Hal8822b_TxpwrLmt.h (worldwide-min per cell, base + rfe_type3 tables) from reference/rtl88x2bu halhwimg8822b_rf.c. - Post-IQK TX/RX antenna-path re-assert + phydm rfe_init/bf_init (the kernel runs config_phydm_trx_mode from the post-calibration channel set). On-air validated on the Archer T3U (2357:012d): 2.4/5 GHz at 20/40/80 MHz, CCK/OFDM/HT rates, per-rate power scaling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove the getenv-gated investigation knobs left over from the on-air TX
bring-up (the TX-broken saga, root-caused to a near-field-sniffer harness
bug, not the driver). All were default-off, so the active runtime path is
unchanged:
TX_FWINFO, TX_H2C, TX_RSVD, RF_REPLAY, TX_REPLAY, NO_DROPDATA, TXBND,
PHYDM, FORCE_1T1R, TX_DRAIN, TX_COEX_LOOP (+ the now-unused _coex_thread),
DLFW_ONLY, SLOW, BB_PATCH, RX_DEBUG, TX_DEBUG, TX_QSEL/RATEID/MACID, TX_EP,
and the RF18_MASK bisection escape (the band-conditional g2 mask is the fix).
Kept the legitimate diagnostics / escape hatches: SKIP_{IQK,COEX,TXPWR,DIG,
RFEINIT,TRX_REASSERT}, RFE, TX_PWR, BB_DUMP, EFUSE_DUMP, IGI,
RX_KEEP_CORRUPTED.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
e4bb1ad to
b16f40d
Compare
The port added a whole chip generation but README still listed Jaguar2 as out of scope. Update it to match, consistent with every other chip: - intro: "two chip generations" -> three; add the Jaguar2 RTL8822BU line - hardware landscape: a Jaguar2 paragraph (hybrid HalMAC + phydm, 2.4/5 GHz 20/40/80 MHz, Archer T3U reference) - support table: RTL8822BU row with measured on-air TX throughput 52 / 50 / 49 Mbps (ch6 / ch36 / ch149, HT MCS7 20 MHz) via tests/bench_onair.py on the Archer T3U — same USRP duty-cycle methodology as every other benchmarked row; short "adapter (pid)" note like the rest - scope note: 8822BU now in scope; only the 8822BE (PCIe) + Kestrel remain out; fix the 8822BU-vs-8822CU naming trap (both supported, separate HALs) - build options: DEVOURER_JAGUAR2 row + -DDEVOURER_JAGUAR2=OFF example tests/bench_onair.py: add the RTL8822BU chip entry (+ rtw88_8822bu to the kernel drivers it frees) and a --only filter to bench a single adapter. Bench note: rail_check flags the bus-3 CU *control* rail, but the T3U is on a separate bus-4 rail and shows no 5 GHz collapse (its own 5G/2.4G duty ratio is 0.94, well above the 0.6 sag threshold), so its 5 GHz numbers are trustworthy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
b16f40d to
d298c13
Compare
Jaguar2 (8822B) is chronologically the middle generation, but because it was ported last it had been appended after Jaguar3 in every ordered list, making it read as the newest. Reorder to old->new (Jaguar1 -> Jaguar2 -> Jaguar3) consistently: - README: hardware-landscape prose (Jaguar2 paragraph before Jaguar3; dropped the "second-generation"/"between the two sits" phrasing) - CLAUDE.md: top-level generation blurbs (Jaguar2 = "a second HAL", Jaguar3 = "a third") and the Architecture HAL descriptions - CMakeLists.txt: option() order, the no-chip-selected FATAL message, and the target_sources blocks; also add -DDEVOURER_JAGUAR2=OFF to the single-chip example comment - CI: build-configs matrix (8822b-only before the 8822c/8822e/jaguar3 rows) - WiFiDriver.cpp: includes, the chip-id mapping comment (drop stale "M2 TBD", use the real 0x0a), and the dispatch branches - demo/txdemo: kRealtekProductIds (8822BU PIDs after 8814AU, before the Jaguar3 PIDs) Pure reordering — no behavioural change. Full build + 8822b-only configure + all-off reject all verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The vendor rtl88x2bu tree (and devourer's dispatch) cover both parts of the 8822B silicon: the 2T2R RTL8822BU and its 1T1R cut RTL8812BU, which read_chip_version selects by REG_SYS_CFG bit 27 — the same 2T2R/1T1R split as RTL8812AU / RTL8811AU on Jaguar1. Add RTL8812BU to the support table and intro. Left its throughput cells blank (not benchmarked — we don't have an 8812BU adapter; the T3U reports 2T2R), like the other unbenchmarked rows. Co-Authored-By: Claude Opus 4.8 <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.

Adds a self-contained Jaguar2 HAL under
src/jaguar2/for the RTL8822BU(chip 8822B, 2T2R USB — Archer T3U
2357:012d/0bda:b82c), the last Realtekadapter still in active FPV use. Same
IRtlDevicecontract as the other twogenerations; dispatched at construction on
SYS_CFG2chip-id0x0a.What it does
On-air validated on the Archer T3U across the full matrix:
power clamped to the regulatory
txpwr_lmttablephydm
check_positiveBB/AGC/RF tables (Jaguar1 lineage, sharedPhyTableLoader), 8822B IQK + LC calibrationDesign
8822B is a hybrid of the existing HALs:
check_positiveformat, walked by theshared
PhyTableLoaderfed byPhyTableLoaderJaguar2glue.Jaguar3).
Generated artifacts (edit the generators, not the output):
hal/phydm/rtl8822b/Hal8822b_PhyTables.{c,h}viatools/extract_8822c_phy_tables.py --chip 8822bhal/phydm/rtl8822b/Hal8822b_TxpwrLmt.h(worldwide-min regulatory limits, base + rfe_type3) viatools/extract_8822b_txpwr_lmt.pyhal/hal8822b_fw.cfirmware blobAll ported from the vendor tree
reference/rtl88x2bu/.Notable bring-up findings
RF18 to
0x00060cff, clearing the 5G band-indicator bits (8/16) the vendorpreserves. That left the LC-calibrated VCO tuned for the wrong band, so every
5G read returned 0. The mask is now band-conditional (2.4G only, where it
suppresses a spurious-bit RF-state quirk).
primary_ch_idxoffset math), driving theconfig_phydm_switch_bandwidth_8822bregister recipe.
physical walk.
Build / CI
DEVOURER_JAGUAR2build option (default ON);8822b-onlybuild-config rowadded;
reject-bad-configsunchanged.ctest(stream_stdin_binary) green.Testing
Hardware-validated out-of-band on the Archer T3U (
DEVOURER_VID=0x2357 DEVOURER_PID=0x012d) via thetests/jaguar2_*.shon-air scripts (chip-snifferPHY-decode + B210 SDR occupied-bandwidth). Not exercised by the automated CI
matrix (no hardware in CI).
🤖 Generated with Claude Code