From 02a5bdb965d88f1f9f2de74dea561024b8099a54 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 1 Aug 2026 09:53:48 +0200 Subject: [PATCH] fix(gamepad): the virtual DualSense stops demanding a firmware update it cannot take MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The emulated pad's firmware-info feature report (0x20) advertised update version 0x0154 — a 2021-era number. PlayStation Accessories compares it against Sony's latest (0x0630 as of 2026-08) and offers an Update that can only end in "can't complete the update", since the virtual pad speaks no DFU; libScePad titles (Stellar Blade) surface the same nag in-game. A real pad plugged in directly reads up to date, which made the prompt look like punktfunk corrupting the controller. The old value was chosen to keep the kernel and SDL on the flag0 COMPATIBLE_VIBRATION convention, but parse_ds_output has since learned the firmware-≥2.24 COMPATIBLE_VIBRATION2 flag as well, so nothing depends on looking old anymore. Advertise 0x0999 — above anything Sony has shipped and comfortably ahead of their ~yearly cadence — instead of chasing their exact latest, which would resurrect the prompt on every Sony release. Writers that read the version now use the v2 flag; both conventions land in the same rumble plane. Bumped in both copies of the blob (host uhid + Windows driver); the DualSense Edge shares them, and its own versioning (0x0217 latest) sits below the new value too. Co-Authored-By: Claude Fable 5 --- .../src/inject/proto/dualsense_proto.rs | 19 +++++++++++++------ .../windows/drivers/pf-gamepad/src/lib.rs | 7 +++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/pf-inject/src/inject/proto/dualsense_proto.rs b/crates/pf-inject/src/inject/proto/dualsense_proto.rs index 9393e0c6..32852914 100644 --- a/crates/pf-inject/src/inject/proto/dualsense_proto.rs +++ b/crates/pf-inject/src/inject/proto/dualsense_proto.rs @@ -35,10 +35,14 @@ pub const DS_FEATURE_PAIRING: &[u8] = &[ // report 0x09 (pairing info: MAC at by 0x00, 0x00, 0x00, 0x00, ]; #[rustfmt::skip] -pub const DS_FEATURE_FIRMWARE: &[u8] = &[ // report 0x20 (firmware info / build date) +pub const DS_FEATURE_FIRMWARE: &[u8] = &[ // report 0x20 (firmware info / build date); bytes 44..46 + // = update version, kept ABOVE Sony's real releases (0x0630 as of 2026-08) — an older value + // makes PlayStation Accessories and libScePad titles demand a firmware update the virtual pad + // cannot take ("can't complete the update"), and ≥ 0x0224 is what puts writers on the + // COMPATIBLE_VIBRATION2 convention parse_ds_output accepts alongside flag0. 0x20, 0x4A, 0x75, 0x6E, 0x20, 0x31, 0x39, 0x20, 0x32, 0x30, 0x32, 0x33, 0x31, 0x34, 0x3A, 0x34, 0x37, 0x3A, 0x33, 0x34, 0x03, 0x00, 0x44, 0x00, 0x08, 0x02, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, - 0xC1, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, + 0xC1, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x09, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; @@ -494,10 +498,13 @@ pub fn parse_ds_output(pad: u8, data: &[u8], fb: &mut DsFeedback) { // data[4]. Scale 0..255 → 0..0xFFFF, same (low, high) convention as the uinput pad's mixer, // and route to the universal rumble plane (0xCA). // Writers on firmware ≥ 2.24 signal rumble via COMPATIBLE_VIBRATION2 in valid_flag2 - // (data[39] BIT2) instead of flag0 BIT0. Our feature report advertises 0x0154 so the - // kernel and SDL stay on the flag0 convention, but a writer that hardcodes v2 would - // otherwise have its rumble — including stops — silently ignored, and a missed stop - // buzzes for the rest of the session (the 500 ms refresh re-sends stale state forever). + // (data[39] BIT2) instead of flag0 BIT0. Our feature report advertises a version + // above 2.24 (DS_FEATURE_FIRMWARE bytes 44..46, chosen to keep Sony's updater + // quiet), so the kernel and SDL write the v2 flag — while older writers, and any + // that never read the version, stay on flag0. Both conventions must land here: a + // rumble dropped on either — including stops — is silently ignored, and a missed + // stop buzzes for the rest of the session (the 500 ms refresh re-sends stale state + // forever). if flag0 & 0x03 != 0 || data[39] & 0x04 != 0 { let high = (data[3] as u16) << 8; let low = (data[4] as u16) << 8; diff --git a/packaging/windows/drivers/pf-gamepad/src/lib.rs b/packaging/windows/drivers/pf-gamepad/src/lib.rs index efc1f131..daefbcb8 100644 --- a/packaging/windows/drivers/pf-gamepad/src/lib.rs +++ b/packaging/windows/drivers/pf-gamepad/src/lib.rs @@ -109,10 +109,13 @@ static DS_FEATURE_PAIRING: [u8; 20] = [ // 0x09 pairing info (MAC at 1..7) 0x00, 0x00, 0x00, 0x00, ]; #[rustfmt::skip] -static DS_FEATURE_FIRMWARE: [u8; 64] = [ // 0x20 firmware info +static DS_FEATURE_FIRMWARE: [u8; 64] = [ // 0x20 firmware info; bytes 44..46 = update version, + // kept ABOVE Sony's real releases (0x0630 as of 2026-08) — an older value makes PlayStation + // Accessories and libScePad titles demand a firmware update the virtual pad cannot take. + // Mirrors inject/proto/dualsense_proto.rs DS_FEATURE_FIRMWARE; keep the two in sync. 0x20, 0x4A, 0x75, 0x6E, 0x20, 0x31, 0x39, 0x20, 0x32, 0x30, 0x32, 0x33, 0x31, 0x34, 0x3A, 0x34, 0x37, 0x3A, 0x33, 0x34, 0x03, 0x00, 0x44, 0x00, 0x08, 0x02, 0x00, 0x01, 0x36, 0x00, 0x00, 0x01, - 0xC1, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, + 0xC1, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x09, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];