feat(host/steam): harden the gadget feature contract — fixes the evdev churn
The virtual Deck's gamepad evdev was churning (destroyed + recreated) because Steam kept re-probing: GetControllerInfo reads HID feature reports, and the gadget served zeros for them. Captured the real contract off a physical Deck (packaging/linux/steam-deck-gadget/get_deck_attrs.c, hidraw HIDIOCGFEATURE — usbmon truncates to 32B) and implemented it in steam_gadget.rs::feature_reply: - 0x83 GET_ATTRIBUTES_VALUES: [83, 2d, 9×(attr-id, u32-LE)] — product id 0x1205, a per-instance unit serial (0x0a/0x04, so a gadget never collides with a real Deck or another gadget), and the capability attrs (0x09=0x2e, 0x0b=0x0fa0, rest 0). - 0xAE GET_STRING_ATTRIBUTE: [ae, len, attr, ascii] — serial (attr 1) / board serial (attr 0). - other commands (0x87 settings): echo the last write. Validated on the Deck: 1 connect / 0 disconnect / 1 gamepad evdev (was constant churn), Steam activates the gadget cleanly (no GetControllerInfo failed, no zombie) and emits its X-Box 360 pad. usbmon on the gadget's bus confirms our state reports (pressed button at byte 8) are delivered on the interrupt-IN and consumed by hid-steam — so with M1/M2's byte-8→BTN_SOUTH decode the input chain is proven end-to-end. Remaining: a foreground-game confirmation of Steam Input's XInput mapping, then default the gadget on for SteamOS. Workspace clippy/fmt/test green. Not pushed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// Query a physical Steam Deck's feature reports (SET command then GET response) over hidraw to get
|
||||
// the FULL blobs (usbmon truncates to 32 bytes). Steam feature reports are unnumbered 64-byte; the
|
||||
// hidraw buffer prefixes a report-id byte (0).
|
||||
#include <linux/hidraw.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
static void dump(const char *name, unsigned char *b, int n) {
|
||||
printf("%s rc=%d:", name, n);
|
||||
for (int i = 0; i < (n > 0 ? n : 0); i++) printf(" %02x", b[i]);
|
||||
printf("\n");
|
||||
}
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) { printf("usage: %s /dev/hidrawN\n", argv[0]); return 1; }
|
||||
int fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) { perror("open"); return 1; }
|
||||
// SET [reportid=0, cmd, len, attr, ...] then GET the response.
|
||||
unsigned char queries[][4] = {
|
||||
{0x83, 0x00, 0x00, 0x00}, // GET_ATTRIBUTES_VALUES
|
||||
{0xae, 0x16, 0x01, 0x00}, // GET_STRING_ATTRIBUTE, serial (attr 1)
|
||||
{0xae, 0x16, 0x00, 0x00}, // GET_STRING_ATTRIBUTE, attr 0
|
||||
{0xae, 0x16, 0x02, 0x00}, // attr 2 (board serial?)
|
||||
};
|
||||
for (int q = 0; q < 4; q++) {
|
||||
unsigned char set[65] = {0};
|
||||
set[0] = 0; // report id 0
|
||||
memcpy(set + 1, queries[q], 4);
|
||||
int sr = ioctl(fd, HIDIOCSFEATURE(65), set);
|
||||
usleep(3000);
|
||||
unsigned char get[65] = {0};
|
||||
get[0] = 0;
|
||||
int gr = ioctl(fd, HIDIOCGFEATURE(65), get);
|
||||
printf("=== query cmd=%02x attr=%02x (SET rc=%d) ===\n", queries[q][0], queries[q][2], sr);
|
||||
dump(" GET", get, gr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user