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:
hathach
2026-07-23 10:18:11 +07:00
parent 1b5c26b76e
commit 8918c4fec4
15 changed files with 120 additions and 91 deletions

View File

@ -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 25 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

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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).

View 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).

View File

@ -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() {

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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`.

View File

@ -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: