feat(host/steam): M1 — byte-exact Deck input serializer, on-box validated
Flesh out inject/proto/steam_proto.rs into the full Steam Deck HID contract, transcribed verbatim from the kernel steam_do_deck_input_event / steam_do_deck_sensors_event and validated field-for-field against kernel 7.0: - SteamState: the u64 button map (bytes 8..16), sticks/triggers/trackpads/IMU stored as raw little-endian report values; serialize_deck_state is a pure, byte-exact memcpy into the 64-byte unnumbered frame. - from_gamepad (XInput frame -> Deck buttons/sticks/triggers) + apply_rich (RichInput touchpad -> right pad, motion -> IMU). - parse_steam_output: the 0xEB ID_TRIGGER_RUMBLE_CMD feedback -> (low, high) for the universal rumble plane. - serial_reply fixed: prepend the report-id-0 byte the kernel strips (steam_recv_report does memcpy(data, buf+1, ...)); M0's reply lacked it, so the kernel fell back to the "XXXXXXXXXX" serial. - SteamModel (Deck now; classic Controller later), command/feature IDs. The spike is repurposed as the M1 validator: it pulses the b9.6 mode-switch to enter gamepad_mode (steam_do_deck_input_event early-returns under the default lizard_mode otherwise), then holds a known test pattern. Reading both evdevs via EVIOCGABS/EVIOCGKEY, every field matched: ABS_X/Y/RX/RY (incl. the kernel Y-negation), both triggers, the touched right-pad HAT1X/Y, the IMU accel/gyro (with ABS_Z/RZ negations), and the 6 expected buttons incl. the L4/R5 grips. 5 unit tests + workspace clippy/fmt/test green. Next: M2 (SteamControllerManager UHID backend + PadBackend wiring). Not pushed — pipeline not yet shippable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,35 @@
|
||||
# Rich Steam Controller & Steam Deck support
|
||||
|
||||
> **Status:** **M0 GREEN — Linux feasibility PROVEN on-box (2026-06-29).** The greenfield virtual
|
||||
> `hid-steam` device works: a `/dev/uhid` `28DE:1205` device binds the kernel `hid-steam` driver,
|
||||
> registers as a real Steam Deck, and parses our input reports. The full client-capture → protocol
|
||||
> → inject pipeline (M1+) is unblocked. This remains the design + milestone plan; the Steam analogue
|
||||
> of the shipped virtual DualSense (`design/windows-dualsense-scoping.md`).
|
||||
> **Status:** **M0 + M1 GREEN — Linux virtual Deck binds AND is byte-exact, on-box (2026-06-29).**
|
||||
> The greenfield virtual `hid-steam` device works: a `/dev/uhid` `28DE:1205` device binds the kernel
|
||||
> `hid-steam` driver, registers as a real Steam Deck, and our full input report is parsed
|
||||
> field-for-field. Next: M2 (the `SteamControllerManager` UHID backend + `PadBackend` wiring). This
|
||||
> remains the design + milestone plan; the Steam analogue of the shipped virtual DualSense
|
||||
> (`design/windows-dualsense-scoping.md`).
|
||||
>
|
||||
> **M1 result (byte-exact serializer, on-box):** `inject/proto/steam_proto.rs` now carries the full
|
||||
> Deck contract transcribed verbatim from the kernel `steam_do_deck_input_event` /
|
||||
> `steam_do_deck_sensors_event`: the `u64` button map (bytes 8..16), sticks/triggers/trackpads/IMU
|
||||
> at their exact offsets, `from_gamepad` + `apply_rich` mappers, the rumble-feedback parser
|
||||
> (`0xEB`), and the serial reply (now with the leading report-id byte the kernel strips — fixes the
|
||||
> M0 `XXXXXXXXXX` fallback). The validator pulses the `b9.6` mode-switch to enter `gamepad_mode`
|
||||
> (the parser early-returns under default `lizard_mode` otherwise), holds a known test pattern, and
|
||||
> reads both evdevs via `EVIOCGABS`/`EVIOCGKEY`: **every field matched** — `ABS_X/Y/RX/RY` (incl. the
|
||||
> kernel Y-negation), both triggers, the touched right-pad `HAT1X/Y`, the IMU accel/gyro (with the
|
||||
> `ABS_Z/RZ` negations), and the 6 expected buttons incl. the L4/R5 grips. `byte 8 bit 7 = BTN_A` IS
|
||||
> correct (the M0 "didn't hold" was a flaky single-bit read before `gamepad_mode` was entered). 5
|
||||
> unit tests + workspace clippy/fmt/test green.
|
||||
>
|
||||
> **M0 result (box: headless Ubuntu 26.04, kernel 7.0, no Steam):** the spike
|
||||
> (`crates/punktfunk-host/src/bin/steam_uhid_spike.rs` + the keeper `inject/proto/steam_proto.rs`)
|
||||
> created a UHID `28DE:1205` device with a minimal vendor descriptor (one feature report — the sole
|
||||
> thing `steam_is_valve_interface` checks) and serviced the handshake (the `UHID_SET_REPORT`
|
||||
> answers the DualSense backend omits). `journalctl -k` showed **`hid-steam` binding it** (rebound
|
||||
> off `hid-generic`), `"Steam Controller … connected"`, and the kernel creating **both** evdevs:
|
||||
> `"Steam Deck"` (gamepad) **and** `"Steam Deck Motion Sensors"` (`INPUT_PROP_ACCELEROMETER`).
|
||||
> Outstanding for later: recognition by a **running Steam** client (needs a box with Steam —
|
||||
> untestable here); the `gamepad_mode` entry strategy on a real host (pulse `b9.6` at session start,
|
||||
> or load `hid_steam lizard_mode=0`) is an M2 backend decision.
|
||||
>
|
||||
> **M0 result (box: headless Ubuntu 26.04, kernel 7.0, no Steam):** the spike
|
||||
> (`crates/punktfunk-host/src/bin/steam_uhid_spike.rs` + the keeper `inject/proto/steam_proto.rs`)
|
||||
|
||||
Reference in New Issue
Block a user