From f667f200672f41b1e5db891539b91a372e1cd65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20J=C3=A4ger?= Date: Fri, 3 Jul 2026 01:10:43 +0200 Subject: [PATCH] feat(dfu_split): add dfu_split, possibility to update peripherals via central MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal JΓ€ger --- docs/docs/main/docs/development/roadmap.md | 2 +- .../flash_firmware/use_embassy_boot.mdx | 183 +- .../rp2040_dfu_split/.cargo/config.toml | 10 + .../use_config/rp2040_dfu_split/.gitignore | 9 + .../use_config/rp2040_dfu_split/Cargo.lock | 3044 +++++++++++++++++ .../use_config/rp2040_dfu_split/Cargo.toml | 45 + .../use_config/rp2040_dfu_split/Makefile.toml | 60 + examples/use_config/rp2040_dfu_split/build.rs | 52 + .../use_config/rp2040_dfu_split/keyboard.toml | 62 + examples/use_config/rp2040_dfu_split/memory.x | 13 + .../rmk-rp2040-dfu-split-peripheral.bin | 1 + .../rp2040_dfu_split/src/central.rs | 7 + .../rp2040_dfu_split/src/peripheral.rs | 7 + .../use_config/rp2040_dfu_split/vial.json | 37 + .../rp2040_dfu_split/.cargo/config.toml | 11 + examples/use_rust/rp2040_dfu_split/.gitignore | 9 + examples/use_rust/rp2040_dfu_split/Cargo.lock | 3044 +++++++++++++++++ examples/use_rust/rp2040_dfu_split/Cargo.toml | 50 + .../use_rust/rp2040_dfu_split/Makefile.toml | 60 + examples/use_rust/rp2040_dfu_split/build.rs | 52 + examples/use_rust/rp2040_dfu_split/memory.x | 13 + .../rmk-rp2040-dfu-split-peripheral.bin | 1 + .../use_rust/rp2040_dfu_split/src/central.rs | 184 + .../use_rust/rp2040_dfu_split/src/keymap.rs | 18 + .../use_rust/rp2040_dfu_split/src/macros.rs | 10 + .../rp2040_dfu_split/src/peripheral.rs | 85 + .../use_rust/rp2040_dfu_split/src/vial.rs | 1 + examples/use_rust/rp2040_dfu_split/vial.json | 37 + rmk-config/src/lib.rs | 8 + rmk-macro/Cargo.toml | 1 + rmk-macro/src/codegen/entry.rs | 12 + rmk-macro/src/codegen/orchestrator.rs | 47 + rmk/Cargo.toml | 2 + rmk/src/crc32.rs | 129 + rmk/src/dfu/mod.rs | 200 +- rmk/src/dfu/split/central.rs | 262 ++ rmk/src/dfu/split/mod.rs | 162 + rmk/src/dfu/split/peripheral.rs | 140 + rmk/src/lib.rs | 2 + rmk/src/split/central.rs | 16 +- rmk/src/split/driver.rs | 493 ++- rmk/src/split/mod.rs | 78 + rmk/src/split/peripheral.rs | 108 +- rmk/src/split/serial/mod.rs | 8 +- rmk/src/usb/mod.rs | 8 +- 45 files changed, 8698 insertions(+), 85 deletions(-) create mode 100644 examples/use_config/rp2040_dfu_split/.cargo/config.toml create mode 100644 examples/use_config/rp2040_dfu_split/.gitignore create mode 100644 examples/use_config/rp2040_dfu_split/Cargo.lock create mode 100644 examples/use_config/rp2040_dfu_split/Cargo.toml create mode 100644 examples/use_config/rp2040_dfu_split/Makefile.toml create mode 100644 examples/use_config/rp2040_dfu_split/build.rs create mode 100644 examples/use_config/rp2040_dfu_split/keyboard.toml create mode 100644 examples/use_config/rp2040_dfu_split/memory.x create mode 100755 examples/use_config/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin create mode 100644 examples/use_config/rp2040_dfu_split/src/central.rs create mode 100644 examples/use_config/rp2040_dfu_split/src/peripheral.rs create mode 100644 examples/use_config/rp2040_dfu_split/vial.json create mode 100644 examples/use_rust/rp2040_dfu_split/.cargo/config.toml create mode 100644 examples/use_rust/rp2040_dfu_split/.gitignore create mode 100644 examples/use_rust/rp2040_dfu_split/Cargo.lock create mode 100644 examples/use_rust/rp2040_dfu_split/Cargo.toml create mode 100644 examples/use_rust/rp2040_dfu_split/Makefile.toml create mode 100644 examples/use_rust/rp2040_dfu_split/build.rs create mode 100644 examples/use_rust/rp2040_dfu_split/memory.x create mode 100755 examples/use_rust/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin create mode 100644 examples/use_rust/rp2040_dfu_split/src/central.rs create mode 100644 examples/use_rust/rp2040_dfu_split/src/keymap.rs create mode 100644 examples/use_rust/rp2040_dfu_split/src/macros.rs create mode 100644 examples/use_rust/rp2040_dfu_split/src/peripheral.rs create mode 100644 examples/use_rust/rp2040_dfu_split/src/vial.rs create mode 100644 examples/use_rust/rp2040_dfu_split/vial.json create mode 100644 rmk/src/crc32.rs create mode 100644 rmk/src/dfu/split/central.rs create mode 100644 rmk/src/dfu/split/mod.rs create mode 100644 rmk/src/dfu/split/peripheral.rs diff --git a/docs/docs/main/docs/development/roadmap.md b/docs/docs/main/docs/development/roadmap.md index d2b032e9f..750784084 100644 --- a/docs/docs/main/docs/development/roadmap.md +++ b/docs/docs/main/docs/development/roadmap.md @@ -64,7 +64,7 @@ There are a bunch of things to do with RMK in the near future. I plan to ship 1. - [ ] πŸ”΄πŸ”΅ GUI keymap configurator which supports windows/macos/linux/web - [ ] Default bootloader - [x] USB DFU -- [ ] Flashing peripherals from the central via uart +- [x] Flashing peripherals from the central via uart - [ ] Flashing peripherals from the central via ble - [ ] OTA updates diff --git a/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx b/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx index b2217c98d..46a7635ff 100644 --- a/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx +++ b/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx @@ -166,11 +166,13 @@ dfu-util -D your-firmware.bin -R Or with a device ID if multiple DFU devices are connected: ```shell -dfu-util -d 4c4b:4643 -D your-firmware.bin +dfu-util -d 4c4b:4643 -D your-firmware.bin -R ``` Find your device's VID:PID via `lsusb` on Linux or Device Manager (under "Universal Serial Bus devices") on Windows. +This will download the firmware binary onto the DFU partition of the flash and set a flag to tell embassy-boot at next boot that the firmware should be updated. Embassy-boot then proceeds to copy the data in the DFU partition into the ACTIVE partition by swapping page-wise. (This way the data in ACTIVE is swapped and can be rolled back). This process takes about 20 seconds for a ~120kB firmware, during which there is not LED activity. + **Installing `dfu-util`:** - Linux: `sudo apt install dfu-util` (Debian/Ubuntu) or `sudo pacman -S dfu-util` (Arch) - macOS: `brew install dfu-util` @@ -211,3 +213,182 @@ Additionally the bootymcbootface bootloader has blinking codes using PIN_25 (RP2 | Successful DFUβ†’ACTIVE copy; about to jump | 5 short blinks (50β€―ms) | | Bootloader itself panicked (e.g. flash read error, invalid state partition) | Morse SOS (... --- ...), repeating | +--- + +## Split peripheral updates (dfu_split) + +When using a split keyboard with separate microcontrollers for each half, +`dfu_split` lets you update the peripheral's firmware without a debug +probe or direct USB connection β€” the central acts as a relay. + +In order for this to work, both, central and peripheral need to be flashed with an embassy-boot bootloader (e.g. bootymcbootface) and with a firmware that has `dfu_split` enabled. (See above on how to do that) + +::: note + +This feature currently only works for split keyboards that use UART (serial) as the interface between them. Split BLE and updates over the air wont work. + +::: + +### Feature flags + +```toml title="Cargo.toml" +[dependencies] +rmk = { features = [ "dfu_rp", "split", "dfu_split"] } # or dfu_nrf instead of dfu_rp +``` + +`dfu_split` requires `dfu` (implied by `dfu_rp` / `dfu_nrf`) and `split`. + +### Architecture + +Two complementary paths are provided: + +| Path | Mechanism | When to use | +|---|---|---| +| **Embedded** | The central includes the peripheral binary at compile time via `include_bytes!()` and flashes it automatically when the peripheral connects. | CI/CD, production, "flash-and-forget" | +| **Passthrough** | `dfu-util -a 1 -D peripheral.bin` sends the firmware through the central's USB DFU interface, which forwards it to the peripheral in real time. | Development, ad-hoc updates without connecting the peripheral to the host to flash | + +Both paths split the firmware into 256-byte chunks and send them over the +split link as `FirmwareChunk` messages. The peripheral writes them to its +own DFU flash partition, verifies the CRC, and resets into the new image. + +### Embedded firmware path + +In order to embed the peripherals firmware into the centrals firmware, we need to tell the central during compilation where the binary of the peripheral is. +Note that this must be a .bin-file. You can create one using `cargo make bin`, see also the Makefile.toml in the examples. + +::: tip + +Use `dfu_lock` (see above) to lock the central against malicious flashing. `dfu_lock` will lock using the central as relay to flash a peripheral as well. + +::: + +import { Tab, Tabs, Rust, Toml } from '@theme' + + +}> + +Add a firmware field with the path to the peripherals firmware binary in the peripheral's section. This is a relative path starting from where the Cargo.toml is. + +```toml title="keyboard.toml" +[[split.peripheral]] +rows = 2 +cols = 1 +firmware = "./peripheral.bin" +update_policy = "MatchHash" # optional +# update_policy = "force" to force a peripheral update every start of central +``` + +By default (`update_policy = "MatchHash"`, or when omitted) the central only updates the peripheral, when the firmware on the peripheral has a different checksum than the included firmware given by firmware. + +The `#[rmk_central]` macro then generates the `set_firmware_update_data` +call automatically. When the field is absent the central relies on +passthrough only. (see below) + + +}> + +Add this inside the main function of the central: + +```rust title="central.rs" +// NOTE: CARGO_MANIFEST_DIR is where your Cargo.toml is +const PERIPHERAL_FW: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/peripheral.bin")); +rmk::dfu::set_firmware_update_data(0, PERIPHERAL_FW, rmk::crc32::crc32(PERIPHERAL_FW)); + +//... + +// in the call to run_peripheral_manaager, set an update policy +// use UpdatePolicy::Force to force the peripheral update at every start of central +run_peripheral_manager::<2, 1, 2, 2, _>(0, uart_receiver, rmk::split::central::UpdatePolicy::MatchHash + ), +``` + +Where the first argument of `set_firmware_update_data` is the index of the peripheral. (The same as the first argument of `run_peripheral_manager`.) + +When the `set_firmware_update_data()` is not called, the central relies on passthrough only. + + + + +After flashing the central, it will ask the peripheral for its firmware checksum, the peripheral answers and if the checksum does not match with the checksum of the embedded peripheral firmware in the central, it will start flashing the embedded peripheral firmware onto the peripheral. The blink codes of the peripherals DFU LED (see above) are the same like during a normal DFU update. Additionally the centrals DFU LED will start blinking when sending the firmware to the peripheral. When UpdatePolicy::Force is given, the central skips this check and sends the firmware regardless of the checksum. +Likewise the peripheral will tell the central it’s firmware checksum proactively at every boot. So the firmware is still updated if the peripheral booted after the central. To avoid infinite loops of flashing and rebooting even when UpdatePolicy::Force is set, the central will not flash the firmware when the CRC is matching. + +### Passthrough path + +The passthrough pass works identically to the normal DFU flash (see above) β€” just select a +different alternate setting (`-a`). Up do four peripherals are supported. + +```shell +# Flash peripheral 0 +dfu-util -a 1 -D peripheral.bin + +# Flash peripheral 1 (if you have a second peripheral) +dfu-util -a 2 -D peripheral2.bin + +# Flash central +dfu-util -a 0 -D central.bin -R +``` + +::: warning +Do **not** use `-R` (reset) when flashing a peripheral. The peripheral +resets itself after the update. `-R` would reset the central. +::: + +::: note +The index of the alternate setting (`-a`) is off by one due to technical reasons. So to flash the peripheral with index 0, you have to pass `-a 1`. +::: + +### Peripheral firmware requirements + +The peripheral must call `init_flash()` to partition its flash and `mark_booted()` so that the +bootloader does not revert the update. With `#[rmk_peripheral(id = …)]` +(TOML API) this is generated automatically when a `[dfu]` section is +present in `keyboard.toml`. + +If you are using the Rust API on the peripheral, ensure you have this in your main function of the peripheral: + +```rust +// for the different addresses for different chips see above +const FLASH_SIZE: u32 = 2 * 1024 * 1024; +const PAGE_SIZE: u32 = 4 * 1024; +const STORAGE_SIZE: u32 = 128 * 1024; +const STATE_OFFSET: u32 = 0x6000; +const STATE_SIZE: u32 = 0x1000; +const ACTIVE_OFFSET: u32 = 0x7000; +let remaining: u32 = FLASH_SIZE - 28 * 1024 - STORAGE_SIZE; +let active_size: u32 = (remaining - PAGE_SIZE) / 2; +let dfu_size: u32 = active_size + PAGE_SIZE; +let dfu_offset: u32 = ACTIVE_OFFSET + active_size; +let storage_offset: u32 = dfu_offset + dfu_size; + +rmk::dfu::init_flash( + p.FLASH, + storage_offset, + STORAGE_SIZE, + STATE_OFFSET, + STATE_SIZE, + dfu_offset, + dfu_size, +); + +// mark the firmware as booted otherwise the bootloader thinks it didn't and will revert to the old firmware +rmk::dfu::mark_booted(); + +// you can also set a DFU LED for the perpheral, it flashes during updates +let mut dfu_led_processor = DfuLedProcessor::new(Output::new(p.PIN_25, Level::Low), false); +// if used, run the procesor in run_all: +join( + run_all!(matrix, dfu_led_processor, watchdog_runner), + run_rmk_split_peripheral(uart_instance), +) +.await; +``` + +Without `mark_booted()` embassy-boot will revert the update at next reboot, because it thinks the new firmware did not boot. (visible as three short flashes of the DFU LED when using bootymcbootface). + +### Troubleshooting + +| Symptom | Likely cause | +|---|---| +| Peripheral flashes 3 times after update | Firmware crashed or `mark_booted()` not called in the peripheral. Add `[dfu]` to `keyboard.toml` (using config) or call `rmk::dfu::mark_booted()` (using Rust). | +| `dfu-util -a 1` shows `dfuERROR` or hangs at 0% | Peripheral not connected or its firmware is built without the `dfu_split` feature. | + diff --git a/examples/use_config/rp2040_dfu_split/.cargo/config.toml b/examples/use_config/rp2040_dfu_split/.cargo/config.toml new file mode 100644 index 000000000..eeab95395 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/.cargo/config.toml @@ -0,0 +1,10 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip RP2040" +linker = "flip-link" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "info" +KEYBOARD_TOML_PATH = { value = "keyboard.toml", relative = true } diff --git a/examples/use_config/rp2040_dfu_split/.gitignore b/examples/use_config/rp2040_dfu_split/.gitignore new file mode 100644 index 000000000..f707059d6 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/.gitignore @@ -0,0 +1,9 @@ +rmk-rp2040-dfu-split-*.bin +rmk-rp2040-dfu-split-*.hex +rmk-rp2040-dfu-split-*.uf2 +peripheral.bin +central.bin +central.hex +peripheral.hex +central.uf2 +peripheral.uf2 diff --git a/examples/use_config/rp2040_dfu_split/Cargo.lock b/examples/use_config/rp2040_dfu_split/Cargo.lock new file mode 100644 index 000000000..905151234 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/Cargo.lock @@ -0,0 +1,3044 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "ascii-canvas" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" +dependencies = [ + "term", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "az" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version 0.2.3", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitfield-struct" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ca6739863c590881f038d033a146c51ddae239186a4327014839fd864f44ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bt-hci" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3c66fd0a41a3b3a5906847b1bf5516048ce083e24fa9341a0fa31d3b4cd73d" +dependencies = [ + "btuuid", + "defmt 1.1.0", + "embassy-sync", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "futures-intrusive", + "heapless 0.9.3", + "serde", +] + +[[package]] +name = "btuuid" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f48f1e9b0aad0a4f05d17bdeae0fa20ff798e272a03a6940ca27ad9c5a6ae7" +dependencies = [ + "defmt 0.3.100", + "serde", + "uuid", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cargo_toml" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa61aec073ec94791433ddf3df2323ff9d1711557c2a0eefb0f99cb4f8dca520" +dependencies = [ + "semver 1.0.28", + "serde", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "defmt 1.1.0", + "num-traits", + "pure-rust-locales", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cmac" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" +dependencies = [ + "cipher", + "dbl", + "digest", +] + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "config" +version = "0.15.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85f248a4de22d204ceabc6299d89d2c70fbd7f09fea53c06c852369652d8139" +dependencies = [ + "async-trait", + "convert_case 0.6.0", + "json5", + "pathdiff", + "ron", + "rust-ini", + "serde-untagged", + "serde_core", + "serde_json", + "toml", + "winnow", + "yaml-rust2", +] + +[[package]] +name = "const-gen" +version = "1.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bb51eeea292ceb9bf5312b4416c1b151b130502e6892a2ae174ae1e4b51e58" +dependencies = [ + "const-gen-derive", +] + +[[package]] +name = "const-gen-derive" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabc01a5585d36649b6bee1f1198d112a7bb4982661d6c18406fba571ce3263e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cordyceps" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +dependencies = [ + "loom", + "tracing", +] + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc-any" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46db9f663dfb869b80fcf59e32d7a80fc6c464a4f6328f3f06a00f5e36d05f8c" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dbl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" +dependencies = [ + "generic-array", +] + +[[package]] +name = "debug-helper" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a4af69c60438a1a82af89d362f4729fd38db7b73f305a237636fad31ceb2bf" + +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f73a4a4a91609e977ae3b7bd831ffa292edfd42ad140a3244a61d805b0e05e" +dependencies = [ + "critical-section", + "defmt 1.1.0", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embassy-boot" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73bfc955c9238df2ce9fecc6f18a64b13ecaa41a3cd42d940a09f304765d10db" +dependencies = [ + "digest", + "document-features", + "embassy-embedded-hal", + "embassy-sync", + "embedded-storage", + "embedded-storage-async", + "signature", +] + +[[package]] +name = "embassy-boot-rp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc17df4cf2a8ba86e8559a6515104e31ca7aa6b36221199310e244157935204" +dependencies = [ + "cfg-if", + "cortex-m", + "cortex-m-rt", + "embassy-boot", + "embassy-rp", + "embassy-sync", + "embassy-time", + "embedded-storage", + "embedded-storage-async", +] + +[[package]] +name = "embassy-embedded-hal" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0641612053b2f34fc250bb63f6630ae75de46e02ade7f457268447081d709ce" +dependencies = [ + "defmt 1.1.0", + "embassy-futures", + "embassy-hal-internal 0.4.0", + "embassy-sync", + "embassy-time", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + +[[package]] +name = "embassy-executor" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0d3b15c9d7dc4fec1d8cb77112472fb008b3b28c51ad23838d83587a6d2f1e" +dependencies = [ + "cordyceps", + "cortex-m", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-executor-macros", + "embassy-executor-timer-queue", +] + +[[package]] +name = "embassy-executor-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11a246f53de5f97a387f40ac24726817cd0b6f833e7603baac784f29d6ff276" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "embassy-executor-timer-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" + +[[package]] +name = "embassy-futures" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "embassy-hal-internal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f10ce10a4dfdf6402d8e9bd63128986b96a736b1a0a6680547ed2ac55d55dba" +dependencies = [ + "num-traits", +] + +[[package]] +name = "embassy-hal-internal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568659fc53866d3d85c60fa33723fb751aa69e71507634fc2c19e7649432fb75" +dependencies = [ + "cortex-m", + "critical-section", + "defmt 1.1.0", + "num-traits", +] + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] +name = "embassy-net-driver-channel" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07d2eb9f05a6fc876500949856ea1be40773d866d8cb99384f72d0ae4568c16" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync", +] + +[[package]] +name = "embassy-nrf" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82051f67e8a06a8faebd183f62cfade184c96be68126c5f064abb8223a591ff1" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "embassy-usb-synopsys-otg", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "nrf-pac", + "rand_core 0.10.1", + "rand_core 0.6.4", + "rand_core 0.9.5", +] + +[[package]] +name = "embassy-rp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d98f472894c1ecffac5596c657af5305c57282d29e8746d7fec033951931bc8" +dependencies = [ + "cfg-if", + "chrono", + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-sync", + "embassy-time", + "embassy-time-driver", + "embassy-time-queue-utils", + "embassy-usb-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "nb 1.1.0", + "pio", + "rand_core 0.6.4", + "rand_core 0.9.5", + "rp-pac", + "rp2040-boot2", + "sha2-const-stable", + "smart-leds", +] + +[[package]] +name = "embassy-sync" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbd85cf5a5ae56bdf26f618364af642d1d0a4e245cdd75cd9aabda382f65a81" +dependencies = [ + "cfg-if", + "critical-section", + "defmt 1.1.0", + "embedded-io-async 0.7.0", + "futures-core", + "futures-sink", + "heapless 0.9.3", +] + +[[package]] +name = "embassy-time" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592b0c143ec626e821d4d90da51a2bd91d559d6c442b7c74a47d368c9e23d97a" +dependencies = [ + "cfg-if", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee71af1b3a0deaa53eaf2d39252f83504c853646e472400b763060389b9fcc9" +dependencies = [ + "document-features", +] + +[[package]] +name = "embassy-time-queue-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168297bf80aaf114b3c9ad589bf38b01b3009b9af7f97cd18086c5bbf96f5693" +dependencies = [ + "embassy-executor-timer-queue", + "heapless 0.9.3", +] + +[[package]] +name = "embassy-usb" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25746d8b152b72fbf2a217f489a083dbbe243f281f09184a1f2cfbe9bbb245f" +dependencies = [ + "bitflags 2.13.0", + "defmt 1.1.0", + "embassy-futures", + "embassy-net-driver-channel", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "embedded-io-async 0.7.0", + "heapless 0.9.3", + "ssmarshal", + "usbd-hid", +] + +[[package]] +name = "embassy-usb-dfu" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3678d16e1e71b052d00f7518620b5839a878f2bdab2019b81d984fb2a74f" +dependencies = [ + "bitflags 2.13.0", + "cortex-m", + "embassy-boot", + "embassy-futures", + "embassy-sync", + "embassy-time", + "embassy-usb", + "embedded-storage", +] + +[[package]] +name = "embassy-usb-driver" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa675c5f4349b6aa0fcffc4bf9b241f18cd11b97c1f8323273fb9a5449937fbd" +dependencies = [ + "defmt 1.1.0", + "embedded-io-async 0.6.1", + "embedded-io-async 0.7.0", +] + +[[package]] +name = "embassy-usb-synopsys-otg" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6efd0c0e0b21bcf91b475b8fa47347c1df32c9a99728ae1e1b0e6625862dfd1a" +dependencies = [ + "critical-section", + "defmt 1.1.0", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "portable-atomic", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" +dependencies = [ + "defmt 0.3.100", +] + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "defmt 0.3.100", + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io 0.6.1", +] + +[[package]] +name = "embedded-io-async" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564b9f813c544241430e147d8bc454815ef9ac998878d30cc3055449f7fd4c0" +dependencies = [ + "defmt 1.1.0", + "embedded-io 0.7.1", +] + +[[package]] +name = "embedded-storage" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" + +[[package]] +name = "embedded-storage-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" +dependencies = [ + "embedded-storage", +] + +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixed" +version = "1.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af2cbf772fa6d1c11358f92ef554cb6b386201210bcf0e91fb7fba8a907fb40" +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generator" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b854b0e584ead1a33f18b2fcad7cf7be18b3875c78816b753639aa501513ae" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hashlink" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "defmt 1.1.0", + "hash32 0.3.1", + "serde_core", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "sha3", + "string_cache", + "term", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" +dependencies = [ + "regex-automata", + "rustversion", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nrf-pac" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83531b9f1e19f493018da9b8b20359e4cedd6d1357553b1ac50f3fa27a0104fa" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "elliptic-curve", + "primeorder", +] + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt 1.1.0", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ba4153cee9585abc451271aa437d9e8defdea8b468d48ba6b8f098cbe03d7f" +dependencies = [ + "pio-core", + "pio-proc", +] + +[[package]] +name = "pio-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61d90fddc3d67f21bbf93683bc461b05d6a29c708caf3ffb79947d7ff7095406" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "pio-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825266c1eaddf54f636d06eefa4bf3c99d774c14ec46a4a6c6e5128a0f10d205" +dependencies = [ + "lalrpop", + "lalrpop-util", + "pio-core", +] + +[[package]] +name = "pio-proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4a76571f5fe51af43cc80ac870fe0c79cc0cdd686b9002a6c4c84bfdd0176b" +dependencies = [ + "codespan-reporting", + "lalrpop-util", + "pio-core", + "pio-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +dependencies = [ + "critical-section", +] + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "defmt 1.1.0", + "heapless 0.7.17", + "postcard-derive", + "serde", +] + +[[package]] +name = "postcard-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pure-rust-locales" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "rmk" +version = "0.8.2" +dependencies = [ + "bt-hci", + "byteorder", + "cortex-m", + "crc32fast", + "defmt 1.1.0", + "document-features", + "embassy-boot", + "embassy-boot-rp", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-nrf", + "embassy-rp", + "embassy-sync", + "embassy-time", + "embassy-usb", + "embassy-usb-dfu", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "futures", + "heapless 0.9.3", + "paste", + "pio", + "postcard", + "rmk-macro", + "rmk-types", + "sequential-storage", + "serde", + "static_cell", + "trouble-host", + "usbd-hid", +] + +[[package]] +name = "rmk-config" +version = "0.6.1" +dependencies = [ + "config", + "once_cell", + "paste", + "pest", + "pest_derive", + "serde", + "serde-inline-default", + "toml", +] + +[[package]] +name = "rmk-macro" +version = "0.7.1" +dependencies = [ + "cargo_toml", + "darling 0.23.0", + "proc-macro2", + "quote", + "rmk-config", + "rmk-types", + "strum", + "syn 2.0.118", +] + +[[package]] +name = "rmk-rp2040-dfu-split" +version = "0.2.0" +dependencies = [ + "const-gen", + "cortex-m-rt", + "defmt 1.1.0", + "defmt-rtt", + "embassy-executor", + "embassy-rp", + "embassy-time", + "json", + "panic-probe", + "portable-atomic", + "rmk", + "static_cell", + "xz2", +] + +[[package]] +name = "rmk-types" +version = "0.2.2" +dependencies = [ + "bitfield-struct", + "defmt 1.1.0", + "heapless 0.9.3", + "postcard", + "rmk-config", + "serde", + "strum", + "toml", +] + +[[package]] +name = "ron" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3" +dependencies = [ + "bitflags 2.13.0", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + +[[package]] +name = "rp-pac" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af65855c40b2c35079514c5489abffc0429347fef25d8467ff98ad84b4322d3" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.28", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sequential-storage" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574e5a379bf43653079b34cb4cf32958404b5f42e0ed5b5adcdff02c2de04d4d" +dependencies = [ + "defmt 1.1.0", + "embedded-storage-async", + "postcard", + "serde", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-inline-default" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf03b7a5281cfd4013bc788d057b0f09fc634397d83bc8973c955bd4f32727a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "smart-leds" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df34e571fa9993fa6f99131a374d58ca3d694b75f9baac93458fe0d6057bf0" +dependencies = [ + "smart-leds-trait", +] + +[[package]] +name = "smart-leds-trait" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f4441a131924d58da6b83a7ad765c460e64630cce504376c3a87a2558c487f" +dependencies = [ + "rgb", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "ssmarshal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850" +dependencies = [ + "encode_unicode", + "serde", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_cell" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "trouble-host" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9fe2be24000037431b036ba244cdb1d26f40e1719548553fb43c3fc62d496b" +dependencies = [ + "aes", + "bt-hci", + "cmac", + "defmt 1.1.0", + "embassy-futures", + "embassy-sync", + "embassy-time", + "embedded-io 0.7.1", + "futures", + "heapless 0.9.3", + "p256", + "rand", + "rand_chacha", + "rand_core 0.6.4", + "serde", + "static_cell", + "trouble-host-macros", + "zerocopy", +] + +[[package]] +name = "trouble-host-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2562dc74f79ad12f7bf088da55c936d5eb1d022f2caac9849175326307a5eec1" +dependencies = [ + "convert_case 0.8.0", + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.118", + "uuid", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "defmt 0.3.100", + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "usbd-hid" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68beab087e4971a2fe76f631478b0e91d39593f58efd2775026ce6dc07a7bac6" +dependencies = [ + "defmt 0.3.100", + "usb-device", + "usbd-hid-macros", +] + +[[package]] +name = "usbd-hid-descriptors" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b297f021719c4308d5d0c61b6c1e7c6b3ba383deba774b49aa5484f996bdb8f1" +dependencies = [ + "bitfield 0.14.0", +] + +[[package]] +name = "usbd-hid-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011a3219e0933f5b3ad7dc90d9a66541a967d084c98c067deed1cd608e557ed7" +dependencies = [ + "byteorder", + "hashbrown 0.13.2", + "log", + "proc-macro2", + "quote", + "serde", + "syn 2.0.118", + "usbd-hid-descriptors", +] + +[[package]] +name = "uuid" +version = "1.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yaml-rust2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/use_config/rp2040_dfu_split/Cargo.toml b/examples/use_config/rp2040_dfu_split/Cargo.toml new file mode 100644 index 000000000..820b7fcf2 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "rmk-rp2040-dfu-split" +version = "0.2.0" +edition = "2021" +build = "build.rs" + +[dependencies] +rmk = { path = "../../../rmk", features = ["dfu_rp", "dfu_split", "split", "storage"] } +embassy-time = { version = "0.5", features = ["defmt"] } +embassy-rp = { version = "0.10", features = ["rp2040", "defmt", "time-driver", "critical-section-impl"] } +embassy-executor = { version = "0.10", features = ["defmt", "platform-cortex-m", "executor-thread"] } +cortex-m-rt = { version = "0.7.5", features = ["set-vtor"] } +static_cell = "2" +portable-atomic = { version = "1.11", features = ["critical-section"] } +defmt = "1.0" +defmt-rtt = "1.0" +panic-probe = { version = "1.0", features = ["print-defmt"] } + +[build-dependencies] +const-gen = "1.6" +json = "0.12" +xz2 = "0.1.7" + +[[bin]] +name = "central" +path = "src/central.rs" + +[[bin]] +name = "peripheral" +path = "src/peripheral.rs" + +[profile.release] +opt-level = "z" +debug = true +lto = true +codegen-units = 1 +overflow-checks = false + +[profile.dev] +opt-level = 1 +debug = true +lto = false +codegen-units = 1 +panic = "unwind" +overflow-checks = true diff --git a/examples/use_config/rp2040_dfu_split/Makefile.toml b/examples/use_config/rp2040_dfu_split/Makefile.toml new file mode 100644 index 000000000..6c8f5e89e --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/Makefile.toml @@ -0,0 +1,60 @@ +[tasks.install-llvm-tools] +install_crate = { rustup_component_name = "llvm-tools" } + +[tasks.flip-link] +install_crate = { crate_name = "flip-link", binary = "flip-link", test_arg = ["-h"] } + +[tasks.build] +command = "cargo" +args = ["build", "--release"] +dependencies = ["bin-peripheral", "install-llvm-tools", "flip-link"] + +[tasks.objcopy-central] +install_crate = { crate_name = "cargo-binutils", binary = "cargo", test_arg = ["objcopy", "--help"] } +command = "cargo" +args = ["objcopy", "--release", "--bin", "central", "--", "-O", "ihex", "rmk-rp2040-dfu-split-central.hex"] +dependencies = ["build"] + +[tasks.objcopy-peripheral] +install_crate = { crate_name = "cargo-binutils", binary = "cargo", test_arg = ["objcopy", "--help"] } +command = "cargo" +args = ["objcopy", "--release", "--bin", "peripheral", "--", "-O", "ihex", "rmk-rp2040-dfu-split-peripheral.hex"] +dependencies = ["build"] + +[tasks.uf2-central] +install_crate = { crate_name = "cargo-hex-to-uf2", binary = "cargo", test_arg = ["hex-to-uf2", "--help"] } +command = "cargo" +args = ["hex-to-uf2", "--input-path", "rmk-rp2040-dfu-split-central.hex", "--output-path", "rmk-rp2040-dfu-split-central.uf2", "--family", "rp2040"] +dependencies = ["objcopy-central"] + +[tasks.uf2-peripheral] +install_crate = { crate_name = "cargo-hex-to-uf2", binary = "cargo", test_arg = ["hex-to-uf2", "--help"] } +command = "cargo" +args = ["hex-to-uf2", "--input-path", "rmk-rp2040-dfu-split-peripheral.hex", "--output-path", "rmk-rp2040-dfu-split-peripheral.uf2", "--family", "rp2040"] +dependencies = ["objcopy-peripheral"] + +[tasks.uf2] +dependencies = ["uf2-central", "uf2-peripheral"] + +[tasks.build-peripheral] +command = "cargo" +args = ["build", "--bin", "peripheral", "--release"] +dependencies = ["install-llvm-tools", "flip-link"] + +[tasks.bin-peripheral] +command = "cargo" +args = ["objcopy", "--release", "--bin", "peripheral", "--", "-O", "binary", "rmk-rp2040-dfu-split-peripheral.bin"] +dependencies = ["build-peripheral"] + +[tasks.build-central] +command = "cargo" +args = ["build", "--bin", "central", "--release"] +dependencies = ["bin-peripheral", "install-llvm-tools", "flip-link"] + +[tasks.bin-central] +command = "cargo" +args = ["objcopy", "--release", "--bin", "central", "--", "-O", "binary", "rmk-rp2040-dfu-split-central.bin"] +dependencies = ["build-central"] + +[tasks.bin] +dependencies = ["bin-central"] diff --git a/examples/use_config/rp2040_dfu_split/build.rs b/examples/use_config/rp2040_dfu_split/build.rs new file mode 100644 index 000000000..eb2be76b4 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/build.rs @@ -0,0 +1,52 @@ +use std::fs::File; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +use const_gen::*; +use xz2::read::XzEncoder; + +fn main() { + println!("cargo:rerun-if-changed=vial.json"); + generate_vial_config(); + + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg=--nmagic"); + println!("cargo:rustc-link-arg=-Tlink.x"); + println!("cargo:rustc-link-arg=-Tdefmt.x"); +} + +fn generate_vial_config() { + let out_file = Path::new(&env::var_os("OUT_DIR").unwrap()).join("config_generated.rs"); + + let p = Path::new("vial.json"); + let mut content = String::new(); + match File::open(p) { + Ok(mut file) => { + file.read_to_string(&mut content).expect("Cannot read vial.json"); + } + Err(e) => println!("Cannot find vial.json {:?}: {}", p, e), + }; + + let vial_cfg = json::stringify(json::parse(&content).unwrap()); + let mut keyboard_def_compressed: Vec = Vec::new(); + XzEncoder::new(vial_cfg.as_bytes(), 6) + .read_to_end(&mut keyboard_def_compressed) + .unwrap(); + + let keyboard_id: Vec = vec![0xB9, 0xBC, 0x09, 0xB2, 0x9D, 0x37, 0x4C, 0xEA]; + let const_declarations = { + let a = const_declaration!(pub VIAL_KEYBOARD_DEF = keyboard_def_compressed); + let b = const_declaration!(pub VIAL_KEYBOARD_ID = keyboard_id); + format!("{}\n{}", a, b) + }; + fs::write(out_file, const_declarations).unwrap(); +} diff --git a/examples/use_config/rp2040_dfu_split/keyboard.toml b/examples/use_config/rp2040_dfu_split/keyboard.toml new file mode 100644 index 000000000..4e8f201a7 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/keyboard.toml @@ -0,0 +1,62 @@ +[keyboard] +name = "RMK RP2040 DFU split" +product_name = "RMK RP2040 DFU split" +vendor_id = 0x4c4b +product_id = 0x4643 +manufacturer = "RMK" +chip = "rp2040" + +[rmk] +split_peripherals_num = 1 + +[split] +connection = "serial" + +[split.central] +rows = 2 +cols = 2 +row_offset = 0 +col_offset = 0 +serial = [{ instance = "UART0", tx_pin = "PIN_0", rx_pin = "PIN_1" }] +[split.central.matrix] +matrix_type = "normal" +row_pins = ["PIN_6", "PIN_7"] +col_pins = ["PIN_19", "PIN_20"] + +[[split.peripheral]] +rows = 2 +cols = 1 +row_offset = 2 +col_offset = 2 +serial = [{ instance = "UART0", tx_pin = "PIN_0", rx_pin = "PIN_1" }] +firmware = "rmk-rp2040-dfu-split-peripheral.bin" +update_policy = "MatchHash" +# update_policy = "force" to force a peripheral update every start of central +[split.peripheral.matrix] +matrix_type = "normal" +row_pins = ["PIN_8", "PIN_9"] +col_pins = ["PIN_10"] + +[layout] +rows = 4 +cols = 3 +layers = 2 +keymap = [ + [ + ["A", "B", "C"], + ["Kc1", "Kc2", "Kc3"], + ["LCtrl", "MO(1)", "LShift"], + ["OSL(1)", "LT(2, Kc9)", "LM(1, LShift | LGui)"] + ], + [ + ["_", "TT(1)", "TG(2)"], + ["_", "_", "_"], + ["_", "_", "_"], + ["_", "_", "_"], + ], +] + +[dfu] +led = "PIN_25" + +[storage] diff --git a/examples/use_config/rp2040_dfu_split/memory.x b/examples/use_config/rp2040_dfu_split/memory.x new file mode 100644 index 000000000..88fbc73cb --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/memory.x @@ -0,0 +1,13 @@ +/* +This memory.x is for the RP2040 with 2MB flash. +For different flash sizes uncomment below. +Be aware you need to change FLASH_SIZE in src/central.rs and src/peripheral.rs accordingly +and flash the matching bootloader. +*/ +MEMORY { +FLASH : ORIGIN = 0x10007000, LENGTH = 944K +/* 4MB: FLASH : ORIGIN = 0x10007000, LENGTH = 1968K */ +/* 8MB: FLASH : ORIGIN = 0x10007000, LENGTH = 4016K */ +/* 16MB: FLASH : ORIGIN = 0x10007000, LENGTH = 8112K */ +RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/use_config/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin b/examples/use_config/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin new file mode 100755 index 000000000..2ded5fab2 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin @@ -0,0 +1 @@ +AAAABBBBCCCCDDDD \ No newline at end of file diff --git a/examples/use_config/rp2040_dfu_split/src/central.rs b/examples/use_config/rp2040_dfu_split/src/central.rs new file mode 100644 index 000000000..db41ba4fb --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/src/central.rs @@ -0,0 +1,7 @@ +#![no_main] +#![no_std] + +use rmk::macros::rmk_central; + +#[rmk_central] +mod keyboard_central {} diff --git a/examples/use_config/rp2040_dfu_split/src/peripheral.rs b/examples/use_config/rp2040_dfu_split/src/peripheral.rs new file mode 100644 index 000000000..36c4a3281 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/src/peripheral.rs @@ -0,0 +1,7 @@ +#![no_main] +#![no_std] + +use rmk::macros::rmk_peripheral; + +#[rmk_peripheral(id = 0)] +mod keyboard_peripheral {} diff --git a/examples/use_config/rp2040_dfu_split/vial.json b/examples/use_config/rp2040_dfu_split/vial.json new file mode 100644 index 000000000..0b9befba1 --- /dev/null +++ b/examples/use_config/rp2040_dfu_split/vial.json @@ -0,0 +1,37 @@ +{ + "name": "RMK Keyboard", + "vendorId": "0x4C4B", + "productId": "0x4643", + "lighting": "none", + "matrix": { + "rows": 4, + "cols": 3 + }, + "layouts": { + "keymap": [ + [ + "0,0", + "0,1", + "0,2" + ], + [ + "1,0", + "1,1", + "1,2" + ], + [ + "2,0", + "2,1", + "2,2" + ], + [ + { + "y": -2, + "x": 4 + }, + "3,0", + "3,2" + ] + ] + } +} diff --git a/examples/use_rust/rp2040_dfu_split/.cargo/config.toml b/examples/use_rust/rp2040_dfu_split/.cargo/config.toml new file mode 100644 index 000000000..bb80da335 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/.cargo/config.toml @@ -0,0 +1,11 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip RP2040" +# runner = "elf2uf2-rs -d" + +linker = "flip-link" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "info" diff --git a/examples/use_rust/rp2040_dfu_split/.gitignore b/examples/use_rust/rp2040_dfu_split/.gitignore new file mode 100644 index 000000000..f707059d6 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/.gitignore @@ -0,0 +1,9 @@ +rmk-rp2040-dfu-split-*.bin +rmk-rp2040-dfu-split-*.hex +rmk-rp2040-dfu-split-*.uf2 +peripheral.bin +central.bin +central.hex +peripheral.hex +central.uf2 +peripheral.uf2 diff --git a/examples/use_rust/rp2040_dfu_split/Cargo.lock b/examples/use_rust/rp2040_dfu_split/Cargo.lock new file mode 100644 index 000000000..e6acda25c --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/Cargo.lock @@ -0,0 +1,3044 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "arrayvec" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" + +[[package]] +name = "ascii-canvas" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" +dependencies = [ + "term", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "az" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version 0.2.3", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitfield-struct" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ca6739863c590881f038d033a146c51ddae239186a4327014839fd864f44ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bt-hci" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3c66fd0a41a3b3a5906847b1bf5516048ce083e24fa9341a0fa31d3b4cd73d" +dependencies = [ + "btuuid", + "defmt 1.1.0", + "embassy-sync", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "futures-intrusive", + "heapless 0.9.3", + "serde", +] + +[[package]] +name = "btuuid" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f48f1e9b0aad0a4f05d17bdeae0fa20ff798e272a03a6940ca27ad9c5a6ae7" +dependencies = [ + "defmt 0.3.100", + "serde", + "uuid", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cargo_toml" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa61aec073ec94791433ddf3df2323ff9d1711557c2a0eefb0f99cb4f8dca520" +dependencies = [ + "semver 1.0.28", + "serde", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "defmt 1.1.0", + "num-traits", + "pure-rust-locales", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cmac" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" +dependencies = [ + "cipher", + "dbl", + "digest", +] + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "config" +version = "0.15.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d0237145f33580b89724f75d16950efd3e2c91b2d823917ecb69ec7f84f0" +dependencies = [ + "async-trait", + "convert_case 0.6.0", + "json5", + "pathdiff", + "ron", + "rust-ini", + "serde-untagged", + "serde_core", + "serde_json", + "toml", + "winnow", + "yaml-rust2", +] + +[[package]] +name = "const-gen" +version = "1.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bb51eeea292ceb9bf5312b4416c1b151b130502e6892a2ae174ae1e4b51e58" +dependencies = [ + "const-gen-derive", +] + +[[package]] +name = "const-gen-derive" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabc01a5585d36649b6bee1f1198d112a7bb4982661d6c18406fba571ce3263e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cordyceps" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +dependencies = [ + "loom", + "tracing", +] + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal", + "bitfield 0.13.2", + "embedded-hal 0.2.7", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d4dec46b34c299ccf6b036717ae0fce602faa4f4fe816d9013b9a7c9f5ba6" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc-any" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" +dependencies = [ + "debug-helper", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dbl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" +dependencies = [ + "generic-array", +] + +[[package]] +name = "debug-helper" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" + +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "defmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "defmt-rtt" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f73a4a4a91609e977ae3b7bd831ffa292edfd42ad140a3244a61d805b0e05e" +dependencies = [ + "critical-section", + "defmt 1.1.0", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embassy-boot" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73bfc955c9238df2ce9fecc6f18a64b13ecaa41a3cd42d940a09f304765d10db" +dependencies = [ + "digest", + "document-features", + "embassy-embedded-hal", + "embassy-sync", + "embedded-storage", + "embedded-storage-async", + "signature", +] + +[[package]] +name = "embassy-boot-rp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc17df4cf2a8ba86e8559a6515104e31ca7aa6b36221199310e244157935204" +dependencies = [ + "cfg-if", + "cortex-m", + "cortex-m-rt", + "embassy-boot", + "embassy-rp", + "embassy-sync", + "embassy-time", + "embedded-storage", + "embedded-storage-async", +] + +[[package]] +name = "embassy-embedded-hal" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0641612053b2f34fc250bb63f6630ae75de46e02ade7f457268447081d709ce" +dependencies = [ + "defmt 1.1.0", + "embassy-futures", + "embassy-hal-internal 0.4.0", + "embassy-sync", + "embassy-time", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-storage", + "embedded-storage-async", + "nb 1.1.0", +] + +[[package]] +name = "embassy-executor" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0d3b15c9d7dc4fec1d8cb77112472fb008b3b28c51ad23838d83587a6d2f1e" +dependencies = [ + "cordyceps", + "cortex-m", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-executor-macros", + "embassy-executor-timer-queue", +] + +[[package]] +name = "embassy-executor-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11a246f53de5f97a387f40ac24726817cd0b6f833e7603baac784f29d6ff276" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "embassy-executor-timer-queue" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" + +[[package]] +name = "embassy-futures" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "embassy-hal-internal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f10ce10a4dfdf6402d8e9bd63128986b96a736b1a0a6680547ed2ac55d55dba" +dependencies = [ + "num-traits", +] + +[[package]] +name = "embassy-hal-internal" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568659fc53866d3d85c60fa33723fb751aa69e71507634fc2c19e7649432fb75" +dependencies = [ + "cortex-m", + "critical-section", + "defmt 1.1.0", + "num-traits", +] + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] +name = "embassy-net-driver-channel" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07d2eb9f05a6fc876500949856ea1be40773d866d8cb99384f72d0ae4568c16" +dependencies = [ + "embassy-futures", + "embassy-net-driver", + "embassy-sync", +] + +[[package]] +name = "embassy-nrf" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82051f67e8a06a8faebd183f62cfade184c96be68126c5f064abb8223a591ff1" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "embassy-usb-synopsys-otg", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "nrf-pac", + "rand_core 0.10.1", + "rand_core 0.6.4", + "rand_core 0.9.5", +] + +[[package]] +name = "embassy-rp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d98f472894c1ecffac5596c657af5305c57282d29e8746d7fec033951931bc8" +dependencies = [ + "cfg-if", + "chrono", + "cortex-m", + "cortex-m-rt", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-sync", + "embassy-time", + "embassy-time-driver", + "embassy-time-queue-utils", + "embassy-usb-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io 0.7.1", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "nb 1.1.0", + "pio", + "rand_core 0.6.4", + "rand_core 0.9.5", + "rp-pac", + "rp2040-boot2", + "sha2-const-stable", + "smart-leds", +] + +[[package]] +name = "embassy-sync" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbd85cf5a5ae56bdf26f618364af642d1d0a4e245cdd75cd9aabda382f65a81" +dependencies = [ + "cfg-if", + "critical-section", + "defmt 1.1.0", + "embedded-io-async 0.7.0", + "futures-core", + "futures-sink", + "heapless 0.9.3", +] + +[[package]] +name = "embassy-time" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "592b0c143ec626e821d4d90da51a2bd91d559d6c442b7c74a47d368c9e23d97a" +dependencies = [ + "cfg-if", + "critical-section", + "defmt 1.1.0", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee71af1b3a0deaa53eaf2d39252f83504c853646e472400b763060389b9fcc9" +dependencies = [ + "document-features", +] + +[[package]] +name = "embassy-time-queue-utils" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168297bf80aaf114b3c9ad589bf38b01b3009b9af7f97cd18086c5bbf96f5693" +dependencies = [ + "embassy-executor-timer-queue", + "heapless 0.9.3", +] + +[[package]] +name = "embassy-usb" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25746d8b152b72fbf2a217f489a083dbbe243f281f09184a1f2cfbe9bbb245f" +dependencies = [ + "bitflags 2.13.0", + "defmt 1.1.0", + "embassy-futures", + "embassy-net-driver-channel", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "embedded-io-async 0.7.0", + "heapless 0.9.3", + "ssmarshal", + "usbd-hid", +] + +[[package]] +name = "embassy-usb-dfu" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3678d16e1e71b052d00f7518620b5839a878f2bdab2019b81d984fb2a74f" +dependencies = [ + "bitflags 2.13.0", + "cortex-m", + "embassy-boot", + "embassy-futures", + "embassy-sync", + "embassy-time", + "embassy-usb", + "embedded-storage", +] + +[[package]] +name = "embassy-usb-driver" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa675c5f4349b6aa0fcffc4bf9b241f18cd11b97c1f8323273fb9a5449937fbd" +dependencies = [ + "defmt 1.1.0", + "embedded-io-async 0.6.1", + "embedded-io-async 0.7.0", +] + +[[package]] +name = "embassy-usb-synopsys-otg" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6efd0c0e0b21bcf91b475b8fa47347c1df32c9a99728ae1e1b0e6625862dfd1a" +dependencies = [ + "critical-section", + "defmt 1.1.0", + "embassy-sync", + "embassy-time", + "embassy-usb-driver", + "portable-atomic", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" +dependencies = [ + "defmt 0.3.100", +] + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "defmt 0.3.100", + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb1aa714776b75c7e67e1da744b81a129b3ff919c8712b5e1b32252c1f07cc7" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io 0.6.1", +] + +[[package]] +name = "embedded-io-async" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564b9f813c544241430e147d8bc454815ef9ac998878d30cc3055449f7fd4c0" +dependencies = [ + "defmt 1.1.0", + "embedded-io 0.7.1", +] + +[[package]] +name = "embedded-storage" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21dea9854beb860f3062d10228ce9b976da520a73474aed3171ec276bc0c032" + +[[package]] +name = "embedded-storage-async" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1763775e2323b7d5f0aa6090657f5e21cfa02ede71f5dc40eead06d64dcd15cc" +dependencies = [ + "embedded-storage", +] + +[[package]] +name = "ena" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1" +dependencies = [ + "log", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixed" +version = "1.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af2cbf772fa6d1c11358f92ef554cb6b386201210bcf0e91fb7fba8a907fb40" +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generator" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b854b0e584ead1a33f18b2fcad7cf7be18b3875c78816b753639aa501513ae" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hashlink" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32 0.3.1", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "defmt 1.1.0", + "hash32 0.3.1", + "serde_core", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "sha3", + "string_cache", + "term", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5baa5e9ff84f1aefd264e6869907646538a52147a755d494517a8007fb48733" +dependencies = [ + "regex-automata", + "rustversion", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nrf-pac" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83531b9f1e19f493018da9b8b20359e4cedd6d1357553b1ac50f3fa27a0104fa" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "elliptic-curve", + "primeorder", +] + +[[package]] +name = "panic-probe" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd402d00b0fb94c5aee000029204a46884b1262e0c443f166d86d2c0747e1a1a" +dependencies = [ + "cortex-m", + "defmt 1.1.0", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pio" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ba4153cee9585abc451271aa437d9e8defdea8b468d48ba6b8f098cbe03d7f" +dependencies = [ + "pio-core", + "pio-proc", +] + +[[package]] +name = "pio-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61d90fddc3d67f21bbf93683bc461b05d6a29c708caf3ffb79947d7ff7095406" +dependencies = [ + "arrayvec", + "num_enum", + "paste", +] + +[[package]] +name = "pio-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825266c1eaddf54f636d06eefa4bf3c99d774c14ec46a4a6c6e5128a0f10d205" +dependencies = [ + "lalrpop", + "lalrpop-util", + "pio-core", +] + +[[package]] +name = "pio-proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed4a76571f5fe51af43cc80ac870fe0c79cc0cdd686b9002a6c4c84bfdd0176b" +dependencies = [ + "codespan-reporting", + "lalrpop-util", + "pio-core", + "pio-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +dependencies = [ + "critical-section", +] + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "defmt 1.1.0", + "heapless 0.7.17", + "postcard-derive", + "serde", +] + +[[package]] +name = "postcard-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pure-rust-locales" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d" +dependencies = [ + "defmt 1.1.0", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "rmk" +version = "0.8.2" +dependencies = [ + "bt-hci", + "byteorder", + "cortex-m", + "crc32fast", + "defmt 1.1.0", + "document-features", + "embassy-boot", + "embassy-boot-rp", + "embassy-embedded-hal", + "embassy-futures", + "embassy-hal-internal 0.5.0", + "embassy-nrf", + "embassy-rp", + "embassy-sync", + "embassy-time", + "embassy-usb", + "embassy-usb-dfu", + "embedded-hal 1.0.0", + "embedded-hal-async", + "embedded-io-async 0.7.0", + "embedded-storage", + "embedded-storage-async", + "fixed", + "futures", + "heapless 0.9.3", + "paste", + "pio", + "postcard", + "rmk-macro", + "rmk-types", + "sequential-storage", + "serde", + "static_cell", + "trouble-host", + "usbd-hid", +] + +[[package]] +name = "rmk-config" +version = "0.6.1" +dependencies = [ + "config", + "once_cell", + "paste", + "pest", + "pest_derive", + "serde", + "serde-inline-default", + "toml", +] + +[[package]] +name = "rmk-macro" +version = "0.7.1" +dependencies = [ + "cargo_toml", + "darling 0.23.0", + "proc-macro2", + "quote", + "rmk-config", + "rmk-types", + "strum", + "syn 2.0.118", +] + +[[package]] +name = "rmk-rp2040-dfu-split" +version = "0.1.0" +dependencies = [ + "const-gen", + "cortex-m-rt", + "defmt 1.1.0", + "defmt-rtt", + "embassy-executor", + "embassy-rp", + "embassy-time", + "json", + "panic-probe", + "portable-atomic", + "rmk", + "static_cell", + "xz2", +] + +[[package]] +name = "rmk-types" +version = "0.2.2" +dependencies = [ + "bitfield-struct", + "defmt 1.1.0", + "heapless 0.9.3", + "postcard", + "rmk-config", + "serde", + "strum", + "toml", +] + +[[package]] +name = "ron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" +dependencies = [ + "bitflags 2.13.0", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + +[[package]] +name = "rp-pac" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af65855c40b2c35079514c5489abffc0429347fef25d8467ff98ad84b4322d3" +dependencies = [ + "cortex-m", + "cortex-m-rt", +] + +[[package]] +name = "rp2040-boot2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21" +dependencies = [ + "crc-any", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.28", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "sequential-storage" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574e5a379bf43653079b34cb4cf32958404b5f42e0ed5b5adcdff02c2de04d4d" +dependencies = [ + "defmt 1.1.0", + "embedded-storage-async", + "postcard", + "serde", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-inline-default" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf03b7a5281cfd4013bc788d057b0f09fc634397d83bc8973c955bd4f32727a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "smart-leds" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66df34e571fa9993fa6f99131a374d58ca3d694b75f9baac93458fe0d6057bf0" +dependencies = [ + "smart-leds-trait", +] + +[[package]] +name = "smart-leds-trait" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f4441a131924d58da6b83a7ad765c460e64630cce504376c3a87a2558c487f" +dependencies = [ + "rgb", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "ssmarshal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850" +dependencies = [ + "encode_unicode", + "serde", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_cell" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "trouble-host" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9fe2be24000037431b036ba244cdb1d26f40e1719548553fb43c3fc62d496b" +dependencies = [ + "aes", + "bt-hci", + "cmac", + "defmt 1.1.0", + "embassy-futures", + "embassy-sync", + "embassy-time", + "embedded-io 0.7.1", + "futures", + "heapless 0.9.3", + "p256", + "rand", + "rand_chacha", + "rand_core 0.6.4", + "serde", + "static_cell", + "trouble-host-macros", + "zerocopy", +] + +[[package]] +name = "trouble-host-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2562dc74f79ad12f7bf088da55c936d5eb1d022f2caac9849175326307a5eec1" +dependencies = [ + "convert_case 0.8.0", + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.118", + "uuid", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "defmt 0.3.100", + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "usbd-hid" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68beab087e4971a2fe76f631478b0e91d39593f58efd2775026ce6dc07a7bac6" +dependencies = [ + "defmt 0.3.100", + "usb-device", + "usbd-hid-macros", +] + +[[package]] +name = "usbd-hid-descriptors" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b297f021719c4308d5d0c61b6c1e7c6b3ba383deba774b49aa5484f996bdb8f1" +dependencies = [ + "bitfield 0.14.0", +] + +[[package]] +name = "usbd-hid-macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011a3219e0933f5b3ad7dc90d9a66541a967d084c98c067deed1cd608e557ed7" +dependencies = [ + "byteorder", + "hashbrown 0.13.2", + "log", + "proc-macro2", + "quote", + "serde", + "syn 2.0.118", + "usbd-hid-descriptors", +] + +[[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc" +dependencies = [ + "vcell", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + +[[package]] +name = "yaml-rust2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/use_rust/rp2040_dfu_split/Cargo.toml b/examples/use_rust/rp2040_dfu_split/Cargo.toml new file mode 100644 index 000000000..1302c7eeb --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "rmk-rp2040-dfu-split" +version = "0.1.0" +authors = ["RMK contributors"] +description = "RMK example: RP2040 split keyboard with embassy-boot and dfu_split" +homepage = "https://github.com/haobogu/rmk" +repository = "https://github.com/haobogu/rmk" +readme = "../../README.md" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +rmk = { path = "../../../rmk", features = ["dfu_rp", "split", "dfu_split"] } +embassy-time = { version = "0.5", features = ["defmt"] } +embassy-rp = { version = "0.10", features = ["rp2040", "defmt", "time-driver", "critical-section-impl"] } +embassy-executor = { version = "0.10", features = ["defmt", "platform-cortex-m", "executor-thread"] } +cortex-m-rt = { version = "0.7.5", features = ["set-vtor"] } +static_cell = "2" +portable-atomic = { version = "1.11", features = ["critical-section"] } +defmt = "1.0" +defmt-rtt = "1.0" +panic-probe = { version = "1.0", features = ["print-defmt"] } + +[build-dependencies] +const-gen = "1.6" +json = "0.12" +xz2 = "0.1.7" + +[[bin]] +path = "src/central.rs" +name = "central" + +[[bin]] +path = "src/peripheral.rs" +name = "peripheral" + +[profile.release] +opt-level = "z" +debug = true +lto = true +codegen-units = 1 +overflow-checks = false + +[profile.dev] +opt-level = 1 +debug = true +lto = false +codegen-units = 1 +panic = "unwind" +overflow-checks = true diff --git a/examples/use_rust/rp2040_dfu_split/Makefile.toml b/examples/use_rust/rp2040_dfu_split/Makefile.toml new file mode 100644 index 000000000..6c8f5e89e --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/Makefile.toml @@ -0,0 +1,60 @@ +[tasks.install-llvm-tools] +install_crate = { rustup_component_name = "llvm-tools" } + +[tasks.flip-link] +install_crate = { crate_name = "flip-link", binary = "flip-link", test_arg = ["-h"] } + +[tasks.build] +command = "cargo" +args = ["build", "--release"] +dependencies = ["bin-peripheral", "install-llvm-tools", "flip-link"] + +[tasks.objcopy-central] +install_crate = { crate_name = "cargo-binutils", binary = "cargo", test_arg = ["objcopy", "--help"] } +command = "cargo" +args = ["objcopy", "--release", "--bin", "central", "--", "-O", "ihex", "rmk-rp2040-dfu-split-central.hex"] +dependencies = ["build"] + +[tasks.objcopy-peripheral] +install_crate = { crate_name = "cargo-binutils", binary = "cargo", test_arg = ["objcopy", "--help"] } +command = "cargo" +args = ["objcopy", "--release", "--bin", "peripheral", "--", "-O", "ihex", "rmk-rp2040-dfu-split-peripheral.hex"] +dependencies = ["build"] + +[tasks.uf2-central] +install_crate = { crate_name = "cargo-hex-to-uf2", binary = "cargo", test_arg = ["hex-to-uf2", "--help"] } +command = "cargo" +args = ["hex-to-uf2", "--input-path", "rmk-rp2040-dfu-split-central.hex", "--output-path", "rmk-rp2040-dfu-split-central.uf2", "--family", "rp2040"] +dependencies = ["objcopy-central"] + +[tasks.uf2-peripheral] +install_crate = { crate_name = "cargo-hex-to-uf2", binary = "cargo", test_arg = ["hex-to-uf2", "--help"] } +command = "cargo" +args = ["hex-to-uf2", "--input-path", "rmk-rp2040-dfu-split-peripheral.hex", "--output-path", "rmk-rp2040-dfu-split-peripheral.uf2", "--family", "rp2040"] +dependencies = ["objcopy-peripheral"] + +[tasks.uf2] +dependencies = ["uf2-central", "uf2-peripheral"] + +[tasks.build-peripheral] +command = "cargo" +args = ["build", "--bin", "peripheral", "--release"] +dependencies = ["install-llvm-tools", "flip-link"] + +[tasks.bin-peripheral] +command = "cargo" +args = ["objcopy", "--release", "--bin", "peripheral", "--", "-O", "binary", "rmk-rp2040-dfu-split-peripheral.bin"] +dependencies = ["build-peripheral"] + +[tasks.build-central] +command = "cargo" +args = ["build", "--bin", "central", "--release"] +dependencies = ["bin-peripheral", "install-llvm-tools", "flip-link"] + +[tasks.bin-central] +command = "cargo" +args = ["objcopy", "--release", "--bin", "central", "--", "-O", "binary", "rmk-rp2040-dfu-split-central.bin"] +dependencies = ["build-central"] + +[tasks.bin] +dependencies = ["bin-central"] diff --git a/examples/use_rust/rp2040_dfu_split/build.rs b/examples/use_rust/rp2040_dfu_split/build.rs new file mode 100644 index 000000000..eb2be76b4 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/build.rs @@ -0,0 +1,52 @@ +use std::fs::File; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + +use const_gen::*; +use xz2::read::XzEncoder; + +fn main() { + println!("cargo:rerun-if-changed=vial.json"); + generate_vial_config(); + + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg=--nmagic"); + println!("cargo:rustc-link-arg=-Tlink.x"); + println!("cargo:rustc-link-arg=-Tdefmt.x"); +} + +fn generate_vial_config() { + let out_file = Path::new(&env::var_os("OUT_DIR").unwrap()).join("config_generated.rs"); + + let p = Path::new("vial.json"); + let mut content = String::new(); + match File::open(p) { + Ok(mut file) => { + file.read_to_string(&mut content).expect("Cannot read vial.json"); + } + Err(e) => println!("Cannot find vial.json {:?}: {}", p, e), + }; + + let vial_cfg = json::stringify(json::parse(&content).unwrap()); + let mut keyboard_def_compressed: Vec = Vec::new(); + XzEncoder::new(vial_cfg.as_bytes(), 6) + .read_to_end(&mut keyboard_def_compressed) + .unwrap(); + + let keyboard_id: Vec = vec![0xB9, 0xBC, 0x09, 0xB2, 0x9D, 0x37, 0x4C, 0xEA]; + let const_declarations = { + let a = const_declaration!(pub VIAL_KEYBOARD_DEF = keyboard_def_compressed); + let b = const_declaration!(pub VIAL_KEYBOARD_ID = keyboard_id); + format!("{}\n{}", a, b) + }; + fs::write(out_file, const_declarations).unwrap(); +} diff --git a/examples/use_rust/rp2040_dfu_split/memory.x b/examples/use_rust/rp2040_dfu_split/memory.x new file mode 100644 index 000000000..88fbc73cb --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/memory.x @@ -0,0 +1,13 @@ +/* +This memory.x is for the RP2040 with 2MB flash. +For different flash sizes uncomment below. +Be aware you need to change FLASH_SIZE in src/central.rs and src/peripheral.rs accordingly +and flash the matching bootloader. +*/ +MEMORY { +FLASH : ORIGIN = 0x10007000, LENGTH = 944K +/* 4MB: FLASH : ORIGIN = 0x10007000, LENGTH = 1968K */ +/* 8MB: FLASH : ORIGIN = 0x10007000, LENGTH = 4016K */ +/* 16MB: FLASH : ORIGIN = 0x10007000, LENGTH = 8112K */ +RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/use_rust/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin b/examples/use_rust/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin new file mode 100755 index 000000000..2ded5fab2 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/rmk-rp2040-dfu-split-peripheral.bin @@ -0,0 +1 @@ +AAAABBBBCCCCDDDD \ No newline at end of file diff --git a/examples/use_rust/rp2040_dfu_split/src/central.rs b/examples/use_rust/rp2040_dfu_split/src/central.rs new file mode 100644 index 000000000..e0a45ff36 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/src/central.rs @@ -0,0 +1,184 @@ +#![no_main] +#![no_std] + +#[macro_use] +mod keymap; +#[macro_use] +mod macros; +mod vial; + +use defmt::info; +use defmt_rtt as _; +use embassy_executor::Spawner; +use embassy_rp::gpio::{Input, Level, Output}; +use embassy_rp::peripherals::{UART0, USB}; +use embassy_rp::uart::{self, BufferedUart}; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_rp::{bind_interrupts, dma}; +use panic_probe as _; +use rmk::config::{BehaviorConfig, DeviceConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig}; +use rmk::debounce::default_debouncer::DefaultDebouncer; +use rmk::futures::future::join; +use rmk::host::HostService; +use rmk::keyboard::Keyboard; +use rmk::matrix::Matrix; +use rmk::processor::builtin::dfu_led::DfuLedProcessor; +use rmk::processor::builtin::wpm::WpmProcessor; +use rmk::split::central::run_peripheral_manager; +use rmk::split::SPLIT_MESSAGE_MAX_SIZE; +use rmk::storage::async_flash_wrapper; +use rmk::usb::UsbTransport; +use rmk::watchdog::Rp2040Watchdog; +use rmk::{initialize_keymap_and_storage, run_all, KeymapData}; +use static_cell::StaticCell; +use vial::{VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID}; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; + UART0_IRQ => uart::BufferedInterruptHandler; + DMA_IRQ_0 => dma::InterruptHandler; +}); + +const PERIPHERAL1_BIN: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/rmk-rp2040-dfu-split-peripheral.bin" +)); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("RMK start!"); + let p = embassy_rp::init(Default::default()); + + let driver = Driver::new(p.USB, Irqs); + + let (row_pins, col_pins) = config_matrix_pins_rp!(peripherals: p, input: [PIN_6, PIN_7], output: [PIN_19, PIN_20]); + + // Flash layout using the bootymcbootface formula: + // state at 0x6000 (4K), active from 0x7000 (size: (flash_size - 28K (= BOOT2 size + embassy-boot + embassy-boot state) - STORAGE_SIZE (= 128K) - page_size (= 4K)) / 2), + // dfu follows active (active_size + page_size (= 4K)) + // + // All offsets (DFU_OFFSET, DFU_SIZE, STORAGE_OFFSET, etc.) are derived + // automatically from FLASH_SIZE below --- change only that constant when using + // bootymcbootface. + // + // ⚠ You can define your own FLASH_SIZE and addresses, but then you must build and + // flash a custom embassy-boot bootloader with a matching memory.x! + const FLASH_SIZE: u32 = 2 * 1024 * 1024; // 2 MB (default) + // const FLASH_SIZE: u32 = 4 * 1024 * 1024; // 4 MB + // const FLASH_SIZE: u32 = 8 * 1024 * 1024; // 8 MB + // const FLASH_SIZE: u32 = 16 * 1024 * 1024; // 16 MB + const PAGE_SIZE: u32 = 4 * 1024; + const STORAGE_SIZE: u32 = 128 * 1024; // 32 sectors Γ— 4K after ACTIVE+DFU + const STATE_OFFSET: u32 = 0x6000; + const STATE_SIZE: u32 = 0x1000; + const ACTIVE_OFFSET: u32 = 0x7000; // after 28K bootloader + state + let remaining: u32 = FLASH_SIZE + - 28 * 1024 // size of boot 2 + embassy-boot + embassy-boot state + - STORAGE_SIZE; + let active_size: u32 = (remaining - PAGE_SIZE) / 2; // DFU = ACTIVE + 1 page (embassy-boot requirement) + let dfu_size: u32 = active_size + PAGE_SIZE; // embassy-boot needs that extra page for swap info + let dfu_offset: u32 = ACTIVE_OFFSET + active_size; // dfu after active + let storage_offset: u32 = dfu_offset + dfu_size; // storage after active + dfu + assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE); // sanity check that we fit everything in flash + + info!( + "Flash layout: state @ 0x{:04X} ({}K), active @ 0x{:04X} ({}K), dfu @ 0x{:04X} ({}K), storage @ 0x{:04X} ({}K)", + STATE_OFFSET, + STATE_SIZE / 1024, + ACTIVE_OFFSET, + active_size / 1024, + dfu_offset, + dfu_size / 1024, + storage_offset, + STORAGE_SIZE / 1024 + ); + + let flash = async_flash_wrapper(rmk::dfu::init_flash( + p.FLASH, + storage_offset, + STORAGE_SIZE, + STATE_OFFSET, + STATE_SIZE, + dfu_offset, + dfu_size, + )); + + let keyboard_device_config = DeviceConfig { + vid: 0x4c4b, + pid: 0x4643, + manufacturer: "Haobo", + product_name: "RMK Keyboard RP2040 embassy-boot split use_rust example", + serial_number: "vial:f64c2b3c:000001", + }; + + let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]); + + let rmk_config = RmkConfig { + device_config: keyboard_device_config, + vial_config, + ..Default::default() + }; + + let mut keymap_data = KeymapData::new(keymap::get_default_keymap()); + let storage_config = StorageConfig { + num_sectors: 32, + start_addr: 0, + clear_storage: false, + clear_layout: false, + }; + let mut behavior_config = BehaviorConfig::default(); + let per_key_config = PositionalConfig::default(); + let (keymap, mut storage) = initialize_keymap_and_storage( + &mut keymap_data, + flash, + &storage_config, + &mut behavior_config, + &per_key_config, + ) + .await; + + // mark the firmware as booted otherwise the bootloader thinks it didn't and will revert to the old firmware + rmk::dfu::mark_booted(); + + // DFU LED processor, optional. Flashes the LED when DFU is active + let mut dfu_led_processor = DfuLedProcessor::new(Output::new(p.PIN_25, Level::Low), false); + + // Register peripheral firmware for DFU update over split + if rmk::dfu::set_firmware_update_data(0, PERIPHERAL1_BIN, rmk::crc32::crc32(PERIPHERAL1_BIN)).is_ok() { + info!("registered peripheral firmware"); + } + + let debouncer = DefaultDebouncer::new(); + let mut matrix = Matrix::<_, _, _, 2, 2, true>::new(row_pins, col_pins, debouncer); + let mut keyboard = Keyboard::new(&keymap); + let host_ctx = rmk::host::KeyboardContext::new(&keymap); + let mut host_service = HostService::new(&host_ctx, &rmk_config); + + let mut usb_transport = UsbTransport::new(driver, rmk_config.device_config); + let mut wpm_processor = WpmProcessor::new(); + + let mut watchdog_runner = Rp2040Watchdog::default_runner(embassy_rp::watchdog::Watchdog::new(p.WATCHDOG)); + + // UART for split peripheral communication + static TX_BUF: StaticCell<[u8; SPLIT_MESSAGE_MAX_SIZE]> = StaticCell::new(); + let tx_buf = &mut TX_BUF.init([0; SPLIT_MESSAGE_MAX_SIZE])[..]; + static RX_BUF: StaticCell<[u8; SPLIT_MESSAGE_MAX_SIZE]> = StaticCell::new(); + let rx_buf = &mut RX_BUF.init([0; SPLIT_MESSAGE_MAX_SIZE])[..]; + let uart_receiver = BufferedUart::new(p.UART0, p.PIN_0, p.PIN_1, Irqs, tx_buf, rx_buf, uart::Config::default()); + + join( + run_all!( + matrix, + storage, + usb_transport, + wpm_processor, + dfu_led_processor, + keyboard, + host_service, + watchdog_runner + ), + // use UpdatePolicy::Force to force the peripheral update at every start of central + run_peripheral_manager::<2, 1, 2, 2, _>(0, uart_receiver, rmk::split::central::UpdatePolicy::MatchHash), + ) + .await; +} diff --git a/examples/use_rust/rp2040_dfu_split/src/keymap.rs b/examples/use_rust/rp2040_dfu_split/src/keymap.rs new file mode 100644 index 000000000..afc920b2d --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/src/keymap.rs @@ -0,0 +1,18 @@ +use rmk::types::action::KeyAction; +use rmk::{k, layer}; + +pub(crate) const COL: usize = 3; +pub(crate) const ROW: usize = 4; +pub(crate) const NUM_LAYER: usize = 1; + +#[rustfmt::skip] +pub const fn get_default_keymap() -> [[[KeyAction; COL]; ROW]; NUM_LAYER] { + [ + layer!([ + [k!(A), k!(B), k!(C)], + [k!(D), k!(E), k!(F)], + [k!(G), k!(H), k!(I)], + [k!(J), k!(K), k!(L)] + ]), + ] +} diff --git a/examples/use_rust/rp2040_dfu_split/src/macros.rs b/examples/use_rust/rp2040_dfu_split/src/macros.rs new file mode 100644 index 000000000..016f48464 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/src/macros.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! config_matrix_pins_rp { + (peripherals: $p:ident, input: [$($in_pin:ident),*], output: [$($out_pin:ident),*]) => { + { + let output_pins = [$(Output::new($p.$out_pin, Level::Low)),*]; + let input_pins = [$(Input::new($p.$in_pin, embassy_rp::gpio::Pull::Down)),*]; + (input_pins, output_pins) + } + }; +} diff --git a/examples/use_rust/rp2040_dfu_split/src/peripheral.rs b/examples/use_rust/rp2040_dfu_split/src/peripheral.rs new file mode 100644 index 000000000..6a6e4834f --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/src/peripheral.rs @@ -0,0 +1,85 @@ +#![no_main] +#![no_std] + +#[macro_use] +mod macros; + +use defmt::*; +use defmt_rtt as _; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::{Input, Level, Output}; +use embassy_rp::peripherals::{UART0, USB}; +use embassy_rp::uart::{self, BufferedUart}; +use embassy_rp::usb::InterruptHandler; +use panic_probe as _; +use rmk::debounce::default_debouncer::DefaultDebouncer; +use rmk::futures::future::join; +use rmk::matrix::Matrix; +use rmk::processor::builtin::dfu_led::DfuLedProcessor; +use rmk::run_all; +use rmk::split::peripheral::run_rmk_split_peripheral; +use rmk::split::SPLIT_MESSAGE_MAX_SIZE; +use rmk::watchdog::Rp2040Watchdog; +use static_cell::StaticCell; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; + UART0_IRQ => uart::BufferedInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("RMK peripheral start!"); + let p = embassy_rp::init(Default::default()); + + let (row_pins, col_pins) = config_matrix_pins_rp!(peripherals: p, input: [PIN_8, PIN_9], output: [PIN_10]); + + // Flash layout for embassy-boot on the peripheral, + // matching the central's layout for consistency. + const FLASH_SIZE: u32 = 2 * 1024 * 1024; + const PAGE_SIZE: u32 = 4 * 1024; + const STORAGE_SIZE: u32 = 128 * 1024; + const STATE_OFFSET: u32 = 0x6000; + const STATE_SIZE: u32 = 0x1000; + const ACTIVE_OFFSET: u32 = 0x7000; + let remaining: u32 = FLASH_SIZE - 28 * 1024 - STORAGE_SIZE; + let active_size: u32 = (remaining - PAGE_SIZE) / 2; + let dfu_size: u32 = active_size + PAGE_SIZE; + let dfu_offset: u32 = ACTIVE_OFFSET + active_size; + let storage_offset: u32 = dfu_offset + dfu_size; + + rmk::dfu::init_flash( + p.FLASH, + storage_offset, + STORAGE_SIZE, + STATE_OFFSET, + STATE_SIZE, + dfu_offset, + dfu_size, + ); + + // mark the firmware as booted otherwise the bootloader thinks it didn't and will revert to the old firmware + rmk::dfu::mark_booted(); + + // DFU LED processor, optional. Flashes the LED when DFU is active + let mut dfu_led_processor = DfuLedProcessor::new(Output::new(p.PIN_25, Level::Low), false); + + // UART for split peripheral communication + static TX_BUF: StaticCell<[u8; SPLIT_MESSAGE_MAX_SIZE]> = StaticCell::new(); + let tx_buf = &mut TX_BUF.init([0; SPLIT_MESSAGE_MAX_SIZE])[..]; + static RX_BUF: StaticCell<[u8; SPLIT_MESSAGE_MAX_SIZE]> = StaticCell::new(); + let rx_buf = &mut RX_BUF.init([0; SPLIT_MESSAGE_MAX_SIZE])[..]; + let uart_instance = BufferedUart::new(p.UART0, p.PIN_0, p.PIN_1, Irqs, tx_buf, rx_buf, uart::Config::default()); + + let debouncer = DefaultDebouncer::new(); + let mut matrix = Matrix::<_, _, _, 2, 1, true>::new(row_pins, col_pins, debouncer); + + let mut watchdog_runner = Rp2040Watchdog::default_runner(embassy_rp::watchdog::Watchdog::new(p.WATCHDOG)); + + join( + run_all!(matrix, dfu_led_processor, watchdog_runner), + run_rmk_split_peripheral(uart_instance), + ) + .await; +} diff --git a/examples/use_rust/rp2040_dfu_split/src/vial.rs b/examples/use_rust/rp2040_dfu_split/src/vial.rs new file mode 100644 index 000000000..33e0a4ad0 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/src/vial.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/config_generated.rs")); diff --git a/examples/use_rust/rp2040_dfu_split/vial.json b/examples/use_rust/rp2040_dfu_split/vial.json new file mode 100644 index 000000000..0b9befba1 --- /dev/null +++ b/examples/use_rust/rp2040_dfu_split/vial.json @@ -0,0 +1,37 @@ +{ + "name": "RMK Keyboard", + "vendorId": "0x4C4B", + "productId": "0x4643", + "lighting": "none", + "matrix": { + "rows": 4, + "cols": 3 + }, + "layouts": { + "keymap": [ + [ + "0,0", + "0,1", + "0,2" + ], + [ + "1,0", + "1,1", + "1,2" + ], + [ + "2,0", + "2,1", + "2,2" + ], + [ + { + "y": -2, + "x": 4 + }, + "3,0", + "3,2" + ] + ] + } +} diff --git a/rmk-config/src/lib.rs b/rmk-config/src/lib.rs index f083fe9ce..f800bd336 100644 --- a/rmk-config/src/lib.rs +++ b/rmk-config/src/lib.rs @@ -828,6 +828,14 @@ pub struct SplitBoardConfig { pub adc_divider_total: Option, /// Output Pin config for the split pub output: Option>, + /// Path to the peripheral firmware binary for automatic dfu_split update. + /// Relative to the project's `Cargo.toml`. When set, the generated code + /// includes the binary with `include_bytes!` and registers it via + /// [`set_firmware_update_data`](crate::set_firmware_update_data). + pub firmware: Option, + /// DFU update policy for this peripheral. "MatchHash" (default) only + /// flashes when the firmware differs; "force" always flashes. + pub update_policy: Option, } /// Serial port config diff --git a/rmk-macro/Cargo.toml b/rmk-macro/Cargo.toml index 691594af0..79b9e41a3 100644 --- a/rmk-macro/Cargo.toml +++ b/rmk-macro/Cargo.toml @@ -25,6 +25,7 @@ watchdog = [] dfu_rp = [] ## Enable nRF DFU firmware update code generation dfu_nrf = [] +dfu_split = [] [lib] proc-macro = true diff --git a/rmk-macro/src/codegen/entry.rs b/rmk-macro/src/codegen/entry.rs index 1f3b21208..939e543d6 100644 --- a/rmk-macro/src/codegen/entry.rs +++ b/rmk-macro/src/codegen/entry.rs @@ -6,6 +6,7 @@ use rmk_config::resolved::{Hardware, Host}; use syn::{ItemFn, ItemMod}; use super::override_helper::Overwritten; +use crate::codegen::feature::{get_rmk_features, is_feature_enabled}; pub(crate) fn expand_rmk_entry( hardware: &Hardware, @@ -157,10 +158,21 @@ pub(crate) fn rmk_entry_select( .instance .to_lowercase() ); + let rmk_features = get_rmk_features(); + let dfu_split_enabled = is_feature_enabled(&rmk_features, "dfu_split"); + let policy = if dfu_split_enabled { + match p.update_policy.as_deref() { + Some("force") => quote! { ::rmk::split::central::UpdatePolicy::Force }, + _ => quote! { ::rmk::split::central::UpdatePolicy::MatchHash }, + } + } else { + quote! {} + }; tasks.push(quote! { ::rmk::split::central::run_peripheral_manager::<#row, #col, #row_offset, #col_offset, _>( #idx, #uart_instance, + #policy ) }); }); diff --git a/rmk-macro/src/codegen/orchestrator.rs b/rmk-macro/src/codegen/orchestrator.rs index 85ae68c0d..73de59fd9 100644 --- a/rmk-macro/src/codegen/orchestrator.rs +++ b/rmk-macro/src/codegen/orchestrator.rs @@ -250,6 +250,13 @@ fn expand_main( quote! {} }; + // Register peripheral firmware binaries for automatic dfu_split updates + let split_firmware_init = if is_feature_enabled(rmk_features, "dfu_split") { + expand_split_firmware_init(&hardware.board) + } else { + quote! {} + }; + // Display configuration β€” for unibody use top-level, for split use central's config let display_config = match &hardware.board { BoardConfig::UniBody(_) => hardware.display.as_ref(), @@ -387,12 +394,52 @@ fn expand_main( // Initialize dfu lock #dfu_lock_init + // Register peripheral firmware binaries for split dfu updates + #split_firmware_init + // Start #run_rmk } } } +/// Generate `set_firmware_update_data` calls for each peripheral that has a +/// `firmware` path configured in `keyboard.toml`. +fn expand_split_firmware_init(board: &BoardConfig) -> TokenStream2 { + let BoardConfig::Split(split_config) = board else { + return quote! {}; + }; + + let inits: Vec<_> = split_config + .peripheral + .iter() + .enumerate() + .filter_map(|(id, p)| { + p.firmware.as_ref().map(|path| { + quote! { + { + const FW: &[u8] = + ::core::include_bytes!(::core::concat!( + ::core::env!("CARGO_MANIFEST_DIR"), "/", #path + )); + let _ = ::rmk::dfu::set_firmware_update_data( + #id, FW, ::rmk::crc32::crc32(FW) + ); + } + } + }) + }) + .collect(); + + if inits.is_empty() { + quote! {} + } else { + quote! { + #(#inits)* + } + } +} + // TODO: move this function to a separate folder pub(crate) fn expand_keymap_and_storage(hardware: &Hardware, layout: &Layout) -> TokenStream2 { let row = layout.rows as usize; diff --git a/rmk/Cargo.toml b/rmk/Cargo.toml index 6c668e2a3..b5979b077 100644 --- a/rmk/Cargo.toml +++ b/rmk/Cargo.toml @@ -231,6 +231,8 @@ dfu = ["dep:embassy-usb-dfu", "dep:embassy-embedded-hal", "rmk-types/dfu"] dfu_rp = ["dfu", "dep:embassy-boot-rp", "dep:embassy-boot", "rp2040", "rmk-macro/dfu_rp"] ## Enable nRF DFU firmware update support (add a chip feature like `nrf52840` too) dfu_nrf = ["dfu", "dep:embassy-boot", "dep:embassy-nrf", "rmk-macro/dfu_nrf"] +## Enable split firmware update support β€” forward firmware to peripherals over split link +dfu_split = ["dfu", "rmk-macro/dfu_split"] ## Enable Plover HID stenography support: adds the steno HID descriptor, ## the steno USB writer endpoint, and the `Action::Steno` variant. diff --git a/rmk/src/crc32.rs b/rmk/src/crc32.rs new file mode 100644 index 000000000..944daea4a --- /dev/null +++ b/rmk/src/crc32.rs @@ -0,0 +1,129 @@ +/// CRC-32 lookup table (IEEE 802.3 / PKZip), computed at compile time. +const CRC32_TABLE: [u32; 256] = { + let mut table = [0u32; 256]; + let mut i = 0u32; + while i < 256 { + let mut crc = i; + let mut j = 0; + while j < 8 { + if crc & 1 != 0 { + crc = (crc >> 1) ^ 0xEDB88320; + } else { + crc >>= 1; + } + j += 1; + } + table[i as usize] = crc; + i += 1; + } + table +}; + +/// Incremental CRC-32 (IEEE 802.3 / PKZip) calculator. +/// +/// Uses polynomial `0xEDB88320` (reflected `0x04C11DB7`), the same as +/// Ethernet, PKZip, and `crc32` on POSIX. `no_std` compatible β€” the +/// lookup table is pre‑computed at compile time. +/// +/// # Example +/// +/// ```rust +/// let mut c = Crc32::new(); +/// c.update(b"hello "); +/// c.update(b"world"); +/// assert_eq!(c.finalize(), 0x0B4C_4A99); +/// ``` +pub struct Crc32 { + state: u32, +} + +impl Crc32 { + /// Create a new CRC-32 calculator initialised to `0xFFFF_FFFF`. + pub const fn new() -> Self { + Self { state: !0u32 } + } + + /// Feed a chunk of data into the running CRC. + pub fn update(&mut self, data: &[u8]) { + for &byte in data { + let idx = ((self.state ^ byte as u32) & 0xFF) as usize; + self.state = (self.state >> 8) ^ CRC32_TABLE[idx]; + } + } + + /// Finalize and return the CRC-32 (XOR‑out with `0xFFFF_FFFF`). + pub const fn finalize(&self) -> u32 { + !self.state + } +} + +/// Compute the CRC-32 of `data` in one shot. +/// +/// Equivalent to: +/// ```rust +/// let mut c = Crc32::new(); +/// c.update(data); +/// c.finalize() +/// ``` +pub fn crc32(data: &[u8]) -> u32 { + let mut c = Crc32::new(); + c.update(data); + c.finalize() +} + +#[cfg(test)] +mod tests { + use super::*; + + // test reference values from https://crccalc.com/ CRC-32/ISO-HDLC + #[test] + fn test_empty() { + assert_eq!(crc32(b""), 0x0000_0000); + } + + #[test] + fn test_hello_world() { + assert_eq!(crc32(b"hello world"), 0x0D4A_1185); + } + + #[test] + fn test_incremental_vs_oneshot() { + let data = b"The quick brown fox jumps over the lazy dog"; + assert_eq!(crc32(data), 0x414F_A339); + + let mut c = Crc32::new(); + c.update(b"The quick brown "); + c.update(b"fox jumps over "); + c.update(b"the lazy dog"); + assert_eq!(c.finalize(), 0x414F_A339); + } + + #[test] + fn test_all_zeros_32() { + let mut c = Crc32::new(); + c.update(&[0u8; 32]); + assert_eq!(c.finalize(), 0x190A_55AD); + } + + #[test] + fn test_all_ffs_32() { + let mut c = Crc32::new(); + c.update(&[0xFFu8; 32]); + assert_eq!(c.finalize(), 0xFF6C_AB0B); + } + + #[test] + fn test_known_values() { + // CRC32 of 4 consecutive bytes 0..4 + assert_eq!(crc32(&[0, 1, 2, 3]), 0x8BB9_8613); + // CRC32 of "123456789" (standard test vector) + assert_eq!(crc32(b"123456789"), 0xCBF4_3926); + } + + #[test] + fn test_new_is_idempotent() { + let c1 = Crc32::new(); + let c2 = Crc32::new(); + assert_eq!(c1.finalize(), c2.finalize()); + } +} diff --git a/rmk/src/dfu/mod.rs b/rmk/src/dfu/mod.rs index f5ddc92f8..52f03442c 100644 --- a/rmk/src/dfu/mod.rs +++ b/rmk/src/dfu/mod.rs @@ -1,3 +1,5 @@ +#[cfg(all(feature = "dfu_split", any(feature = "dfu_rp", feature = "dfu_nrf")))] +use core::sync::atomic::Ordering; #[cfg(feature = "dfu_lock")] use core::sync::atomic::{AtomicBool, Ordering}; @@ -7,8 +9,10 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::control::{InResponse, OutResponse, Request}; use embassy_usb::driver::Driver; -use embassy_usb::types::StringIndex; +use embassy_usb::types::{InterfaceNumber, StringIndex}; use embassy_usb::{Builder, Handler}; +#[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] +use embassy_usb_dfu::{ResetImmediate, dfu::FirmwareHandler}; use static_cell::StaticCell; #[cfg(feature = "dfu_lock")] @@ -44,6 +48,23 @@ use self::nrf::{MutexType, PartitionType}; #[cfg(feature = "dfu_rp")] use self::rp::{MutexType, PartitionType}; +// --------------------------------------------------------------------------- +// dfu_split sub-module (behind feature flag) +// --------------------------------------------------------------------------- + +#[cfg(feature = "dfu_split")] +mod split; +#[cfg(feature = "dfu_split")] +use self::split::PassthroughDfuHandler; +#[cfg(feature = "dfu_split")] +pub(crate) use self::split::{ + PASSTHROUGH_TARGET, PassthroughCommand, passthrough_done_if_empty, passthrough_pending, passthrough_take_command, +}; +#[cfg(feature = "dfu_split")] +pub use self::split::{ + SplitDfuHandler, get_firmware_update_data, read_embedded_firmware_hash, set_firmware_update_data, +}; + // --------------------------------------------------------------------------- // DfuFlashManager β€” shared by RP2040 and nRF // --------------------------------------------------------------------------- @@ -135,10 +156,12 @@ pub fn is_dfu_unlocked() -> bool { // RmkDfuHandler // --------------------------------------------------------------------------- +#[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] +use embassy_usb::class::dfu::dfu_mode::DfuState; #[cfg(feature = "dfu")] use embassy_usb::class::dfu::{ consts::Status, - dfu_mode::{self, DfuState}, + dfu_mode::{self}, }; #[cfg(any(feature = "dfu", feature = "dfu_lock"))] use rmk_types::dfu::DfuStatus; @@ -151,6 +174,7 @@ use crate::event::publish_event; #[cfg(feature = "dfu")] struct RmkDfuHandler { inner: H, + target_id: Option, } #[cfg(feature = "dfu")] @@ -164,7 +188,10 @@ impl dfu_mode::Handler for RmkDfuHandler { } #[cfg(feature = "dfu_lock")] DFU_STARTED.store(true, Ordering::Release); - info!("dfu: DFU download started"); + match self.target_id { + Some(id) => info!("dfu: DFU download started (passthrough peripheral {})", id), + None => info!("dfu: DFU download started (central)"), + } publish_event(crate::event::DfuStatusEvent::new(DfuStatus::Started)); self.inner.start() } @@ -190,22 +217,101 @@ impl dfu_mode::Handler for RmkDfuHandler { } // --------------------------------------------------------------------------- -// register_dfu_interface +// RmkDfuInterface β€” single USB handler for all DFU alt settings // --------------------------------------------------------------------------- +/// Max passthrough alt settings supported on a single DFU interface. +#[cfg(feature = "dfu_split")] +const MAX_PASSTHROUGH_ALTS: usize = 4; + +/// Single USB `Handler` that owns all DFU alternate settings. +/// +/// Alt 0 is always the central's own DFU flash. Alt 1..N are passthrough +/// slots for split peripherals (requires `dfu_split`). #[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] -use { - embassy_boot::{BlockingFirmwareUpdater, FirmwareUpdaterConfig}, - embassy_usb_dfu::{ResetImmediate, dfu::FirmwareHandler}, -}; +struct RmkDfuInterface { + central: + DfuState>>, + #[cfg(feature = "dfu_split")] + passthrough: [Option>>; MAX_PASSTHROUGH_ALTS], + #[cfg(feature = "dfu_split")] + num_passthrough: usize, + current_alt: u8, +} + +#[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] +impl Handler for RmkDfuInterface { + fn set_alternate_setting(&mut self, _iface: InterfaceNumber, alternate_setting: u8) { + self.current_alt = alternate_setting; + } + + fn control_out(&mut self, req: Request, data: &[u8]) -> Option { + match self.current_alt { + 0 => self.central.control_out(req, data), + #[cfg(feature = "dfu_split")] + n => { + let idx = (n as usize).saturating_sub(1); + self.passthrough_slots(idx).and_then(|s| s.control_out(req, data)) + } + #[cfg(not(feature = "dfu_split"))] + _ => None, + } + } + + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + match self.current_alt { + 0 => self.central.control_in(req, buf), + #[cfg(feature = "dfu_split")] + n => { + let idx = (n as usize).saturating_sub(1); + let buf_ptr = buf.as_mut_ptr(); + let resp = self.passthrough_slots(idx).and_then(|s| s.control_in(req, buf)); + // ── Flow control: dfuDNBUSY override ────────────────────── + // Byte 4 of the GETSTATUS response (6 bytes) is the `state` + // field. While PASSTHROUGH_TARGET is set (!= usize::MAX) we override + // it with 4 (= dfuDNBUSY). dfu-util then waits 50β€―ms and polls again + // β€” adaptive back-pressure without a fixed timeout. + // Uses a volatile store because `buf` is still borrowed by + // `resp` at this point. + if resp.is_some() && PASSTHROUGH_TARGET.load(Ordering::Acquire) != usize::MAX { + unsafe { + core::ptr::write_volatile(buf_ptr.add(4), 4u8); + } + } + resp + } + #[cfg(not(feature = "dfu_split"))] + _ => None, + } + } +} + +#[cfg(feature = "dfu_split")] +impl RmkDfuInterface { + fn passthrough_slots(&mut self, idx: usize) -> Option<&mut DfuState>> { + self.passthrough.get_mut(idx)?.as_mut() + } +} + +#[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] +static RMK_DFU_INTERFACE: StaticCell = StaticCell::new(); + +// --------------------------------------------------------------------------- +// register_dfu_interface β€” register DFU interface with central + passthrough alts +// --------------------------------------------------------------------------- /// Register a DFU interface on the USB builder. +/// +/// Alt 0 is always the central's own DFU flash. +/// Alt 1..N are passthrough slots for split peripherals (requires `dfu_split`). #[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] pub fn register_dfu_interface>( builder: &mut Builder<'static, D>, mgr: &'static DfuFlashManager, product_name: &'static str, + #[cfg(feature = "dfu_split")] num_peripherals: usize, ) { + use embassy_boot::{BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_usb::class::dfu::consts::DfuAttributes; let dfu_part = mgr.dfu_partition(); @@ -218,16 +324,36 @@ pub fn register_dfu_interface>( let aligned: &'static mut [u8] = ALIGNED.init([0; DFU_WRITE_SIZE]); let updater = BlockingFirmwareUpdater::new(config, aligned); - let attrs = DfuAttributes::CAN_DOWNLOAD | DfuAttributes::WILL_DETACH; - - let inner = FirmwareHandler::new(updater, ResetImmediate); - let handler = RmkDfuHandler { inner }; - let state = DfuState::new(handler, attrs); - - type DfuStateInner = - RmkDfuHandler>; - static DFU_STATE: StaticCell> = StaticCell::new(); - let state_ref = DFU_STATE.init(state); + // Alt 0: Central flash + let central_attrs = DfuAttributes::CAN_DOWNLOAD | DfuAttributes::WILL_DETACH; + let central_handler = RmkDfuHandler { + inner: FirmwareHandler::new(updater, ResetImmediate), + target_id: None, + }; + let central_state = DfuState::new(central_handler, central_attrs); + + // Alt 1..N: Passthrough + #[cfg(feature = "dfu_split")] + let passthrough_count = num_peripherals.min(MAX_PASSTHROUGH_ALTS); + #[cfg(feature = "dfu_split")] + let passthrough = { + let mut arr: [Option>>; MAX_PASSTHROUGH_ALTS] = + Default::default(); + for id in 0..passthrough_count { + let state = DfuState::new( + RmkDfuHandler { + inner: PassthroughDfuHandler { + target_id: id, + written: 0, + }, + target_id: Some(id), + }, + DfuAttributes::CAN_DOWNLOAD, + ); + arr[id] = Some(state); + } + arr + }; let string_idx = builder.string(); @@ -237,7 +363,7 @@ pub fn register_dfu_interface>( alt.descriptor( 0x21, &[ - attrs.bits(), + central_attrs.bits(), 0xc4, 0x09, (BLOCK_SIZE_DFU & 0xff) as u8, @@ -246,8 +372,34 @@ pub fn register_dfu_interface>( 0x01, ], ); + + #[cfg(feature = "dfu_split")] + for _ in 0..passthrough_count { + let mut alt = iface.alt_setting(0xFE, 0x01, 0x02, Some(string_idx)); + alt.descriptor( + 0x21, + &[ + DfuAttributes::CAN_DOWNLOAD.bits(), + 0xc4, + 0x09, + (BLOCK_SIZE_DFU & 0xff) as u8, + ((BLOCK_SIZE_DFU >> 8) & 0xff) as u8, + 0x10, + 0x01, + ], + ); + } drop(func); - builder.handler(state_ref); + + let iface_ref = RMK_DFU_INTERFACE.init(RmkDfuInterface { + central: central_state, + #[cfg(feature = "dfu_split")] + passthrough, + #[cfg(feature = "dfu_split")] + num_passthrough: passthrough_count, + current_alt: 0, + }); + builder.handler(iface_ref); static STRING_PROVIDER: StaticCell = StaticCell::new(); let string_provider = STRING_PROVIDER.init(DfuStringProvider { @@ -273,7 +425,13 @@ pub async fn run_peripheral_dfu>( let product_name = device_config.product_name; if let Some(mgr) = get_manager() { - register_dfu_interface(&mut builder, mgr, product_name); + register_dfu_interface( + &mut builder, + mgr, + product_name, + #[cfg(feature = "dfu_split")] + 0, + ); } let mut device = builder.build(); diff --git a/rmk/src/dfu/split/central.rs b/rmk/src/dfu/split/central.rs new file mode 100644 index 000000000..321e286fa --- /dev/null +++ b/rmk/src/dfu/split/central.rs @@ -0,0 +1,262 @@ +use core::cell::RefCell; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_usb::class::dfu::consts::Status; +use embassy_usb::class::dfu::dfu_mode::{self}; +use heapless; + +// --------------------------------------------------------------------------- +// Firmware update data registry +// --------------------------------------------------------------------------- + +/// A reference-counted firmware binary and its pre-computed CRC-32. +struct FirmwareSlot { + data: &'static [u8], + hash: u32, +} + +/// Maximum number of peripherals with registered firmware. +const MAX_FW_SLOTS: usize = 4; + +/// Global registry: peripheral ID β†’ (`FirmwareSlot`). +/// +/// Populated via [`set_firmware_update_data`]. +/// Looked up by [`PeripheralManager`] on +/// connection to decide whether an update is needed. +static FW_SLOTS: Mutex>> = + Mutex::new(RefCell::new(heapless::Vec::new())); + +/// Register a peripheral firmware binary for automatic dfu_split updates. +/// +/// The central calls this (typically at startup) so that +/// `PeripheralManager` can verify and, if needed, update the peripheral's +/// firmware when the split link is established. +/// +/// `id` must match the peripheral index in `[[split.peripheral]]` (or the +/// `id` argument of `run_peripheral_manager`). `hash` is the CRC-32 of +/// the firmware binary β€” typically computed via [`crate::crc32::crc32`]. +/// `id` must be unique; if a slot for the same `id` already exists, it will be replaced. +/// Every peripheral has only one firmware slot, given by its unique `id`. +/// +/// Returns `Err(())` if the registry is full (max `MAX_FW_SLOTS` entries). +pub fn set_firmware_update_data(id: usize, firmware: &'static [u8], hash: u32) -> Result<(), ()> { + FW_SLOTS.lock(|cell| { + let slots = &mut cell.borrow_mut(); + if let Some(slot) = slots.iter_mut().find(|(i, _)| *i == id) { + *slot = (id, FirmwareSlot { data: firmware, hash }); + } else { + slots + .push((id, FirmwareSlot { data: firmware, hash })) + .map_err(|_| ())?; + } + Ok(()) + }) +} + +/// Retrieve the firmware binary and its expected CRC-32 for a given +/// peripheral ID, if one has been registered. +pub fn get_firmware_update_data(id: usize) -> Option<(&'static [u8], u32)> { + FW_SLOTS.lock(|cell| { + let slots = cell.borrow(); + slots.iter().find(|(i, _)| *i == id).map(|(_, s)| (s.data, s.hash)) + }) +} + +/// ── PASSTHROUGH QUEUE: USB ISR β†’ async PeripheralManager ───────── +/// +/// The central gets 512B DNLOAD blocks from the host, while the peripheral gets +/// 256B chunks over the split link. The USB ISR splits each block into two chunks +/// and in order to smooth out the flow, a small FIFO queue is used to decouple +/// the two contexts. To prevent the host from sending more data than the queue +/// can hold, the GETSTATUS reply is modified to return `dfuDNBUSY` while the +/// queue is not empty. +/// +/// ```text +/// USB Host Central MCU +/// ──────── ────────── +/// +/// dfu-util -a 1 -D fw.bin +/// β”‚ +/// β”‚ USB Control Transfer (DNLOAD, 512 bytes) +/// v +/// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +/// β”‚ PassthroughDfuHandler β”‚ USB ISR (synchronous) +/// β”‚ .write(data) β”‚ splits 512B β†’ 2 Γ— 256B chunks +/// β”‚ β”‚ calls passthrough_push() each +/// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +/// β”‚ +/// β”‚ passthrough_push(Chunk{offset, data}) +/// v +/// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +/// β”‚ PASSTHROUGH_CMD β”‚ heapless::Vec +/// β”‚ [ Chunk(0) ] β”‚ FIFO-queue +/// β”‚ [ Chunk(256) ] β”‚ max 4 entries (QUEUE_SIZE) +/// β”‚ [ ... ] β”‚ protected by CriticalSectionMutex +/// β”‚ [ free ] β”‚ +/// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +/// β”‚ +/// β”‚ PASSTHROUGH_TARGET = peripheral_id (doorbell) +/// v +/// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +/// β”‚ PeripheralManager β”‚ async event loop (every 5 ms) +/// β”‚ .handle_passthrough() β”‚ +/// β”‚ β”‚ while passthrough_pending(id): +/// β”‚ 1. passthrough_take() β”‚ cmd = queue.pop() +/// β”‚ 2. send() over split β”‚ send(FirmwareChunk) to peripheral +/// β”‚ 3. wait for Ack β”‚ wait(FirmwareChunkAck) +/// β”‚ 4. clear doorbell β”‚ if queue.empty(): target = MAX +/// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +/// β”‚ +/// β”‚ UART +/// β”‚ (SplitMessage::FirmwareChunk) +/// v +/// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +/// β”‚ Peripheral β”‚ +/// β”‚ SplitDfuHandler β”‚ +/// β”‚ .write_chunk() β”‚ flash erase + write, send Ack +/// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +/// +/// +/// ── FLOW CONTROL ──────────────────────────────────────────────── +/// +/// While PASSTHROUGH_TARGET != MAX every GETSTATUS reply has +/// state = dfuDNBUSY (4). The host polls again after 50 ms. +/// Once the queue is drained and passthrough_done_if_empty() +/// clears the target, the real DFU state is returned and the +/// host sends the next DNLOAD block. +/// +/// ``` + +/// DFU `Handler` used for **passthrough** alternate settings on the +/// central's USB DFU interface. +/// +/// Runs inside the USB interrupt. Each incoming DNLOAD block is split +/// into 256-byte chunks and pushed into [`PASSTHROUGH_CMD`]. The +/// async `PeripheralManager` task drains the queue and forwards chunks +/// to the peripheral over the split link. GETSTATUS flow control +/// (cf. [`PASSTHROUGH_TARGET`]) ensures the host waits when the queue +/// is not empty. +pub(crate) struct PassthroughDfuHandler { + /// Which peripheral this handler forwards to. + pub target_id: usize, + /// Accumulated byte count (used as flash offset on the peripheral). + pub written: u32, +} + +impl dfu_mode::Handler for PassthroughDfuHandler { + fn start(&mut self) -> Result<(), Status> { + self.written = 0; + Ok(()) + } + + fn write(&mut self, data: &[u8]) -> Result<(), Status> { + for chunk in data.chunks(256) { + let mut buf = [0u8; 256]; + buf[..chunk.len()].copy_from_slice(chunk); + if passthrough_push(PassthroughCommand::Chunk(PassthroughChunk { + offset: self.written, + len: chunk.len() as u16, + data: buf, + })) + .is_err() + { + error!("dfu_split: passthrough queue full"); + return Err(Status::ErrUnknown); + } + self.written += chunk.len() as u32; + } + PASSTHROUGH_TARGET.store(self.target_id, Ordering::Release); + Ok(()) + } + + fn finish(&mut self) -> Result<(), Status> { + if passthrough_push(PassthroughCommand::Finish).is_err() { + error!("dfu_split: passthrough queue full at finish"); + return Err(Status::ErrUnknown); + } + PASSTHROUGH_TARGET.store(self.target_id, Ordering::Release); + Ok(()) + } + + fn system_reset(&mut self) {} +} + +/// A single chunk of firmware data queued for passthrough. +pub(crate) struct PassthroughChunk { + /// Flash offset where this chunk should be written. + pub offset: u32, + /// Raw data (zero-padded to 256 bytes). + pub data: [u8; 256], + /// Actual number of meaningful bytes in `data`. + pub len: u16, +} + +/// Commands flowing from the USB ISR +/// ([`PassthroughDfuHandler`]) to the async +/// [`PeripheralManager`](crate::split::driver::PeripheralManager). +pub(crate) enum PassthroughCommand { + /// A firmware chunk to be forwarded. + Chunk(PassthroughChunk), + /// Signal that all chunks have been sent; triggers end-to-end CRC + /// verification. + Finish, +} + +/// Maximum number of pending chunks in the fire-and-forget queue. +const PASSTHROUGH_QUEUE_SIZE: usize = 4; + +/// Fire-and-forget command queue. +/// +/// The USB DFU handler (ISR context) pushes; the async +/// `PeripheralManager` pops. Protected by a critical-section mutex +/// so it is safe from both contexts. +static PASSTHROUGH_CMD: Mutex< + CriticalSectionRawMutex, + RefCell>, +> = Mutex::new(RefCell::new(heapless::Vec::new())); + +/// Doorbell atomic: set to a peripheral ID when there is work in +/// [`PASSTHROUGH_CMD`], `usize::MAX` when idle. +/// +/// Read by [`RmkDfuInterface::control_in`] to inject `dfuDNBUSY` into +/// the GETSTATUS response (adaptive host-side flow control). +pub(crate) static PASSTHROUGH_TARGET: AtomicUsize = AtomicUsize::new(usize::MAX); + +/// Check whether a passthrough command is pending for the given +/// peripheral ID. +pub(crate) fn passthrough_pending(id: usize) -> bool { + PASSTHROUGH_TARGET.load(Ordering::Acquire) == id +} + +/// Push a command into the queue (ISR-safe). +fn passthrough_push(cmd: PassthroughCommand) -> Result<(), ()> { + PASSTHROUGH_CMD.lock(|c| c.borrow_mut().push(cmd).map_err(|_| ())) +} + +/// Pop the next pending command (async task). +pub(crate) fn passthrough_take_command() -> Option { + PASSTHROUGH_CMD.lock(|c| { + let v = &mut *c.borrow_mut(); + if !v.is_empty() { Some(v.remove(0)) } else { None } + }) +} + +/// Clear the target doorbell if the queue is empty. +/// +/// Called after every command is processed. If the queue still has +/// items the target stays set, keeping the host in `dfuDNBUSY` until +/// the PeripheralManager catches up. +pub(crate) fn passthrough_done_if_empty() { + let empty = PASSTHROUGH_CMD.lock(|c| c.borrow().is_empty()); + if empty { + PASSTHROUGH_TARGET.store(usize::MAX, Ordering::Release); + } +} + +/// Drain all pending passthrough commands (e.g. on disconnect). +pub(crate) fn drain_passthrough() { + PASSTHROUGH_CMD.lock(|c| c.borrow_mut().clear()); + PASSTHROUGH_TARGET.store(usize::MAX, Ordering::Release); +} diff --git a/rmk/src/dfu/split/mod.rs b/rmk/src/dfu/split/mod.rs new file mode 100644 index 000000000..646bdbf42 --- /dev/null +++ b/rmk/src/dfu/split/mod.rs @@ -0,0 +1,162 @@ +//! DFU split firmware update β€” update split peripherals over the split link. +//! +//! ```text +//! β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +//! β”‚ SPLIT DFU UPDATE β”‚ +//! β”‚ (peripheral connects / is connected) β”‚ +//! β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +//! +//! +//! ═══ PHASE 0: HANDSHAKE ── hash comparison ─────────────────────────── +//! Do we need to update the peripheral? +//! +//! ```text +//! Central Peripheral +//! β”‚ β”‚ +//! β”‚ β”œβ”€β”€ (on boot) FirmwareHashResponse(hash) +//! β”‚ β”‚ <- read_embedded_firmware_hash() +//! β”‚ β”‚ = CRC32(__vector_table..__veneer_limit) +//! β”‚ β”‚ +//! β”‚ ── or ── β”‚ +//! β”‚ β”‚ +//! β”œβ”€β”€ FirmwareHashQuery ─────────────>β”‚ +//! β”‚<── FirmwareHashResponse(hash) ───── +//! β”‚ β”‚ +//! β”‚ hash == expected_hash? β”‚ +//! β”‚ β”œβ”€ Yes β†’ STOP (up-to-date) β”‚ +//! β”‚ └─ No β†’ ↓ β”‚ +//! β”‚ β”‚ +//! ``` +//! +//! +//! ═══ PHASE 1: CHUNK TRANSFER ── per-chunk CRC ──────────────────────── +//! +//! Central calculates CRC of every chunk it sends, peripheral does as well +//! and sends the CRC back in a FirmwareChunkAck. If they match, central +//! sends the next chunk; if not, retry. Central also accumulates the +//! overall CRC (`central_crc.update(chunk)`). +//! +//! ```text +//! (outer attempt loop: 1..3) +//! +//! Central Peripheral +//! β”‚ β”‚ +//! β”‚ central_crc = Crc32::new() β”‚ +//! β”‚ β”‚ +//! β”‚ for chunk in firmware[256]: β”‚ +//! β”‚ chunk_crc = CRC32(chunk) β”‚ +//! β”‚ central_crc.update(chunk) β”‚ +//! β”‚ β”‚ +//! β”‚ retry = 0 β”‚ +//! β”‚ β”Œβ”€ retry < 3 ─────────────────┐ β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”œβ”€β”€ FirmwareChunk{offset,data} ──>β”‚ β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ β”‚ handler.write_chunk() +//! β”‚ β”‚ β€” incremental erase (page-by-page) +//! β”‚ β”‚ β€” write to flash +//! β”‚ β”‚ chunk_crc = CRC32(chunk) β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ β”‚<─ FirmwareChunkAck{offset,─── β”‚ +//! β”‚ β”‚ crc: chunk_crc} β”‚ β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ CRC match: β”‚ β”‚ +//! β”‚ ack_crc == chunk_crc? β”‚ β”‚ +//! β”‚ β”œβ”€ Yes β†’ next chunk β”‚ β”‚ +//! β”‚ └─ No β†’ retry++ ────── β”€β”€β”€β”€β”˜ β”‚ +//! β”‚ β”‚ +//! β”‚ All chunks acked? β”‚ +//! β”‚ β”œβ”€ Yes β†’ ↓ β”‚ +//! β”‚ └─ No β†’ attempt++ β†’ Phase 1 β”‚ +//! ``` +//! +//! +//! ═══ PHASE 2: END-TO-END CRC ── flash readback ────────────────────── +//! +//! Central compares its accumulated CRC with the firmware hash. If ok it +//! sends FirmwareUpdateComplete. Peripheral reads back the DFU partition +//! from flash and sends FirmwareCrcReport. Central verifies; on match it +//! sends FirmwareCrcOk, peripheral marks the firmware valid and resets. +//! +//! ```text +//! Central Peripheral +//! β”‚ β”‚ +//! β”‚ central_crc.finalize() β”‚ +//! β”‚ == expected_hash? β”‚ +//! β”‚ β”œβ”€ No β†’ ABORT (binary bug!) β”‚ +//! β”‚ └─ Yes β†’ ↓ β”‚ +//! β”‚ β”‚ +//! β”œβ”€β”€ FirmwareUpdateComplete ────────>β”‚ +//! β”‚ β”‚ +//! β”‚ handler.compute_dfu_crc() +//! β”‚ = CRC32(whole DFU partition) +//! β”‚ via flash readback (256B blocks) +//! β”‚ β”‚ +//! β”‚<── FirmwareCrcReport(dfu_crc) ───── +//! β”‚ β”‚ +//! β”‚ dfu_crc == expected_hash? β”‚ +//! β”‚ β”œβ”€ Yes β†’ ↓ β”‚ +//! β”‚ └─ No β†’ send CrcFail ─────┐ β”‚ +//! β”‚ β”‚ β”‚ +//! β”‚ β”‚ attempt++ β†’ Phase 1 +//! β”‚ β”‚ β”‚ +//! β”‚ β”œβ”€β”€ FirmwareCrcOk ────────>β”‚ β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ β”‚ handler.mark_updated_and_reset() +//! β”‚ β”‚ (only after CrcOk β€” never into corrupt FW) +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ β”‚<─ FirmwareUpdateConfirm ── β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ β”‚ β”‚ β”‚ +//! β”‚ └── CrcOk in 5s? ──────────│ β”‚ +//! β”‚ No β†’ timeout β†’ abort β”‚ β”‚ +//! β”‚ β”‚ β”‚ +//! β”‚ └── CrcFail in 5s? ───── ─── β”‚ +//! β”‚ No β†’ timeout β†’ abort β”€β”˜ β”‚ +//! ``` +//! +//! +//! ═══ RETRY SUMMARY ════════════════════════════════════════════════════ +//! +//! ```text +//! Layer Max Trigger Consequence +//! ───── ─── ─────── ────────── +//! Per-chunk 3Γ— Ack CRC mismatch Re-send same chunk +//! or 2s timeout +//! +//! Outer attempt 3Γ— Chunk never acked Full restart of +//! or E2E CRC mismatch Phase 1 + 2 +//! or CRC timeout +//! +//! No retry β€” Central CRC != expected Abort β€” binary +//! (central_crc.finalize()) mismatch, not TX +//! ``` +//! +//! +//! ═══ SAFETY GATES ════════════════════════════════════════════════════ +//! +//! ```text +//! Where What Why +//! ───── ──── ─── +//! Peripheral boot mark_updated_and_reset() Never boot into +//! only on FirmwareCrcOk corrupt firmware +//! +//! After all chunks central_crc == expected_hash Catch binary bugs +//! before asking peripheral +//! +//! End-to-end compute_dfu_crc() Catch silent flash +//! flash readback write errors +//! +//! Per-chunk CRC32(data) in Ack Catch bitflips, +//! packet loss, +//! wrong offset +//! ``` +mod central; +mod peripheral; + +pub(crate) use central::{ + PASSTHROUGH_TARGET, PassthroughCommand, PassthroughDfuHandler, passthrough_done_if_empty, passthrough_pending, + passthrough_take_command, +}; +pub use central::{get_firmware_update_data, set_firmware_update_data}; +pub use peripheral::{SplitDfuHandler, read_embedded_firmware_hash}; diff --git a/rmk/src/dfu/split/peripheral.rs b/rmk/src/dfu/split/peripheral.rs new file mode 100644 index 000000000..657d586a4 --- /dev/null +++ b/rmk/src/dfu/split/peripheral.rs @@ -0,0 +1,140 @@ +use core::sync::atomic::Ordering; + +use rmk_types::dfu::DfuStatus; +use static_cell::StaticCell; + +use super::super::{PartitionType, get_manager}; +use crate::event::{DfuStatusEvent, publish_event}; + +// ========================================================================= +// SplitDfuHandler β€” peripheral-side firmware writing +// ========================================================================= + +/// Handles firmware chunk writes on the **peripheral** side of a split +/// keyboard during an over-the-split-link DFU update. +/// +/// Created on demand when the first [`SplitMessage::FirmwareChunk`] +/// arrives. Flash pages are erased incrementally (only when a new page +/// boundary is hit), so the first chunk does not stall the split link +/// for the full erase time. +/// +/// # Lifecycle +/// +/// 1. [`SplitDfuHandler::new`] β€” acquire partition handles from +/// the global [`DfuFlashManager`]. +/// 2. [`write_chunk`](SplitDfuHandler::write_chunk) β€” erase + write. +/// 3. [`compute_dfu_crc`](SplitDfuHandler::compute_dfu_crc) β€” read +/// back the entire DFU partition and return its CRC-32. +/// 4. [`mark_updated_and_reset`](SplitDfuHandler::mark_updated_and_reset) +/// β€” tell embassy-boot the new firmware is valid, then reset into it. +pub struct SplitDfuHandler { + dfu_partition: PartitionType, + state_partition: PartitionType, + last_erased_page: Option, + written_len: u32, +} + +impl SplitDfuHandler { + /// Create a new handler from the global [`DfuFlashManager`]. + /// Returns `None` if `init_flash` has not been called yet. + pub fn new() -> Option { + let mgr = get_manager()?; + Some(Self { + dfu_partition: mgr.dfu_partition(), + state_partition: mgr.state_partition(), + last_erased_page: None, + written_len: 0, + }) + } + + /// Write a chunk of firmware data at the given partition offset. + /// + /// Pages are erased on demand β€” only the first time a particular page + /// is encountered. This avoids a long blocking erase of the entire + /// DFU partition on the very first chunk. + pub fn write_chunk(&mut self, offset: u32, data: &[u8]) -> Result<(), ()> { + use embedded_storage::nor_flash::NorFlash; + let mut dfu = self.dfu_partition.clone(); + let erase_size = ::ERASE_SIZE as u32; + let start_page = offset / erase_size; + let end = offset + data.len() as u32; + let end_page = (end - 1) / erase_size; + for page in start_page..=end_page { + if self.last_erased_page != Some(page) { + dfu.erase(page * erase_size, (page + 1) * erase_size).map_err(|_| ())?; + self.last_erased_page = Some(page); + } + } + dfu.write(offset, data).map_err(|_| ())?; + self.written_len = self.written_len.max(offset + data.len() as u32); + publish_event(DfuStatusEvent::new(DfuStatus::Downloading)); + Ok(()) + } + + /// Read back the entire DFU partition and compute its CRC-32. + /// + /// Called by the peripheral during end-to-end verification before + /// resetting into the new firmware. Only the bytes up to the + /// highest written offset are included. + pub fn compute_dfu_crc(&self) -> u32 { + use embedded_storage::nor_flash::ReadNorFlash; + let mut dfu = self.dfu_partition.clone(); + let len = self.written_len as usize; + let mut crc = crate::crc32::Crc32::new(); + let mut buf = [0u8; 256]; + let mut pos = 0u32; + while (pos as usize) < len { + let chunk_len = core::cmp::min(256, len - pos as usize); + dfu.read(pos, &mut buf[..chunk_len]).ok(); + crc.update(&buf[..chunk_len]); + pos += chunk_len as u32; + } + crc.finalize() + } + + /// Mark the new firmware as valid and reset into it. + /// + /// Calls `embassy-boot`'s `mark_updated` and then performs a + /// system reset. The bootloader will copy the DFU slot to the + /// active slot on the next boot. + pub fn mark_updated_and_reset(&self) -> Result<(), ()> { + #[cfg(feature = "dfu_nrf")] + use embassy_boot::{BlockingFirmwareUpdater, FirmwareUpdaterConfig}; + #[cfg(feature = "dfu_rp")] + use embassy_boot_rp::{BlockingFirmwareUpdater, FirmwareUpdaterConfig}; + let config = FirmwareUpdaterConfig { + dfu: self.dfu_partition.clone(), + state: self.state_partition.clone(), + }; + static ALIGNED: StaticCell<[u8; super::super::DFU_WRITE_SIZE]> = StaticCell::new(); + let mut updater = BlockingFirmwareUpdater::new(config, ALIGNED.init([0; super::super::DFU_WRITE_SIZE])); + updater.mark_updated().map_err(|_| ())?; + publish_event(DfuStatusEvent::new(DfuStatus::Finished)); + cortex_m::peripheral::SCB::sys_reset() + } +} + +/// Return the CRC-32 of the currently running firmware binary. +/// +/// The result is computed once and cached. The firmware region is +/// determined by the `__vector_table` / `__veneer_limit` linker symbols, +/// covering the entire `.text` + `.rodata` + `.data` sections. +pub fn read_embedded_firmware_hash() -> u32 { + use core::sync::atomic::AtomicU32; + static CACHED_HASH: AtomicU32 = AtomicU32::new(0); + let cached = CACHED_HASH.load(Ordering::Acquire); + if cached != 0 { + return cached; + } + unsafe extern "C" { + static __vector_table: u8; + static __veneer_limit: u8; + } + let start = unsafe { &__vector_table as *const u8 }; + let end = unsafe { &__veneer_limit as *const u8 }; + let len = end as usize - start as usize; + let data = unsafe { core::slice::from_raw_parts(start, len) }; + let hash = crate::crc32::crc32(data); + CACHED_HASH.store(hash, Ordering::Release); + hash +} diff --git a/rmk/src/lib.rs b/rmk/src/lib.rs index ac361be81..ef883c894 100644 --- a/rmk/src/lib.rs +++ b/rmk/src/lib.rs @@ -59,6 +59,8 @@ pub mod boot; pub mod channel; pub mod config; pub mod core_traits; +#[cfg(feature = "dfu_split")] +pub mod crc32; pub mod debounce; #[cfg(feature = "dfu")] pub mod dfu; diff --git a/rmk/src/split/central.rs b/rmk/src/split/central.rs index fdcf1c56f..0aeec6975 100644 --- a/rmk/src/split/central.rs +++ b/rmk/src/split/central.rs @@ -11,12 +11,17 @@ use { trouble_host::prelude::*, }; +#[cfg(feature = "dfu_split")] +pub use crate::split::driver::UpdatePolicy; + /// Run central's peripheral manager task. /// /// # Arguments /// * `id` - peripheral id /// * `addr` - (optional) peripheral's BLE static address. This argument is enabled only for nRF BLE split now +/// * `stack` - (optional) BLE stack. This argument is enabled only for BLE split now /// * `receiver` - (optional) serial port. This argument is enabled only for serial split now +/// * `policy` - (optional, `dfu_split` only) how to decide whether to update the peripheral's firmware #[allow(clippy::extra_unused_lifetimes)] pub async fn run_peripheral_manager< 'b, @@ -35,6 +40,7 @@ pub async fn run_peripheral_manager< #[cfg(feature = "_ble")] addr: &RefCell>>, #[cfg(feature = "_ble")] stack: &'b Stack<'s, C, DefaultPacketPool>, #[cfg(not(feature = "_ble"))] receiver: S, + #[cfg(feature = "dfu_split")] policy: crate::split::driver::UpdatePolicy, ) where 's: 'b, { @@ -47,6 +53,12 @@ pub async fn run_peripheral_manager< #[cfg(not(feature = "_ble"))] { use crate::split::serial::run_serial_peripheral_manager; - run_serial_peripheral_manager::(id, receiver).await; - }; + run_serial_peripheral_manager::( + id, + receiver, + #[cfg(feature = "dfu_split")] + policy, + ) + .await; + } } diff --git a/rmk/src/split/driver.rs b/rmk/src/split/driver.rs index f24fc5bb7..fccb8c4d6 100644 --- a/rmk/src/split/driver.rs +++ b/rmk/src/split/driver.rs @@ -1,5 +1,3 @@ -//! The abstracted driver layer of the split keyboard. -//! use embassy_futures::select::{Either, select}; use futures::FutureExt; @@ -17,23 +15,14 @@ pub(crate) enum SplitDriverError { Disconnected, } -/// Split message reader from other split devices pub(crate) trait SplitReader { async fn read(&mut self) -> Result; } -/// Split message writer to other split devices pub(crate) trait SplitWriter { async fn write(&mut self, message: &SplitMessage) -> Result; } -/// PeripheralManager runs in central. -/// It reads split message from peripheral and updates key matrix cache of the peripheral. -/// -/// When the central scans the matrix, the scanning thread sends sync signal and gets key state cache back. -/// -/// The `ROW` and `COL` are the number of rows and columns of the corresponding peripheral's keyboard matrix. -/// The `ROW_OFFSET` and `COL_OFFSET` are the offset of the peripheral's matrix in the keyboard's matrix. pub(crate) struct PeripheralManager< const ROW: usize, const COL: usize, @@ -41,20 +30,39 @@ pub(crate) struct PeripheralManager< const COL_OFFSET: usize, T: SplitReader + SplitWriter, > { - /// Receiver transceiver: T, - /// Peripheral id id: usize, + #[cfg(feature = "dfu_split")] + passthrough_crc: crate::crc32::Crc32, + /// Whether to skip hash comparison and always flash firmware. + #[cfg(feature = "dfu_split")] + policy: UpdatePolicy, +} + +/// Defines how the central decides whether to flash a peripheral. +#[cfg(feature = "dfu_split")] +#[derive(Clone, Copy)] +pub enum UpdatePolicy { + /// Compare the firmware hash β€” only flash when it differs. + MatchHash, + /// Always flash the firmware regardless of the current version. + Force, } impl PeripheralManager { - pub(crate) fn new(transceiver: T, id: usize) -> Self { - Self { transceiver, id } + pub(crate) fn new(transceiver: T, id: usize, #[cfg(feature = "dfu_split")] policy: UpdatePolicy) -> Self { + Self { + transceiver, + id, + #[cfg(feature = "dfu_split")] + passthrough_crc: crate::crc32::Crc32::new(), + #[cfg(feature = "dfu_split")] + policy, + } } - /// Send a message to the peripheral, returning Err on disconnect. async fn send(&mut self, msg: &SplitMessage) -> Result<(), ()> { debug!("Sending message to peripheral {}: {:?}", self.id, msg); match self.transceiver.write(msg).await { @@ -67,22 +75,14 @@ impl SplitMessage::KeyboardIndicator(e.0.into_bits()), @@ -114,11 +120,7 @@ impl match read_result { - Ok(split_message) => { - self.process_peripheral_message(split_message).await; - } - Err(e) => { - error!("Peripheral message read error: {:?}", e); + #[cfg(feature = "dfu_split")] + Ok(SplitMessage::FirmwareHashResponse(hash)) => { + self.handle_proactive_hash(hash).await; } + Ok(split_message) => self.process_peripheral_message(split_message).await, + Err(e) => error!("Peripheral message read error: {:?}", e), }, - Either::Second(msg) => { + Either::Second(Either::First(msg)) => { if self.send(&msg).await.is_err() { return; } } + Either::Second(Either::Second(())) => {} } } } - /// Process a single message from the peripheral. async fn process_peripheral_message(&self, split_message: SplitMessage) { trace!("Got message from peripheral: {:?}", split_message); match split_message { SplitMessage::Key(e) => match e.pos { KeyboardEventPos::Key(key_pos) => { - // Verify the row/col if key_pos.row as usize >= ROW || key_pos.col as usize >= COL { error!("Invalid peripheral row/col: {} {}", key_pos.row, key_pos.col); return; } - - let adjusted_key_event = KeyboardEvent::key( + publish_event_async(KeyboardEvent::key( key_pos.row + ROW_OFFSET as u8, key_pos.col + COL_OFFSET as u8, e.pressed, - ); - publish_event_async(adjusted_key_event).await; - } - _ => { - // For rotary encoder - publish_event_async(e).await; + )) + .await; } + _ => publish_event_async(e).await, }, - // Non-key events are drop-on-full to keep the split read loop responsive. SplitMessage::Pointing(e) => publish_event(e), #[cfg(feature = "_ble")] SplitMessage::BatteryStatus(state) => { - use crate::event::PeripheralBatteryEvent; - publish_event(PeripheralBatteryEvent { id: self.id, state }) + publish_event(crate::event::PeripheralBatteryEvent { id: self.id, state }) + } + #[cfg(feature = "dfu_split")] + SplitMessage::FirmwareHashResponse(hash) => { + info!("dfu_split: stale hash response ({:#x}) in event loop", hash); + } + #[cfg(feature = "dfu_split")] + SplitMessage::FirmwareChunkAck { offset, crc: _ } => { + info!("dfu_split: stale chunk ack (offset {}) in event loop, ignoring", offset); + } + #[cfg(feature = "dfu_split")] + SplitMessage::FirmwareUpdateConfirm => { + info!("dfu_split: stale update confirm in event loop, ignoring"); } _ => warn!("{:?} should not come from peripheral", split_message), } } + + /// Handle a proactive `FirmwareHashResponse` received in the main event + /// loop (after the initial `check_firmware_update` may have timed out + /// because the peripheral was not yet booted). + #[cfg(feature = "dfu_split")] + async fn handle_proactive_hash(&mut self, hash: u32) { + let (firmware, expected_hash) = match crate::dfu::get_firmware_update_data(self.id) { + Some(d) => d, + None => { + info!( + "dfu_split: no firmware data set for peripheral {}, skipping proactive hash", + self.id + ); + return; + } + }; + info!("dfu_split: proactive hash from peripheral ({:#x}), checking...", hash); + if hash == expected_hash { + info!("dfu_split: hash matches ({:#x}), no update needed", hash); + return; + } + info!("dfu_split: hash mismatch, starting update ({} bytes)", firmware.len()); + self.send_firmware_update(firmware, expected_hash).await; + } + + /// Process passthrough DFU chunks (fire-and-forget with per-chunk ack). + /// + /// Called from the event loop when [`passthrough_pending`] returns + /// `true`. Drains the entire `PASSTHROUGH_CMD` queue, forwarding + /// each chunk over the split link and waiting for a + /// `FirmwareChunkAck` before proceeding to the next. + /// + /// On `Finish`, triggers end-to-end CRC verification: the peripheral + /// reads back its DFU partition, sends the CRC-32, the central + /// compares, and sends `FirmwareCrcOk` / `FirmwareCrcFail`. + #[cfg(feature = "dfu_split")] + async fn handle_passthrough(&mut self) { + use embassy_time::{Duration, Timer}; + + while let Some(cmd) = crate::dfu::passthrough_take_command() { + match cmd { + crate::dfu::PassthroughCommand::Chunk(chunk) => { + debug!( + "dfu_split/passthrough: sending chunk @ offset {} ({} bytes)", + chunk.offset, chunk.len + ); + self.passthrough_crc.update(&chunk.data[..chunk.len as usize]); + let msg = SplitMessage::FirmwareChunk { + offset: chunk.offset, + len: chunk.len, + data: super::FirmwareChunkData(chunk.data), + }; + if self.send(&msg).await.is_err() { + error!("dfu_split/passthrough: disconnected during chunk send"); + crate::dfu::passthrough_done_if_empty(); + return; + } + + // Wait for the peripheral to acknowledge this chunk + loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(2))).await { + Either::First(Ok(SplitMessage::FirmwareChunkAck { offset, .. })) + if offset == chunk.offset => + { + break; + } + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("dfu_split/passthrough: read error: {:?}", e); + break; + } + Either::Second(_) => { + error!("dfu_split/passthrough: timeout waiting for chunk ack"); + break; + } + } + } + + crate::dfu::passthrough_done_if_empty(); + } + crate::dfu::PassthroughCommand::Finish => { + info!("dfu_split/passthrough: DFU download complete, starting end-to-end verification"); + + if self.send(&SplitMessage::FirmwareUpdateComplete).await.is_err() { + error!("dfu_split/passthrough: disconnected during finish"); + crate::dfu::passthrough_done_if_empty(); + return; + } + + let crc = loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(5))).await { + Either::First(Ok(SplitMessage::FirmwareCrcReport(crc))) => break Some(crc), + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("dfu_split/passthrough: read error: {:?}", e); + break None; + } + Either::Second(_) => { + error!("dfu_split/passthrough: timeout waiting for CRC"); + break None; + } + } + }; + + let Some(peripheral_crc) = crc else { + error!("dfu_split/passthrough: CRC verification failed"); + self.send(&SplitMessage::FirmwareCrcFail).await.ok(); + crate::dfu::passthrough_done_if_empty(); + return; + }; + + let central_crc = self.passthrough_crc.finalize(); + self.passthrough_crc = crate::crc32::Crc32::new(); + + if central_crc != peripheral_crc { + error!( + "dfu_split/passthrough: CRC mismatch (central={:#010x}, peripheral={:#010x})", + central_crc, peripheral_crc + ); + self.send(&SplitMessage::FirmwareCrcFail).await.ok(); + crate::dfu::passthrough_done_if_empty(); + return; + } + + info!("dfu_split/passthrough: CRC OK, confirming update"); + if self.send(&SplitMessage::FirmwareCrcOk).await.is_err() { + error!("dfu_split/passthrough: disconnected during CRC OK"); + crate::dfu::passthrough_done_if_empty(); + return; + } + + loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(2))).await { + Either::First(Ok(SplitMessage::FirmwareUpdateConfirm)) => { + info!("dfu_split/passthrough: peripheral confirmed, update complete"); + break; + } + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("dfu_split: FirmwareUpdateConfirm error {:?}", e); + break; + } + Either::Second(_) => { + info!("dfu_split: FirmwareUpdateConfirm timeout on confirm"); + break; + } + } + } + + crate::dfu::passthrough_done_if_empty(); + } + } + } + } + + /// Check if the peripheral's firmware is up to date and update if needed. + /// + /// Called once at connection start. Depending on [`UpdatePolicy`]: + /// + /// * `MatchHash` β€” sends a `FirmwareHashQuery`, compares the + /// peripheral's response against the expected CRC-32, and only + /// flashes when they differ. + /// * `Force` β€” skips the hash query entirely and always flashes. + #[cfg(feature = "dfu_split")] + async fn check_firmware_update(&mut self) { + use embassy_time::{Duration, Timer}; + + let (firmware, expected_hash) = match crate::dfu::get_firmware_update_data(self.id) { + Some(d) => d, + None => { + info!("dfu_split: no firmware data for peripheral {}", self.id); + return; + } + }; + + match self.policy { + UpdatePolicy::Force => { + info!("dfu_split: force update enabled, flashing {} bytes", firmware.len()); + self.send_firmware_update(firmware, expected_hash).await; + return; + } + UpdatePolicy::MatchHash => {} + } + + info!("dfu_split: checking peripheral firmware..."); + if self.send(&SplitMessage::FirmwareHashQuery).await.is_err() { + error!("dfu_split: disconnected during hash query"); + return; + } + + let hash = loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(2))).await { + Either::First(Ok(SplitMessage::FirmwareHashResponse(h))) => break Some(h), + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("read error: {:?}", e); + break None; + } + Either::Second(_) => break None, + } + }; + + let peripheral_hash = match hash { + Some(h) => h, + None => { + info!("dfu_split: no hash, starting update"); + self.send_firmware_update(firmware, expected_hash).await; + return; + } + }; + + if peripheral_hash == expected_hash { + info!("dfu_split: hash matches, no update needed"); + return; + } + + info!("dfu_split: hash mismatch, starting update ({} bytes)", firmware.len()); + self.send_firmware_update(firmware, expected_hash).await; + } + + /// Send the full firmware binary to the peripheral in 256-byte chunks. + /// + /// Each chunk is checked with per-chunk CRC-32 verification. If a + /// chunk fails (CRC mismatch or timeout) it is retried up to 3 times. + /// The entire transfer is retried up to 3 attempts on failure. + /// + /// On success, the peripheral confirms and resets into the new + /// firmware. + #[cfg(feature = "dfu_split")] + async fn send_firmware_update(&mut self, firmware: &[u8], expected_hash: u32) { + use embassy_time::{Duration, Timer}; + const MAX_RETRIES: u32 = 3; + const MAX_ATTEMPTS: u32 = 3; + + for attempt in 1..=MAX_ATTEMPTS { + info!("dfu_split: update attempt {}/{}", attempt, MAX_ATTEMPTS); + publish_event(crate::event::DfuStatusEvent::new(rmk_types::dfu::DfuStatus::Started)); + + let mut central_crc = crate::crc32::Crc32::new(); + let mut all_acked = true; + + for (offset, chunk) in firmware.chunks(256).enumerate() { + let offset_bytes = (offset * 256) as u32; + let mut data = [0u8; 256]; + data[..chunk.len()].copy_from_slice(chunk); + let chunk_crc = crate::crc32::crc32(&data[..chunk.len()]); + central_crc.update(&data[..chunk.len()]); + + let mut retries = 0; + let mut acked = false; + + while !acked && retries < MAX_RETRIES { + if retries > 0 { + info!( + "dfu_split: retry {}/{} for chunk at offset {}", + retries + 1, + MAX_RETRIES, + offset_bytes + ); + } + + if self + .send(&SplitMessage::FirmwareChunk { + offset: offset_bytes, + len: chunk.len() as u16, + data: super::FirmwareChunkData(data), + }) + .await + .is_err() + { + error!("dfu_split: disconnected during chunk send"); + return; + } + publish_event(crate::event::DfuStatusEvent::new( + rmk_types::dfu::DfuStatus::Downloading, + )); + + let got = loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(2))).await { + Either::First(Ok(SplitMessage::FirmwareChunkAck { + offset: ack_offset, + crc: ack_crc, + })) => { + if ack_offset == offset_bytes { + if ack_crc == chunk_crc { + break true; + } + warn!( + "dfu_split: per-chunk CRC mismatch at offset {} (peripheral={:#010x}, central={:#010x})", + offset_bytes, ack_crc, chunk_crc + ); + break false; + } + info!( + "dfu_split: got ack for offset {}, waiting for {}", + ack_offset, offset_bytes + ); + } + Either::First(Ok(other)) => warn!("unexpected message: {:?}", other), + Either::First(Err(e)) => { + error!("dfu_split: FirmwareChunkAck error {:?}", e); + break false; + } + Either::Second(_) => break false, + } + }; + acked = got; + retries += 1; + } + + if !acked { + error!( + "dfu_split: chunk at offset {} failed after {} retries", + offset_bytes, MAX_RETRIES + ); + all_acked = false; + break; + } + } + + if !all_acked { + continue; + } + + let local_crc = central_crc.finalize(); + if local_crc != expected_hash { + error!("dfu_split: central CRC mismatch β€” aborting"); + return; + } + + if self.send(&SplitMessage::FirmwareUpdateComplete).await.is_err() { + return; + } + + let peripheral_crc = loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(5))).await { + Either::First(Ok(SplitMessage::FirmwareCrcReport(crc))) => break Some(crc), + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("dfu_split: FirmwareCrcReport error {:?}", e); + break None; + } + Either::Second(_) => break None, + } + }; + + let Some(dfu_crc) = peripheral_crc else { + continue; + }; + + if dfu_crc == expected_hash { + info!("dfu_split: end-to-end CRC matches, confirming"); + self.send(&SplitMessage::FirmwareCrcOk).await.ok(); + loop { + match select(self.transceiver.read(), Timer::after(Duration::from_secs(2))).await { + Either::First(Ok(SplitMessage::FirmwareUpdateConfirm)) => { + info!("dfu_split: peripheral confirmed CRC, complete"); + publish_event(crate::event::DfuStatusEvent::new(rmk_types::dfu::DfuStatus::Finished)); + return; + } + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("dfu_split: FirmwareCrcOk error {:?}", e); + return; + } + Either::Second(_) => { + error!("dfu_split: FirmwareCrcOk timeout"); + return; + } + } + } + } else { + warn!("dfu_split: end-to-end CRC mismatch, retrying"); + self.send(&SplitMessage::FirmwareCrcFail).await.ok(); + Timer::after(Duration::from_millis(100)).await; + } + } + + error!("dfu_split: all {} update attempts failed", MAX_ATTEMPTS); + } } diff --git a/rmk/src/split/mod.rs b/rmk/src/split/mod.rs index 3fab6ddbe..de5ad13b6 100644 --- a/rmk/src/split/mod.rs +++ b/rmk/src/split/mod.rs @@ -1,6 +1,8 @@ use postcard::experimental::max_size::MaxSize; use rmk_types::connection::ConnectionStatus; use serde::{Deserialize, Serialize}; +#[cfg(feature = "dfu_split")] +use serde::{Deserializer, Serializer}; #[cfg(feature = "_ble")] use crate::event::BatteryStatusEvent; @@ -54,4 +56,80 @@ pub(crate) enum SplitMessage { /// Battery status, from peripheral to central #[cfg(feature = "_ble")] BatteryStatus(BatteryStatusEvent), + + // ----------------------------------------------------------------------- + // dfu_split β€” firmware update over split link + // ----------------------------------------------------------------------- + /// Central β†’ Peripheral: query the hash of the ACTIVE slot firmware. + #[cfg(feature = "dfu_split")] + FirmwareHashQuery, + /// Peripheral β†’ Central: respond with the CRC32 of the ACTIVE slot firmware. + #[cfg(feature = "dfu_split")] + FirmwareHashResponse(u32), + /// Central β†’ Peripheral: a chunk of the new firmware at a given offset. + #[cfg(feature = "dfu_split")] + FirmwareChunk { + offset: u32, + len: u16, + data: FirmwareChunkData, + }, + /// Peripheral β†’ Central: acknowledge that `offset` bytes have been written, + /// together with the CRC-32 of **this single chunk**. + #[cfg(feature = "dfu_split")] + FirmwareChunkAck { offset: u32, crc: u32 }, + /// Central β†’ Peripheral: all chunks sent, peripheral should compute DFU CRC. + #[cfg(feature = "dfu_split")] + FirmwareUpdateComplete, + /// Peripheral β†’ Central: CRC-32 of the full DFU partition. + #[cfg(feature = "dfu_split")] + FirmwareCrcReport(u32), + /// Central β†’ Peripheral: end-to-end CRC matches, safe to reset. + #[cfg(feature = "dfu_split")] + FirmwareCrcOk, + /// Central β†’ Peripheral: end-to-end CRC mismatch, do NOT reset. + #[cfg(feature = "dfu_split")] + FirmwareCrcFail, + /// Peripheral β†’ Central: confirm mark_updated succeeded, about to reset. + #[cfg(feature = "dfu_split")] + FirmwareUpdateConfirm, +} + +// ----------------------------------------------------------------------- +// FirmwareChunkData β€” buffer for dfu_split firmware transfer +// ----------------------------------------------------------------------- + +/// Fixed-size buffer for firmware chunk transfer over the split link. +/// +/// Postcard's COBS encoding stores this as `&[u8]` (varint length prefix +/// + bytes) rather than a fixed `[u8; 256]` β€” necessary because serde +/// does not implement `Deserialize` for arrays larger than 32 elements. +#[cfg(feature = "dfu_split")] +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FirmwareChunkData(pub [u8; 256]); + +#[cfg(feature = "dfu_split")] +impl Serialize for FirmwareChunkData { + fn serialize(&self, serializer: S) -> Result { + self.0.as_slice().serialize(serializer) + } +} + +#[cfg(feature = "dfu_split")] +impl<'de> Deserialize<'de> for FirmwareChunkData { + fn deserialize>(deserializer: D) -> Result { + use serde::de::Error; + let buf: &[u8] = Deserialize::deserialize(deserializer)?; + if buf.len() > 256 { + return Err(D::Error::custom("firmware chunk exceeds 256 bytes")); + } + let mut data = [0u8; 256]; + data[..buf.len()].copy_from_slice(buf); + Ok(FirmwareChunkData(data)) + } +} + +#[cfg(feature = "dfu_split")] +impl MaxSize for FirmwareChunkData { + const POSTCARD_MAX_SIZE: usize = 258; } diff --git a/rmk/src/split/peripheral.rs b/rmk/src/split/peripheral.rs index a02812dbe..f0f156cdd 100644 --- a/rmk/src/split/peripheral.rs +++ b/rmk/src/split/peripheral.rs @@ -60,18 +60,35 @@ pub async fn run_rmk_split_peripheral< /// The split peripheral instance. pub(crate) struct SplitPeripheral { split_driver: S, + #[cfg(feature = "dfu_split")] + dfu_handler: Option, } impl SplitPeripheral { pub(crate) fn new(split_driver: S) -> Self { - Self { split_driver } + Self { + split_driver, + #[cfg(feature = "dfu_split")] + dfu_handler: None, + } } /// Run the peripheral keyboard service. /// - /// The peripheral uses the general matrix, does scanning and send the key events through `SplitWriter`. - /// If also receives split messages from the central through `SplitReader`. + /// The peripheral uses the general matrix, does scanning and sends key events through `SplitWriter`. + /// It also receives split messages from the central through `SplitReader`. pub(crate) async fn run(&mut self) { + // Proactively announce our firmware hash so the central can detect + // us even when it booted first and already gave up waiting for a query response. + #[cfg(feature = "dfu_split")] + { + let hash = crate::dfu::read_embedded_firmware_hash(); + self.split_driver + .write(&SplitMessage::FirmwareHashResponse(hash)) + .await + .ok(); + } + let mut key_sub = KeyboardEvent::subscriber(); #[cfg(feature = "_ble")] let mut charging_state_sub = ChargingStateEvent::subscriber(); @@ -122,9 +139,7 @@ impl SplitPeripheral { publish_event(LayerChangeEvent::new(layer)); } #[cfg(feature = "display")] - SplitMessage::Wpm(wpm) => { - publish_event(WpmUpdateEvent::new(wpm)); - } + SplitMessage::Wpm(wpm) => publish_event(WpmUpdateEvent::new(wpm)), #[cfg(feature = "display")] SplitMessage::Modifier(bits) => { publish_event(ModifierEvent { @@ -135,6 +150,87 @@ impl SplitPeripheral { SplitMessage::SleepState(sleeping) => { publish_event(SleepStateEvent::new(sleeping)); } + // --- dfu_split: firmware update handlers --- + #[cfg(feature = "dfu_split")] + SplitMessage::FirmwareHashQuery => { + let hash = crate::dfu::read_embedded_firmware_hash(); + info!("dfu_split: hash query, responding with {:#x}", hash); + self.split_driver + .write(&SplitMessage::FirmwareHashResponse(hash)) + .await + .ok(); + } + #[cfg(feature = "dfu_split")] + SplitMessage::FirmwareChunk { offset, len, data } => { + if self.dfu_handler.is_none() { + self.dfu_handler = crate::dfu::SplitDfuHandler::new(); + if self.dfu_handler.is_none() { + error!("dfu_split: FlashManager not initialized, skipping chunk"); + continue; + } + } + let handler = self.dfu_handler.as_mut().unwrap(); + let actual_len = len as usize; + let chunk_data = &data.0[..actual_len]; + match handler.write_chunk(offset as u32, chunk_data) { + Ok(()) => { + debug!("dfu_split: wrote {} bytes at offset {}", actual_len, offset); + let ack = SplitMessage::FirmwareChunkAck { + offset, + crc: crate::crc32::crc32(chunk_data), + }; + self.split_driver.write(&ack).await.ok(); + } + Err(()) => error!("dfu_split: write error at offset {}", offset), + } + } + #[cfg(feature = "dfu_split")] + SplitMessage::FirmwareUpdateComplete => { + if let Some(ref mut handler) = self.dfu_handler { + let dfu_crc = handler.compute_dfu_crc(); + info!("dfu_split: DFU partition CRC: {:#010x}", dfu_crc); + let crc_msg = SplitMessage::FirmwareCrcReport(dfu_crc); + self.split_driver.write(&crc_msg).await.ok(); + info!("dfu_split: CRC report sent"); + + let ok = loop { + match select( + self.split_driver.read(), + embassy_time::Timer::after(embassy_time::Duration::from_secs(5)), + ) + .await + { + Either::First(Ok(SplitMessage::FirmwareCrcOk)) => { + info!("dfu_split: central confirmed CRC, resetting"); + break true; + } + Either::First(Ok(SplitMessage::FirmwareCrcFail)) => { + warn!("dfu_split: central rejected CRC, stopping update"); + break false; + } + Either::First(Ok(_)) => {} + Either::First(Err(e)) => { + error!("read error: {:?}", e); + break false; + } + Either::Second(_) => { + error!("timeout"); + break false; + } + } + }; + + if ok { + self.split_driver.write(&SplitMessage::FirmwareUpdateConfirm).await.ok(); + embassy_time::Timer::after_millis(50).await; + handler.mark_updated_and_reset().ok(); + } else { + self.dfu_handler = None; + } + } else { + error!("dfu_split: no active DFU session"); + } + } _ => (), }, Err(e) => { diff --git a/rmk/src/split/serial/mod.rs b/rmk/src/split/serial/mod.rs index c47739fd1..c94f8b625 100644 --- a/rmk/src/split/serial/mod.rs +++ b/rmk/src/split/serial/mod.rs @@ -21,9 +21,15 @@ pub(crate) async fn run_serial_peripheral_manager< >( id: usize, receiver: S, + #[cfg(feature = "dfu_split")] policy: crate::split::driver::UpdatePolicy, ) { let split_serial_driver: SerialSplitDriver = SerialSplitDriver::new(receiver); - let peripheral_manager = PeripheralManager::::new(split_serial_driver, id); + let peripheral_manager = PeripheralManager::::new( + split_serial_driver, + id, + #[cfg(feature = "dfu_split")] + policy, + ); info!("Running peripheral manager {}", id); peripheral_manager.run().await; diff --git a/rmk/src/usb/mod.rs b/rmk/src/usb/mod.rs index 939e3349d..3118d6e17 100644 --- a/rmk/src/usb/mod.rs +++ b/rmk/src/usb/mod.rs @@ -244,7 +244,13 @@ impl> UsbTransport { let product_name = device_config.product_name; #[cfg(any(feature = "dfu_rp", feature = "dfu_nrf"))] if let Some(mgr) = ::rmk::dfu::get_manager() { - ::rmk::dfu::register_dfu_interface(&mut builder, mgr, product_name); + ::rmk::dfu::register_dfu_interface( + &mut builder, + mgr, + product_name, + #[cfg(feature = "dfu_split")] + crate::SPLIT_PERIPHERALS_NUM, + ); } } let (keyboard_reader, keyboard_writer) = keyboard_rw.split();