Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
compile_commands.json
build
.cache
__pycache__/
*.pyc
13 changes: 13 additions & 0 deletions src/RtlUsbAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,19 @@ void RtlUsbAdapter::ReadEFuseByte(uint16_t _offset, uint8_t *pbuf) {
uint8_t readbyte;
uint16_t retry;

/* Match the kernel `88XXau` driver's per-iteration EFUSE_TEST clear.
* Cold-init usbmon diff (2026-05-28, devourer-testrig VM kernel-side
* vs host devourer-side) shows the kernel does an RD-then-WR sequence
* at REG_EFUSE_TEST (0x0034) = 0x0000 (16-bit) BEFORE every EFUSE byte
* read, 312 times per init; devourer never touched 0x0034. We mirror
* the sequence so the EFUSE state machine sees identical wire shape
* across all 312 byte reads. Empirically harmless on its own (does
* NOT fix the RTL8814AU TX-on-air gate per a sniffer run with this
* patch + bulk-IN drainer enabled) but removes a known concrete
* wire-level divergence flagged by tools/usbmon_pcap_diff.py. */
(void)rtw_read16(REG_EFUSE_TEST);
rtw_write16(REG_EFUSE_TEST, 0x0000);

/* Write Address */
rtw_write8(EFUSE_CTRL + 1, (uint8_t)(_offset & 0xff));
readbyte = rtw_read8(EFUSE_CTRL + 2);
Expand Down
85 changes: 85 additions & 0 deletions tests/test_urbscript_roundtrip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""End-to-end smoke test: pcapng → urbscript emitter → C replay (dry-run).

Synthesises a small pcapng with a mix of control writes, control reads, a
bulk OUT, and an interrupt IN. Runs tools/pcapng_to_urbscript.py to produce
a .urbs file, then runs build/usbmon_replay --dry-run on it and verifies
the emitted submit counts match the script.

Requires build/usbmon_replay to have been compiled
(cc -O2 -Wall -Wextra -o build/usbmon_replay tools/usbmon_replay.c).
"""

from __future__ import annotations

import os
import subprocess
import sys
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))

from tests.test_usbmon_pcap_diff import ( # noqa: E402
_build_pcap,
_bulk_out,
_ctrl_read,
_ctrl_write,
_interrupt_in,
)


def main() -> int:
replay_bin = ROOT / "build" / "usbmon_replay"
if not replay_bin.exists():
print(f"FAIL: {replay_bin} not built", file=sys.stderr)
print(" cc -O2 -Wall -Wextra -o build/usbmon_replay tools/usbmon_replay.c",
file=sys.stderr)
return 1

with tempfile.TemporaryDirectory() as d:
tmp = Path(d)
pcap = tmp / "in.pcap"
urbs = tmp / "in.urbs"

records = []
# 3 control writes, 1 control read, 1 bulk OUT, 1 interrupt IN.
records += list(_ctrl_write(0x0100, b"\x01", 1_000_000, 1))
records += list(_ctrl_write(0x0102, b"\xab\xcd", 1_000_500, 2))
records += list(_ctrl_write(0x0C90, b"\x55\xaa\x55\xaa", 1_001_000, 3))
records += list(_ctrl_read(0x0F00, b"\x12\x34", 1_001_500, 4))
records += list(_bulk_out(0x02, b"hello world " * 8, 1_002_000, 5))
records += list(_interrupt_in(0x83, b"\xc2\xc2\xc2", 1_002_500, 6))
pcap.write_bytes(_build_pcap(records))

# Stage 1 — pcap → urbscript.
r = subprocess.run(
[sys.executable, str(ROOT / "tools" / "pcapng_to_urbscript.py"),
str(pcap), "-o", str(urbs)],
check=True, capture_output=True, text=True,
)
assert urbs.exists(), "urbscript not emitted"
out = r.stdout + r.stderr
# 6 submits total.
assert "wrote 6 URB records" in out, f"unexpected stage-1 output:\n{out}"
print(f" stage 1 (pcap → urbs): ok")

# Stage 2 — replay --dry-run.
r = subprocess.run(
[str(replay_bin), "--device", "/dev/null",
"--urbs", str(urbs), "--dry-run", "-v"],
check=True, capture_output=True, text=True,
)
out = r.stderr
# Expect 6 submits, 6 ok, by kind: ctrl=4 bulk=1 intr=1, in=2 out=4
assert "6 submits, ok=6" in out, f"summary missing or wrong:\n{out}"
assert "ctrl=4 bulk=1 intr=1" in out, f"by-kind wrong:\n{out}"
assert "in=2 out=4" in out, f"by-dir wrong:\n{out}"
print(f" stage 2 (urbs → replay --dry-run): ok")
print("ALL OK")
return 0


if __name__ == "__main__":
sys.exit(main())
Loading
Loading