Repository navigation
feat(backend): use both backends in one build, chosen by type - #8
Merged
Merged
Conversation
`nusb::Device::control_in`/`control_out` are unavailable on Windows, so the control-only arms failed to compile there once the nusb backend stopped being feature-gated off. `open` already refuses a failed claim on Windows, leaving the fallback unreachable; route it through cfg'd helpers mirroring `read_report_descriptor_unclaimed`.
karstx
force-pushed
the
feat/runtime-backend-selection
branch
from
September 2, 2026 17:47
6e348cd to
e56b493
Compare
The hidraw backend was shared with Linux and nusb has no enumeration there, so the target carried a cfg arm and a CI check without being exercised.
Both backends are additive and picked at run time, so building the USB transport in by default makes `Backend::Nusb` selectable without a feature flag. `--no-default-features` still drops it.
Enumerates through every available backend in one process and opens a device by falling back from one to the next.
karstx
force-pushed
the
feat/runtime-backend-selection
branch
from
September 2, 2026 20:49
20b1560 to
7eb8aca
Compare
`HidraBuilder::enumerate(bool)` read as a sibling of `Hidra::enumerate(vid, pid)` while doing something unrelated, and the two appeared within thirty lines of each other in the conformance suite. Rename it to `enumerate_on_build`, deprecate `new_without_enumerate` now that the builder expresses it, and derive `Backend::default` from `available` so the preference order lives in one place.
The rustup bootstrapper was pinned at the `dist/` URL, which is replaced in place on each release, so its hash drifted and the toolchain layer stopped building. Point it at the immutable `archive/1.29.1/` path, which serves byte-identical content, matching how the VS bootstrappers are pinned. Flakes read NIXPKGS_ALLOW_UNFREE only under `--impure`, so the documented commands could not evaluate the ISO derivation.
nusb is in the default feature set, so its own floor is the crate's: no 0.2.x line reaches 1.77 (0.2.0-0.2.5 need 1.79, 0.2.6+ need 1.85), and claiming 1.77 only bought a confusing resolver error on `cargo add`. 1.85's cargo reads the v4 lockfile, so the regenerate-lockfile step goes too, and `is_none_or` is now available to the example.
The backend table and the paragraph about picking between backends link Backend and Hidra::builder, neither of which exists on wasm, so every cargo doc --target wasm32-unknown-unknown failed. Nothing caught it because the docs job only builds the native target, so the wasm job now builds docs too.
HID mandates an interrupt IN endpoint, but control-only devices that declare bNumEndpoints 0 exist -- the Sinowealth ISP bootloaders are exactly that, and their descriptor is the shape this reproduces. The backend's fallback for them was untested, because g_hid always creates an interrupt IN endpoint and so cannot produce the device; FunctionFS can, since userspace supplies the descriptors verbatim. The test pins down that enumeration still finds it, that reports go over the control pipe both ways, that write falls back to SET_REPORT(Output), and that read refuses rather than blocking forever on a report that can never arrive. That last error claimed the reader thread had terminated, when for this shape there was never one to terminate; it now says what is actually wrong.
cargo package sweeps in untracked files, so anything left in the tree ships: the VM image nix run .#test-vm drops here is 419M, well past the crates.io limit. An include whitelist is the only thing that reliably keeps a stray artifact out; the image is gitignored as well, since it is a build product. docs.rs was also building Linux and wasm only, so the Windows and macOS backends were absent from the rendered docs along with the API they carry (container_id, set_write_timeout, set_open_exclusive).
The comments restated what the code already said, and the README had grown half as long again explaining the backend selector.
The enum existed so a caller could hold either backend in one variable. That is a caller's problem, and a small one: sinowisp, the only consumer that swaps at run time, touches five methods, while the enum had to forward the whole public surface. Backends are types now. A backend this build lacks cannot be named, so choosing wrongly is a compile error rather than a run-time Unsupported, and is_available/available/default/Unsupported go away with the enum they served. The per-OS extensions are inherent to Native, so reaching for set_write_timeout through Nusb no longer compiles; the Results that only ever carried "unsupported under the other backend" are gone too. Hidra::new stays on Hidra<Native>, the way HashMap::new sits on the RandomState impl: a default type parameter does not drive inference.
They hold raw OS handles, so the impls are opaque rather than derived. Only the Linux ones had them; the Windows and macOS types were newly public too and the lint is denied in CI.
The backend module docs still described the dispatch enum, down to a link to the deleted module, and the nusb one still said "selected at run time". The e2e README named the old enum variants and described a run-time choice that is now the type parameter each target passes.
The example took a backend from HIDRA_BACKEND, which cost it a generic function and a cfg-gated match arm to show something backends.rs already shows. Nothing in the library reads the environment; the variable is a convention, and the only place that needs it is the cynthion suite, where run.sh drives both backends through one binary.
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.
Lets a program use both backends, the OS HID stack and raw USB via nusb, in the same build, choosing per
Hidrainstance:The backend used to be picked by Cargo feature, so enabling
nusbanywhere in the graph changed which backend every crate got. It is a type parameter now:HidBackendandHidDeviceBackendare public, implemented byNative,NativeDevice,NusbandNusbDevice. A backend this build does not have cannot be named, so choosing wrongly is a compile error that names the missing feature rather than a run-timeUnsupported.Holding either in one variable is the caller's job;
examples/backends.rsis a complete one in about thirty lines, and sinowisp does the same in karstx/sinowisp#152.Also in here
Native, so reaching forset_write_timeoutthroughNusbno longer compiles, and they lose theResultthat only ever carried "unsupported under the other backend".cargo doc --target wasm32-unknown-unknownfailed. The docs job only builds native, so nothing caught it; the wasm job builds docs now too.bNumEndpoints0, the shape of the Sinowealth ISP bootloaders, whichg_hidcannot produce. It pins down thatwritefalls back toSET_REPORT(Output)andreadrefuses rather than blocking forever.cargo packageincludes untracked files, so the 419 MB VM imagenix run .#test-vmleaves in the tree would have been published. Anincludewhitelist fixes it (29 files, 97 KB). docs.rs was building Linux and wasm only, so the Windows and macOS APIs were missing from the rendered docs.Testing
fmt, clippy, tests and both doc targets;
cargo checkfor all four targets. The full Linux e2e matrix (uhid, nusb, control-only, WebHID) passes in the test VM.