summaryrefslogtreecommitdiff
path: root/examples
AgeCommit message (Collapse)Author
32 hoursci-pinned-boards: curate the board set, esp upload via hil-build-esp, rp2040 ↵hathach
fixes 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 -> lpc11/lpcxpresso11u68 for dcd_lpc_ip3511, lpc17/lpcxpresso1769 -> lpc40/ea4088_quickstart for dcd_lpc17_40+ohci, and samd5x_e5x/same54_xplained -> 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 <board>/<example> 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=<name> 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 <board>` 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/<pid>/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 <example> 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).
32 hoursmetrics: replace the linkermap CI pipeline with membrowsehathach
Membrowse becomes TinyUSB's size-analytics system, in CI and locally. .github/membrowse-targets.json names the boards CI builds per family and the drivers each covers; a checker enforced by pre-commit fails when a dcd/hcd driver (plus ehci/ohci) is neither claimed nor documented in `uncovered`, validates board and family names against hw/bsp, and requires a board claiming an hcd driver to build at least one host/dual example. tools/build.py resolves that file into the build matrix and gates uploads on it. Locally, tools/membrowse_compare.py diffs `membrowse report --json --all-symbols` per source file, keyed on object_file (membrowse 1.2.9 truncates source_file to a basename), and metrics_compare_base.py uses it as the default engine, with `--engine linkermap` as the legacy map.json fallback (still required for --combined/--ci). A guard errors instead of writing a silent all-zero table when the filters match nothing. linkermap stops running on every build: the POST_BUILD hook goes, the explicit <target>-linkermap target and a new examples-linkermap aggregate stay, and family_add_linkermap() no-ops when the dep is not fetched. CI loses the whole linkermap pipeline - the code-metrics job, the sticky PR size comment, release metrics assets, per-family artifacts, metrics_pair_compare.py, the membrowse-onboard workflow and CircleCI's dead tinyusb_metrics lane. The cmake job builds the named boards (esp-idf included, membrowse wired into the IDF docker path via family_initialize_project and MEMBROWSE_LD_OVERRIDE) and uploads only those, keeping the --identical path for code-unchanged runs. ci_select follows: the size tooling no longer runs in any CI build, while the board list decides what a family's legs compile and so forces a full matrix. Adds the espressif, same7x, cxd56 and f1c100s families the board list needs, each build-verified. The upload itself moves out of cmake: family_add_membrowse()'s ~90-line inline bash becomes tools/membrowse_report.py, carrying ninja linker-script extraction with recursive INCLUDE resolution, --defsym collection, map-file detection, --identical fallback and upload composition. The API key is read from the environment at run time, passed via argv and redacted in logs - nothing is baked into build.ninja at configure time, which the CMake-expanded $ENV{MEMBROWSE_API_KEY} used to do. tools/membrowse_onboard.py wraps `membrowse onboard` for history backfill, deriving the build script, elf path and the CI-matching target name (<board>/<basename>) so a backfill cannot land under a name CI never uploads to; it refuses to start on a dirty worktree, since onboard checks out past commits in place. A membrowse skill documents local reports, size diffs, board-list maintenance, uploads and backfill; the code-size skill records the new default engine and its linkermap fallback. Three follow-up handoffs capture what was deliberately split out: full linkermap removal, membrowse --combined support, and the dead build_filtered plumbing.
2 daysexamples: align device-info descriptor handlinghathach
2 daysexamples: write the host cdc console over RTT when that is the consolehathach
console_write() forwards what the attached CDC device sent back to the board's console, and reached for board_uart_write() directly to stay non-blocking. On a board whose console is the debug probe there is no UART: board_uart_write() is a stub returning -1, so everything the device echoed was dropped, and the do/while loop retried a write that could never make progress and spun until more data arrived. Send it through SEGGER_RTT_Write() on LOGGER_RTT builds -- already non-blocking in the default NO_BLOCK_SKIP mode, which is what the UART path wanted board_uart_write() for. The retry is also bounded now. board_uart_write() is only documented to return the number of bytes sent, and the stubs disagree on how they say "no console here" -- lpc40 returns -1, apm32f0xx returns 0 -- so the loop cannot decide from the return value alone when to stop. It gives up after 10 ms without progress instead, which no draining console ever hits, and keeps the negative return as a fast path so a stub UART does not cost that wait on every read. The RTT header include lives in board_api.h so any example can reach it; the include path only exists when LOGGER=rtt defines LOGGER_RTT. host/cdc_msc_hid now passes on ea4088_quickstart, whose probe (LPC-Link2) has no VCOM: the CDC echo round-trips through a CH340 with TX-RX shorted.
2 daysdual: run host_info_to_device_cdc on ea4088_quickstarthathach
The board has both USB ports wired: U1 to the Type-A host connector and U2 to the micro-B device connector, and board_init() already puts the port select in that mode, so it can run the dual example. - Allow the example on LPC40XX. - Point the host stack at rhport 0: ohci maps the TinyUSB rhport onto its root-hub port index, and the host controller here is index 0, while the dual examples default to rhport 1. - Move the string-descriptor buffers out of the stack. They are written by the host controller, so on parts whose USB master cannot reach the CPU stack (this family) the strings came back as garbage. host/device_info already keeps its transfer buffer in CFG_TUH_MEM_SECTION; do the same here. Verified on ea4088_quickstart with a WCH-Link on the Type-A port and the micro-B plugged into a PC: mounted device 1 Device 1: ID 1a86:8010 SN 7FD88F0604B5 iManufacturer 1 wch.cn iProduct 2 WCH-Link
5 daysimprove example detailsHiFiPHile
Signed-off-by: HiFiPHile <[email protected]>
5 daysimplement per channel volume controlHiFiPHile
Signed-off-by: HiFiPHile <[email protected]>
11 daysImprove audio host stream event reportingHiFiPHile
Report asynchronous start and stop completion with transfer results. Replace the ambiguous stream error callback and byte count with explicit transfer-failure events, and ignore stale completions after stopping.
11 daysaudio: service example FIFOs at watermarksHiFiPHile
Move capture and playback FIFO servicing into the main-loop task at half-full and half-drained watermarks. Use transfer callbacks only for diagnostics, share one conversion buffer, and demonstrate that application reads and writes need not be synchronized to USB callbacks. Signed-off-by: HiFiPHile <[email protected]>
11 daysaudio: expose generic Audio Control requestsHiFiPHile
Remove the narrow public Feature Unit request API while retaining managed mute and volume helpers. Expose validated Audio Control descriptors during enumeration and provide raw asynchronous and synchronous entity requests for advanced controls. Signed-off-by: HiFiPHile <[email protected]>
11 daysaudio: allow stopped duplex streams to change rateHiFiPHile
Enforce a shared capture/playback sample rate only while the opposite stream is running. This lets both stopped streams be configured in either order while preventing a live shared clock from changing underneath a transfer. Signed-off-by: HiFiPHile <[email protected]>
11 daysaudio: add UAC2 host streaming supportHiFiPHile
Add protocol-selectable UAC1/UAC2 parsing, UAC2 terminal and Feature Unit topology, Clock Source discovery, sampling-frequency ranges, and protocol-specific stream controls. Cover UAC2 playback, capture, control discovery, and malformed descriptors with unit tests. Signed-off-by: HiFiPHile <[email protected]>
12 daysimplement explicit feeback supportHiFiPHile
Signed-off-by: HiFiPHile <[email protected]>
13 daysaddress copilot review findingsHiFiPHile
Signed-off-by: HiFiPHile <[email protected]>
13 daysMerge remote-tracking branch 'origin/master' into feature/tuh-audio-uac-1.0HiFiPHile
13 daysrestrict example to supported hcdHiFiPHile
Signed-off-by: HiFiPHile <[email protected]>
13 daysfeat(audio): discover stream mute and volume controlsHiFiPHile
Inspect each associated Feature Unit during mount, cache master mute support and the common MIN, MAX, and RES volume range, and ignore units with neither control. Add typed asynchronous and synchronous mute and volume APIs and demonstrate them in the host example. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): discard capture data without playbackHiFiPHile
Continue draining the capture FIFO but skip channel conversion and writes when playback is unavailable or intentionally stopped. This keeps capture running safely in microphone-only phases. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): keep stream state consistent on control failuresHiFiPHile
Route asynchronous activation and sampling-frequency failures through the stream error callback. Keep a running stream active when SET_INTERFACE alt 0 cannot be submitted so stop can be retried without diverging from device state. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): generate a continuous high-resolution test toneHiFiPHile
Replace the six-sample waveform with a 64-entry sine table and phase accumulator. The example now generates a continuous 1 kHz tone at the selected rate and packs the configured channel count correctly. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): avoid playback alt-setting glitch at startupHiFiPHile
Enter the microphone-only phase before starting playback when both directions exist. This avoids briefly selecting the playback alternate setting and immediately returning it to alt 0. Signed-off-by: HiFiPHile <[email protected]>
13 daysrefactor(audio): separate stream configuration and startHiFiPHile
Make configuration a synchronous local operation that selects and opens the endpoint. Start now activates the alternate setting and sets the sampling frequency afterward, including every restart, so devices cannot reset the selected rate with SET_INTERFACE. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): drive streams from transfer completionHiFiPHile
Keep capture and playback transfers continuously armed from their completion callbacks. Capture overwrites the oldest complete frames when full, while playback sends silence on underrun without consuming partial queued audio. Signed-off-by: HiFiPHile <[email protected]>
13 daysMerge master updates into the UAC1 host branchHiFiPHile
Bring the audio work onto the current host core and build files before applying the remaining review fixes. Signed-off-by: HiFiPHile <[email protected]>
13 daysfeat(audio): demonstrate speaker volume controlHiFiPHile
Look up the playback Feature Unit in the host example and set its master volume after configuration. The output also reports when a stream has no controllable Feature Unit. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): resolve Feature Units independent of orderHiFiPHile
Record terminal and Feature Unit links while parsing the AudioControl block, then resolve the stream association after all entities are known. Common capture and playback descriptor orders are both supported. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): reject continuous UAC1 sample-rate rangesHiFiPHile
Collect only format descriptors with discrete sampling frequencies. The configuration API exposes concrete tuples and cannot safely represent a continuous range as one selectable rate. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): honor full-speed isochronous intervalsHiFiPHile
Convert full-speed bInterval values to the scheduler microframe timebase and size transfers for the complete service interval. Endpoints with bInterval greater than one no longer run at a one-frame cadence. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): configure playback-only example devicesHiFiPHile
Select capture and playback configurations independently in the example. Speaker-only devices can now start playback without a capture configuration. Signed-off-by: HiFiPHile <[email protected]>
13 daysfeat(audio): track a Feature Unit per stream directionHiFiPHile
Associate one directly connected Feature Unit with each capture and playback stream. This lets applications control microphone and speaker paths independently. Signed-off-by: HiFiPHile <[email protected]>
13 daysfix(audio): repair the host audio example buildHiFiPHile
Add the missing preset and shared declarations, remove duplicate initialization, and correct callback call sites so the new example builds through the normal CMake flow. Signed-off-by: HiFiPHile <[email protected]>
14 daysci: an empty selection must build nothing, plus selector follow-ups (#3845)Ha Thach
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 -> 14.6s) with 0 divergences over 260 paths, and stop scoping the membrowse upload by the PR example filter.
2026-08-21examples: keep CFG_TUH_VENDOR 0 in tusb_config.hhathach
Removing the obsolete host vendor driver also dropped the `#define CFG_TUH_VENDOR 0` line from the six example configs that carried it. Put it back: host vendor is coming, and the configs are where a reader looks for the set of host classes an example can turn on. Restored byte-identical to the pre-removal state, each file keeping its own column alignment. The define is inert today - nothing under src/, hw/ or tools/ reads CFG_TUH_VENDOR - and it is 0 everywhere, so ci_select still reads the vendor class as enabled by no example and a change to it still selects nothing. Note the option's default in src/tusb_option.h is still gone; implementing the driver will need that back alongside the usbh driver-table entry.
2026-08-21ci: scope the build matrix and the HIL run to what a PR affectshathach
Every PR built all 74 legs (2494 example builds on GHA cmake alone) and flashed all 30 rig boards, whatever it touched. One classifier now walks the PR diff twice and answers three questions: which families to build, which examples per family, and which boards run which tests. Fail-open throughout - anything no rule classifies, any exception, any unusable output falls back to the full matrix, and a master push always builds everything. test/hil/helper/hil_select.py moves to tools/ci_select.py: it is no longer HIL-only, and tools/ is where the build side can import it. test_hil_select.py follows it as test_ci_select.py. Rules (docs/superpowers/specs/2026-08-19-ci-build-family-filter-design.md holds the full table): a port selects the families whose family.cmake references it, and its role - a dcd change skips host examples and vice versa; a class selects only the examples whose tusb_config.h enables its CFG_TU[DH]_ macro, following cross-class includes; an example selects itself; hw/bsp selects its family or board; hw/mcu and lib select whoever references them. CMake is the reference for all of it - make follows whatever cmake decides, family.mk is never scanned. Empty means empty (maintainer ruling): a rule that classifies a path to nothing selects nothing. Ports no family references, classes no config enables, libs no example builds and hw/mcu paths that resolve nowhere are all real - nothing compiles them, so nothing can validate them, and the master-push build is the net. Structural tests pin each such case with an explicit allowlist, so the day one stops being empty it fails pre-commit instead of silently narrowing CI. Per-example builds: build.py grows a repeatable -e, resolved against the targets CMake actually registered and batched into one `cmake --build --target a b c`. build_utils mirrors CMake's family_filter (the whole FAMILY_MCUS list, ${...} and string(TOUPPER ...) resolved) for the cmake side, while the make side keeps master's algorithm verbatim - the two build systems answer differently and a shared answer breaks lpc54's make link. hil-build gains this even on a full selection: 1702 example builds become 515. Transport: the selection travels as a file, never an argv or env var - a mass-sweep diff selects 261 KB against a 128 KiB exec limit, and E2BIG would fail the step before its own fallback could run. CircleCI carries the example map inside the generated config (pipeline parameters cap at 512 chars), swapped into the parameter defaults by sentinel match, and drops the scoping wholesale if that rewrite fails. Every PR-derived value written to $GITHUB_ENV/$GITHUB_OUTPUT is character-screened. Code metrics follow the scoping: metrics.py emits per-example totals, and metrics_pair_compare compares the (board, example) pairs present on both sides instead of a scoped run against a full-matrix average. The selector's own suite gates it in both providers: a selector that exits 0 with valid-but-wrong JSON is the one failure fail-open cannot catch, so a red suite means the full matrix.
2026-08-21examples: restore host builds on samd2x_l2xhathach
Nine examples/host/*/only.txt gate on family:samd21, but 2a8811ebb merged the samd2x and saml2x BSPs into hw/bsp/samd2x_l2x. skip_example takes `family:` from the directory name, so since that rename every one of these examples has been skipped on every board of the family, under make as well as cmake - although hw/bsp/samd2x_l2x/family.cmake wires src/portable/microchip/samd/hcd_samd.c. 107 host firmware images were being compiled nowhere. The merged family is wider than the old samd21 one, so three boards need an explicit skip rather than the rename alone: atsaml21_xpro, saml22_feather, sensorwatch_m0 - not samd21, so hcd_samd.c is not compiled for them (previous commit); SAML22 has no host controller at all curiosity_nano - SAMD21 with 16 KB RAM; msc_file_explorer_freertos overflows it by 3688 bytes (ram 122.51%). Only that one example; the other eight fit. Verified across the whole family: cmake 11 boards x 9/9 examples + curiosity_nano 8/9, three boards skipped, 0 failures; make 98 OK / 0 failed (was 98/9 before). Device examples on saml21 and saml22 are unaffected.
2026-08-21vendor: remove the obsolete host vendor driverhathach
vendor_host.c/.h implemented a CFG_TUH_VENDOR class driver that no example, board or test ever enabled: usbh's driver table entry was compiled out everywhere, and the six tusb_config.h files that mentioned the macro all set it to 0. Maintainer call - dead code, not a shrinking of supported classes. Removes the sources, the usbh driver-table entry, the CFG_TUH_VENDOR default in tusb_option.h, the tusb.h include, both build-system source lists, the rp2040 family.cmake entry and the IAR project template rows.
2026-08-18examples: document and work around the i.MX RT and LPC55 USB erratahathach
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.
2026-08-14example(midi2): report device identity in midi2_deviceSaulo Veríssimo
2026-08-14feat(class/audio): rework TUH_AUDIO into a WASAPI/ALSA-like stream APIZhang, Zhenjiang
Provide a high-level audio streaming API over UAC 1.0 devices while keeping the USB topology private: applications select supported {format, sample_rate, channels} configurations per logical stream, and the driver owns the mapping to AS interface, alternate setting, and endpoint. - One logical stream per direction per instance; multiple AS interfaces and alternate settings in a direction are merged into the stream's configuration list (discrete tuples; continuous ranges exposed as a single configuration at the top rate) - Asynchronous tuh_audio_configure(): SET_INTERFACE to the selected alternate setting, open/reconfigure the endpoint, set the sampling frequency when supported, initialize the FIFO and packet scheduler, then invoke the completion callback - Frame-based FIFO streaming: tuh_audio_read()/tuh_audio_write() queue whole frames; the driver owns transfer replenishment and fractional packet scheduling (44.1 kHz pays back the 0.1 frame/ms remainder via an accumulator for exact average pacing) - tuh_audio_start()/tuh_audio_stop() activate/deactivate the stream interface through SET_INTERFACE (alt n / alt 0) Driver correctness fixes: - Parse only the AC header's interface collection; MIDI Streaming and other subclasses are skipped - Keep every discrete format as a separate configuration; endpoints are opened only for the alternate setting selected by tuh_audio_configure() - Check tuh_interface_set() return values and SET_INTERFACE transfer results instead of ignoring failures - Validate instance state, direction, buffers, and frame counts in every transfer API - Feature Unit requests use the control's real width (mute/AGC/loudness 1 byte, others 2 bytes) and convert multibyte values to host order - Failed/stalled/aborted isochronous transfers reach only the error callback, never the capture/playback callbacks The audio_host example uses the new API: 48 kHz stereo by default, automatic stream restart on error callbacks, a sine test tone on the playback stream, and periodic mic-only / spk-only / echo phase switching.
2026-07-25Enable APM32F0 dependency fetching and CIJie Feng
2026-07-21Refactor TUH_AUDIO API and simplify multi-AS interface supportZhang, Zhenjiang
This commit refactors the TUH_AUDIO (USB Audio Host) class driver to simplify its public API and improve multi-AS (Audio Streaming) interface support. The changes are focused on three files: the core driver (audio_host.c/h) and the example application (audio_app.c). Key changes in src/class/audio/audio_host.h: - Remove tuh_audio_descriptor_cb_t and tuh_audio_mount_cb_t structures. The mount callback no longer passes a large descriptor-info struct; applications query per-AS info via tuh_audio_as_get_info(). - Add tuh_audio_get_dev_addr() and tuh_audio_get_feature_unit_id() accessors to retrieve device address and feature-unit ID from an interface index. - Simplify control-transfer APIs by replacing (daddr, itf_num, unit_id) parameters with a single idx parameter: tuh_audio_set_sampling_freq(idx, as_idx, ...) tuh_audio_get_sampling_freq(idx, as_idx, ...) tuh_audio_feature_unit_set(idx, control_selector, channel, ...) tuh_audio_feature_unit_get(idx, control_selector, channel, ...) - Add synchronous wrapper APIs using TU_API_SYNC macro: tuh_audio_get_sampling_freq_sync() tuh_audio_set_sampling_freq_sync() tuh_audio_feature_unit_set_sync() tuh_audio_feature_unit_get_sync() - Update isochronous endpoint APIs to use (idx, as_idx) instead of (daddr, idx): tuh_audio_receive(idx, as_idx, buffer, len) tuh_audio_send(idx, as_idx, buffer, len) - Remove tuh_audio_descriptor_cb() weak callback. - Update tuh_audio_mount_cb() signature from mount_cb(param) to no param. - Update tuh_audio_rx_cb()/tuh_audio_tx_cb() first parameter from idx to dev_addr for consistency with other class drivers. Key changes in src/class/audio/audio_host.c: - Delete tuh_audio_descriptor_cb weak stub. - Refactor get_idx_by_ep_addr() to iterate all AS interfaces per device instead of relying on single ep_in/ep_out fields. - Add audioh_get_ep_addr_by_dir() helper to find an endpoint address by direction across multiple AS interfaces. - Simplify audioh_close() cleanup: remove now-removed single-endpoint fields (ep_in, ep_out) and rely on tu_memclr(p_audio->as, ...). - Update audioh_xfer_cb() to pass dev_addr (not idx) to rx/tx callbacks, matching the new callback signature. - Simplify audioh_open(): remove descriptor-callback emission and the temporary desc_cb structure; store only ac_itf_num instead of bInterfaceNumber + iInterface + as_interface_num. - Rename local descriptor pointers for clarity: desc_input_terminal (was desc_it) desc_output_terminal (was desc_ot) Key changes in examples/host/audio_host/src/audio_app.c: - Remove now-unnecessary globals: audio_ep_in, audio_ep_out, audio_ac_itf, audio_feature_unit_id. - Initialize audio_dev_addr, audio_idx, audiostream_in_idx, audiostream_out_idx to 0xFF (TUSB_INDEX_INVALID_8) instead of 0. - Update print_as_interfaces() to use tuh_audio_as_get_count() and tuh_audio_as_get_info() instead of accessing mount_cb_data. - Update all callback signatures and API calls to match the new driver API.
2026-07-19misc fixesJie Feng
2026-07-19examples now buildJie Feng
2026-07-19Merge pull request #3775 from hathach/claude/add-usb-debug-sniffHa Thach
usb-target-debug/usb-sniffer skills
2026-07-18dcd_lpc17_40: address review findings in the iso pathshathach
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->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.
2026-07-17skill: add usb-sniffer — wire-level capture with the ataradov hardware taphathach
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.
2026-07-17migrate NXP Kinetis khci to chipidea ci_fs driver (device + host)hathach
Complete the khci -> chipidea ci_fs migration that was started for device (commit d70403f1f "host is not yet"): - device: switch kinetis_k/kl/k32l (Makefiles + k32l CMake) to dcd_ci_fs.c - host: add hcd_ci_fs.c (port of hcd_khci.c onto ci_fs_regs_t) and switch all Kinetis families to it; remove src/portable/nxp/khci entirely - enable host examples (device_info, cdc_msc_hid) for mcu:KINETIS_K - README: merge the KL and K32L2 rows into a single "KL, K32L" ci_fs row hcd_ci_fs.c also fixes two pre-existing host bugs found via HIL on frdm_k64f (present in the old hcd_khci.c too): - data toggle was flipped on a NAK in suspend_transfer; a NAK transfers no data so the toggle must be preserved, else the retried bulk packet is silently discarded by the device (MSC CBW/CSW hang). See comment in file. - prepare_packets asserted and dropped a transfer when the single shared BDT was still owned by an in-flight transfer under concurrent activity; now it returns busy and resume_transfer defers/retries on the next SOF. HIL verified on frdm_k64f: device 13/13, host cdc_msc_hid (CDC mount + echo + MSC mount, through a hub). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01ExGPLP5eU43LR7o6yYLpNi
2026-07-17dcd_lpc17_40: fix stale EP0 out_received, add isochronous supporthathach
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 <= 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).
2026-07-17fix(class/audio): fix control request byte order and buffer usage in audio hostZhang, Zhenjiang
- Fix missing tu_htole16() conversions for wValue and wIndex in tuh_audio_set_sampling_freq, tuh_audio_get_sampling_freq, tuh_audio_feature_unit_set, and tuh_audio_feature_unit_get - Fix incorrect wIndex parameter order in feature unit requests (unit_id and itf_num were swapped) - Replace static freq_buf with per-endpoint ctrl buffer in tuh_audio_set_sampling_freq to avoid concurrency issues - Update audio_host README to match actual example behavior
2026-07-17fix(class/audio): handle non-audio interface in enumeration and fix async ↵Zhang, Zhenjiang
control transfer buffer - Stop parsing at first non-Audio interface in audioh_open to avoid claiming unrelated interfaces - Call usbh_driver_set_config_complete for AS and unknown interfaces to allow enumeration to continue - Add global ctrl endpoint buffer to audioh_epbuf_t to fix use-after-return in feature_unit_set - Add sampling_freq NULL check and initialize to 0 in tuh_audio_get_sampling_freq - Change BOARD_TUH_RHPORT from 1 to 0 in audio_host example - Add only.txt with supported MCU/family list for audio_host example