fix(hid): serve the controller's REAL 0x09/0x20/0x05 feature data to the host

The native-recognition shortcut answered 0x09 (pairing), 0x20 (firmware) and 0x05
(calibration) with zeros + a valid crc. hid_playstation accepted that (it checks
size+crc only), but games validate the actual CONTENT: Cyberpunk GET-read 0x20/0x09
~156x in a retry storm rejecting the zeros, never completed the DualSense handshake,
and showed no controller / no adaptive triggers.

init_feature() already caches the real reports from the controller over BT, so serve
those (via get_feature_data) for 0x09/0x20/0x05, keeping the crc-valid synthetic stub
only as a fallback for the USB-enumeration probe before the BT link (so the kernel
still binds then). The dongle now returns the genuine firmware string / pairing /
calibration, and native adaptive triggers work in games through the dongle, 1:1 with
a wired DualSense.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarcelineVPQ
2026-06-07 10:36:10 -06:00
co-authored by Claude Opus 4.8
parent 36687ce76d
commit 8d811f09cc
+19 -4
View File
@@ -257,15 +257,30 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t
// only for the report DATA (reqlen = report_size - 1); usbhid prepends the // only for the report DATA (reqlen = report_size - 1); usbhid prepends the
// report-id byte itself. The two CRC'd reports validate crc32 over // report-id byte itself. The two CRC'd reports validate crc32 over
// [0xA3 feature-seed, report_id, data...] in the last 4 bytes. // [0xA3 feature-seed, report_id, data...] in the last 4 bytes.
if (report_id == 0x09) { // pairing info → controller MAC at [0..5] // hid_playstation (kernel) AND the game's native DualSense detection both read
// 0x09 (pairing), 0x20 (firmware) and 0x05 (calibration). The KERNEL only checks
// size + crc, but the GAME validates the actual CONTENT — so synthesized zeros
// pass the kernel yet get rejected by the game (a ~156x GET retry storm, and no
// native adaptive triggers). Serve the REAL controller data, which init_feature()
// caches from the controller over BT (get_feature_data returns it incl. the
// report-id at [0]). Fall back to a crc-valid synthetic answer ONLY when the
// controller isn't linked yet (USB-enumeration probe before the BT link), so the
// kernel still binds at that moment.
if (report_id == 0x09 || report_id == 0x20 || report_id == 0x05) {
if (reqlen == 0) return 0; if (reqlen == 0) return 0;
std::vector<uint8_t> real = get_feature_data(report_id, reqlen);
if (real.size() > 1) { // real cached response present
uint16_t n = (uint16_t)(real.size() - 1);
if (n > reqlen) n = reqlen;
memcpy(buffer, real.data() + 1, n);
return n;
}
memset(buffer, 0, reqlen); memset(buffer, 0, reqlen);
if (report_id == 0x09) { // not linked yet: MAC-only stub
if (reqlen >= 6) bt_get_addr(buffer); if (reqlen >= 6) bt_get_addr(buffer);
return reqlen; return reqlen;
} }
if (report_id == 0x20 || report_id == 0x05) { // firmware info / calibration (CRC-checked) if (reqlen < 5) return 0; // 0x20 / 0x05 stub: zeros + valid crc32
if (reqlen < 5) return 0;
memset(buffer, 0, reqlen); // zero payload; kernel validates size + CRC only
uint8_t tmp[2 + 64]; uint8_t tmp[2 + 64];
tmp[0] = 0xA3; tmp[1] = report_id; tmp[0] = 0xA3; tmp[1] = report_id;
memcpy(tmp + 2, buffer, reqlen - 4); memcpy(tmp + 2, buffer, reqlen - 4);