tools: add usbmon-text diff for bulk-OUT investigations#53
Merged
Conversation
Standalone Python utility parsing the text-format usbmon output from
/sys/kernel/debug/usb/usbmon/Nu (kernel 6.18 emits text via this path).
Filters bulk-OUT URBs to a specific (bus, devnum, endpoint), pairs
submit ('S') with complete ('C') records by URB id, and reports
per-file metrics: count, mean/p50/p99 submit->complete latency, status
distribution, length distribution, inter-submit interval. Cross-file
diff highlights metrics where two captures disagree.
Built and used during the #36 (root cause isolated to REG_CR=0 wedge)
and #50 (8814AU on-air silence) investigations. Compares devourer's
libusb-routed URBs against qemu USB-host-passthrough URBs from a VM-
side kernel driver -- both pass through the host's xhci-hcd so a
host-side usbmon capture sees both.
Text format strips URB flags (URB_NO_INTERRUPT, URB_ZERO_PACKET, etc)
and truncates data at 32 bytes -- for flag-level diffs, use Wireshark
on usbmonN binary output and the usb.copy_of_transfer_flags field.
This tool stays useful for latency/status/cadence comparison without
needing pcap-ng.
Usage:
sudo cat /sys/kernel/debug/usb/usbmon/4u > /tmp/A.txt &
# ... run workload A ...
sudo cat /sys/kernel/debug/usb/usbmon/4u > /tmp/B.txt &
# ... run workload B ...
python3 tools/usbmon_diff.py --file-a /tmp/A.txt --file-b /tmp/B.txt \
--ep 0x02 --devnum-a 2 --devnum-b 2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
02e5efa to
b30f1a8
Compare
7 tasks
josephnef
added a commit
that referenced
this pull request
May 28, 2026
## Summary Adds a binary-fidelity USB diff toolchain to compare kernel `aircrack-ng/88XXau` TX vs devourer's on RTL8814AU, plus three env-gated diagnostic switches in `WiFiDriverTxDemo` and one library change to close a concrete kernel-vs-devourer wire-level divergence. The existing `tools/usbmon_diff.py` (#53, text-format) is bulk-OUT only and cannot represent EP0 control transfers — the carrier for ~5000 init register pokes per chip cold-init. Without those axes, "the wire matches" diff conclusions are not load-bearing. The new tool was the difference between "the gate is somewhere in path-A RF table application" (the previous narrowing, indirect) and a concrete URB-level diff against a real kernel-side capture (this PR, direct). ### Tooling - `tools/usbmon_pcap_diff.py` — pcapng-aware diff (Linux usbmon binary, `LINKTYPE_USB_LINUX_MMAPPED = 220`). Surfaces setup packet, full payload + SHA-256, URB flags, status, timestamps, and IN URBs as first-class records. Modes: default position-aligned diff w/ lookahead resync, `--offload-probe`, `--phase-split`, `--aggregate`. Real-capture caveat handled: modern kernels write `flag_setup=0` (not `' '/0x20` as the old docs say) for valid setup packets — parser decodes setup unconditionally on CTRL submits. - `tools/pcapng_to_urbscript.py` — pcapng → binary URB script emitter. - `tools/usbmon_replay.c` → `build/usbmon_replay` — `USBDEVFS_SUBMITURB` verbatim replay. `--dry-run`, `--disconnect`, capped inter-URB gaps, bulk-OUT default `URB_ZERO_PACKET` matching `RtlUsbAdapter::send_packet`. - Unit + roundtrip tests (synthetic pcaps, no hardware) — `tests/test_usbmon_pcap_diff.py`, `tests/test_urbscript_roundtrip.py`. - `tools/usbmon_diff.py` — banner update pointing forward. ### Diagnostic gates in `WiFiDriverTxDemo` (all OFF by default) - `DEVOURER_USB_SENTINEL=1` — 0xDEAD/0xBEEF writes to `REG_DUMMY (0x04FC)` bracketing init, so `--phase-split` can use sentinels instead of the gap heuristic. - `DEVOURER_DRAIN_BULK_IN=1` — background bulk-IN drainer on EP 0x81 (kernel pre-arms 8×32KB; devourer in TX-only mode never had any IN URBs in flight). With this gate the chip starts pushing 176–390 B C2H/status messages back. Empirically necessary but not sufficient — does NOT alone unblock on-air TX. - `DEVOURER_POLL_INTR_IN=1` — EP 0x85 interrupt-IN poller. Confirmed empirically the chip does NOT push on EP 0x85 during devourer init; upstream's `CONFIG_USB_INTERRUPT_IN_PIPE` codepath is not load-bearing for the TX gate. Kept as a diagnostic. ### Library change `RtlUsbAdapter::ReadEFuseByte`: mirror the kernel's per-byte-read `REG_EFUSE_TEST (0x0034) = 0x0000` (16-bit RD-then-WR), 312 times per init. Removes a known concrete divergence flagged by the new diff. Empirically harmless on its own (does NOT close the TX-on-air gate) but matches upstream wire shape — useful as bisection ground truth. ### Real-capture findings (first watertight kernel-vs-devourer diff on 8814AU) Kernel cold-init capture taken inside `devourer-testrig` VM (host kernel 6.18 cannot build `aircrack-ng/88XXau`); devourer-side on the host with chip then handed back. | Axis | Kernel | Devourer | Δ | |---|---|---|---| | Realtek ctrl writes | 6146 | 5399 | +747 | | Realtek ctrl reads | 2337 | 1651 | +686 | | Bulk-IN URBs (EP 0x81) | 8 × 32 KB | 0 | +8 | | Reg 0x1998 (BB cal loop) | 1029 | 781 | **+248** ← biggest single-reg | | Reg 0x0034 (`REG_EFUSE_TEST`) | 312 | 0 → 312 (after patch) | closed | | Path-A/B/C/D LSSI | 370 / 296 / 296 / 296 | 354 / 282 / 282 / 282 | +14 each | Hypotheses tested & falsified this session: FW-offload of path-A RF table; EP 0x85 interrupt-IN polling; REG_CR missing MAC enables; EFUSE_TEST 0x0034 missing writes; bulk-IN drainage as sufficient cause. Strongest remaining open candidate: register 0x1998 (248-write deficit) at `hal/phydm/rtl8814a/Hal8814_PhyTables.c:3607+` — a BB calibration loop whose phydm conditional evaluates a different branch between kernel and devourer. ## Test plan - [x] `cmake --build build -j` clean - [x] `python3 tests/test_usbmon_pcap_diff.py` — 8 cases pass - [x] `python3 tests/test_urbscript_roundtrip.py` — pcap → urbs → replay --dry-run pass - [x] `WiFiDriverTxDemo` with `DEVOURER_USB_SENTINEL=1` runs init+TX with 2 visible sentinel writes - [x] Real pcapng captured via `tshark -i usbmon4 -s 0` parses cleanly; phase-split lands on sentinel boundary deterministically - [x] Kernel-side cold-init capture inside `devourer-testrig` VM, diff against host devourer-side cold-init produces concrete divergence numbers (table above) - [ ] Followup PR: investigate and address the 0x1998 248-write deficit 🤖 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
tools/usbmon_diff.py(~200 LOC) — text-formatusbmonparser + bulk-OUT URB diff utility./sys/kernel/debug/usb/usbmon/Nufile twice.Test plan
/sys/kernel/debug/usb/usbmon/Nu(note: on this kernel theusuffix emits text, not binary).tools/.Limitations
Text usbmon truncates URB data at 32 bytes and strips URB flags (
URB_NO_INTERRUPT,URB_ZERO_PACKET). For flag-level diffs, use Wireshark/tshark onusbmonNbinary output and theusb.copy_of_transfer_flagsfield. This tool stays useful for latency / status / cadence comparison without needing pcap-ng.🤖 Generated with Claude Code