Skip to content

RTL8821AU: 8821-specific init flow (replaces trace-derived parity)#42

Merged
josephnef merged 1 commit into
masterfrom
feat/pr22-minimal-hotplug-fix
May 25, 2026
Merged

RTL8821AU: 8821-specific init flow (replaces trace-derived parity)#42
josephnef merged 1 commit into
masterfrom
feat/pr22-minimal-hotplug-fix

Conversation

@josephnef
Copy link
Copy Markdown
Collaborator

Cherry-picks the HalModule-only delta from @RomanLut's #22 minimal-fix commit (caa46d92 on RomanLut/devourer:rtl8811au_support), which isolates his original wholesale-svpcom port down to just the 8821 init-flow changes — same _eepromManager->version_id.ICType == CHIP_8821 dispatch points, but using the proper rtl8812au-derived init sequence instead of master's trace-replay approach from #37.

Supersedes #22 (which is now functionally a superset of this PR plus already-reverted noise). Same fix, isolated.

Motivating bug

@RomanLut reported that master's 8821AU works for cold init but fails on hotplug: after unplug/replug, PixelPilot stops receiving until the app is restarted. Independently reproduced in his hx-esp32cam-fpv Android GS builds.

He confirmed the Android path goes through libusb_wrap_sys_device() with an fd from UsbDeviceConnection.getFileDescriptor(), not the traditional libusb_open_device_with_vid_pid.

Linux-side reproduction with sysfs unbind/rebind never showed the failure mode (see #22 comment chain — three forms of "hotplug" tested, master passes all). The diagnosis that makes both observations consistent:

Master's #37 8821 init is a trace-derived register-poke replay that works on first init (because that's the chip state the trace was captured from) but doesn't necessarily produce a valid chip state from arbitrary starting conditions. A freshly-wrapped Android fd may present such a condition; this PR's proper init flow re-initializes from any starting state.

What this changes

HalModule::rtl8812au_hal_init dispatch points where 8821 currently falls through 8812's path, switched to 8821-specific:

  • REG_RF_CTRL = 5/7 + REG_RF_B_CTRL_8812 = 5/7 writes skipped for 8821
  • LLT init uses TX_PAGE_BOUNDARY_8821 (vs _8812)
  • Queue/buffer init dispatches to new _InitQueueReservedPage_8821AUsb and _InitTxBufferBoundary_8821AUsb (added below); _InitTransferPageSize_8812AUsb skipped for 8821
  • Post-MACTXEN/MACRXEN: if (REG_SYS_CFG+3 & BIT0) → 0x7C |= BIT6
  • HalPwrSeqCmdParsing gains GET_PWR_CFG_CUT_MASK + GET_PWR_CFG_FAB_MASK filtering; for 8821, cutMask = NORMAL_CHIP ? PWR_CUT_A_MSK : PWR_CUT_TESTCHIP_MSK
  • _InitBurstPktLen for 8821: REG_AMPDU_MAX_TIME_8812 = 0x5e (vs 0x70), force speedvalue = BIT7, REG_FWHW_TXQ_CTRL = 0x80 + REG_FAST_EDCA_CTRL = 0x03087777 when !wifi_spec

New helpers in HalModule:

  • _InitQueueReservedPage_8821AUsb: uses NORMAL_PAGE_NUM_{HPQ,LPQ,NPQ}_8821 (or WMM_NORMAL_PAGE_NUM_* under wifi_spec) and TX_TOTAL_PAGE_NUMBER_8821
  • _InitTxBufferBoundary_8821AUsb: writes TX_PAGE_BOUNDARY_8821 to BCNQ_BDNY / MGQ_BDNY / WMAC_LBK_BF_HD / TRXFF_BNDY / TDECTRL+1

All constants already exist in hal/rtl8812a_hal.h and hal/HalPwrSeqCmd.h.

Linux-side validation on lab rig

tests/regress.py --full-matrix --channel 100 on 8814AU + 8821AU T2U Plus, VM mode (devourer-testrig + aircrack-ng/88XXau). No regressions vs #34 baseline; 8821 cells uniformly improved:

TX → RX #34 baseline with this patch
8814 kernel → 8821 kernel 271 ✓ 430 ✓
8814 kernel → 8821 devourer 200 ✓ 400 ✓
8821 kernel → 8814 kernel 260 ✓ 365 ✓
8821 devourer → 8814 kernel 4341 ✓ 5865 ✓

8814-TX cells still 0 (known broken, separate issue). 8814-RX cells still 0 (known broken, separate issue).

What this doesn't validate

The Android hotplug end-to-end. That requires @RomanLut building PixelPilot against this branch and exercising the unplug/replug flow on an actual Android device — which we can't do from the Linux lab.

@RomanLut: could you build PixelPilot / hx-esp32cam-fpv against this branch (feat/pr22-minimal-hotplug-fix) and confirm that the hotplug failure mode goes away? If yes, this is ready to merge and supersedes #22.

What this doesn't touch

  • RtlUsbAdapter.cpp (rxagg thresholds, infinite_read early-return) — your minimal-fix commit reverted those, so they're not in scope here. We can revisit separately if you find they're needed.
  • EepromManager.cpp (IsRtl8821A vs is_rtl8821a_pid allow-list) — both work, master's allow-list is intentional, you've already reverted.
  • The trace-derived register pokes from RTL8821AU: trace-derived register-init parity (partial — does not fix RX asymmetry) #37 — this PR doesn't remove them yet. They still apply on the post-fwdl path and may now be redundant; cleaning that up is a follow-up.

Test plan

Co-authored with @RomanLut (Co-Authored-By trailer in commit).

🤖 Generated with Claude Code

