From f34acf1d73b1cdc4144b4a2186b412c0ffba9bfb Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sun, 9 Aug 2026 18:16:24 +0200 Subject: [PATCH] fix(drivers/pf-gamepad): the Xbox descriptor never declared the channel-proof report, so the pad served neutral forever MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `XBOX_RDESC` declared only Input report 1. The sealed pad channel delivers its DATA section over a vendor Feature report `0x85` (`ProofTransport::HidFeatureReport`), and the proof handler's own comment records the assumption that made this invisible — "0x85 is already declared as a Feature report in all three captured descriptors". True of the captured PlayStation blobs; false of this hand-constructed one. So hidclass rejected the host's `HidD_GetFeature` before the driver ever saw it, the host refused to hand over the section, and the pad answered every read with its neutral report. The HID Xbox pad had never delivered a single input report since it was written. Declaring `0x85` with a 63-byte payload (1 id + 63 = 64 = FeatureReportByteLength) fixes it. Verified on glass on .173: `gamepad driver attached to the shared section proto=3 late=false`, and WGI's RawGameController path then reads the pad live — advancing timestamps, the devtest's left-stick sweep, buttons toggling. Before the fix: 12 consecutive samples, one frozen timestamp, every axis at dead centre. This is the descriptor-provenance warning in this file coming true. It is still CONSTRUCTED rather than captured, and that remains the open risk — `xinputhid` appears to validate the descriptor and refuses ours, and a real Elite is a multi-collection device where ours has one. Codec layout tests still 11/11; fmt clean. Only device_type 4 is affected, which nothing shipping uses yet. --- .../windows/drivers/pf-gamepad/src/lib.rs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/packaging/windows/drivers/pf-gamepad/src/lib.rs b/packaging/windows/drivers/pf-gamepad/src/lib.rs index 763d51f0..0db9911d 100644 --- a/packaging/windows/drivers/pf-gamepad/src/lib.rs +++ b/packaging/windows/drivers/pf-gamepad/src/lib.rs @@ -291,8 +291,19 @@ static DECK_RDESC: [u8; 38] = [ // `HKLM\SYSTEM\CurrentControlSet\Enum\BTHENUM\...\Device Parameters` or use a HID monitor; // `hidapi`'s `hidapi-hidtest` and Linux `/sys/class/hidraw/hidrawN/device/report_descriptor` both // dump it directly. Replace this blob and re-run the `xbox_proto` layout tests. +// +// ⚠️ The trailing vendor-defined Feature report `0x85` is NOT cosmetic and must not be trimmed as +// "unused": it is the CHANNEL PROOF transport (`ProofTransport::HidFeatureReport`). The captured +// PlayStation descriptors already declared `0x85`, which is why the proof "costs no descriptor +// change" there — but this descriptor is constructed, so it has to declare the report itself. Built +// without it the pad enumerates perfectly and then delivers NOTHING: hidclass rejects the host's +// `HidD_GetFeature` before the driver sees it, the host refuses to hand over the DATA section +// (measured on .173 2026-08-09 — WGI `RawGameController` saw `045E:0B13` with every axis pinned at +// 0.5000 and a timestamp frozen for 12 consecutive samples), and the pad serves only its neutral +// report forever. `0x3F` payload bytes so `FeatureReportByteLength` lands on 64, the buffer size +// `channel_proof::query` asks with; the proof itself needs 17. #[rustfmt::skip] -static XBOX_RDESC: [u8; 132] = [ +static XBOX_RDESC: [u8; 150] = [ 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x05, // Usage (Game Pad) 0xA1, 0x01, // Collection (Application) @@ -355,6 +366,17 @@ static XBOX_RDESC: [u8; 132] = [ 0x75, 0x01, // Report Size (1) 0x95, 0x01, // Report Count (1) 0x81, 0x03, // Input (Cnst,Var,Abs) — pad to a byte boundary + // The channel-proof feature report — see the ⚠️ above. Declared last so it cannot disturb the + // INPUT layout `xbox_proto` packs against: every global item here (Report Size/Count, Logical + // Min/Max) is re-stated after the final Input item, so nothing above is retroactively changed. + 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + 0x85, 0x85, // Report ID (0x85) + 0x09, 0x2D, // Usage (0x2D) — the id the PS descriptors use for it + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x3F, // Report Count (63) — 1 id + 63 = 64 = FeatureReportByteLength + 0xB1, 0x02, // Feature (Data,Var,Abs) 0xC0, // End Collection ]; @@ -371,7 +393,7 @@ static HID_DESC: [u8; 9] = [0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x11, 0x01 static DS4_HID_DESC: [u8; 9] = [0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0xFB, 0x01]; static EDGE_HID_DESC: [u8; 9] = [0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x85, 0x01]; static DECK_HID_DESC: [u8; 9] = [0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x26, 0x00]; // 38 bytes -static XBOX_HID_DESC: [u8; 9] = [0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x84, 0x00]; // 132 bytes +static XBOX_HID_DESC: [u8; 9] = [0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, 0x96, 0x00]; // 150 bytes // HID_DEVICE_ATTRIBUTES (32 bytes): Size(u32)=32, VendorID, ProductID, VersionNumber, Reserved[11]. // `devtype` selects the identity: PS family (same Sony VID/version) or the N4-spike Deck.