Skip to content

Add bounded split application messages - #984

Draft
colonelpanic8 wants to merge 21 commits into
rmk-rs:feat/rynkfrom
colonelpanic8:glove80-rmk/split-app
Draft

Add bounded split application messages#984
colonelpanic8 wants to merge 21 commits into
rmk-rs:feat/rynkfrom
colonelpanic8:glove80-rmk/split-app

Conversation

@colonelpanic8

Copy link
Copy Markdown
Contributor

What

  • add a 26-byte opaque application payload to the split wire protocol
  • expose bounded central-to-peripheral, peripheral-to-central, and receive queues
  • expose split-link state so applications can resynchronize after reconnects
  • keep key events ahead of application traffic and use drop-on-full semantics to preserve split responsiveness

Why

RMK-based firmware sometimes needs to coordinate application-owned state across halves without adding hardware-specific meaning to the core split protocol. Per-key lighting is one consumer, but this API stays opaque and allocation-free.

Impact

Existing split behavior is unchanged unless the new queues are used. The implementation currently targets a single split peripheral and documents that limitation.

Checks

  • repository formatter
  • full RMK feature-matrix tests
  • Rynk host tests, doctests, WASM checks/package typecheck, and clippy

HaoboGu and others added 21 commits July 19, 2026 21:34
Replace the placeholder rmk protocol module with the Rynk wire format: command, endpoint, and topic tables, error and message types, layout and system payloads, plus wire snapshots. Add tsify-based TypeScript type generation for host reuse.
…[keymap.layer]]

Separate the physical key map from the keymap: drop the legacy matrix_map/keymap path and its codegen, and add the macro codegen for the new schema.
… handler

Route USB and BLE host access through one handler and replace the vial_lock feature with host_lock.
Includes the lock gate, device-info and layout-blob endpoints, and keymap bulk transfer across layer boundaries.
Generates the TypeScript type bindings consumed by the web demo.
… examples

Add rp2040_rynk and qemu-riscv-rynk, adapt example sources, and regenerate Cargo.lock files.
Signed-off-by: Haobo Gu <haobogu@outlook.com>
Store each tap-hold's timing profile as a u8 index into a small,
deduplicated `taphold_profiles` table rather than an inline 4-byte
`MorseProfile`. `KeyAction::TapHold` shrinks from 12 to 7 bytes, which
also drops the u32's 4-byte alignment padding across every
KeyAction-sized RAM buffer (keymap array, held-key buffer, combos,
forks, morse cache).

Measured on the nrf52840 example at a 5x14x5 (350-key) board:
.bss -2408 bytes, .text -4 bytes (no flash cost). Savings scale with
board size; the table cost (CAP*4, default CAP=16 -> 64 B) is fixed.

- keyboard.toml: named profiles are interned automatically by the
  proc-macro; users keep referencing them by name.
- pure-Rust: default macros (th!/mt!/lt!/tt!) are unchanged; the
  custom-profile macros (thp!/mtp!/ltp!/ttp!) now take a table index
  and the user populates `behavior.morse.taphold_profiles`.
