Repository navigation
librador: score iso meta packets once their frames have been assembled (iso6 false alarms) - #467
Merged
Merged
Conversation
On iso6 the ingest thread scored each meta packet the moment it was processed, but assembled iso6 frames only after the whole batch (drain_iso6_queues at the end of ingest_thread_function). Whenever a block's meta URB landed in the same batch as its six data URBs, all 33 metas were matched against a checksum ring that was a full block behind: one ok, two bad, thirty unvalidated per such block. That is the ~0.6% bad / ~12% unvalidated reported on healthy iso6 streams (#462), and the reason iso1 (which dispatches inline) never showed it. Metas now wait in pending_meta until the candidate frames around their slot (lag -3..+1) have been noted, or until the ring is about to lap them, and are scored then. iso6 frames are also drained as soon as their six slices are queued rather than at batch end, so they reach the buffers without a batch-sized lag. note_seq no longer adds 65535 to frames_dropped when the device repeats a header (negative delta). Validated with a host-only simulation that drives these functions with the firmware's lag-1 meta model: every batch-collision and meta-before-data scenario drops from 0.76% bad / 12% unvalidated to 0 / 0 with no change to frame assembly. A lost data URB (six-queue lockstep skew) now reads as ~93% bad instead of mostly unvalidated. Not yet verified on iso6 hardware: this machine is macOS, where only the bulk transport may be opened.
EspoTek
added a commit
to AronRubin/Labrador
that referenced
this pull request
Oct 13, 2026
…auto - DeviceSession treated librador_connect() == 1 (board open but on a firmware librador will not stream from) as success, so capture armed and waited forever on an empty buffer. It now fails with the board's version against the expected one. librador.h documents the 0 / 1 / negative contract of librador_connect(). - New `labrador-cli update-firmware [--hex FILE] [--force]`: flashes the bundled labrafirm hex (assets/firmware next to the binary, as the app lays it out; cli/CMakeLists copies it for dev builds) when the board's firmware differs, launches and if needed reflashes a board stuck in bootloader mode, and exits 0 when the board ends up on the expected firmware. A Gobindar-state board is reported, not handled. - --transport defaults to auto (librador's per-platform choice: iso6 on Windows and Linux, bulk on macOS); the iso6 false-alarm fix that makes that default trustworthy is espotek-org#467. `bulk` is selectable. - Command list column sized to the longest command name.
This was referenced Oct 13, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the false
frames_bad_checksum/frames_unvalidatedcounts on the iso6 transport that @AronRubin reported in #462 (about 0.6% bad and 12% unvalidated on a healthy stream, with no actual sample corruption). Host-side only; no firmware change.Root cause
ingest_thread_functionscored each meta packet the moment it was processed, but assembled iso6 frames only after the whole batch (drain_iso6_queues()at the end of the loop). A block's six data URBs and its meta URB are submitted together, so whenever the meta URB landed in the same ingest batch as the data URBs, all 33 metas of that block were matched against a checksum ring that was a full block behind: one ok, two bad, thirty unvalidated per such block. That 1:15 bad-to-unvalidated ratio is what Aron measured. iso1 never showed it becausehandle_data_iso_packetdispatches and notes the checksum inline.Fix
pending_metauntil the candidate frames around their slot (lag -3..+1, nominal -1) have been noted, or until the ring is about to lap them, and are scored then. This is order-independent, so it also covers libusb delivering a meta URB ahead of its data URBs.note_seqno longer adds 65535 toframes_droppedwhen the device repeats a header (negative delta).Validation
A host-only simulation drives the real functions from
usbcallhandler.cppwith the firmware's lag-1 meta model (meta polled in frame X+1 carries SOF-X's checksums). Shipped code with the meta URB sharing the batch one block in eight: 0.76% bad / 12.06% unvalidated. With this change, every batch-collision and meta-before-data scenario scores 0 / 0 with identical frame assembly. A genuinely lost data URB (which permanently skews the six-queue lockstep) now reads as ~93% bad instead of mostly unvalidated.Hardware A/B (macOS 15.7.5 host with a scratch build that enables iso on macOS, firmware 0x000C variant 3,
tools/librador_bulk_test: three phases of about 6 s each, modes 0 and 2, quiet and with the generator looped back). Counts are per phase.LABRADOR_INLINE_DISPATCH=1The inline-dispatch row (drain after every packet, scoring still immediate) removes most of it, which pins the batch-end drain as the main mechanism; the residual 1-2% is the meta URB occasionally completing ahead of its data URBs, which only the deferred scoring covers. Zero dropped frames throughout.
Follow-up, not addressed here
iso6 has no cross-endpoint frame tag: a data URB that completes with an error is dropped whole, leaving that endpoint's queue 33 packets behind for the rest of the session, after which every frame mixes two USB frames. The counters then show a high bad rate but nothing resyncs. Worth a separate change (queue zero-length placeholders for a failed URB so it becomes 33 invalid frames instead of a permanent skew).