1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Rynk protocol ICD — RMK's native host-communication protocol.
//!
//! Carries RMK's canonical types (`KeyAction`, `Combo`, `Morse`, `Fork`,
//! `EncoderAction`, `BatteryStatus`, `BleStatus`) on the wire as a 3-byte
//! fixed header + postcard-encoded payload, COBS-framed.
//!
//! ## Wire format
//!
//! ```text
//! ┌──────────────┬────────────┐
//! │ CMD u16 LE │ SEQ u8 │ ← 3-byte header
//! ├──────────────┴────────────┤
//! │ postcard-encoded payload │
//! └───────────────────────────┘
//! ```
//!
//! On the wire the whole frame is COBS-encoded and terminated by a single
//! `0x00` delimiter (see [`message`] and [`Deframer`]), making the byte
//! stream self-synchronizing.
//!
//! - **CMD** — `0x0000..=0x7FFF` request/response, `0x8000..=0xFFFF` topic
//! (server→host push).
//! - **SEQ** — the sequence number of current request. Topics send SEQ = 0.
//!
//! ## Sizing
//!
//! One parameter drives every derived size: `rynk_buffer_size` in `keyboard.toml`
//! (`constants::RYNK_BUFFER_SIZE`). It's the physical RAM of each frame buffer;
//! COBS framing overhead is deducted internally.
//! - [`RYNK_MAX_PAYLOAD_SIZE`]: the largest payload one frame can carry.
//! - [`MAX_BULK_ITEMS`]/[`MAX_BULK_KEYS`]: how many bulk-page items fit.
//!
//! ## Module layout
//!
//! [`command`] is the only public sub-module: the [`Cmd`] ids, the table
//! binding each command to its payload types, and the
//! [`Endpoint`](command::Endpoint) trait its rows implement. Firmware and host
//! both compile against it, so the two ends can't disagree about a message's
//! types. Everything else — the framing (`message`, [`RynkHeader`],
//! [`encode_frame`], [`RynkMessage`], the COBS [`Deframer`]), [`RynkError`],
//! and the per-domain payload types — is private and re-exported flat at
//! `protocol::rynk::*`.
//!
//! ## Compatibility
//!
//! `Cmd::GetVersion = 0x0001` and its `Result<ProtocolVersion, RynkError>` reply
//! are frozen across all versions.
//! Within a `major`, changes must keep old hosts working, so `minor`
//! is informational: a new `Cmd` or a new/extended topic is a `minor` bump (old
//! peers answer `UnknownCmd` or ignore trailing topic bytes), while reshaping an
//! existing request/response — *including appending a field* — is a `major` bump,
//! since hosts reject trailing response bytes. The `snapshots/*.snap` golden
//! files (`tests.rs`) fail on any accidental drift.
pub
pub use ;
pub use Deframer;
pub use RynkError;
pub use ;
pub use *;
/// Largest single GATT write/notification on the Rynk BLE characteristics.
pub const RYNK_BLE_CHUNK_SIZE: usize = 244;
/// Fixed size of one Rynk-over-WebHID report (`RynkHidService`)
pub const RYNK_HID_REPORT_SIZE: usize = 32;
/// Rynk GATT service UUID
pub const RYNK_SERVICE_UUID: u128 = 0x10900067_537f_4f0a_9b55_929e271f61ab;
/// Rynk `input_data` characteristic UUID.
pub const RYNK_INPUT_CHAR_UUID: u128 = 0x80f9319b_0c74_43a5_9738_c59d6dda3db9;
/// Rynk `output_data` characteristic UUID.
pub const RYNK_OUTPUT_CHAR_UUID: u128 = 0x19802524_6f90_4346_93c2_63dbc509ab55;
/// Informational marker the `rynk` firmware prepends to its USB serial number.
/// Host discovery keys on the vendor interface triple below, not on this.
pub const RYNK_MAGIC: &str = "rynk:";
/// Class of the Rynk vendor-specific USB bulk interface.
pub const RYNK_USB_INTERFACE_CLASS: u8 = 0xFF;
/// Subclass of the Rynk USB interface: `'R'`.
pub const RYNK_USB_INTERFACE_SUBCLASS: u8 = 0x52;
/// Protocol of the Rynk USB interface: `'R'`.
pub const RYNK_USB_INTERFACE_PROTOCOL: u8 = 0x52;