mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 02:53:35 +00:00
docs(skills): rename debug skills, drop the PC-host/TinyUSB-device assumption
Rename usb-target-debug -> target-debug, usb-debug -> usb-kernel-debug, usb-recover -> usb-kernel-recover (script filenames unchanged), and make all debug skills/agents decide tool applicability by which end of the link runs Linux: TinyUSB may run the device or host stack, and its peer may be a Linux PC, another TinyUSB board, or a Linux gadget (e.g. Raspberry Pi UDC). - usbmon: exists only when a Linux PC is the link's host - usb-kernel-debug: either Linux end; allowlist gains dwc3/libcomposite/udc_core for the gadget side of a Linux peer - usb-sniffer: the only full-visibility capture when TinyUSB is the host - target-debug: covers dcd_* and hcd_*/tuh_ debugging; channel choice by topology - update target-debugger/hil-operator agents, pre-pr, hil-validate.js, and the USB_RECOVER path constant in test/hil/usbtest.py - CLAUDE.md: fold the dcd/hcd datasheet cross-check rule into the read-doc line
This commit is contained in:
@ -8,8 +8,8 @@ model: sonnet
|
||||
You operate physical USB test hardware. These repo skills are your source of truth — read the relevant one BEFORE acting:
|
||||
|
||||
- `.claude/skills/hil/SKILL.md` — run `hostname` first (host `ci` = local mode with `test/hil/tinyusb.json`; host `htpc` = local `local.json` or remote via `test/hil/hil_ci.sh`); the board lock protocol; exact `hil_test.py` invocations.
|
||||
- `.claude/skills/usb-recover/SKILL.md` — only when a device/fixture is wedged or processes hang in D state.
|
||||
- `.claude/skills/usb-debug/SKILL.md` — only when you need to explain WHY the host rejected a device (dmesg analysis).
|
||||
- `.claude/skills/usb-kernel-recover/SKILL.md` — only when a device/fixture on the rig's Linux host is wedged or processes hang in D state.
|
||||
- `.claude/skills/usb-kernel-debug/SKILL.md` — only when you need to explain WHY the Linux kernel rejected a device (dmesg analysis).
|
||||
|
||||
## Board lock protocol (CI runs concurrently — NEVER stop the actions-runner)
|
||||
|
||||
@ -30,7 +30,7 @@ The GitHub Actions runner keeps running during your work. Per-board flock locks
|
||||
|
||||
- HIL runs take 2–5 min per board: use Bash timeouts >= 20 min (1200000 ms) and NEVER cancel early.
|
||||
- One hardware action at a time. You are never run concurrently with another hil-operator.
|
||||
- On test failure: retry once with `-v -r 1` appended (one verbose attempt for diagnosis — the first run already did the flake-retries). If a board/fixture stops enumerating or tools hang in D state, consult usb-recover and capture `dmesg | tail -50` into `detail`; set `wedged` true.
|
||||
- On test failure: retry once with `-v -r 1` appended (one verbose attempt for diagnosis — the first run already did the flake-retries). If a board/fixture stops enumerating or tools hang in D state, consult usb-kernel-recover and capture `dmesg | tail -50` into `detail`; set `wedged` true.
|
||||
|
||||
## Output contract
|
||||
|
||||
|
||||
@ -1,25 +1,32 @@
|
||||
---
|
||||
name: target-debugger
|
||||
description: Root-cause one USB misbehavior on real HIL hardware by instrumenting the TinyUSB device side — TU_LOG/RTT, RAM ring-buffer trace, GDB autopsy, J-Link PC-sampling — correlated with host-side and wire-level capture. Long serial debug loop under one held board lock; strictly one instance. Produces a diagnosis with on-target evidence (plus a candidate fix when one emerges), never a merged patch.
|
||||
description: Root-cause one USB misbehavior on real HIL hardware by instrumenting the TinyUSB target — device or host stack — with TU_LOG/RTT, RAM ring-buffer trace, GDB autopsy, J-Link PC-sampling, correlated with capture from the link's other end (Linux PC host, another TinyUSB board, or a Linux gadget peer) and the wire. Long serial debug loop under one held board lock; strictly one instance. Produces a diagnosis with on-target evidence (plus a candidate fix when one emerges), never a merged patch.
|
||||
model: opus
|
||||
---
|
||||
|
||||
You debug one failing USB behavior on one physical board until you can name the
|
||||
mechanism — or report exactly what you ruled out. These repo skills are your
|
||||
source of truth; read the relevant SKILL.md BEFORE acting:
|
||||
mechanism — or report exactly what you ruled out. The target may run the device
|
||||
stack, the host stack, or both; its link peer may be the Linux PC, another
|
||||
TinyUSB board, or a Linux gadget (e.g. a Raspberry Pi) — pick capture channels
|
||||
by which end runs Linux, not by habit. These repo skills are your source of
|
||||
truth; read the relevant SKILL.md BEFORE acting:
|
||||
|
||||
- `.claude/skills/usb-target-debug/SKILL.md` — your primary playbook: technique
|
||||
choice by intrusiveness, capture recipes, GDB autopsy, all rig warnings.
|
||||
- `.claude/skills/target-debug/SKILL.md` — your primary playbook: technique
|
||||
choice by intrusiveness, channel choice by link topology, capture recipes,
|
||||
GDB autopsy, all rig warnings.
|
||||
- `.claude/skills/hil/SKILL.md` — host/config selection, board lock protocol,
|
||||
`hil_test.py` invocation.
|
||||
- `.claude/skills/usbmon/SKILL.md` — host-side URB capture (the default posture
|
||||
is dual-side: host + target simultaneously).
|
||||
- `.claude/skills/usbmon/SKILL.md` — Linux-host URB capture; exists only when a
|
||||
Linux PC is the link's host (the default posture is dual-side: both ends
|
||||
simultaneously).
|
||||
- `.claude/skills/usb-sniffer/SKILL.md` — wire-level capture with the hardware
|
||||
tap, when the host can't see the bus (device never enumerates, pre-URB
|
||||
failures) or when usbmon and device logs disagree — the wire arbitrates.
|
||||
- `.claude/skills/usb-debug/SKILL.md` — why the host acted (dmesg/dynamic debug).
|
||||
- `.claude/skills/usb-recover/SKILL.md` — only when the DUT or fixture wedges
|
||||
the host stack.
|
||||
tap: when the host can't see the bus (device never enumerates, pre-URB
|
||||
failures), when usbmon and target logs disagree — the wire arbitrates — or
|
||||
when TinyUSB is the host and no end has usbmon.
|
||||
- `.claude/skills/usb-kernel-debug/SKILL.md` — why the Linux kernel acted
|
||||
(dmesg/dynamic debug); the PC host, or a Linux gadget peer's device side.
|
||||
- `.claude/skills/usb-kernel-recover/SKILL.md` — only when the DUT or fixture
|
||||
wedges the rig PC's Linux host stack.
|
||||
|
||||
## The loop (deliberately serial — no fan-out)
|
||||
|
||||
|
||||
@ -38,5 +38,5 @@ Invoke the Workflow tool:
|
||||
|
||||
- Per-stage table: unit / build:<board> / size / pvs, then HIL per board — pass/fail with the first error for each failure.
|
||||
- If the hardware result has non-empty `locked` (a CI job held those boards): ask the user with AskUserQuestion — **Force now** (re-invoke `hil-validate` with `force: true` for those boards; user accepts the risk of colliding with a mid-test CI job), **Keep waiting** (re-invoke `hil-validate` for them after a few minutes; ask again if still locked), or **Accept** the partial verdict. Never force without the user's answer.
|
||||
- Wedged boards: point at `.claude/skills/usb-recover/SKILL.md`.
|
||||
- Wedged boards: point at `.claude/skills/usb-kernel-recover/SKILL.md`.
|
||||
- End with a clear ship / no-ship verdict and what to fix first.
|
||||
|
||||
@ -1,23 +1,28 @@
|
||||
---
|
||||
name: usb-target-debug
|
||||
description: Use when a TinyUSB device misbehaves on real hardware and host-side capture can't explain it — a HIL test fails but usbmon shows only Submits with no Completes, the device silently NAKs, wedges, STALLs, babbles, or drops data, EP0 starves, an ISR or DCD/HCD state bug is suspected — and you need device-side evidence: TU_LOG/RTT logs, GDB state dumps, a RAM ring-buffer event trace, or PC-sampling of where the core spins.
|
||||
name: target-debug
|
||||
description: Use when TinyUSB firmware — device or host stack — misbehaves on real hardware and capture from the other end can't explain it: a HIL test fails but usbmon shows only Submits with no Completes, the device silently NAKs, wedges, STALLs, babbles, or drops data, EP0 starves, tuh_ enumeration of an attached device fails, an ISR or DCD/HCD state bug is suspected — and you need target-side evidence: TU_LOG/RTT logs, GDB state dumps, a RAM ring-buffer event trace, or PC-sampling of where the core spins.
|
||||
---
|
||||
|
||||
# usb-target-debug — device-side capture & debugging on the HIL rig
|
||||
# target-debug — target-side capture & debugging on the HIL rig
|
||||
|
||||
Completes the debugging trio (the `usb-sniffer` skill adds a fourth,
|
||||
wire-level view when hardware tapping is available):
|
||||
The **target** is whichever MCU runs TinyUSB — device stack (`dcd_*`), host
|
||||
stack (`hcd_*`/`tuh_*`), or both. Its link peer is not always a Linux PC: a
|
||||
TinyUSB host may face another TinyUSB board or a Linux gadget (e.g. a
|
||||
Raspberry Pi). Pick capture channels by which end runs Linux, not by habit:
|
||||
|
||||
| Skill | Answers |
|
||||
|---|---|
|
||||
| `usbmon` | what the host actually exchanged (URBs) |
|
||||
| `usb-debug` | why the host acted (dmesg / dynamic debug) |
|
||||
| **`usb-target-debug`** | **what the device did** (logs, driver state, PC) |
|
||||
| `usb-sniffer` | what crossed the wire (PIDs, handshakes, resets — hardware tap) |
|
||||
| Skill | Answers | Exists when |
|
||||
|---|---|---|
|
||||
| `usbmon` | what the Linux host exchanged (URBs) | a Linux PC is the link's host |
|
||||
| `usb-kernel-debug` | why the Linux kernel acted (dmesg / dynamic debug) | Linux on either end: PC host or Linux gadget peer |
|
||||
| **`target-debug`** | **what the target did** (logs, driver state, PC) | always — either role, needs a debug probe |
|
||||
| `usb-sniffer` | what crossed the wire (PIDs, handshakes, resets) | hardware tap cabled in — role-agnostic |
|
||||
|
||||
For enumeration/transfer bugs the default posture is **dual-side capture** —
|
||||
usbmon on the host *and* a target-side channel, simultaneously — not
|
||||
host-first-then-escalate.
|
||||
both ends simultaneously, not one-side-first-then-escalate: usbmon plus a
|
||||
target channel when a Linux PC is the host. When TinyUSB is the host there is
|
||||
no usbmon on either end — pair the target channel with the wire
|
||||
(`usb-sniffer`) and, if the peer is a Linux gadget, `usb-kernel-debug` on the
|
||||
peer.
|
||||
|
||||
## Rig discipline — lock first, always
|
||||
|
||||
@ -91,7 +96,8 @@ wedged; do not rebuild while the wedge is still on the board. The debug-loop
|
||||
specifics:
|
||||
|
||||
```gdb
|
||||
p/x _usbd_dev.ep_status # usbd core [epnum][dir] (1=IN): busy/stalled/claimed
|
||||
p/x _usbd_dev.ep_status # device stack: usbd [epnum][dir] (1=IN): busy/stalled/claimed
|
||||
p _usbh_devices[0] # host stack: usbh per-device state (addr, enum/config)
|
||||
p/x <port's private state> # per-port names — read the board's dcd_*.c first
|
||||
x/32wx <USB peripheral base> # raw EP/FIFO regs; base = the macro the dcd uses
|
||||
watch xfer_status[2][1].total_len # HW watchpoint (Cortex-M: ~4); dwc2 names shown
|
||||
@ -153,7 +159,9 @@ top entries are the spin site; a flat histogram = core is servicing normally.
|
||||
|
||||
## Dual-side capture — the default for enumeration/transfer bugs
|
||||
|
||||
Start both channels, then trigger the failing test:
|
||||
Start both channels, then trigger the failing test (Linux-PC-host link shown;
|
||||
TinyUSB-as-host: swap the usbmon line for a `usb-sniffer` capture, plus
|
||||
`usb-kernel-debug` on the peer if it is a Linux gadget):
|
||||
|
||||
```bash
|
||||
.claude/skills/usbmon/scripts/usbcap.sh cafe: 30 /tmp/host.pcapng & # host URBs (usbmon skill)
|
||||
@ -172,8 +180,8 @@ the wire itself: `usb-sniffer` skill (hardware tap, PID-level).
|
||||
|
||||
- **Halting/resetting via the probe does NOT disconnect the device**: a DWC2
|
||||
soft-connect pullup stays up through core halt *and* reset, so the host's
|
||||
stuck URBs stay stuck and a wedged DUT stays wedged — recover the host side
|
||||
with the `usb-recover` skill.
|
||||
stuck URBs stay stuck and a wedged DUT stays wedged — recover the Linux
|
||||
host side with the `usb-kernel-recover` skill.
|
||||
- **A bug that vanishes under LOG=2 is a timing bug**, not fixed: switch to
|
||||
the ring buffer; if it vanishes under GDB too, PC-sampling only.
|
||||
- **UART TU_LOG blocks in the write path** (worst perturbation, including
|
||||
@ -1,36 +0,0 @@
|
||||
---
|
||||
name: usb-debug
|
||||
description: Use when USB enumeration fails or misbehaves and usbmon alone can't explain WHY the host acted — port reset storms, repeated re-enumeration, address errors, xHCI ring/command errors, "device descriptor read error", babble, or when you need the host driver's own reasoning from dmesg on the ci HIL rig.
|
||||
---
|
||||
|
||||
# usb-debug — host-side kernel dynamic debug for USB
|
||||
|
||||
usbmon shows the URBs; kernel **dynamic debug** shows the host driver's
|
||||
*reasoning* usbmon can't: port resets and their causes, enumeration retries,
|
||||
address (re)assignment, EP halts, xHCI ring/command errors.
|
||||
|
||||
Run this skill's `scripts/usb_dyndbg.sh` with `sudo` (abbreviated to
|
||||
`usb_dyndbg.sh` in the examples below). It flips the dynamic-debug print flag
|
||||
for an allowlisted set of USB host modules only:
|
||||
|
||||
```bash
|
||||
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 cdc_acm usb_storage uas`.
|
||||
|
||||
## 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).
|
||||
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.
|
||||
|
||||
Pair with the `usbmon` skill: usbmon for what crossed the bus, dynamic debug for
|
||||
why the host reacted. For a wedged device/bus use the `usb-recover` skill.
|
||||
|
||||
Requires `CONFIG_DYNAMIC_DEBUG` and mounted debugfs (standard on distro kernels).
|
||||
47
.claude/skills/usb-kernel-debug/SKILL.md
Normal file
47
.claude/skills/usb-kernel-debug/SKILL.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
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` (abbreviated to
|
||||
`usb_dyndbg.sh` in the examples below). It flips the dynamic-debug print flag
|
||||
for an allowlisted set of USB modules only:
|
||||
|
||||
```bash
|
||||
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).
|
||||
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# usb_dyndbg.sh — toggle kernel dynamic-debug on USB host drivers; run with sudo.
|
||||
# Flips +p/-p only on an allowlisted set of USB modules, so it can't reach
|
||||
# arbitrary kernel debug or unrelated subsystems.
|
||||
# usb_dyndbg.sh — toggle kernel dynamic-debug on USB drivers (host or gadget
|
||||
# side); run with sudo. Flips +p/-p only on an allowlisted set of USB modules,
|
||||
# so it can't reach arbitrary kernel debug or unrelated subsystems.
|
||||
#
|
||||
# Usage:
|
||||
# sudo usb_dyndbg.sh on <module>... # enable +p (e.g. usbcore xhci_hcd)
|
||||
@ -10,8 +10,9 @@
|
||||
set -euo pipefail
|
||||
|
||||
CTL=/sys/kernel/debug/dynamic_debug/control
|
||||
# Allowlist: USB host-controller + core + common host class drivers.
|
||||
ALLOW='usbcore xhci_hcd xhci_pci xhci_pci_renesas ehci_hcd ehci_pci ohci_hcd ohci_pci uhci_hcd dwc2 cdc_acm usb_storage uas'
|
||||
# Allowlist: USB core + host-controller + common class drivers, plus the
|
||||
# gadget/UDC side of a Linux peer (dwc2/dwc3, udc_core, libcomposite).
|
||||
ALLOW='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'
|
||||
|
||||
die() { echo "usb_dyndbg: $*" >&2; exit 1; }
|
||||
usage() {
|
||||
@ -1,9 +1,9 @@
|
||||
---
|
||||
name: usb-recover
|
||||
description: Use when a USB device or fixture on the ci HIL rig is stuck, hung, not enumerating, or wedged after a failed flash or test, or when processes touching USB (testusb, JLinkExe, uhubctl, libusb tools) start hanging in D state.
|
||||
name: usb-kernel-recover
|
||||
description: Use when a USB device or fixture attached to the ci HIL rig's Linux host is stuck, hung, not enumerating, or wedged after a failed flash or test, or when processes touching USB (testusb, JLinkExe, uhubctl, libusb tools) start hanging in D state. Linux-kernel-side only — a bus owned by a TinyUSB host is out of reach (reset the target / cycle its VBUS instead); the rig's probes and serial fixtures always remain in scope.
|
||||
---
|
||||
|
||||
# USB Recovery on the HIL Rig
|
||||
# USB Recovery on the HIL Rig (Linux kernel side)
|
||||
|
||||
Run this skill's `scripts/usb_recover.sh` with `sudo` (abbreviated to
|
||||
`usb_recover.sh` in the examples below). It wraps the sysfs reset actions, a
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: usb-sniffer
|
||||
description: Use when you need wire-level USB evidence that host-side capture can't provide — a device that never enumerates (usbmon shows nothing or only Submits), suspected NAK storms/STALL/babble/bad handshakes, bus-reset or enumeration timing, split-transaction issues, or a usbmon-vs-device-log disagreement the wire must arbitrate. Captures LS/FS/HS packets (PIDs, tokens, handshakes, SE0/line states) with the ataradov usb-sniffer hardware into Wireshark pcapng.
|
||||
description: Use when you need wire-level USB evidence that host-side capture can't provide — a device that never enumerates (usbmon shows nothing or only Submits), suspected NAK storms/STALL/babble/bad handshakes, bus-reset or enumeration timing, split-transaction issues, a usbmon-vs-device-log disagreement the wire must arbitrate, or any link where TinyUSB is the host (no Linux PC host to run usbmon on). Captures LS/FS/HS packets (PIDs, tokens, handshakes, SE0/line states) with the ataradov usb-sniffer hardware into Wireshark pcapng.
|
||||
---
|
||||
|
||||
# usb-sniffer — wire-level capture with the ataradov hardware analyzer
|
||||
@ -9,14 +9,16 @@ Extends the debugging trio with the layer below URBs:
|
||||
|
||||
| Skill | Answers |
|
||||
|---|---|
|
||||
| `usbmon` | what the host software exchanged (URBs) |
|
||||
| `usb-debug` | why the host acted (dmesg / dynamic debug) |
|
||||
| `usb-target-debug` | what the device firmware did |
|
||||
| `usbmon` | what a Linux PC host exchanged (URBs) |
|
||||
| `usb-kernel-debug` | why the Linux kernel acted (dmesg / dynamic debug) |
|
||||
| `target-debug` | what the TinyUSB target did (device or host role) |
|
||||
| **`usb-sniffer`** | **what actually crossed D+/D-** (PIDs, handshakes, resets, timing) |
|
||||
|
||||
Reach for it when usbmon can't see (device never binds, pre-enumeration
|
||||
failures) or can't be trusted (URB completed but did the wire really ACK?).
|
||||
For everything visible in URBs, usbmon is cheaper — no hardware, no locks.
|
||||
failures), can't be trusted (URB completed but did the wire really ACK?), or
|
||||
doesn't exist — a link where TinyUSB is the host has no usbmon on either end
|
||||
(an MCU host runs no kernel; a Linux gadget peer's UDC bypasses usbmon).
|
||||
Where a Linux PC is the host, usbmon is cheaper — no hardware, no locks.
|
||||
|
||||
## Rig inventory — find the sniffer and what it taps
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
---
|
||||
name: usbmon
|
||||
description: Use when capturing, analyzing, or debugging USB bus traffic for TinyUSB device development on Linux — enumeration failures, STALLed control transfers, missing/short bulk or interrupt transfers, isochronous/audio dropouts, or descriptor problems. Captures host-side URBs with usbmon + tshark into a Wireshark pcapng and decodes them. Use whenever you need to see what the host actually exchanged with a device on real hardware, even if the user just says "sniff USB", "capture the enumeration", or "why won't my device enumerate".
|
||||
description: Use when capturing, analyzing, or debugging USB bus traffic on a link where a Linux PC is the host (TinyUSB in device role) — enumeration failures, STALLed control transfers, missing/short bulk or interrupt transfers, isochronous/audio dropouts, or descriptor problems. Captures host-side URBs with usbmon + tshark into a Wireshark pcapng and decodes them. Not applicable when TinyUSB is the host — no URBs traverse the PC (use usb-sniffer / target-debug). Use whenever you need to see what the Linux host actually exchanged with a device on real hardware, even if the user just says "sniff USB", "capture the enumeration", or "why won't my device enumerate".
|
||||
---
|
||||
|
||||
# usbmon — capture & debug USB traffic
|
||||
|
||||
`usbmon` records host-side **URBs** — control / bulk / interrupt / isochronous transfers, descriptors, class requests, STALLs, short packets — i.e. exactly what the host exchanged with a device. Use it to debug a TinyUSB device on real hardware. (It's host/URB-level, not wire-level; for SOF/ACK/electrical use a hardware analyzer.)
|
||||
`usbmon` records host-side **URBs** — control / bulk / interrupt / isochronous transfers, descriptors, class requests, STALLs, short packets — i.e. exactly what the host exchanged with a device. Use it to debug a TinyUSB device on real hardware. (It's host/URB-level, not wire-level; for SOF/ACK/electrical use a hardware analyzer.) It exists only on the Linux host side of a link: when TinyUSB runs the *host* stack (peer = another TinyUSB board or a Linux gadget, e.g. a Raspberry Pi), neither end has usbmon — capture the wire (`usb-sniffer` skill) or instrument the target (`target-debug` skill).
|
||||
|
||||
**Setup (assumed in place):** `usbmon` loaded and a udev rule `SUBSYSTEM=="usbmon", GROUP="wireshark", MODE="0640"` with your user in the `wireshark` group — so `tshark` captures with no `sudo`. Freshly added to the group? The running shell doesn't have it yet (group adds need a new login) — wrap captures in `sg wireshark -c 'tshark -i usbmon3 -s 128 -a duration:30 -w /tmp/cap.pcapng'`; reading a finished `.pcapng` (`tshark -r`) needs no group. `-s 128` (snaplen) keeps only URB headers/status, not payloads — use it for long/high-throughput captures.
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: usbtest
|
||||
description: Use when running, debugging, or porting the Linux usbtest/testusb battery (examples/device/usbtest, cafe:4010) — device "did not bind", SET_CONFIGURATION fails, a case fails with errno 110/32/5/71, toggle-clear/halt/unlink/iso failures, iso packets dropped, or a new MCU/DCD needs the full 30/30 sign-off.
|
||||
description: Use when running, debugging, or porting the Linux usbtest/testusb battery (examples/device/usbtest, cafe:4010) — device "did not bind", SET_CONFIGURATION fails, a case fails with errno 110/32/5/71, toggle-clear/halt/unlink/iso failures, iso packets dropped, or a new MCU/DCD needs the full 30/30 sign-off. Needs a Linux PC as the link's host driving TinyUSB in device role — it exercises the DCD, not the TinyUSB host stack.
|
||||
---
|
||||
|
||||
# usbtest — porting & debugging the Linux kernel USB battery
|
||||
|
||||
@ -52,7 +52,7 @@ if (!args.force) {
|
||||
}
|
||||
|
||||
const wedged = results.filter(r => r.wedged).map(r => r.board)
|
||||
if (wedged.length) log(`WEDGED boards needing usb-recover: ${wedged.join(', ')}`)
|
||||
if (wedged.length) log(`WEDGED boards needing usb-kernel-recover: ${wedged.join(', ')}`)
|
||||
// Workers cannot prompt the user — surface still-locked boards for the main
|
||||
// session to ask: force (re-invoke with force: true), wait, or accept.
|
||||
const locked = args.force ? [] : results.filter(r => !r.pass && r.detail.startsWith('board locked')).map(r => r.board)
|
||||
|
||||
@ -116,7 +116,7 @@ Cutting a release — version bump, regenerated files, the per-release changelog
|
||||
|
||||
## References
|
||||
|
||||
- MCU reference manuals, datasheets, schematics: before answering register/bitfield/pinout/errata/timing questions from memory or the web, use the `read-doc` skill (`.claude/skills/read-doc/SKILL.md`) to search and read them from `$HOME/Documents/calibre-library` (skill no-ops if the library is absent).
|
||||
- MCU reference manuals, datasheets, schematics: before answering register/bitfield/pinout/errata/timing questions from memory or the web — or changing a specific dcd/hcd driver — use the `read-doc` skill (`.claude/skills/read-doc/SKILL.md`) to cross-check against docs in `$HOME/Documents/calibre-library`; tell the user if the needed document is missing (skill no-ops if the library is absent).
|
||||
- Supported MCUs/boards: `hw/bsp/` and `docs/reference/boards.rst`.
|
||||
- USB classes: `src/class/{cdc,hid,msc,audio,…}/` — each has `*_device.c` and `*_host.c`.
|
||||
- Key files: `src/tusb.h`, `src/tusb_config.h`, `tools/get_deps.py`, `tools/build.py`, `test/unit-test/project.yml`.
|
||||
|
||||
@ -38,7 +38,7 @@ PID = '4010'
|
||||
GZ_REF = '0525 a4a0' # copy Gadget Zero's capability profile (ctrl_out+iso+intr)
|
||||
SYS_USB = Path('/sys/bus/usb/devices')
|
||||
DRIVER = Path('/sys/bus/usb/drivers/usbtest')
|
||||
USB_RECOVER = Path(__file__).resolve().parents[2] / '.claude/skills/usb-recover/scripts/usb_recover.sh'
|
||||
USB_RECOVER = Path(__file__).resolve().parents[2] / '.claude/skills/usb-kernel-recover/scripts/usb_recover.sh'
|
||||
PATTERN_PARAM = Path('/sys/module/usbtest/parameters/pattern')
|
||||
|
||||
# Battery per tier, in run order: control sanity first, then simple bulk,
|
||||
@ -391,7 +391,7 @@ def main():
|
||||
if pci:
|
||||
print(f'aborting battery: kernel-side hang, device wedged mid-transfer.\n'
|
||||
f'auto-recovering: sudo {USB_RECOVER} pci-reset {pci} '
|
||||
f'(see .claude/skills/usb-recover)', file=sys.stderr)
|
||||
f'(see .claude/skills/usb-kernel-recover)', file=sys.stderr)
|
||||
# FLR frees the D-state ioctl without the device lock; must run BEFORE
|
||||
# any unbind/remove_id, which would deadlock the bus otherwise
|
||||
if sudo([str(USB_RECOVER), 'pci-reset', pci]).returncode != 0:
|
||||
@ -418,7 +418,7 @@ def main():
|
||||
try:
|
||||
if unrecovered_hang:
|
||||
# testusb is still stuck in a usbfs ioctl holding the device lock; remove_id/unbind
|
||||
# would join the convoy and deadlock the bus (see usb-recover skill) — leave it be
|
||||
# would join the convoy and deadlock the bus (see usb-kernel-recover skill) — leave it be
|
||||
print('skipping cleanup after unrecovered hang: reboot required to release the bus',
|
||||
file=sys.stderr)
|
||||
elif not args.keep_binding:
|
||||
|
||||
Reference in New Issue
Block a user