docs(design): trim shipped plans, consolidate cluster, add index
Much of design/ described work that has since shipped. Trim each doc to
its durable rationale + still-open items (the code is the source of truth
for shipped detail; git history holds the full originals).
- Shipped plans -> status stubs: stats-capture, gamestream-host-plan,
apple-stage2-presenter, windows-service.
- Trimmed completed-out / open-kept: implementation-plan, hdr-pipeline,
host-latency, gpu-contention (fixed stale status table), game-library,
linux-setup (fixed m0->spike + stale zero-copy claim),
session-aware-host-followups, windows-client-bootstrap,
windows-dualsense-{scoping,game-detection}, windows-virtual-display,
security-review (per-finding status table; #12 still open),
apollo-comparison (shipped backlog collapsed to one-liners).
- Windows-host cluster consolidated: windows-host.md -> redirect into
windows-host-rewrite.md (whose stale scorecard is corrected -- goal1 is
merged, M4 done); windows-secure-desktop.md archived (now a fallback
behind IDD-push primary).
- Kept evergreen: ci.md, gamescope-multiuser.md, windows-build-and-packaging.md.
- New design/README.md: per-doc status table + consolidated open-items
roll-up so nothing is tracked in only one buried doc.
- Repoint 5 code comments to the archived secure-desktop doc path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,25 +1,27 @@
|
||||
# Windows virtual DualSense — game detection handoff
|
||||
|
||||
> **Status:** Identity fix SHIPPED (commits `6db3525`, `aa159df`, `4a73102`) —
|
||||
> `crates/punktfunk-host/src/inject/windows/dualsense_windows.rs` (`create_swdevice`). This doc is trimmed
|
||||
> to the root-cause analysis, the SwDeviceCreate identity rationale, the GameInput fallback design, and the
|
||||
> still-open on-glass Cyberpunk verification. The implementation walkthrough, probe tooling, and the
|
||||
> (now-fixed) secondary driver gaps are cut — the shipped code is the source of truth.
|
||||
|
||||
Goal: get the host's virtual DualSense **detected and usable in games** (Cyberpunk's native PS5 path +
|
||||
others) on the Windows host. This doc is the portable handoff (the investigation lives here, not in any
|
||||
one agent's memory). Run the experiments **on the Windows host** (`.173`, repo at
|
||||
`C:\Users\Public\punktfunk-native`).
|
||||
others) on the Windows host. Run the decisive experiments **on the interactive desktop of the Windows host**
|
||||
(`.173`) — not over SSH.
|
||||
|
||||
## Status (2026-06-22)
|
||||
## Where it works / where it doesn't
|
||||
|
||||
- **Input works.** Client → host → virtual DualSense → games read input. Verified in Steam's controller
|
||||
test (buttons/sticks).
|
||||
- **The HID is a CORRECT, COMPLETE DualSense.** An SDL3 probe reports our live device as
|
||||
`name='DualSense Wireless Controller' vid=0x054C pid=0x0CE6 isGamepad=True gamepadType=PS5`. SDL =
|
||||
HIDAPI = what Steam (and many games) build on → that's why Steam works. So the report descriptor,
|
||||
feature reports, and identity are right; this is **not** a descriptor/feature-report problem.
|
||||
- **Cyberpunk's native DualSense path does NOT detect it at all.** (Steam Input was off — Cyberpunk was
|
||||
reading the raw HID.)
|
||||
- **Rumble:** host-side is proven working (driver captures the game's `0x02`, `parse_ds_output` extracts
|
||||
the motors, host forwards `0xCA` — log: `rumble: forwarding to client (0xCA) low=16128 high=16128`).
|
||||
The break is the **client** (macOS) not rendering `0xCA` onto the physical pad. Separate task/agent.
|
||||
- **Input works.** Client → host → virtual DualSense → games read input (verified in Steam's controller
|
||||
test).
|
||||
- **The HID is a CORRECT, COMPLETE DualSense.** SDL3 reports the live device as
|
||||
`name='DualSense Wireless Controller' vid=0x054C pid=0x0CE6 isGamepad=True gamepadType=PS5`. SDL = HIDAPI =
|
||||
what Steam (and many games) build on → that's why Steam works. This is **not** a descriptor/feature-report
|
||||
problem.
|
||||
- **Cyberpunk's native DualSense path does NOT detect it at all** (Steam Input was off — Cyberpunk was
|
||||
reading the raw HID). This is the problem the identity fix targets; on-glass confirmation is still open.
|
||||
|
||||
## Root cause — CONFIRMED (2026-06-22, run live on the interactive desktop, console session 3)
|
||||
## Root cause — the PnP identity, not the HID descriptor (CONFIRMED, run live in console session 3)
|
||||
|
||||
The break is the device's **PnP identity / device-interface path**, not the HID descriptor or feature
|
||||
reports. `hidclass` derives the HID child's path token and its `HID\VID_054C&PID_0CE6` hardware-ids from the
|
||||
@@ -40,7 +42,7 @@ Device-interface paths (from `HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasse
|
||||
| Real DualShock 4 (USB, registry remnant) | `\\?\HID#VID_054C&PID_05C4&REV_0100#…` |
|
||||
| Real DualSense (BT, registry remnant) | `\\?\HID#{00001124-…}_VID&0002054c_PID&0ce6#…` |
|
||||
|
||||
**Cross-API enumeration (the decisive experiment — impossible over SSH, run live in the console session):**
|
||||
**Cross-API enumeration matrix (the decisive experiment — impossible over SSH, run live in the console):**
|
||||
|
||||
| API | Sees our virtual DS5? | Identity reported | Reads from |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -54,26 +56,19 @@ Device-interface paths (from `HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasse
|
||||
The GameInput result is the clincher: it **does** enumerate our pad — descriptor fingerprint matches exactly
|
||||
(15 buttons, 6 axes, 1 hat, usage Game Pad 0x05) — but reports **vid/pid = 0**, while it reads the real
|
||||
8BitDo's `vid=0x3434` correctly. So GameInput (and, by the same logic, a native PS5 path) takes VID/PID from
|
||||
the **PnP device path / hardware-ids, NOT from `HIDD_ATTRIBUTES`**, and ours carry no `VID_054C&PID_0CE6`.
|
||||
Everything that reads attributes directly (SDL / RawInput / WGI-raw) is fine; everything that keys off the
|
||||
device *identity/path* (GameInput, native DualSense detection) sees a generic, unidentified gamepad → no
|
||||
PS5 path.
|
||||
the **PnP device path / hardware-ids, NOT from `HIDD_ATTRIBUTES`**. Everything that reads attributes directly
|
||||
(SDL / RawInput / WGI-raw) is fine; everything that keys off the device *identity/path* (GameInput, native
|
||||
DualSense detection) sees a generic, unidentified gamepad → no PS5 path.
|
||||
|
||||
**⇒ The fix must put `VID_054C&PID_0CE6` into the device-interface path and the `HID\VID&PID` hardware-ids**
|
||||
(give the device a real-USB-like PnP identity), not merely correct `HIDD_ATTRIBUTES`. See "Fix options".
|
||||
(give the device a real-USB-like PnP identity), not merely correct `HIDD_ATTRIBUTES`.
|
||||
|
||||
**Secondary driver gaps found (not the detection blocker, but fix while here):**
|
||||
- `IOCTL_HID_GET_STRING` (id 4, ioctl `0x000b0013`) returns `STATUS_NOT_IMPLEMENTED` — a game polls it
|
||||
repeatedly (seen live in `pfds-driver.log`). Implement manufacturer / product / serial strings
|
||||
(`"DualSense Wireless Controller"`, a serial). Native PS5 code can read the serial to tell USB from BT.
|
||||
- `DS_FEATURE_CALIBRATION` is **42** bytes but the report descriptor declares feature `0x05` as **41**
|
||||
(`0x95 0x28` = 40 data + 1 id). Trim to 41 (motion-only; SDL accepts it regardless).
|
||||
## The fix — SwDeviceCreate identity (shipped)
|
||||
|
||||
## Fix — implemented & validated at the identity layer (2026-06-22)
|
||||
`create_swdevice` sets the identity via **`SW_DEVICE_CREATE_INFO` struct fields** (NOT `pProperties` — a
|
||||
`DEVPROPERTY` write of these PnP-owned identity keys is empirically *ignored*; the create-time struct fields
|
||||
are the supported lever, confirmed on `.173`):
|
||||
|
||||
`create_swdevice` (`inject/dualsense_windows.rs`) now sets, via **`SW_DEVICE_CREATE_INFO` struct fields**
|
||||
(NOT `pProperties` — empirically a `DEVPROPERTY` write of these PnP-owned identity keys is ignored; the
|
||||
create-time struct fields are the supported lever, confirmed on `.173`):
|
||||
- **`pszzCompatibleIds`** = `USB\VID_054C&PID_0CE6`, `USB\Class_03&SubClass_00&Prot_00`, `USB\Class_03`
|
||||
(Windows appends `SWD\Generic`). HIDAPI/SDL/libScePad walk HID-child → `CM_Get_Parent` → this parent's
|
||||
CompatibleIds and string-match `"USB"` → **`bus_type` now resolves to USB** (was UNKNOWN).
|
||||
@@ -81,161 +76,68 @@ create-time struct fields are the supported lever, confirmed on `.173`):
|
||||
`USB\VID_054C&PID_0CE6&REV_0100`, `USB\VID_054C&PID_0CE6`. hidclass then derives the real-DS5 child ids
|
||||
**`HID\VID_054C&PID_0CE6[&REV_0100]`** (previously only `HID\VID_054C&UP:0001_U:0005`).
|
||||
- **`pContainerId`** = a deterministic per-pad GUID `{50464453-0000-0000-0000-00000000000<idx>}` ("PFDS")
|
||||
(avoids the null-sentinel-ContainerId `xinput1_4` slot-skip bug; groups the pad's devnodes).
|
||||
— avoids the null-sentinel-ContainerId `xinput1_4` slot-skip bug, and groups the pad's devnodes.
|
||||
|
||||
**Validated live** (real shipping path, `dualsense-windows-test --index 1` alongside the running service's
|
||||
pad 0): INF still binds (`Service=MsHidUmdf`), parent CompatibleIds/HardwareIds + per-pad ContainerId set,
|
||||
the HID child gains `HID\VID_054C&PID_0CE6`, and the HIDAPI parent-walk reports **bus_type=USB**.
|
||||
SDL / RawInput / WGI `RawGameController` identity stays correct (054C:0CE6).
|
||||
the HID child gains `HID\VID_054C&PID_0CE6`, and the HIDAPI parent-walk reports **bus_type=USB**. SDL /
|
||||
RawInput / WGI `RawGameController` identity stays correct (054C:0CE6).
|
||||
|
||||
**Remaining gap (NOT fixed by the above): GameInput VID/PID still reads 0.** GameInput parses VID/PID from
|
||||
the HID child's **instance path** (`HID\punktfunk\1&…`), which carries no `VID_…&PID_…` token; neither
|
||||
CompatibleIds nor HardwareIds change the instance path. Only a real USB-bus instance path
|
||||
(`HID\VID_054C&PID_0CE6\…`) does — i.e. a **ViGEm-style KMDF USB-emulating bus driver** (the rank-3, last
|
||||
resort). Pursue only if a target title uses GameInput AND the identity fix above doesn't satisfy it; prior
|
||||
art (HIDMaestro) shows pure user-mode pads ARE accepted by WGI/GameInput, so other parity (descriptor /
|
||||
strings / mapping) may matter more than a genuine USB bus.
|
||||
**Why this may still not satisfy GameInput / a native PS5 path:** GameInput parses VID/PID from the HID
|
||||
child's **instance path** (`HID\punktfunk\1&…`), which carries no `VID_…&PID_…` token; neither CompatibleIds
|
||||
nor HardwareIds change the instance path. Only a real USB-bus instance path (`HID\VID_054C&PID_0CE6\…`) does —
|
||||
i.e. a **ViGEm-style KMDF USB-emulating bus driver** (see fallback below). Prior art (HIDMaestro) shows pure
|
||||
user-mode pads ARE accepted by WGI/GameInput, so other parity (descriptor / strings / mapping) may matter
|
||||
more than a genuine USB bus.
|
||||
|
||||
## Next steps
|
||||
## GameInput fallback design (rank-3, only if needed)
|
||||
|
||||
> **Deployed to `.173` (2026-06-22):** the host identity fix is live in the `PunktfunkHost` service (release
|
||||
> rebuilt + restarted) and the driver fixes are installed + signed (`oem74.inf`, `punktfunk-ds-test` cert).
|
||||
> The box is ready for the decisive on-glass test. A rollback copy of the prior driver is at
|
||||
> `C:\Users\Public\giprobe\driver-backup-oem74`.
|
||||
If a target title uses **GameInput** AND the shipped identity fix above doesn't satisfy it, the last-resort
|
||||
option is a **rank-3 KMDF USB-emulating bus driver** (the way ViGEmBus presents a real-looking device)
|
||||
instead of SwDeviceCreate + UMDF-HID — it produces a genuine `HID\VID_054C&PID_0CE6\…` instance path, the one
|
||||
thing GameInput keys off. Pursue this only if required by a target title; it is heavier than the user-mode
|
||||
path and HIDMaestro suggests user-mode pads can be made acceptable to GameInput without it.
|
||||
|
||||
1. **Decisive on-glass test (only the user can run):** launch Cyberpunk 2077 with Steam Input OFF against a
|
||||
virtual DS5 carrying the new identity; check the in-game glyphs/prompt switch to DualSense. Cleanest
|
||||
single-pad test (frees the service's pad 0 so only the new-identity pad is present):
|
||||
`sc stop PunktfunkHost` → `target\debug\punktfunk-host.exe dualsense-windows-test --index 0 --seconds 600`
|
||||
(new identity + live cycling Cross/stick), launch the game; then deploy the release + restart with
|
||||
`scripts\windows\deploy-host.ps1`.
|
||||
2. **Driver-side correctness — DONE & installed (2026-06-22).** Rebuilt/resigned/reinstalled per the recipe
|
||||
below; validated live (`hidstrings` probe + `pfds-driver.log`):
|
||||
- `IOCTL_HID_GET_STRING` now implemented (was `STATUS_NOT_IMPLEMENTED`). **Discovery:** Windows polls
|
||||
this device's string slots with low-word ids **`0x0E`/`0x0F`/`0x10`** (lang `0x0409`) cyclically — NOT
|
||||
the `0/1/2` `HID_STRING_ID_*` constants. The handler maps them (+ `0/1/2` as fallbacks):
|
||||
`0x0E`→manufacturer "Sony Interactive Entertainment", `0x0F`→product "DualSense Wireless Controller",
|
||||
`0x10`→serial "35533AD6E774" (the `0x09` pairing-report MAC). Verified: `HidD_GetManufacturer/Product/
|
||||
SerialNumberString` now return those three distinct strings.
|
||||
- `DS_FEATURE_CALIBRATION` trimmed 42 → 41 bytes (1 id + 40 data) to match the descriptor's feature
|
||||
`0x05` (`0x95 0x28`).
|
||||
- The repo source (`packaging/windows/dualsense-driver/src/lib.rs`) and the m0 build copy were diverged
|
||||
by *formatting only*; they are now back in sync (the repo file was copied to m0 before building).
|
||||
3. If a GameInput-only title needs the real VID/PID → the rank-3 KMDF USB-emulating bus driver.
|
||||
## On-glass Cyberpunk verification procedure (open — only the user can run it)
|
||||
|
||||
## On-box experiment tooling (built 2026-06-22, `C:\Users\Public\giprobe\`)
|
||||
- `probe.cpp` (+`build.bat`) — GameInput enumeration/fingerprint via `LoadLibrary("GameInput.dll")` +
|
||||
`GameInputCreate`/`RegisterDeviceCallback` (GDK header). Prints each device's vid/pid/usage/counts —
|
||||
this is what proved GameInput reads our pad as vid=0.
|
||||
- `swexp.cpp` (+`build-swexp.bat`) — standalone `SwDeviceCreate` identity experiment: variations for
|
||||
`pszzCompatibleIds` (struct field) vs `DEVPKEY_Device_CompatibleIds` (pProperties — ignored),
|
||||
`pszzHardwareIds` USB ids, `pContainerId`. Create at a spare instance id, hold, inspect. Built with the
|
||||
VS18 MSVC toolchain via `vcvars64.bat`.
|
||||
- WGI probe: Windows PowerShell **5.1** WinRT projection of `RawGameController`/`Gamepad` (pump the message
|
||||
loop; subscribe `RawGameControllerAdded` to kick enumeration).
|
||||
- Parent-walk bus check: from the HID child, `DEVPKEY_Device_Parent` → that node's
|
||||
`DEVPKEY_Device_CompatibleIds`, match `^USB`/`^BTH` — mirrors HIDAPI's `hid_internal_detect_bus_type()`.
|
||||
- NOTE: the agent shell's PowerShell tool chokes on inline `@'…'@` here-strings feeding `Add-Type` (throws
|
||||
a spurious "Remove-Item on system path '/' is blocked"); write C#/scripts to a file and run them instead.
|
||||
Must run on the interactive desktop (RDP in or run locally) — WGI / RawInput / GameInput enumeration returns
|
||||
**empty from a headless SSH session** (no window/message pump); only HIDAPI works headless.
|
||||
|
||||
## How to reproduce / iterate (on `.173`)
|
||||
|
||||
### 1. Spawn a live virtual DualSense to test against
|
||||
```
|
||||
C:\Users\Public\punktfunk-native\target\debug\punktfunk-host.exe dualsense-windows-test --seconds 60
|
||||
```
|
||||
Creates `SWD\PUNKTFUNK\PF_PAD_0` (+ its HID child) and holds it, pushing a cycling input. Or just connect
|
||||
a client — the real session creates the identical device. (Build with the env `CMAKE_POLICY_VERSION_MINIMUM=3.5`.)
|
||||
|
||||
### 2. SDL3 detection oracle (already set up: `C:\Users\Public\sdltest\SDL3.dll`)
|
||||
Confirms HID-level recognition (HIDAPI). Run while a device from step 1 is live. PowerShell + C# (note:
|
||||
PS 5.1's Add-Type is C# 5 — **no** interpolated strings, **no** inline `out` vars, **no**
|
||||
`Marshal.PtrToStringUTF8`; SDL3 bools are 1 byte → `[return: MarshalAs(UnmanagedType.I1)]`):
|
||||
```powershell
|
||||
$cs = @'
|
||||
using System; using System.Runtime.InteropServices; using System.Text;
|
||||
public static class S {
|
||||
const string D = @"C:\Users\Public\sdltest\SDL3.dll";
|
||||
[DllImport(D)][return: MarshalAs(UnmanagedType.I1)] public static extern bool SDL_Init(uint f);
|
||||
[DllImport(D)] public static extern IntPtr SDL_GetJoysticks(out int c);
|
||||
[DllImport(D)] public static extern IntPtr SDL_GetJoystickNameForID(uint id);
|
||||
[DllImport(D)] public static extern ushort SDL_GetJoystickVendorForID(uint id);
|
||||
[DllImport(D)] public static extern ushort SDL_GetJoystickProductForID(uint id);
|
||||
[DllImport(D)][return: MarshalAs(UnmanagedType.I1)] public static extern bool SDL_IsGamepad(uint id);
|
||||
[DllImport(D)] public static extern IntPtr SDL_OpenGamepad(uint id);
|
||||
[DllImport(D)] public static extern int SDL_GetGamepadType(IntPtr g);
|
||||
static string U(IntPtr p){ if(p==IntPtr.Zero)return""; int n=0; while(Marshal.ReadByte(p,n)!=0)n++; byte[] b=new byte[n]; Marshal.Copy(p,b,0,n); return Encoding.UTF8.GetString(b); }
|
||||
public static string Run(){ if(!SDL_Init(0x2000))return"init fail"; System.Threading.Thread.Sleep(1500);
|
||||
int n=0; IntPtr a=SDL_GetJoysticks(out n); StringBuilder sb=new StringBuilder("joysticks: "+n+"\n");
|
||||
for(int i=0;i<n;i++){ uint id=(uint)Marshal.ReadInt32(a,i*4); bool ig=SDL_IsGamepad(id); int t=ig?SDL_GetGamepadType(SDL_OpenGamepad(id)):-1;
|
||||
sb.AppendLine(" '"+U(SDL_GetJoystickNameForID(id))+"' vid=0x"+SDL_GetJoystickVendorForID(id).ToString("x4")+" pid=0x"+SDL_GetJoystickProductForID(id).ToString("x4")+" isGamepad="+ig+" type="+t+" (PS5=6)"); }
|
||||
return sb.ToString(); }
|
||||
}
|
||||
'@
|
||||
Add-Type -TypeDefinition $cs; [S]::Run()
|
||||
```
|
||||
Expected today: it lists our device with `type=6` (PS5). That's the baseline "HID is correct".
|
||||
|
||||
## Next experiments — MUST run ON THE INTERACTIVE DESKTOP, not over SSH
|
||||
|
||||
WGI / RawInput / GameInput enumeration returns **empty from a headless SSH session** (no window/message
|
||||
pump) — only HIDAPI works headless. So these must run in the logged-in desktop session (RDP in, or run
|
||||
locally) while a DualSense session is live:
|
||||
|
||||
1. **Determine which API Cyberpunk uses and whether it sees the SWD device.** Enumerate via, separately:
|
||||
- `Windows.Gaming.Input` (`RawGameController.RawGameControllers`, `Gamepad.Gamepads`),
|
||||
- RawInput (`GetRawInputDeviceList` → filter HID gamepad usage 01/05),
|
||||
- GameInput (`GameInputCreate` → `EnumerateDevices`) — `GameInputRedistService` is installed on `.173`.
|
||||
Compare which list our `VID_054C&PID_0CE6` appears in. The one(s) it's *missing from* point at the API
|
||||
Cyberpunk uses.
|
||||
2. **If WGI/GameInput exclude it:** make the SwDeviceCreate device enumerate more like a real USB device.
|
||||
`SwDeviceCreate` takes a `pProperties` (`DEVPROPERTY[]`) array — try setting bus-type / container-id /
|
||||
compatible-IDs so the newer APIs accept it. If that's insufficient, the heavyweight option is a
|
||||
USB-emulating bus driver (the way ViGEmBus presents a real-looking device) instead of SwDeviceCreate +
|
||||
UMDF-HID.
|
||||
3. **Rule out an XInput device taking priority** (a leftover ViGEm pad, etc.).
|
||||
4. **Correctness (not the detection blocker):** `DS_FEATURE_CALIBRATION` in the driver is **42 bytes**
|
||||
but the report descriptor declares feature `0x05` as **41** (1 id + 40 data, `0x95 0x28`). Trim to 41;
|
||||
wrong calibration only affects motion, and SDL accepts the device regardless.
|
||||
|
||||
## On-box layout (`.173`, builds + tools)
|
||||
|
||||
- **Host repo / build:** `C:\Users\Public\punktfunk-native` → `cargo build -p punktfunk-host`
|
||||
(debug for `dualsense-windows-test`; `--release --features nvenc` is what the service runs). The
|
||||
build env is persisted Machine-scope (`PUNKTFUNK_NVENC_LIB_DIR`, `LIBCLANG_PATH`,
|
||||
`CMAKE_POLICY_VERSION_MINIMUM`) — see `scripts\windows\`. **One-call rebuild+redeploy of the
|
||||
service: `scripts\windows\deploy-host.ps1`** (stop → build → restart, `.bak` rollback); web:
|
||||
`scripts\windows\build-web.ps1`. bun=`C:\Users\Public\bun`, node=`C:\Users\Public\node-v22.11.0-win-x64`.
|
||||
- **Host service:** scheduled task / SCM `PunktfunkHost` runs `…\target\release\punktfunk-host.exe
|
||||
service run` → spawns `serve` (currently native-only, `PUNKTFUNK_HOST_CMD=serve` in
|
||||
`C:\ProgramData\punktfunk\host.env`). Restart: `sc stop/start PunktfunkHost`. Native port 9777, mgmt
|
||||
47990. (NB: Sunshine/Apollo conflicts on the GameStream ports — keep it stopped, or run native-only.)
|
||||
- **UMDF driver build project:** `C:\Users\Public\m0\windows-drivers-rs\examples\pf-dualsense`
|
||||
(`pf_dualsense.inx` + `src\lib.rs` live here; the canonical copies are in the repo under
|
||||
`packaging/windows/dualsense-driver/` — keep them in sync). Rebuild + reinstall recipe (e.g. after the
|
||||
calibration fix), all from that dir, env `LIBCLANG_PATH=C:\Program Files\LLVM\bin`,
|
||||
`Version_Number=10.0.26100.0`:
|
||||
1. `cargo make` → `target\debug\pf_dualsense_package\`
|
||||
2. **Clear the FORCE_INTEGRITY PE bit** (wdk-build sets `/INTEGRITYCHECK`, which blocks self-signed
|
||||
load): clear bit 0x80 at `PE_header_offset+0x5e` of `pf_dualsense.dll`, then re-sign.
|
||||
3. `signtool sign /fd SHA256 /sha1 6A52984E54376C45A1C236B1A2C8A746C5AB6131 pf_dualsense.dll`
|
||||
4. `Inf2Cat /driver:<pkg> /os:10_x64` → re-sign the `.cat` with the same thumbprint.
|
||||
5. `pnputil /delete-driver <old oemNN.inf> /uninstall /force` then `pnputil /add-driver
|
||||
pf_dualsense.inf /install`. (Self-signed cert is already trusted on `.173`; Secure Boot ON, HVCI off.)
|
||||
- **SDL oracle:** `C:\Users\Public\sdltest\SDL3.dll`. **Test device:** `punktfunk-host.exe
|
||||
dualsense-windows-test --seconds N` creates one `SWD\PUNKTFUNK\PF_PAD_0` and holds it.
|
||||
1. Free the service's pad 0 so only the new-identity pad is present:
|
||||
`sc stop PunktfunkHost`
|
||||
2. Spawn a single virtual DS5 carrying the new identity (cycles Cross/stick so input is visible):
|
||||
`target\debug\punktfunk-host.exe dualsense-windows-test --index 0 --seconds 600`
|
||||
3. Launch **Cyberpunk 2077 with Steam Input OFF** (so the game reads the raw HID). Check the in-game
|
||||
glyphs/prompt **switch to DualSense**.
|
||||
4. Restore the service afterward: redeploy the release + restart with `scripts\windows\deploy-host.ps1`.
|
||||
|
||||
## Key code
|
||||
|
||||
| What | File |
|
||||
| --- | --- |
|
||||
| Host backend (`create_swdevice`, the `Global\pfds-shm-<idx>` section, write_state/service/pump) | `crates/punktfunk-host/src/inject/dualsense_windows.rs` |
|
||||
| UMDF driver (HID descriptor, feature reports, `on_output_report`) | `packaging/windows/dualsense-driver/src/lib.rs` |
|
||||
| Shared report codec (`serialize_state` input, `parse_ds_output` feedback) | `crates/punktfunk-host/src/inject/dualsense_proto.rs` |
|
||||
| Host backend (`create_swdevice`, the `Global\pfds-shm-<idx>` section, write_state/service/pump) | `crates/punktfunk-host/src/inject/windows/dualsense_windows.rs` |
|
||||
| UMDF driver (HID descriptor, feature reports, `on_output_report`) | `packaging/windows/drivers/pf-dualsense/src/lib.rs` |
|
||||
| Shared report codec (`serialize_state` input, `parse_ds_output` feedback) | `crates/punktfunk-host/src/inject/proto/dualsense_proto.rs` |
|
||||
| Pad seam (`PadBackend`, `pump` → rumble `0xCA` / hidout `0xCD`) | `crates/punktfunk-host/src/punktfunk1.rs` |
|
||||
|
||||
## Open items
|
||||
|
||||
1. **Decisive on-glass Cyberpunk test — pending execution.** Launch Cyberpunk 2077 with Steam Input OFF
|
||||
against a virtual DS5 carrying the new identity; verify the in-game glyphs switch to DualSense (procedure
|
||||
above).
|
||||
2. **GameInput rank-3 KMDF USB-emulating bus-driver fallback — optional.** Only if a GameInput-only title
|
||||
needs the real VID/PID and the shipped SwDeviceCreate identity fix doesn't satisfy it.
|
||||
|
||||
## Facts proven (don't re-litigate)
|
||||
|
||||
- `SwDeviceCreate` requirements: enumerator must have **no underscore** (`punktfunk`); the completion
|
||||
**callback is mandatory** (NULL → E_INVALIDARG). Per-session device works; auto-removed on disconnect.
|
||||
- The identity keys must be set via the **`SW_DEVICE_CREATE_INFO` struct fields**, not `pProperties` — a
|
||||
`DEVPROPERTY` write of the PnP-owned identity keys is ignored.
|
||||
- HID descriptor + feature reports are DS5-accurate enough that **SDL identifies it as PS5**.
|
||||
- Host-side rumble works end to end; the client (macOS) rendering of `0xCA` is the open rumble bug.
|
||||
- The `IOCTL_HID_GET_STRING` and `DS_FEATURE_CALIBRATION` (42 → 41 bytes) driver gaps were fixed + shipped;
|
||||
the driver answers `HidD_GetManufacturer/Product/SerialNumberString` with distinct strings. (Detail in
|
||||
`packaging/windows/drivers/pf-dualsense/src/lib.rs`.)
|
||||
- Host-side rumble works end to end (driver captures the game's `0x02`, `parse_ds_output` extracts the
|
||||
motors, host forwards `0xCA`); the client (macOS) rendering of `0xCA` onto the physical pad is a separate
|
||||
open bug, not part of game detection.
|
||||
|
||||
Reference in New Issue
Block a user