From 83ca75bd4c2ec7de3475f549143543ff187e5556 Mon Sep 17 00:00:00 2001 From: Joseph <162703152+josephnef@users.noreply.github.com> Date: Tue, 26 May 2026 20:44:56 +0300 Subject: [PATCH] RTL8814AU: align TX descriptor with aircrack-ng monitor-injection format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In RtlJaguarDevice::send_packet the SET_TX_DESC_*_8812 macros are bit-identical to the SET_TX_DESC_*_8814A macros (verified against hal/rtl8814a_xmit.h), so devourer can keep using the 8812 macro set on 8814A. But a usbmon byte-diff against a working VM-passthrough 88XXau monitor-injection session (qemu USB-host-passthrough → VM kernel 88XXau → bulk-OUT URBs back through host xhci) shows three field-value mismatches on 8814A: Dword 0 bit 31 — 8812 calls it OWN, 8814A calls it DISQSELSEQ. 88XXau leaves bit 31 = 0 for monitor-injected frames; devourer's SET_TX_DESC_OWN_8812(..., 1) sets it to 1, which on 8814A means DISQSELSEQ=1 (disable queue-select-based sequence numbering). Dword 2 bits 24-29 (GID) — 88XXau leaves at 0 for injection; devourer writes 0x3F. Dword 4 bits 18-23 (DATA_RETRY_LIMIT) — 88XXau leaves at 0 for injection; devourer writes 12 (RETRY_LIMIT_ENABLE stays 1 in both). Skip those writes on 8814A so the emitted descriptor byte-matches aircrack-ng's reference monitor-injection format. Add a DEVOURER_TX_LEGACY_8812_DESC=1 env-gate to restore the old behaviour without rebuilding, in case anything downstream depends on it. This does NOT resolve #50 (8814AU on-air silence has a separate root cause that vendor-control-write replay cannot reach — both sessions on 2026-05-26 ruled out 9 distinct hypotheses including a binary URB-flag diff, see comment-4546974748). The change is purely about descriptor correctness — aligning devourer's TX descriptor format with the byte-level reference that the working kernel driver produces. 8812AU and 8821AU paths are bit-for-bit identical to current master (is_8814a is false there and all writes fire as before). Smoke-tested on the live bench: 8812AU: 760 submits / 760 complete / 0 fail 8814AU (new): 3572 submits / 3572 complete / 0 fail (vs current master's behaviour, which is identical at libusb level because devourer's descriptor differences from 88XXau are no-ops at the bulk-OUT path post-PR-#49) 8814AU (DEVOURER_TX_LEGACY_8812_DESC=1): same as without env Refs #50 (partial — descriptor alignment only, not the on-air gate). Co-Authored-By: Claude Opus 4.7 (1M context) --- src/RtlJaguarDevice.cpp | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/RtlJaguarDevice.cpp b/src/RtlJaguarDevice.cpp index e298ca6..88d22d5 100644 --- a/src/RtlJaguarDevice.cpp +++ b/src/RtlJaguarDevice.cpp @@ -2,6 +2,8 @@ #include "EepromManager.h" #include "RadioManagementModule.h" +#include + RtlJaguarDevice::RtlJaguarDevice(RtlUsbAdapter device, Logger_t logger) : _device{device}, _eepromManager{std::make_shared(device, logger)}, @@ -143,10 +145,41 @@ bool RtlJaguarDevice::send_packet(const uint8_t *packet, size_t length) { SET_TX_DESC_DATA_BW_8812(usb_frame, BWSettingOfDesc); - /* Single-fragment frame: LAST_SEG=1 (no FIRST_SEG); OWN=1 so chip - * processes the descriptor. */ + /* The SET_TX_DESC_*_8812 macros have bit-identical positions to the + * SET_TX_DESC_*_8814A macros (verified against hal/rtl8814a_xmit.h). But + * a few of the field NAMES differ on 8814A, and a usbmon byte-diff + * against a working VM-passthrough 88XXau monitor-injection session shows + * three field-value mismatches on 8814A: + * + * Dword 0 bit 31 — 8812 calls it OWN, 8814A calls it DISQSELSEQ. + * 88XXau leaves bit 31 = 0 for monitor-injected frames; devourer's + * SET_TX_DESC_OWN_8812(usb_frame, 1) sets it to 1, which on 8814A + * means DISQSELSEQ=1 (disable queue-select-based sequence numbering). + * Dword 2 bits 24-29 (GID) — 88XXau leaves at 0 for injection; + * devourer writes 0x3F. + * Dword 4 bits 18-23 (DATA_RETRY_LIMIT) — 88XXau leaves at 0 for + * injection; devourer writes 12. + * + * Skip those writes on 8814A to byte-match aircrack-ng's reference + * monitor-injection descriptor. Does NOT resolve #50 (on-air silence + * has a different root cause that vendor-control-write replay can't + * reach), but aligns devourer's TX descriptor with the working + * kernel-driver format. Override with DEVOURER_TX_LEGACY_8812_DESC=1 + * to restore the old behaviour without rebuilding. + * + * 8812AU and 8821AU paths are bit-for-bit identical to current master -- + * is_8814a is false there and all writes fire as before. */ + const bool is_8814a = + _eepromManager->version_id.ICType == CHIP_8814A && + !std::getenv("DEVOURER_TX_LEGACY_8812_DESC"); + + /* Single-fragment frame: LAST_SEG=1 (no FIRST_SEG). */ SET_TX_DESC_LAST_SEG_8812(usb_frame, 1); - SET_TX_DESC_OWN_8812(usb_frame, 1); + if (!is_8814a) { + /* OWN=1 needed on 8812/8821 so chip processes the descriptor. On + * 8814A the same bit is DISQSELSEQ -- leave at 0 to match 88XXau. */ + SET_TX_DESC_OWN_8812(usb_frame, 1); + } SET_TX_DESC_PKT_SIZE_8812(usb_frame, static_cast(real_packet_length)); @@ -172,10 +205,17 @@ bool RtlJaguarDevice::send_packet(const uint8_t *packet, size_t length) { SET_TX_DESC_QUEUE_SEL_8812(usb_frame, 0x12); SET_TX_DESC_HWSEQ_EN_8812(usb_frame, static_cast(1)); - SET_TX_DESC_GID_8812(usb_frame, static_cast(0x3F)); + if (!is_8814a) { + /* 88XXau leaves GID=0 for monitor injection on 8814A. */ + SET_TX_DESC_GID_8812(usb_frame, static_cast(0x3F)); + } SET_TX_DESC_SW_DEFINE_8812(usb_frame, static_cast(0x001)); SET_TX_DESC_RETRY_LIMIT_ENABLE_8812(usb_frame, 1); - SET_TX_DESC_DATA_RETRY_LIMIT_8812(usb_frame, 12); + if (!is_8814a) { + /* 88XXau leaves DATA_RETRY_LIMIT=0 for monitor injection on 8814A + * (RETRY_LIMIT_ENABLE stays set to 1 in both). */ + SET_TX_DESC_DATA_RETRY_LIMIT_8812(usb_frame, 12); + } if (sgi) { _logger->info("short gi enabled,set sgi"); SET_TX_DESC_DATA_SHORT_8812(usb_frame, 1);