<feed xmlns='http://www.w3.org/2005/Atom'>
<title>tinyusb.git, branch claude/audio-fifo-priming</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/tinyusb.git/atom/?h=claude%2Faudio-fifo-priming</id>
<link rel='self' href='http://cgit.235523.xyz/tinyusb.git/atom/?h=claude%2Faudio-fifo-priming'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/'/>
<updated>2026-08-28T19:47:14Z</updated>
<entry>
<title>docs: point the pr3851 followup at the merged PR (#3866) commit</title>
<updated>2026-08-28T19:47:14Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T18:54:42Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=0508df6fbbee2c956bfc2d92d89cb87994101ffc'/>
<id>urn:sha1:0508df6fbbee2c956bfc2d92d89cb87994101ffc</id>
<content type='text'>
</content>
</entry>
<entry>
<title>audio_test_freertos: prime and hold the EP IN FIFO at its threshold</title>
<updated>2026-08-28T19:47:14Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T18:08:56Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=c74b2904d91e59d533a8a63802aad6e34185786f'/>
<id>urn:sha1:c74b2904d91e59d533a8a63802aad6e34185786f</id>
<content type='text'>
The 1 ms producer tick and host SOF are independent clocks. Writing one
frame per tick from stream open leaves the FIFO within a frame of empty
for ~180 ms (flow control climbs ~1.5 B/ms), and on underrun the driver
replays the previous packet's tail (hfp HIL: 'expected 8702, got 8696' -
exactly one HS packet of 6 samples back). Hold the FIFO at the driver's
own flow-control threshold instead: primed at open, topped up per tick,
each write still requiring a full frame of room so the overwritable FIFO
never drops samples. Filling above threshold is deliberately avoided -
it parks flow control at max-size packets (48.73 kHz) and overruns the
host capture. Verified by simulating the driver's control law over the
2 s capture: zero underruns at +/-400 us jitter vs 3 events before.

The driver-side half (audiod_tx_xfer_isr ignoring a short tu_fifo_read_n
and submitting stale bytes) is docs/superpowers/followup/pr3851-audiod-
short-fifo-read.md, for its own PR.
</content>
</entry>
<entry>
<title>Merge pull request #3851 from hathach/etmtrace-rp2350</title>
<updated>2026-08-28T19:41:13Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T19:41:13Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=84dcec43c709643f8a1b1ebbab6d08fbbc605a6d'/>
<id>urn:sha1:84dcec43c709643f8a1b1ebbab6d08fbbc605a6d</id>
<content type='text'>
rp2350: ETM trace board pico2_etm_trace</content>
</entry>
<entry>
<title>docs: msc-host TUR retry handoff, split out of this PR</title>
<updated>2026-08-28T18:08:40Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T09:40:25Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=f7eb812679598aecc1d7702264ba48e6c178d5da'/>
<id>urn:sha1:f7eb812679598aecc1d7702264ba48e6c178d5da</id>
<content type='text'>
</content>
</entry>
<entry>
<title>rp2350: PIO-USB runs the pico-sdk stock 150 MHz</title>
<updated>2026-08-28T18:08:40Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T09:40:07Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=839c324458f6900a58182d9aa23d846e8ec55c94'/>
<id>urn:sha1:839c324458f6900a58182d9aa23d846e8ec55c94</id>
<content type='text'>
Closes the sys-clock question. rp2350: the dynamic 156 MHz switch is
removed - 150 MHz soak-tested clean (and a runtime switch truncates ETM
capture on the trace carrier). rp2040 keeps its existing 120 MHz: soak
sweeps show 120 = 8/8, stock 125 = 0/3 (bulk-OUT collapses, device NAKs
~600:1 with zero CRC errors on the wire), and 132 = 2/10 flaky despite an
exact 12n/375k divider - no divider criterion predicts rp2040 PIO-USB
health (the RX state machine samples at raw sysclk), so only
soak-validated clocks ship.
</content>
</entry>
<entry>
<title>pico2_etm_trace: RP2350 board on the MIPI-20 ETM trace carrier</title>
<updated>2026-08-28T18:08:31Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T09:39:58Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=6e8e2caf7fc2dc5575f5e0c71e69c1b19b3f5699'/>
<id>urn:sha1:6e8e2caf7fc2dc5575f5e0c71e69c1b19b3f5699</id>
<content type='text'>
Board files for the trace carrier (console GP12/13, LED GP10, I2C GP8/9,
PIO-USB host on GP20, all retargeted in board.cmake so the SDK defaults
cannot mux a trace pin), compile-time trace pin-conflict checks, the
measured DBGPAUSE rationale, Ozone project, and skill/docs updates.
Trace validated at the stock 150 MHz (75 MHz TRACECLK, +1 ns sampling):
zero overflow through a 15 s throughput soak; V2 probe ceiling 120 MHz.
</content>
</entry>
<entry>
<title>Merge pull request #3860 from hathach/claude/hil-blindness</title>
<updated>2026-08-28T09:09:22Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T09:09:22Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=64d952c027df194ea8b107b3d5d0b6c4cb9a994b'/>
<id>urn:sha1:64d952c027df194ea8b107b3d5d0b6c4cb9a994b</id>
<content type='text'>
hil: drop the sysfs blindness subsystem and derive the recovery reserve</content>
</entry>
<entry>
<title>test/hil, docs: move the containment history into the design doc</title>
<updated>2026-08-28T07:18:41Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T08:26:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=20bb94fcf9ad7fca7fb685e53307f4d03b1340fd'/>
<id>urn:sha1:20bb94fcf9ad7fca7fb685e53307f4d03b1340fd</id>
<content type='text'>
The modules were 21% comment, much of it review-cycle argument rather than
guidance -- _kill_kids stated 'descendant by construction, no argv check needed'
twice, eight lines apart. Deleting such comments outright makes maintenance
worse: the next reader simplifies the thing the comment was defending. So the
history moves to the 2026-07-30 fleet-wedge design doc, which gains a trim
addendum recording what was removed, what was deliberately kept, and the rule
that decided each -- the CI ceiling bounds how long a run burns, and does nothing
about state that outlives it.

One comment was not merely long but WRONG: the report wipe carried 'The unlink is
DEFERRED to inside the pool try/except below', which is the opposite of what the
code does -- it sits before Manager() with its own comment explaining why. That
is the failure mode this pass is about, so it is deleted rather than reworded.

Kept everywhere: citations that refute a plausible wrong reading. That
usb_lock_device_interruptible is why the readers are killable, that usblp_mutex
is driver-global, that rawmidi honours O_NONBLOCK where usblp does not.

Two follow-ups are retired with them: pr3803-hil-blindness-reporting.md (there
is no blindness to report any more) and pr3803-usbtest-recovery-reserve.md (the
reserve is derived now). Kept: pr3803-flasher-recover.md, which PR #3832
implements, plus pr3803-pci-rebind-stranding.md and pr3803-hil-iar-rerun-spec.md,
both independent of this work.
</content>
</entry>
<entry>
<title>test/hil: drop the sysfs blindness subsystem and derive the recovery reserve</title>
<updated>2026-08-28T07:18:41Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T08:26:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=b190840e1d85f806575f58635827502d714febcf'/>
<id>urn:sha1:b190840e1d85f806575f58635827502d714febcf</id>
<content type='text'>
Two layers whose cost was a contract to reason about rather than an outcome.

SYSFS_UNKNOWN was a three-valued return five files had to keep apart, and
misreading unknown as absence was silent: a healthy board reported as a firmware
regression. What it guarded is real -- `serial` is served by usb_string_attr,
which takes usb_lock_device_interruptible (v6.12.96 sysfs.c:141-143), the same
lock a wedged usbfs ioctl holds -- so the BOUND stays, on every caller by
default. usb_scan reads `serial` on every device matching the VID, and hil_lock's
controller_of does that on essentially every board, so one wedged DUT would
otherwise stall every worker, not one. What goes is the third value.

read_sysfs now returns str or None, and the question the third value existed to
answer is asked directly instead, by two predicates that say which question they
answer: sysfs_stranded() is process-wide and sticky, for hil_pool_check's footer
("could anything here be the tool losing sight of healthy hardware?"), and
path_stranded(path) is per-device, which is what usbtest needs to tell a DUT
whose `serial` is held under device_lock from one that genuinely left the bus --
that difference decides whether it performs driver-registry writes that take the
uninterruptible device_lock.

Gone: _SysfsUnknown, SYSFS_UNKNOWN, sysfs_blind, sysfs_blind_note,
note_sysfs_strand, the cross-process blindness publishing and its report banner,
usb_scan's (list, bool) return, usbtest's inconclusive abort, _blind_note's slot
in the result tuple, and bounded_open, whose last caller went in the previous
commit.

The strand memo is rewritten around the one invariant that makes it safe to
reuse: it is keyed by the path's kernfs inode, captured BEFORE the read. A
busport does not change when a board returns to the same physical port, so a
path-only blacklist outlives the wedge and hil_pool_check's own recovery flow --
reset, reflash, wait_device polling that busport -- would never look at the
board again. A re-enumeration destroys the kernfs node and makes a new one, so a
changed inode is the all-clear. Two ceilings bound different things: per path
(_PATH_STRAND_MAX) for a board that flaps while still wedged, and per process
(_STRAND_MAX) as a backstop against RLIMIT_NOFILE, counted per PATH rather than
per reader because hil_pool_check runs four poll threads over one bus.

A board the pool guard never reached is now reported as run-aborted rather than
pool-timed-out, and outranks a stale board-locked cell for the same reason the
pool-timeout cell does.

Both predicates answer conservatively where they are consulted before something
irreversible. path_stranded() covers the paths read_sysfs answered None for
WITHOUT reading -- past _STRAND_MAX it declines to start another reader, and
vouching for a path nobody looked at hands usbtest's fail-CLOSED guard a
fabricated all-clear, running remove_id/unbind against a wedged device. usbtest's
startup lookup carries the same caveat hil_test's absent arm already did, because
its stderr is relayed verbatim into the report cell.

strand_note() survives the removal for the same reason master had it: every
caller that can say "not found" needs the same sentence, and the one site left to
re-invent it got missed -- a wedged-but-enumerated printer was reported as an
enumeration failure, sending a maintainer after firmware.

The two predicates are not interchangeable, and usbtest needs both. Its per-case
verdict is per-DUT -- a peer that stranded at case 2 must not make our board
report wedged at case 29 -- but the finally block's cleanup is process-wide:
remove_id plus an unbind of EVERY interface under the driver, including that
peer's, each taking the uninterruptible device_lock. So the verdict uses
path_stranded() and the global cleanup stays gated on sysfs_stranded().

USBTEST_RECOVERY_BUDGET was an independent 250s that could not actually contain
the ladder it reserved for, which is why usbtest.py carried a _time_left() gate
re-deciding before every step -- with a bare '- 35' for costs paid downstream
that nobody could re-derive. Between them the two produced a recovery that
skipped its own steps for most real hangs.

The reserve is now derived from the bounds usbtest itself declares, per flasher
and per target: a probe reset, a reflash, and the Rescue-DP POR plus retry a
wedged RP DAP needs, plus the settles and hil_util.REAP_GRACE for each bounded
step. The Rescue-DP legs are openocd-only and gated on the RP target cfg, and
a stub reset is screened out, so the reserve tracks each board's real ladder
instead of one fleet number: 390s for the two RP boards -- whose ladder the old
250 could not contain, which is exactly why the gates skipped their steps --
190s for the other seventeen probe-reset boards, and 150s for esptool and
lm4flash, whose reset is a no-op. Changing a bound in usbtest moves the
reserve with it, and a unit test asserts it covers the ladder.

With the room actually reserved, the child runs the ladder straight through:
recovery_steps, _time_left, the three per-step gates and the parsed-but-unused
--outer-timeout are gone. What stays is what decides outcomes -- the convoy_safe
gate, reset-before-reflash, the no_op screen so a stub that resets nothing is not
claimed, and wedged_pids() as the arbiter, because a clean flash only proves the
probe wrote the MCU.

hil_util.py 616 -&gt; 514 lines.
</content>
</entry>
<entry>
<title>test/hil: run the printer write in a child, like the read</title>
<updated>2026-08-28T07:18:41Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T08:26:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=a5938f71fbc704c85979121bb9e37d5d636ef20d'/>
<id>urn:sha1:a5938f71fbc704c85979121bb9e37d5d636ef20d</id>
<content type='text'>
test_device_printer_to_cdc opened /dev/usb/lp* on the worker itself and let
hil_util.bounded_open abandon a thread when the open blocked. usblp allows one
opener -- usblp_open() returns -EBUSY while usblp-&gt;used (v6.12.96 usblp.c) --
so the abandoned thread's fd poisoned the node for every later test that worker
ran. The read half already avoided this by forking; the write half now does too,
via the same run_alongside, and a killed child takes its fd with it.

This removes the only production caller of bounded_open.
</content>
</entry>
</feed>
