A single USB488 TRIGGER message left the bulk-OUT endpoint un-armed, so the
host's next bulk-OUT transfer timed out. The trigger itself succeeded
silently, so the failure surfaced on a later, unrelated command; only a
USBTMC device clear recovered it. The bundled examples/device/usbtmc
reproduced this as shipped.
Every other branch of the STATE_IDLE dispatch in usbtmcd_xfer_cb() leaves
the endpoint in a defined state: it either transitions out of STATE_IDLE so
a later tud_usbtmc_start_bus_read() can re-arm it, or it stalls and lets the
CLEAR_FEATURE(ENDPOINT_HALT) handler recover it. USBTMC_MSGID_USB488_TRIGGER
did neither, and because the state stayed STATE_IDLE, even an application
following the contract documented in usbtmc_device.h got a silent no-op from
tud_usbtmc_start_bus_read().
Transition to STATE_NAK so the re-arm can take effect, and stall the
endpoint when trigger is unsupported or the application callback rejects it,
matching the existing handling for messages the driver cannot process. The
callback result is deliberately not wrapped in TU_VERIFY(), which would
return before the stall/re-arm and reintroduce the same hang.
Since the driver now re-arms after a trigger, drop tud_usbtmc_msg_trigger_cb
from the list of callbacks after which the application must do so.
Fixes#3821
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An XFER_COMPLETE dropped by a full event queue leaves its endpoint's
BUSY|CLAIMED state set forever - the consumer that normally clears it
never sees the event, so usbd_edpt_claim()/usbd_edpt_xfer() fail from
then on and the class never re-arms the endpoint. Clear both flags when
the enqueue fails: the completion is lost either way, but the endpoint
stays usable.
Unit test: arm a bulk endpoint, drop its completion against a full
queue, verify the endpoint can be claimed and re-armed.
Read the 128-bit device UUID from the flash PFR region at 0x0009FC70
(UM11126 rev 2.1, section 48.8) rather than falling back to the fixed
weak default in hw/bsp/board.c.
Verified on lpcxpresso55s69: cdc_msc enumerates with SerialNumber
E059C3E208F9B955B3BA4C5CC7F3D13D, matching the uid already recorded for
that board in test/hil/local.json.
A SETUP counted before a bus reset must not be carried across it: the
consumer would either skip a post-reset SETUP (count drained by the
stale entry) or, if the count leaked high for any other reason, skip
them all. usbd_reset() now zeroes the counter; the consumer already
guards on zero, and any pre-reset SETUP still in the queue is stale by
definition and correctly discarded.
A SETUP arriving while the event queue is full is silently dropped by
queue_event(), but _usbd_queued_setup has already been incremented. The
leaked count makes the event handler skip every subsequent SETUP
("Skipped since there is other SETUP in queue") forever: EP0 stays deaf
until tud_init() while the device otherwise looks alive - enumerated,
endpoints armed. Undo the increment when the enqueue fails.
Unit test: fill the queue so a SETUP is dropped, then verify the next
SETUP still completes a GET_DESCRIPTOR control transfer.
The self-contained net_ncm fuzz harness #includes ncm_device.c and stubs
the usbd symbols it references rather than linking the device stack.
tud_network_link_state() now calls usbd_defer_func(), so add a matching
no-op stub to keep the harness linking.
tud_network_link_state() delivered the NETWORK_CONNECTION notification
edge-triggered and fire-once: if a previous notification was still in
flight, notification_xmit() returned early and the notification for the
new link state was never queued. Because link_is_up is committed before
the send, the host could be left reporting a stale carrier state - e.g.
a permanent NO-CARRIER after a link up. The notification state was also
mutated from both the caller and the notify xfer-completion callback
with no serialisation, so on RTOS ports where tud_network_link_state()
runs in a task other than tud_task() the two could race.
Defer the whole link-state update onto the usbd task, so it can no
longer race the completion callback. A collision with an in-flight
notification is resolved by re-arming notification_xmit_state and
letting the existing completion callback drive it forward on the next
xfer completion, rather than adding a separate pending/retry flag.
A link toggle does not change the link speed, so strictly only the
NETWORK_CONNECTION notification needs (re)sending, but reusing the
existing speed-then-connection state machine keeps the fix on a single,
already-serialised code path.
Closes#3760
The comment above audiod_tx_packet_size() states flow control needs a FIFO
of at least 4*Navg, but the guard tests nominal_size[1] <= fifo_depth * 4 -
true for any FIFO larger than a quarter packet - instead of
nominal_size[1] * 4 <= fifo_depth. As written, flow control engages on
FIFOs far below its own documented minimum, where the depth/2 setpoint sits
within one packet of empty and the packet_size = 0 branch (a zero-length
packet, i.e. an audible 1 ms dropout for audio-class hosts) is reachable
from ordinary scheduling jitter rather than only from gross clock
deviation. With the guard corrected, undersized FIFOs fall back to the
plain min(count, max) path as intended.
test/hil: one openocd flasher, per-board verify and firmware extension
The four WCH boards move to `openocd`, leaving one flasher for all.
`verify` is now a per-board opt-out, not dropped fleet-wide: WCH cannot read flash back
over the WCH-Link sdi transport; the other seven openocd boards can, and say so explicitly.
FLASHER_SUFFIX decides each flasher's extension once — find_firmware returns the full path
and the flashers pass it through, so a build with only the wrong artifact is skipped rather
than failed mid-flash. --skip-flash bypasses the filter.
rescue_openocd() power-on-resets a wedged RP2040/RP2350 via its Rescue DP from the flash
retry; the probe has no reset line.
Drops unused openocd_adi, stflash, wlink_rs and uniflash, parks the unstable ra6m5_ek, and
tests that every roster flasher name dispatches.
hil, ci: scope HIL builds and tests to the boards a PR affects
Add test/hil/hil_select.py, a stdlib-only selector that maps a PR diff to the
rig boards, tests and BSP families a change can affect, and wire it into CI so
pull requests build and run only those. A port change picks its families' boards,
a class change picks the examples enabling that class, and device/host changes
prune the other role. Anything unclassified — infra, an unmapped port, a selector
error — falls back to the full matrix, and push/schedule runs are untouched.
Move the shared example lists to hil_examples.py; 54 hardware-free tests cover
the rules.
test/hil: add board-pool health check, split hil_test into focused modules (#3794)
Add test/hil/hil_pool_check.py: per-board rig health scan — probe presence,
light-example flash (dfu_runtime; device_info + serial check for host-only
boards), uid re-enumeration, safe recovery (probe authorized-toggle, board
reset), verified board_test re-park, USB topology report, and a markdown
summary table. Missing firmware is built on the spot (tools/build.py, idf.py
for espressif, one get_deps retry); row statuses: ok, flash-failed, failed,
locked. Board locks are always respected, never bypassed.
Refactor hil_test.py into hil_lock.py (flock protocol, controller permits,
hold/release/status CLI; replaces board_lock.py) and hil_flash.py (flashers,
find_firmware, run_cmd). Update WCH probe uids and the board roster in
tinyusb.json; add the hil-pool-check skill.
The plan doc records why the fork exists and how each vendor source was
ported; the interim handoff it superseded is dropped.
CLAUDE.md: a new worktree should symlink the dependency dirs to the
primary checkout rather than re-fetching them, replacing a single
symlink only when the branch needs a different dep revision. Also allow
'linke' in codespell - WCH-LinkE is a product name.
bsp, hil: flash with the unified OpenOCD fork
https://github.com/hathach/openocd (branch tinyusb) is mainline plus every
config these boards need: RPi RP2350, ADI max32/max78, the MounRiver WCH
configs, and the wlinke adapter on mainline's riscv target. It is a superset
of the vendor forks, so one 'openocd' covers all boards; -DOPENOCD=/OPENOCD=
still select another, msdk's when MAXIM_PATH is set.
Drops family_flash_openocd_wch and the OPENOCD_WCH pair, dedups
family_flash_openocd_adi, aligns ch583's work area, and points hil at the
flasher's own config instead of generating one per probe.
Verified: HIL green on all four WCH boards and max32666fthr.
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.
Coverage rows now resolve the de-collided module-qualified key introduced
for same-named statics. RA boards: document that --attach requires a
debugger-booted target - the C_DEBUGEN gate exists because an unguarded
TRCKCR write bricks standalone boots (hardware-proven), so the limitation
is documented rather than the guard weakened; a debugger-side TRCKCR hook
can lift it later once re-verified on hardware. The stm32n6 board-gating
suggestion is not taken: N6 trace pins are AF0-fixed chip-level, the same
family-wide pattern as stm32h7.
The board-specific PHY-reset nets move behind a board.h opt-in
(TRACE_ETM_QUIET_ENET_PHY on same70_xplained and mimxrt1170_evkb) so other
boards of those families cannot inherit a foreign GPIO write; the
chip-level trace pin muxes stay family-wide by design (same pattern as
stm32h7). same70 reference: width 1 is the validated default until the
J403.16 rework, and the hooks now wait (bounded) for PCKRDY3 before Ozone
arms trace. ra8m1 reference caches the boot ROM in AfterTargetConnect so
--attach sessions decode ROM execution too. etm_capture rejects an
unexpanded CMake JLINK_DEVICE with a clear error; PIO-USB + TRACE_ETM on
RP2350 is now a compile error (48 MHz trace clock is too slow for PIO-USB
and a runtime switch would desync the stream); etm_profile keeps
same-named statics from different modules as distinct rows.
Build-verified: same70_xplained, mimxrt1170_evkb, raspberry_pi_pico2.
Bot findings (Copilot/Codex): no-op board_trace_pinmux stubs for
lpcxpresso18s37/43s67 (TRACE_ETM otherwise broke their build), SAME70
ID_PIOD clock enable, capture-script duplicate BeforeTargetConnect on the
RA references, profile-script support for --no-timestamps itraces.
Deep review (whole branch): same70_xplained board row + caveat restored,
stale pico2 72 MHz claim corrected to the shipped 48, explicit
SetTracePortWidth(4) in the three references that relied on Ozone's
default, coverage-cell guard, median-based SysTick calibration, dead
session flag removed, stale RA8M1 divider comment fixed (0x02 = /4 is the
validated chip max) and the debugger guard indented.
EVKB bench findings: only R1884/D3 remains open (D1/D2 meter-verified);
RT1176 trace width is 1 or 4 only - J-Link arms the CSSYS TPIU and its
own sampler at 4-bit for any width>=2 request; a powered MCU-Link USB
breaks the external probe even with JP4 shorted.
Fresh bring-up pass on mimxrt1170_evkb: holding the 100M RTL8201 in reset
(ENET_RST_B = GPIO_LPSR_04) stops its RMII lines driving against the
shared trace pads and doubles the clean trace-pin rate to 50 MHz
(100 MHz CSTRACE root; 133 MHz root is marginal, stock 132 corrupts).
Validated 3x 8 s TinyUSB captures at 11.46M fetches. D1-D3 remain silent
in every configuration - the welded R1882-R1884 are electrically open;
reflow is the remaining step to width 4. Board notes gain JP4 (must be
shorted for an external probe on J58).
Fifth capture view alongside usbmon/kernel/target/wire: exact execution
history via J-Trace, existing only where the trace header is wired -
confirm with the user before reaching for it.
Instruction-level trace outranks PC-sampling when samples cannot resolve a
mechanism, but the J-Trace is exclusive per-board hardware: the agent uses
it only when its prompt says the board is trace-wired or the user asked,
and otherwise proposes it in notes - mirroring the lock-force consent rule.
target-debug replaced usb-target-debug in the debug-skill overhaul; update
the cross-skill table and PC-sampling pointer. Add the consent gate: the
J-Trace is a single probe moved between boards, so captures on a board the
user did not just ask about need explicit confirmation that it is wired.
J403 (bottom-side Cortex Debug+ETM footprint, header required):
TRACECLK=PD8 peripheral D, TRACED0-3=PD4-7 peripheral C. TRACE_ETM builds
hold the KSZ8081 PHY in reset (PD4-7 are its RMII receive outputs and it
drives against the trace stream), clock the TPIU from PCK3 (MCK/2) and
mux the pins; the ozone reference starts PCK3 in the post-reset/download
hooks - TPIU programming while PCK3 is stopped is silently lost. Width-1
validated at the stock 300 MHz core; width 4 blocked on a dead D1 line
(suspect probe channel, h743eval crosscheck pending).
J-Link's built-in RP2350 script owns the whole chip-side path (component
map is not ROM-table-discoverable; a custom JLinkScript replaces the
built-in one and kills pin trace), re-arming at every resume - firmware
does no trace setup. TRACE_ETM builds pin clk_sys to 48 MHz from crt0
(fly-wire seating-proof; the port is DDR at clk_sys/2 and the J-Trace PRO
V2 cliff sits just above 40 MHz TRACECLK - SEGGER requires V3.0+ for this
chip), clear TIMER0/1 DBGPAUSE (default freezes the us-timer while any
core is debug-halted and sleep_ms spins forever), and run the UART console
TX-only (GPIO1 = default UART0 RX = TRACECLK).
Generic TRCKCR setup gated on DHCSR.C_DEBUGEN (a standalone-boot TRCKCR
write wedges the chip un-attachable until power-cycle), two-step write per
the hardware manual. ra6m5_ek: div-4 (25 MHz pin) - div-2 is dead on this
board at every width/timing; J9 must be closed. ra8m1_ek: chip-max
120 MHz TRCLK / 60 MHz pin via the committed JLinkScript whose empty
OnTraceStart defers the trace clock to firmware (J-Link's from-reset
enable steps the clock mid-stream at the FSP MOCO-to-PLL switch);
ReadIntoTraceCache covers runtime ROM execution. J9 closed on both EKs -
open = SWD contention up to apparent bricks.
300 MHz core, 50 MHz TRACECLK, width 2: SB11/SB12 stub TRACED2/3 onto Zio
CN8 and kill width 4 under IRQ-heavy USB traffic (removal = width-4 TODO
at 600 MHz). Session note: --attach while a host actively polls the
device wedges its USB session.
metro_m7_1011 (custom ETM-header rework): 500 MHz core, 66 MHz TRACECLK
width 4, +50 ps; trace_etm_init ungates the 132 MHz trace root that
BOARD_BootClockRUN leaves gated. mimxrt1170_evkb: 996 MHz CM7 at width 1,
CSTRACE pinned to 50 MHz (stock 132 corrupts - the Ethernet PHY loads the
CLK net) and the CM7 platform trace-funnel port enabled in firmware:
J-Link does not program that funnel and everything reads register-perfect
yet silent without it. FlexSPI boot needs the committed SP/PC hooks; D1-D3
stay dead pending the R1882-R1884 continuity check (width-4 TODO).
H5 hangs its debug AP if trace CoreSight is touched unclocked (recover =
power-cycle): the reference's AfterTargetConnect clocks the DBGMCU trace
domain but defers IOEN to firmware, or the mid-boot clock switch desyncs
the decoder. Stock solder bridges make the CN5 path marginal: validated
config is 100 MHz core, width 1, +5 ns (board.h selects the reduced clock
for TRACE_ETM builds); width 4 / 250 MHz retest waits on SB removal.
nrf52840dk: 16 MHz TRACECLK (hardware cap) width 4, P25 soldered, SW7=Alt;
no family code needed (J-Link arms TRACECONFIG). nrf5340dk: TRACE_ETM
builds force the TAD port to 16 MHz (SystemInit's 64 MHz is marginal),
+3 ns sample timing; the interface MCU's UART1 flow control drives the
trace pins - SB27/SB28 must be cut (P0.10/P0.11 = TRACEDATA1/0).
BSP mux + board.h are register-proven; the module routes TRACECLK to the
header only with SJ1's 0-ohm resistor moved to pads 2-3 (Lauterbach doc
confirms), so hardware validation waits on that rework.
60 MHz TRACECLK (CCLK/2) width 4 with J5 DBG_EN fitted; board.h drops the
trace-line pull-ups and the ozone reference points at the device example.
A badly-mated ribbon reads register-perfect yet silent - re-seat first.