Skip to content

Replace hidapi with hidra#131

Merged
carlossless merged 9 commits into
masterfrom
hidra
Jul 10, 2026
Merged

Replace hidapi with hidra#131
carlossless merged 9 commits into
masterfrom
hidra

Conversation

@carlossless

Copy link
Copy Markdown
Owner

Swaps the hidapi dependency for hidra, a pure-Rust HID library. This removes the C/libusb build dependency (no pkg-config or system libusb needed).

Changes

  • Cargo.toml: hidapi -> hidra (git dependency, usb feature).
  • New src/hid.rs: selects the backend per platform, matching the old linux-static-libusb setup. Linux uses hidra's nusb-based usb backend; Windows and macOS use the native HID stack. Both expose the same method surface, so the rest of the tool is unchanged.
  • Mechanical updates in device_selector.rs, isp_device.rs, util.rs: device paths are &str instead of &CStr, BusType is compared directly, and is_expected_error matches hidra's typed HidError variants instead of backend-specific message strings.

Testing

Verified end-to-end against real hardware (a NuPhy Air60) on all three desktop platforms, performing the full ISP flash cycle:

  • Linux (nusb backend): list, read, and write all produce output byte-identical to the hidapi build; firmware read+write round-trips verified by MD5.
  • Windows (native hid.dll backend): list, read, write all working.
  • macOS (IOHIDManager backend): list, read, write all working.

Unit tests pass. The tests/list_test.rs integration tests fail in my environment, but they fail identically on master with hidapi: they snapshot a stock-firmware keyboard, and my test unit runs custom firmware. They are not affected by this change.

- actions/checkout v4 -> v6
- actions/cache v4 -> v5
- actions/upload-artifact v4 -> v7
- actions/download-artifact v4 -> v8
- cachix/cachix-action v14 -> v17
- softprops/action-gh-release v2 -> v3

cachix/install-nix-action is already on the latest major (v31).
hidra now exposes one HidApi/HidDevice whose I/O methods return futures
(the nusb-model redesign); there is no separate hidra::usb. Adapt:

- hid.rs: re-export the single hidra::{HidApi, HidDevice} plus MaybeFuture;
  the `nusb` feature still selects hidra's nusb backend (now on the unified
  type), so the per-OS backend cfg aliasing is gone.
- device_selector.rs / isp_device.rs: drive the now-async open_path,
  get_report_descriptor, send_feature_report and get_feature_report calls
  with MaybeFuture::wait (the tool stays synchronous).
- Gate the macOS set_open_exclusive call on `not(feature = "nusb")` to match
  where hidra exposes it.
- Bump the hidra git pin to the redesigned commit.

Also drops a redundant comment on is_expected_error.
Extract the ISP bootloader protocol, device/platform specs, payload
utilities, and ihex helpers into a new sinowealth-isp library crate, and
keep the CLI in a sinowealth-kb-tool bin crate. Both live under crates/
in a virtual workspace.

The protocol is now expressed as async methods over hidra::HidDevice, so
the exact same code drives the native CLI (via MaybeFuture::wait) and a
future web flasher (via .await on wasm32 with the WebHID backend). To stay
UI- and runtime-free, the library:

- reports progress through a caller-supplied Progress callback instead of
  printing or driving indicatif directly (the CLI maps it onto a progress
  bar in progress.rs)
- waits for the device to settle through a cross-target async sleep (a
  thread-backed timer on native, setTimeout on the web) instead of
  thread::sleep
- takes an optional separate transfer handle (ISPDevice::new(.., None) on
  Linux/macOS/WebHID; Some(handle) for the Windows two-collection split),
  moving that platform branching to the CLI device selector

is_expected_error now cfg-gates the native-only HidError::Io variant so it
compiles for the web backend.

CI gains a wasm build check for sinowealth-isp; the nix build uses
singleStep so naersk handles the workspace layout.
sinowealth-isp now exposes only the individual ISP protocol operations
(enable_firmware, init_read/read_page, init_write/write_page, erase,
reboot) as async methods over hidra::HidDevice. The full read_cycle /
write_cycle orchestration, the page loops, progress reporting, and the
post-erase/reboot settle delays now live in the CLI (new flasher.rs).

This keeps the library free of orchestration, timing, and UI concerns:
- erase/reboot no longer sleep; callers insert the delay (the CLI uses
  thread::sleep, a web app uses setTimeout)
- the cross-target async sleep module is gone, so the library no longer
  needs js-sys/wasm-bindgen/web-sys/wasm-bindgen-futures (or log)
- the Progress callback is gone; the CLI drives indicatif directly in its
  own page loop

reboot now returns a Result instead of swallowing errors, so the caller
decides how to treat the expected disconnect (the CLI checks
is_expected_error on the unwrapped HidError).
The page loops (init_read + read_page x N, init_write + write_page x N)
are mechanical protocol with no timing, so they belong in the library as
ISPDevice::read/write rather than being duplicated in every consumer.

They take a dep-free '&dyn Fn(done, total)' progress callback, so the loop
stays in the lib without dragging back any runtime/UI dependency. The CLI
flasher (and the web app) now just create their progress bar, pass a
callback, and keep owning the surrounding cycle: sequencing, the
post-erase/reboot settle delays, and verification.
… single crate

Master renamed the package sinowealth-kb-tool -> sinowisp. This branch
had split the project into a workspace (sinowealth-isp lib + CLI crate).
Resolved by collapsing back into a single `sinowisp` crate that ships
both the library (src/lib.rs) and the CLI (src/main.rs):

- Moved crates/sinowealth-isp/src/* and crates/sinowealth-kb-tool/src/*
  into a single src/; tests into tests/.
- CLI-only deps (clap, dialoguer, hidparser, indicatif, itertools, md5,
  simple_logger, log) are gated behind a default `cli` feature; the bin
  target requires it. This keeps the library buildable on its own for
  the web (WebHID/wasm) via `--no-default-features`.
- Updated the wasm CI job to `cargo build --lib --no-default-features`.
- Rewrote intra-crate references sinowealth_isp -> sinowisp.

Claude-Session: https://claude.ai/code/session_01MiFqdrz3fGWL2Ue6N8rka2
Removes the git dependency on hidra so the crate can be published to
crates.io (cargo publish rejects git deps without a version). Verified
the published 0.0.1 compiles for the native CLI, nusb backend, and the
wasm/WebHID lib build; `cargo publish --dry-run` now succeeds.

Claude-Session: https://claude.ai/code/session_01MiFqdrz3fGWL2Ue6N8rka2
@carlossless
carlossless merged commit 7bdc9b1 into master Jul 10, 2026
10 checks passed
@carlossless
carlossless deleted the hidra branch July 10, 2026 06:22
carlossless added a commit that referenced this pull request Jul 10, 2026
Swaps the `hidapi` dependency for
[`hidra`](https://github.com/carlossless/hidra), a pure-Rust HID
library. This removes the C/libusb build dependency (no pkg-config or
system libusb needed).

## Changes

- `Cargo.toml`: `hidapi` -> `hidra` (git dependency, `usb` feature).
- New `src/hid.rs`: selects the backend per platform, matching the old
`linux-static-libusb` setup. Linux uses hidra's nusb-based `usb`
backend; Windows and macOS use the native HID stack. Both expose the
same method surface, so the rest of the tool is unchanged.
- Mechanical updates in `device_selector.rs`, `isp_device.rs`,
`util.rs`: device paths are `&str` instead of `&CStr`, `BusType` is
compared directly, and `is_expected_error` matches hidra's typed
`HidError` variants instead of backend-specific message strings.

## Testing

Verified end-to-end against real hardware (a NuPhy Air60) on all three
desktop platforms, performing the full ISP flash cycle:

- **Linux** (nusb backend): `list`, `read`, and `write` all produce
output byte-identical to the `hidapi` build; firmware read+write
round-trips verified by MD5.
- **Windows** (native `hid.dll` backend): `list`, `read`, `write` all
working.
- **macOS** (IOHIDManager backend): `list`, `read`, `write` all working.

Unit tests pass. The `tests/list_test.rs` integration tests fail in my
environment, but they fail identically on `master` with `hidapi`: they
snapshot a stock-firmware keyboard, and my test unit runs custom
firmware. They are not affected by this change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant