Files
tinyusb/.claude/skills/usb-kernel-debug/SKILL.md
Ha Thach 72f95d7d61 test/hil: replace PCI reset with root-port VBUS cycle for D-state recovery (#3789)
test/hil: replace PCI reset with root-port VBUS cycle for D-state recovery

pci-reset was documented as an FLR, but no controller on either rig has FLR, so
it issued a PCIe secondary bus reset on a live, driver-bound xHCI -- halting the
card until the PVE host was power-cycled, and returning success so the caller
could not tell. It destroyed the ci controller twice.

Replace it with root-cycle, which cuts VBUS at the xHCI root port and touches
only the root hub, so it never takes the per-device lock the wedged ioctl holds.
uhubctl needs -S, or its sysfs backend disconnects the child before cutting
power and blocks on that same lock. Success is proven by the device's sysfs
directory inode changing: node existence proves nothing, and devnum is reused
once the per-bus map wraps.

usbtest.py's hang path invokes it, then confirms via /proc that nothing still
holds the device node. Skill scripts now run from the repo; the drifted
/usr/local/sbin copies are deleted.
2026-07-28 00:17:14 +07:00

2.6 KiB

name: usb-kernel-debug description: Use when USB enumeration fails or misbehaves and packet/URB capture can't explain WHY the Linux kernel acted — port reset storms, repeated re-enumeration, address errors, xHCI ring/command errors, "device descriptor read error", babble — on whichever end of the link runs Linux: the PC host when testing a TinyUSB device, or a Linux gadget peer (e.g. Raspberry Pi) when testing the TinyUSB host stack.

usb-kernel-debug — Linux kernel dynamic debug for USB

Kernel dynamic debug shows the Linux side's reasoning that packet capture can't: port resets and their causes, enumeration retries, address (re)assignment, EP halts, xHCI ring/command errors. It applies wherever Linux sits in the link — the rig PC when it is the host, or a Linux gadget peer (dwc2/UDC + gadget modules) when TinyUSB is the host. It cannot see inside the TinyUSB MCU — that is the target-debug skill.

Run this skill's scripts/usb_dyndbg.sh with sudo. It flips the dynamic-debug print flag for an allowlisted set of USB modules only:

# all examples below abbreviate:  sudo .claude/skills/usb-kernel-debug/scripts/usb_dyndbg.sh
sudo usb_dyndbg.sh on  usbcore xhci_hcd   # enable +p; pick modules from `lsusb -t` Driver=
sudo usb_dyndbg.sh status [module]        # list enabled print sites
sudo usb_dyndbg.sh off usbcore xhci_hcd   # ALWAYS turn off when done — very noisy

Allowlisted modules: usbcore xhci_hcd xhci_pci xhci_pci_renesas ehci_hcd ehci_pci ohci_hcd ohci_pci uhci_hcd dwc2 dwc3 cdc_acm usb_storage uas libcomposite udc_core (dwc2/dwc3 + the last two cover a Linux gadget peer's device side).

Workflow

  1. sudo usb_dyndbg.sh on usbcore <hcd-module>usbcore for enumeration/hub logic, plus the controller module (lsusb -t shows the driver per bus). On a gadget peer: dwc2 (or dwc3) + udc_core + libcomposite instead — run on the peer itself (its SSH/serial console); the script is self-contained, copy it over or use the raw dynamic_debug/control writes from the usbmon skill.
  2. Reproduce (replug / re-enumerate / rerun the failing test) while following sudo dmesg -w (or grab sudo dmesg | tail afterwards).
  3. sudo usb_dyndbg.sh off ... — leaving it on floods the log and skews timing.

On a Linux-PC-host link, pair with the usbmon skill: usbmon for what crossed the bus, dynamic debug for why the kernel reacted. A gadget peer's UDC has no usbmon — pair with usb-sniffer on the wire instead. For a wedged device/bus on the rig PC use the usb-kernel-recover skill.

Requires CONFIG_DYNAMIC_DEBUG and mounted debugfs (standard on distro kernels).