feat(hid): answer DS5 0x09/0x20/0x05 feature reports so Linux binds hid_playstation

Linux's hid_playstation driver reads three feature reports at probe — 0x09
(pairing info / controller MAC), 0x20 (firmware info) and 0x05 (calibration),
the latter two CRC-checked. Without valid answers the kernel never creates a
gamepad, so games outside Steam Input (Heroic/Proton/native) see no controller.

Synthesize them in tud_hid_get_report_cb: 0x09 returns the BT MAC at [0..5];
0x20/0x05 fill zeros plus a valid trailing crc32 over [0xA3 seed, report_id,
data]. Confirmed: the dongle now binds hid_playstation — a real gamepad on Linux.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarcelineVPQ
2026-06-07 00:39:46 -06:00
co-authored by Claude Opus 4.8
parent c879b24364
commit d090df6820
+26
View File
@@ -251,6 +251,32 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
(void) buffer; (void) buffer;
(void) reqlen; (void) reqlen;
// --- DualSense feature reports that Linux's hid_playstation reads at probe ---
// Without valid answers the kernel never creates a gamepad device, so games
// outside Steam Input (Heroic/Proton/native) see no controller. The host asks
// only for the report DATA (reqlen = report_size - 1); usbhid prepends the
// report-id byte itself. The two CRC'd reports validate crc32 over
// [0xA3 feature-seed, report_id, data...] in the last 4 bytes.
if (report_id == 0x09) { // pairing info → controller MAC at [0..5]
if (reqlen == 0) return 0;
memset(buffer, 0, reqlen);
if (reqlen >= 6) bt_get_addr(buffer);
return reqlen;
}
if (report_id == 0x20 || report_id == 0x05) { // firmware info / calibration (CRC-checked)
if (reqlen < 5) return 0;
memset(buffer, 0, reqlen); // zero payload; kernel validates size + CRC only
uint8_t tmp[2 + 64];
tmp[0] = 0xA3; tmp[1] = report_id;
memcpy(tmp + 2, buffer, reqlen - 4);
uint32_t crc = crc32_seeded(tmp, (size_t)(2 + (reqlen - 4)), 0);
buffer[reqlen - 4] = (uint8_t)(crc);
buffer[reqlen - 3] = (uint8_t)(crc >> 8);
buffer[reqlen - 2] = (uint8_t)(crc >> 16);
buffer[reqlen - 1] = (uint8_t)(crc >> 24);
return reqlen;
}
if (is_pico_cmd(report_id)) { if (is_pico_cmd(report_id)) {
return pico_cmd_get(report_id, buffer, reqlen); return pico_cmd_get(report_id, buffer, reqlen);
} }