- Table capacity is configurable via `[rmk] taphold_profile_max_num`.
- Vial wire format is unchanged (profile isn't carried); the stored
  KeyAction encoding changes but auto-migrates via the build-hash guard.

Regenerated the rmk_protocol keymap endpoint snapshots (KeyAction
schema change propagates to keymap/combo/encoder/fork endpoints).
* refactor: redesign core rynk traits, achieve full-duplex communication

Signed-off-by: Haobo Gu <haobogu@outlook.com>

* refactor: update rynk crates to use the new contract

Signed-off-by: Haobo Gu <haobogu@outlook.com>

* chore(rynk): trim comments

* refactor(rynk): third simplification pass

- rynk-wasm: drop the label round-trip (JS already owns the picker name)
  and the WebDevice wrapper — JsByteLink is a local wasm-bindgen type, so
  it implements RynkDevice directly; device.rs deleted
- qemu_behavior: drop six request_raw calls duplicating the typed calls
  right above them (request delegates to request_raw, same code path)
- driver/tests: drop three tests subsumed by supersets (cmd mismatch,
  topic-during-request, cancelled-request recovery)
- rynk-serial: declare the tokio feature actually used (io-util, not time)

Signed-off-by: Haobo Gu <haobogu@outlook.com>

---------

Signed-off-by: Haobo Gu <haobogu@outlook.com>
Signed-off-by: Haobo Gu <haobogu@outlook.com>
Rebasing onto origin/main linearized away the merge commit 9b8d64c,
which carried hand-made changes not present in either parent: the
nrf54lm20_ble example tweaks, the rynk_loopback fixture downsize, and
the rynk/Cargo.lock removal. Restore them to match the pre-rebase tree.
Signed-off-by: Haobo Gu <haobogu@outlook.com>
- features/rynk.md: drop the non-existent `bulk` Cargo feature (the
  `features = ["bulk"]` snippet failed to compile); bulk is always on with
  `rynk`, sized by `rynk_buffer_size`. Note the tool-author example needs
  `embassy-futures`.
- migration/v08_v09.md: fix the fabricated `bulk_transfer` -> `bulk` rename
  (removed, folded into `rynk`); document the `KeyAction::TapHold` u8-index
  break, the removed `[rmk].protocol_max_bulk_size` key, and the new
  unknown-key strictness.
- configuration/layout.md: remove the `PDF(n)` action (unsupported in
  keyboard.toml — the keymap grammar has no rule for it), document the `Trns`
  transparent form, and split two examples that shared one toml fence.
- rmk/CHANGELOG.md: add the Rynk feature entry and the BREAKING `keyboard.toml`
  layout-schema change.
A USB bulk-IN transfer completes on the host only once it sees a packet shorter than the max packet size. A device-to-host frame whose length is a multiple of that size (64 at Full-Speed) ends on a full packet, so the host read stays pending. Wrap the CDC Sender in a small Write adapter that appends a zero-length packet after such a frame.

Scoped to the USB transport; run_session, UART, and BLE are untouched. This is an independent robustness fix, not the root cause of the nRF52840 "CDC IN never completes" report — there even the short handshake response and raw libusb hung, which a ZLP cannot explain.

Signed-off-by: Haobo Gu <haobogu@outlook.com>
Add an opaque, bounded application payload that firmware can exchange
between the split central and peripheral alongside the normal split
traffic, without ever taking priority over key events.

* `split_app` module: `SplitAppData` (a small, `MaxSize`, postcard
  length-prefixed payload) plus four statics — `SPLIT_APP_TX`
  (central -> peripheral), `SPLIT_APP_PERIPH_TX` (peripheral ->
  central), the symmetric `SPLIT_APP_RX` inbox, and the `SPLIT_APP_LINK`
  watch that reports split-link state to the application.
* Producers use `try_send` only (bounded, drop-on-full) so the split
  read/write loops never block or get starved by application traffic;
  key events are always polled first.
* The split driver and peripheral drain the application queues as the
  lowest-priority arm of their outgoing selects and forward received
  `SplitMessage::Application` payloads into the inbox.
* `SPLIT_APP_LINK` is state-based (a `Watch`), so a late-subscribing
  application still observes the current link state; the `false -> true`
  edge is a resync trigger. Link-down edges are emitted from a drop
  guard so they survive async cancellation of the split session.
* On the peripheral the link is raised on the FIRST inbound message from
  the central rather than on bare connection: over BLE, notifications to
  a central that has not yet subscribed are silently dropped, so the
  connection alone is not proof the application channel is usable.

Developed for a split keyboard port.
@Schievel1

Copy link
Copy Markdown
Contributor

Iirc this is also useful to transport state of the keyboard to the peripheral that is not available in the usual split protocol right?

Like for example I have a special function in my firmware that moves the mouse pointer a bit automatically when it's active. And I have a display on the peripheral that should display whether it's active.
Could I use this PR for that?

@HaoboGu
HaoboGu force-pushed the feat/rynk branch 5 times, most recently from c09015e to d2c97ab Compare July 31, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants