<feed xmlns='http://www.w3.org/2005/Atom'>
<title>tinyusb.git/examples/device, branch rework-metrics</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/tinyusb.git/atom/examples/device?h=rework-metrics</id>
<link rel='self' href='http://cgit.235523.xyz/tinyusb.git/atom/examples/device?h=rework-metrics'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/'/>
<updated>2026-09-06T08:57:00Z</updated>
<entry>
<title>ci-pinned-boards: curate the board set, esp upload via hil-build-esp, rp2040 fixes</title>
<updated>2026-09-06T08:57:00Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-09-03T23:07:53Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=2a84a2c8b32116b9a9a34eb3b0f39d0644710e06'/>
<id>urn:sha1:2a84a2c8b32116b9a9a34eb3b0f39d0644710e06</id>
<content type='text'>
The cmake job builds and uploads every pinned board to membrowse on
every run, PRs and pushes alike (--identical when code did not
change) - it is the single owner of that upload, as before this
branch touched it.

Curate the board set itself. cxd56/spresense, da1469x/da1469x_dk_pro,
fomu/fomu and ft9xx/mm900evxb are no longer pinned CI boards: none sit
on either HIL roster and no other CI board carries their driver, so
their size tracking was build-only with no hardware behind it -
documented with honest `uncovered` reasons instead of silently
dropping coverage. Swap lpc15/lpcxpresso1549 -&gt; lpc11/lpcxpresso11u68
for dcd_lpc_ip3511, lpc17/lpcxpresso1769 -&gt; lpc40/ea4088_quickstart
for dcd_lpc17_40+ohci, and samd5x_e5x/same54_xplained -&gt;
metro_m4_express; adafruit_fruit_jam now also claims dcd_rp2040. All
four changed boards build-verified (device/cdc_msc, cmake), and the
rp2040 group's (family, board) sort order restored. Two stale notes
the swap left behind are fixed: lpcxpresso11u68 is on neither roster,
so its "HIL (htpc)" claim (copy-pasted from the entry it replaced) was
false, and ea4088_quickstart is a maintainer bench board noted the
same way frdm_kl25z and lpcxpresso1549 were.

Rename the board file to .github/ci-pinned-boards.json, with matching
--ci-pinned-boards/--ci-pinned-boards-only flags: it pins the board
that represents each family in CI, and membrowse history is keyed on
each pinned board's &lt;board&gt;/&lt;example&gt; target names, so the set is
explicitly held rather than derived. Content and target names are
unchanged; the per-entry "family" key is dropped - board names are
unique across hw/bsp/*/boards, so it was redundant data that could
drift from the tree. build.py's resolve_ci_boards() and the coverage
checker derive the family from the board dir, and entries are sorted
by board name, the only key left to sort on.

espressif builds have no leg in the cmake job (esp-idf is by far the
slowest toolchain, and its boards are already enumerated by name for
the rig) - hil-build-esp keeps its own upload-membrowse + secrets:
inherit instead, and hil-build-esp-identical covers the --identical
row for a no-code-change push, the same gap hil-build-esp's own
code-changed gate would otherwise leave. build_util.yml's Membrowse
Upload step skips any leg whose args carry --build-name, an extra -D
or --cflag - a HIL variant build - since it configures with the same
-DBOARD=&lt;name&gt; as the plain build and would otherwise fight it for
the same upload target name; this protects hil-build-esp's DMA
variants specifically. The step also does not scope by $EX_ARGS
(kept on the Build step): scoping the upload by the PR's example
filter would leave examples outside that filter with no upload
attempt at all - a silent history gap rather than the --identical row
the design intends.

Fork PRs: GHA withholds secrets, so MEMBROWSE_API_KEY reaches
membrowse_report.py empty. Master's CMake-expanded bare --api-key
fell through to membrowse's GitHub tokenless auth; the wrapper now
does the same by omitting --api-key entirely when the env var is
empty, rather than exiting - the previous hard-exit was silently
hidden behind the workflow's continue-on-error, so every fork PR
lost its upload with no visible failure.

Retiring ci_skip_boards let CircleCI's plain family sweep reach five
rp2040 boards for the first time; two didn't build. pico_sdk fails
dual/host_hid_to_device_cdc and dual/host_info_to_device_cdc - a
meta-board with no board.h pin defines of its own has no
PICO_DEFAULT_PIO_USB_* macros, which a dual-role rp2040 example needs
for its PIO-USB host side - so each example gained a skip.txt entry
naming the failure. feather_rp2040_max3421 failed host/bare_api on
-Wmissing-prototypes for max3421_int_handler in hw/bsp/rp2040/family.c;
it is only ever used as a same-file GPIO IRQ callback (no other caller),
so it is marked static rather than skip-listing a zero-risk one-line
fix. All 8 rp2040 boards now build clean, `-b &lt;board&gt;` and the bare
`rp2040` family sweep alike.

drivers_coverage_check.py type-checks `drivers` before iterating: a
string previously iterated character-by-character into nonsense "X
matches no driver source file" errors, and null raised an unhandled
TypeError.

Not fixed, flagged for a follow-up: MEMBROWSE_API_KEY reaches
membrowse_report.py as a child-process argv (visible in
/proc/&lt;pid&gt;/cmdline on the runner, even though the logged command line
is redacted) rather than via the environment.

Final-review fixes: the espressif Membrowse Upload docker run only
forwarded MEMBROWSE_API_KEY and CI into the container, so membrowse's
--github metadata detection (GITHUB_EVENT_NAME/GITHUB_SHA/...) and its
fork-PR tokenless auth never saw the actual event - add
GITHUB_ACTIONS/GITHUB_EVENT_NAME/GITHUB_SHA/GITHUB_REF_NAME plus a
read-only bind mount of GITHUB_EVENT_PATH (containers don't inherit
runner env by default; the esp Build step needs none of this, since
its `--target all` never builds the *-membrowse/-upload custom
targets, so its now-unused `pip install membrowse` is also dropped).
check-paths' code filter also gains tools/membrowse_report.py, so a PR
touching only that file exercises the real build+upload matrix
(ci_select rule 2d) instead of 74 no-op --identical legs.

Review fixes: generate_membrowse_sizes()'s all-examples scope (-b with no -e)
now averages each file's size across the elfs it appears in instead of
summing, matching metrics.py's compute_avg() semantics from the linkermap
engine this branch replaced - a file linked by N examples no longer reports
~N times its real size, and the Flash/RAM columns are a real binary's size
again; single-example (-e) stays byte-identical (sum, same as before). The
Membrowse Upload step (build_util.yml) and hil-build-esp-identical's loop
(build.yml) now strip any -e &lt;example&gt; pairs hil_ci_set_matrix.py bakes into
a PR-scoped hil-build-esp leg's args before invoking, so a PR-scoped run
still uploads every example of the pinned board rather than only the
PR-selected subset - closing exactly the silent history gap the step's own
comment already warned against.

Also: build.py's espressif branch gates the idf.py build path on 'all' being
present in build_targets rather than being exactly ['all'], so a combined
--target invocation still runs the build instead of silently skipping on a
fresh dir; membrowse_compare.py's layout-based bucketing unions the actual
flash/ram buckets of a multi-region section's regions instead of assuming
any 2+-region split is a flash+ram pair (e.g. RAM_D1/RAM_D2 now stays
ram-only); membrowse_report.py's extract_defsyms() dedupes to match its own
docstring.

Post-validate fixes, folded in. The Membrowse Upload step threads the
PR's -e filter through for BOARD ELIGIBILITY only: without it,
resolve_ci_boards() ran with examples=None and still resolved the
pinned board even when the Build step above had fallen back to a
substitute, uploading --identical for a board that run never built.
Under --ci-pinned-boards-only tools/build.py now nulls the example
filter after board selection, so the upload still touches every
example of whichever board got resolved - real rows for what was
built, --identical for the rest.

same7x earns its pin instead of assuming it: a full family sweep
showed five *_freertos examples failing (no FreeRTOSConfig.h wired
for same7x - skip.txt'd as family:same7x, naming the reason) and
same70_qmtech's board.cmake/mk pointing at a same70n19b_flash.ld
that never existed in the tree (the SDK ships none) - the board now
carries its own, Q21B's script with the N19B's MEMORY sizes (512K
flash / 256K ram per same70n19b.h), like the other boards with a
board-local .ld. Both same7x boards now build their full example sets
green under the real budget. cxd56 stays OUT of
ci_set_matrix's family_list and its BSP reverts to master untouched:
the same sweep showed NuttX-header failures across three examples,
spresense is unpinned anyway, and a -Wno-error suppression for a
family no CI leg builds silences a real diagnostic class for nothing.

drivers_coverage_check.py gains two guards: a pinned family that no
CI toolchain actually compiles is now a validity error (the dead-pin
case the same7x episode demonstrated), and a non-dict entry in
"boards" reports one clear error instead of a traceback. The spec and
plan docs are updated to the shipped names (membrowse-targets.json /
--board-pins / --pins-only never shipped; 71 occurrences), and stale
test fixtures naming the deleted tinyusb_metrics target are repointed
at examples-membrowse-upload.

Validate-loop fixes, folded in. membrowse_compare.py's report_for_elf()
now passes the elf's own linker scripts and --defsym values (read from
its ninja build graph, exactly as the CI upload path does) to
`membrowse report`: with none given membrowse falls back to its default
Code/Data regions, which the layout bucketing cannot map, so `.data`
lost its flash-side load image and was counted RAM-only (verified on
stm32h743eval: region 'Data' without the script, ['FLASH','RAM_D1']
with it). _bucket_from_layout() also unions in the flash side for a
BOTH_SECTIONS section whose layout lists only its RAM region -
pico-sdk's script never lists `.data` under FLASH, undercounting
raspberry_pi_pico's flash by 11.2K.

membrowse_onboard.py: `membrowse onboard` checks out and `git clean
-fdx`s every historical commit in whatever directory it runs, which
would detach HEAD and wipe the deps symlinks in the repo root - so the
wrapper runs it in a disposable worktree under cmake-metrics/, and the
composed build script relinks deps and reconfigures the (also wiped)
build dir before every historical build, not just the first.
--build-dirs now includes the example's own directory, so a change to
its sources no longer backfills as --identical. Not fixable in the
wrapper: onboard applies one commit's linker scripts to every commit in
the range (no per-commit override in its CLI).
</content>
</entry>
<entry>
<title>ci: an empty selection must build nothing, plus selector follow-ups (#3845)</title>
<updated>2026-08-25T02:46:42Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-25T02:46:42Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=da255b1d2db10b8f31332a779b2a526f579acee1'/>
<id>urn:sha1:da255b1d2db10b8f31332a779b2a526f579acee1</id>
<content type='text'>
ci: an empty selection must build nothing, plus selector follow-ups

A PR whose build axis legitimately selected nothing rebuilt everything.
build.yml reads .build.families twice - as a |-joined regex, and implicitly
as "is anything selected" - but tested only -z "$FAMILY_REGEX", which an
empty list and a charset-rejected one both satisfy while meaning opposite
things. ci_set_matrix had already returned the correct all-empty matrix;
the fall-open branch discarded it. #3842 and #3840 each spent 74 cmake legs
on it. Branch on the two cases instead, rename FAM_* to FAMILY_*, and cover
the block with a test that extracts it from build.yml and executes it - it
had no test at all, which is how this shipped through two merges.

Follow-ups to the same machinery: glob.escape the repo root at five sites,
so a checkout path containing [ or * stops failing closed; drop the ci-full
label, read after the matrix was already computed and so never functional;
delete 13 mcu:MKL25ZXX / mcu:SAME5X skip tokens matching no board; carry the
rule table in the module docstring, guarded against drift; and pin six
selection behaviours a mutation pass proved untested.

Cut the selector's cost 1.8x (26.0s -&gt; 14.6s) with 0 divergences over 260
paths, and stop scoping the membrowse upload by the PR example filter.</content>
</entry>
<entry>
<title>examples: document and work around the i.MX RT and LPC55 USB errata</title>
<updated>2026-08-18T15:07:49Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-16T18:02:54Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=19ff2ed615e4a97984aab5551ac8835ead53b9e7'/>
<id>urn:sha1:19ff2ed615e4a97984aab5551ac8835ead53b9e7</id>
<content type='text'>
ERR050101: while an isochronous IN endpoint is active, an IN token addressed to
that same endpoint number on ANOTHER device sharing the host can silently
unprime one of this device's OUT endpoints - control, bulk, interrupt or
isochronous alike. NXP states it cannot be detected by software and raises no
interrupt, so the endpoint simply stops answering and the transfer never
completes.

The workaround is a uniqueness requirement rather than a particular number: the
isochronous IN endpoint must not share its number with any IN endpoint in use on
the bus. One family-wide constant therefore defeats it, since two affected
boards on the same hub then pick the same number and each becomes the other's
aggressor. CFG_TUSB_MIMXRT1XXX_ERRATA_ERR050101 is set only for the parts whose
errata list it - RT1015, RT1020, RT1024 and RT1050, where it is marked no fix
scheduled, plus RT1060 and RT1064 rev A - so RT1010 and the RT11xx family keep
the ordinary number and cannot collide with an affected board beside them.
Several affected boards on one hub can still be given distinct numbers with
-DEPNUM_ISO_IN.

The guard covers every example that has an isochronous IN endpoint: audio_test,
audio_4_channel_mic, uac2_headset, cdc_uac2, usbtest, video_capture and
video_capture_2ch. The video examples move the endpoint only when streaming
isochronously, since the bulk configuration is unaffected, and video_capture_2ch
takes two numbers because it has two streams.

The macro name follows CFG_TUSB_RP2_ERRATA_E2/E4/E15 already in tree, and its
is fixed, and which cannot be told apart at compile time - a way to define it to
0.

device_issues.rst records ERR050101 against every affected part with a link to
each errata sheet, and adds the LPC55S2x USB.3 speed-detection and USB.5
isochronous IN entries, neither of which TinyUSB works around. The branch's
design notes are included under docs/superpowers.

Verified: 340 wedge-free runs on mimxrt1064_evk, which previously wedged within
hours, and the macro resolving to endpoint 0x87 on mimxrt1064_evk against 0x83
on mimxrt1010_evk and stm32f407disco.
</content>
</entry>
<entry>
<title>example(midi2): report device identity in midi2_device</title>
<updated>2026-08-14T18:21:24Z</updated>
<author>
<name>Saulo Veríssimo</name>
<email>sauloverissimo@gmail.com</email>
</author>
<published>2026-08-14T18:21:24Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=3c9e92c60abf3959ef0367f5965b53b2def28d58'/>
<id>urn:sha1:3c9e92c60abf3959ef0367f5965b53b2def28d58</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Enable APM32F0 dependency fetching and CI</title>
<updated>2026-07-25T03:28:29Z</updated>
<author>
<name>Jie Feng</name>
<email>hjf3108@gmail.com</email>
</author>
<published>2026-07-25T03:25:05Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=7b9761f1919014152d3e9ee1aaf203d7fb0e762b'/>
<id>urn:sha1:7b9761f1919014152d3e9ee1aaf203d7fb0e762b</id>
<content type='text'>
</content>
</entry>
<entry>
<title>misc fixes</title>
<updated>2026-07-19T12:38:54Z</updated>
<author>
<name>Jie Feng</name>
<email>hjf3108@gmail.com</email>
</author>
<published>2026-06-18T09:28:49Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=9418aba918d7c4107026c894e863d9ef0ec5db81'/>
<id>urn:sha1:9418aba918d7c4107026c894e863d9ef0ec5db81</id>
<content type='text'>
</content>
</entry>
<entry>
<title>examples now build</title>
<updated>2026-07-19T11:24:40Z</updated>
<author>
<name>Jie Feng</name>
<email>hjf3108@gmail.com</email>
</author>
<published>2026-06-10T10:49:07Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=6e0f455634126e7eba315ebc279e563aefbf5815'/>
<id>urn:sha1:6e0f455634126e7eba315ebc279e563aefbf5815</id>
<content type='text'>
</content>
</entry>
<entry>
<title>dcd_lpc17_40: address review findings in the iso paths</title>
<updated>2026-07-17T17:15:53Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-17T10:58:25Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=cb224400931b7fbc3477a87a258c0602092abe6b'/>
<id>urn:sha1:cb224400931b7fbc3477a87a258c0602092abe6b</id>
<content type='text'>
From a second max-effort review of the branch:

- Drop the dead TUSB_XFER_ISOCHRONOUS case in dcd_edpt_open: iso endpoints
  are armed via dcd_edpt_iso_alloc/activate (TUP_DCD_EDPT_ISO_ALLOC is
  defined for this IP), never through dcd_edpt_open, so the case and its
  dd-&gt;isochronous assignment were unreachable and asserted a false
  invariant. Only bulk/interrupt reach the switch now.

- Extend the iso compile gate to the classes that actually arm an iso
  endpoint: DCD_ISO_ENABLED now includes CFG_TUD_BTH (bth_device.c opens
  an iso voice endpoint). Without it a BTH build would compile the iso
  machinery out and fail SET_INTERFACE at runtime.

- Un-skip LPC175X_6X in the usbtest example: it shares dcd_lpc17_40.c with
  LPC40XX verbatim, so the "DCD has no isochronous support" skip reason no
  longer holds. Build-verified for lpcxpresso1769 (previously blocked by
  the skip).

- TU_ATTR_UNUSED on the ep_id_is_iso helper: every caller is under
  #if DCD_ISO_ENABLED, so non-iso builds don't reference it and clang's
  -Wunused-function (fatal in CI) rejected the build — gcc stays quiet.
  Verified with the full lpc17 and lpc40 example sets under arm-clang.

A fifth finding — bounding control_ep_read's PACKET_READY spin with a
timeout — was implemented and REVERTED: a naive 100k-iteration bound fires
on legitimately-slow control reads and intermittently drops the device
(hardware-proven by interleaved A/B testing against the pre-fix binary).
The infinite wait is retained; the read is only reached once out_received/
out_queued signal data is present, so the theoretical IRQ-off hang is not
reachable in practice.

Re-verified on ea4088_quickstart: usbtest 30/30 (repeated) + HIL 14/14.
</content>
</entry>
<entry>
<title>skill: add usb-sniffer — wire-level capture with the ataradov hardware tap</title>
<updated>2026-07-17T10:32:47Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-16T07:11:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=e5b47c9306471b41bd2d2ecbbe9ea8932028b380'/>
<id>urn:sha1:e5b47c9306471b41bd2d2ecbbe9ea8932028b380</id>
<content type='text'>
Fourth view in the USB debugging toolset (usbmon = host URBs, usb-debug =
host reasoning, usb-target-debug = device firmware, usb-sniffer = what
actually crossed D+/D-). Covers the ataradov/usb-sniffer analyzer:
headless pcapng capture (--speed ls/fs/hs, --fold, --limit self-exit),
Wireshark/tshark analysis recipes, and the wire realities that bite:
downstream broadcast, sniffer self-capture noise, xHCI devnum != wire
address, tap-point-dependent reset visibility (hub choreography anchors),
FS-behind-HS-hub splits. Every recipe hardware-validated on the rig,
including the capture-window floor (a 3 s window provably misses the
enumeration ladder; 3M packets minimum).

Two udev files with distinct audiences, not one:
- examples/device/99-tinyusb-examples.rules (renamed from 99-tinyusb.rules):
  the user-facing rules the examples need — cafe VID access, hidraw, the
  ModemManager blacklist, a couple of board probes. getting_started.rst,
  the webusb_serial README and its source comment point here.
- tools/88-tinyusb.rules: the HIL rig's private probe/analyzer allowlist,
  now with the sniffer (6666:6620 + blank FX2LP 04b4:8613). Installed on
  the rig only; the usb-sniffer skill references it.
</content>
</entry>
<entry>
<title>dcd_lpc17_40: fix stale EP0 out_received, add isochronous support</title>
<updated>2026-07-17T09:48:01Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-16T07:11:01Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=36cd9f9f46ca20be907ed57b874d9d1dc7b3bf64'/>
<id>urn:sha1:36cd9f9f46ca20be907ed57b874d9d1dc7b3bf64</id>
<content type='text'>
EP0 control-OUT fix (usbtest 14/21, errno 110/-74): usbd queues the
status-stage OUT ZLP of every control read with buffer=NULL, so the ISR's
`if (out_buffer)` check missed it and marked the arriving ZLP as
out_received instead. The stale flag poisoned the next control-OUT with
data: its first chunk "completed" instantly from an empty EP0 buffer and
the host's real DATA NAKed forever. Track queued transfers with an
explicit out_queued flag and void half-finished control state on a new
SETUP.

Isochronous support (UM10562 12.15.6): 5-word DMA descriptors with
per-packet size memory, buflen/present_count in packets, one packet per
FRAME (no DMARSet/EpIntEn involvement), completion at EOT for both
directions. Details that matter:
- the iso machinery (5th DD word + packet-size memory) is compiled only
  when an iso-capable class is enabled (CFG_TUD_AUDIO/VIDEO/VENDOR), so
  non-iso builds pay nothing: _dcd stays 648 B vs 1032 B with iso
- ISR dispatch keys on the hardware's fixed ep-number/type map
  (ep_id_is_iso), never on dd fields that thread mode rebuilds
- iso OUT honors Packet_valid (bit 16) and prefills the hardware
  writeback slots with 0, so a missed frame counts as 0 bytes instead of
  reading back stale buffer contents as data
- packet count is validated (tu_div_ceil &lt;= ISO_MAX_PACKETS) before the
  DD is touched, so an oversized transfer is refused without leaving a
  serviceable half-built descriptor armed for the frame engine
- dcd_edpt_iso_alloc and iso_activate both enforce the fixed iso endpoint
  numbers (3/6/9/12); classes ignore alloc's return value, so activate
  must not trust it

Un-skip LPC40XX in the usbtest example; tier 4 now enumerates and passes
iso cases 15/16/22/23. cdc_msc_throughput and printer_to_cdc had bulk on
iso-only EP3 (SET_CONFIGURATION failed with -32); add the LPC17/40 EPNUM
block (bulk on EP2/EP5) like other fixed-EP examples.

Verified on ea4088_quickstart: usbtest tier-4 battery 30/30 repeatedly
and the full device HIL suite 14/14 (incl. audio_test iso).
</content>
</entry>
</feed>