Cherry-picks the HalModule-only delta from @RomanLut's PR #22 minimal-fix
commit (caa46d9 on RomanLut/devourer:rtl8811au_support), which isolates
his original wholesale-svpcom port down to just the 8821 init-flow
changes — same `_eepromManager->version_id.ICType == CHIP_8821` dispatch
points, but using the proper rtl8812au-derived init sequence instead of
master's trace-replay approach from PR #37.

## Motivating bug

@RomanLut reported on #22 that master's 8821AU support works for cold
init but fails on hotplug: after unplugging and replugging the adapter,
PixelPilot stops receiving until the app is restarted. Independently
reproduced in his hx-esp32cam-fpv Android GS builds. He confirmed in
#22 that the Android path uses `libusb_wrap_sys_device()` with an fd
from `UsbDeviceConnection.getFileDescriptor()`, not the traditional
`libusb_open_device_with_vid_pid`.

Linux-side reproduction with sysfs unbind/rebind never showed the
failure mode (#22 comment chain documents three forms of "hotplug"
tested, all pass on master). Diagnosis: master's PR #37 8821 init is
a trace-derived register-poke replay that works on first init (because
that's the chip state the trace was captured from) but doesn't
necessarily produce a valid chip state from arbitrary starting
conditions — and a freshly-wrapped Android fd may present such a
condition.

## What this changes

`HalModule::rtl8812au_hal_init` dispatch points where 8821 currently
falls through 8812's path, switched to 8821-specific:

  - REG_RF_CTRL = 5/7 + REG_RF_B_CTRL_8812 = 5/7 writes skipped for 8821
  - LLT init uses TX_PAGE_BOUNDARY_8821 (vs _8812)
  - Queue/buffer init dispatches to new _InitQueueReservedPage_8821AUsb
    and _InitTxBufferBoundary_8821AUsb (added below); skip
    _InitTransferPageSize_8812AUsb for 8821
  - Post-MACTXEN/MACRXEN: `if (REG_SYS_CFG+3 & BIT0) → 0x7C |= BIT6`
  - HalPwrSeqCmdParsing gains GET_PWR_CFG_CUT_MASK + GET_PWR_CFG_FAB_MASK
    filtering; for 8821, cutMask = NORMAL_CHIP ? PWR_CUT_A_MSK :
    PWR_CUT_TESTCHIP_MSK
  - _InitBurstPktLen for 8821: REG_AMPDU_MAX_TIME_8812 = 0x5e (vs 0x70),
    force speedvalue = BIT7, REG_FWHW_TXQ_CTRL = 0x80 +
    REG_FAST_EDCA_CTRL = 0x03087777 when !wifi_spec

New helpers:
  - _InitQueueReservedPage_8821AUsb: uses NORMAL_PAGE_NUM_{HPQ,LPQ,NPQ}_8821
    (or WMM_NORMAL_PAGE_NUM_* under wifi_spec) and TX_TOTAL_PAGE_NUMBER_8821
  - _InitTxBufferBoundary_8821AUsb: writes TX_PAGE_BOUNDARY_8821 to the 5
    register stops (BCNQ_BDNY / MGQ_BDNY / WMAC_LBK_BF_HD / TRXFF_BNDY /
    TDECTRL+1)

All constants already exist in hal/rtl8812a_hal.h (TX_PAGE_BOUNDARY_8821 et
al) and hal/HalPwrSeqCmd.h (PWR_CUT_A_MSK, PWR_CUT_TESTCHIP_MSK,
GET_PWR_CFG_CUT_MASK, GET_PWR_CFG_FAB_MASK, PWR_FAB_ALL_MSK).

## Linux-side validation on lab rig

`tests/regress.py --full-matrix --channel 100` on 8814 + 8821 T2U Plus,
VM mode (devourer-testrig + aircrack-ng/88XXau). No regressions vs PR
#34 baseline; several cells improved:

| TX → RX                            | PR #34 baseline | with this patch |
|------------------------------------|-----------------|-----------------|
| 8814 kernel → 8821 kernel          |  271 ✓          |  430 ✓          |
| 8814 kernel → 8821 devourer        |  200 ✓          |  400 ✓          |
| 8821 kernel → 8814 kernel          |  260 ✓          |  365 ✓          |
| 8821 devourer → 8814 kernel        | 4341 ✓          | 5865 ✓          |

8814 TX cells still 0 (known broken on master, separate issue). 8814
RX cells still 0 (known broken, separate issue). 8821 paths uniformly
improved.

## What this doesn't validate

The Android-side hotplug end-to-end. That requires building PixelPilot
against this branch and exercising the unplug/replug flow on an actual
Android device — which we can't do from the Linux lab. Asking @RomanLut
to verify before merge.

Co-Authored-By: Roman Lut <11955117+RomanLut@users.noreply.github.com>

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@josephnef josephnef merged commit 01cad75 into master May 25, 2026
5 checks passed
@josephnef josephnef deleted the feat/pr22-minimal-hotplug-fix branch May 25, 2026 19:03
@josephnef
Copy link
Copy Markdown
Collaborator Author

Merged on the strength of the Linux-side validation (8821 cells uniformly improved, no regressions). The Android hotplug end-to-end is still the open question — @RomanLut, when you have a moment, please build PixelPilot / hx-esp32cam-fpv against current master (post-01cad75) and confirm the unplug/replug failure mode is fixed.

If it's NOT fixed, the next thing to revisit is your RtlUsbAdapter.cpp deltas (rxagg thresholds + infinite_read early-return) that you reverted in caa46d92. Easy to put those back as a small follow-up if needed.

@josephnef josephnef mentioned this pull request May 25, 2026
@RomanLut
Copy link
Copy Markdown

Replug works on rtl8812au and rtl8821au in Pixelpilot and hx-esp32fpv android gs with current devourer master.
Fixed.

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.

2 participants