From d090df68208530da63110c596946ba9a88160064 Mon Sep 17 00:00:00 2001 From: MarcelineVPQ Date: Sun, 7 Jun 2026 00:39:46 -0600 Subject: [PATCH] feat(hid): answer DS5 0x09/0x20/0x05 feature reports so Linux binds hid_playstation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/main.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index c0af18f..3a50d38 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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) 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)) { return pico_cmd_get(report_id, buffer, reqlen); }