diff --git a/driver/README.md b/driver/README.md index f9f3c7e..7f36a6f 100644 --- a/driver/README.md +++ b/driver/README.md @@ -17,7 +17,7 @@ to Windows via `VhfReadReportSubmit`. ## Status -**Phase 1 — implemented and compiling.** The driver source under +**Phase 1 — implemented, installed, and enumerating.** The driver source under [`RioGamepad/`](RioGamepad/) builds cleanly to `RioGamepad.sys` against the EWDK (KMDF 1.15 + VHF, x64). It is a thin VHF relay: it creates the virtual HID device from the report descriptor and, on each `IOCTL_RIO_SUBMIT_REPORT`, forwards the @@ -25,9 +25,12 @@ caller's 25-byte report to `VhfReadReportSubmit`. All report packing lives in th C# client (`RioJoy.Core.Hid.RioHidReport`, unit-tested) so the wire format is pinned on both sides ([`RioGamepad/Public.h`](RioGamepad/Public.h)). -Not yet done: **test-signing + install + verify in `joy.cpl`** (the on-cabinet -step), and wiring the real `DeviceIoControl` feeder sink (replacing the C# side's -`NullJoystickSink`). +Test-signed, installed, and verified on the cabinet: the devnode starts clean +(`CM_PROB_NONE`), `vhf` attaches as a lower filter, the VHF HID child enumerates, +and the controller registers under `VID_1209&PID_5249` in `joy.cpl`. + +Not yet done: wiring the real `DeviceIoControl` feeder sink (replacing the C# +side's `NullJoystickSink`). ## Building @@ -63,15 +66,65 @@ powershell -ExecutionPolicy Bypass -File sign.ps1 -Ewdk E: :: 3. (admin) Trust the cert, stage the driver, enable test signing powershell -ExecutionPolicy Bypass -File install.ps1 -Ewdk E: -:: 4. REBOOT (test signing only takes effect after a restart) +:: 4. Verify test signing is actually set before rebooting: +:: bcdedit /enum "{current}" -> must list testsigning Yes +:: (if missing, Code 52 will persist; enable with: bcdedit /set testsigning on) -:: 5. (admin) Create the device; PnP then installs the staged driver +:: 5. REBOOT (test signing only takes effect after a restart) + +:: 6. (admin) Create the device; PnP then installs the staged driver powershell -ExecutionPolicy Bypass -File install.ps1 -Ewdk E: -CreateDevice ``` Then open **`joy.cpl`** and confirm **"RIOJoy Virtual Gamepad"** appears with 6 axes / POV / 96 buttons. To roll back: `uninstall.ps1 [-DisableTestSigning] [-RemoveCert]`. +> **Friendly name in `joy.cpl`.** A VHF virtual HID device cannot supply a HID +> product string: `VHF_CONFIG` has no string field and VHF handles +> `IOCTL_HID_GET_STRING` itself rather than forwarding it to the source driver. +> Left alone, the controller shows as the generic *"Virtual HID Framework (VHF) +> HID device"*. DirectInput instead reads the display name from a registry +> `OEMName` value keyed by VID/PID, so `install.ps1 -CreateDevice` writes +> `RIOJoy Virtual Gamepad` to +> `…\MediaProperties\PrivateProperties\Joystick\OEM\VID_1209&PID_5249\OEMName` +> (under both HKLM and HKCU); `uninstall.ps1` removes it. `joy.cpl` must be +> reopened to pick up the change. + +> **Secure Boot, drive encryption, and Code 52.** Test signing does not take +> effect while **Secure Boot is enabled**, and a test-signed driver shows up in +> Device Manager as **Code 52** ("Windows cannot verify the digital signature") +> until Secure Boot is turned off in the BIOS/UEFI. **Turn off drive encryption +> (BitLocker / device encryption) *before* you disable Secure Boot.** Changing +> Secure Boot alters the platform measurements BitLocker is sealed to, so if the +> drive is still encrypted the machine boots into BitLocker recovery and demands +> the recovery key. Decrypt the system drive (or at minimum suspend BitLocker) +> first, then disable Secure Boot, then reboot. +> +> If Code 52 persists *after* Secure Boot is already disabled: confirm test +> signing is actually on (`bcdedit /enum` should list `testsigning Yes`), +> re-trust the cert and re-stage with `install.ps1`, and reboot once more — the +> `testsigning` flag only takes effect after a restart. + +> **Code 31 (`CM_PROB_FAILED_ADD`) — VHF lower filter.** A different failure +> from Code 52: here the image *loads* (signing is fine) but `AddDevice` fails. +> For a VHF driver this is almost always `VhfCreate` returning +> `STATUS_INVALID_DEVICE_REQUEST` (`0xC0000010`) because **`vhf.sys` is not +> attached beneath the device as a lower filter**. The INF must add it: +> +> ```ini +> [RioGamepad_Device.NT.HW] +> AddReg = RioGamepad_Device.NT.AddReg +> +> [RioGamepad_Device.NT.AddReg] +> HKR,, "LowerFilters", 0x00010000, "vhf" +> ``` +> +> Confirm with `Get-PnpDeviceProperty -InstanceId -KeyName DEVPKEY_Device_LowerFilters` +> (should return `vhf`). The exact failing NTSTATUS for any Code 31/52 is in the +> devnode's `DEVPKEY_Device_ProblemStatus`, and image-load rejections are logged +> under **Event Viewer → Microsoft-Windows-CodeIntegrity/Operational** and System +> event 219. + The signed package (`package/`) and the exported cert are local artifacts and are git-ignored. Redistribution beyond owned hardware would instead use Microsoft **attestation signing** via Partner Center (see [../docs/PLAN.md](../docs/PLAN.md), diff --git a/driver/RioGamepad/RioGamepad.inf b/driver/RioGamepad/RioGamepad.inf index 7f5240a..6f4e146 100644 --- a/driver/RioGamepad/RioGamepad.inf +++ b/driver/RioGamepad/RioGamepad.inf @@ -32,6 +32,15 @@ CopyFiles = RioGamepad_CopyFiles [RioGamepad_CopyFiles] RioGamepad.sys +; VHF (vhf.sys) must sit beneath this FDO as a lower filter, otherwise +; VhfCreate fails in EvtDeviceAdd with STATUS_INVALID_DEVICE_REQUEST +; (0xC0000010) and the device shows Code 31 (CM_PROB_FAILED_ADD). +[RioGamepad_Device.NT.HW] +AddReg = RioGamepad_Device.NT.AddReg + +[RioGamepad_Device.NT.AddReg] +HKR,, "LowerFilters", 0x00010000, "vhf" + [RioGamepad_Device.NT.Services] AddService = RioGamepad, 0x00000002, RioGamepad_Service diff --git a/driver/install.ps1 b/driver/install.ps1 index 2bdf337..045a3fc 100644 --- a/driver/install.ps1 +++ b/driver/install.ps1 @@ -36,6 +36,27 @@ $cer = Join-Path $scriptDir "RIOJoyTest.cer" $devgen = Join-Path $Ewdk "Program Files\Windows Kits\10\Tools\10.0.28000.0\x64\devgen.exe" $hardwareId = "root\RioGamepad" +# DirectInput / joy.cpl friendly name. A VHF virtual HID device cannot supply a +# HID product string (VHF_CONFIG has no string field and VHF owns +# IOCTL_HID_GET_STRING), so without this the controller shows as the generic +# "Virtual HID Framework (VHF) HID device". DirectInput reads the display name +# from this OEMName value, keyed by VID/PID (see RioGamepad\Public.h: +# RIO_VENDOR_ID 0x1209 / RIO_PRODUCT_ID 0x5249). +$oemVidPid = "VID_1209&PID_5249" +$friendlyName = "RIOJoy Virtual Gamepad" + +function Set-RioJoyFriendlyName { + # Write the OEMName under both HKLM (machine-wide, authoritative) and HKCU + # (immediate effect for the current user) so joy.cpl shows $friendlyName. + $rel = "SYSTEM\CurrentControlSet\Control\MediaProperties\PrivateProperties\Joystick\OEM\$oemVidPid" + foreach ($hive in @("HKLM:", "HKCU:")) { + $key = "$hive\$rel" + New-Item -Path $key -Force | Out-Null + New-ItemProperty -Path $key -Name "OEMName" -Value $friendlyName -PropertyType String -Force | Out-Null + } + Write-Host "Set joy.cpl friendly name for $oemVidPid -> '$friendlyName'." +} + if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator)) { throw "This script must be run from an elevated (Administrator) shell." @@ -45,6 +66,7 @@ if ($CreateDevice) { if (-not (Test-Path $devgen)) { throw "devgen not found: $devgen" } Write-Host "Creating device node ($hardwareId)..." & $devgen /add /instanceid "RIOJOY01" /hardwareid $hardwareId + Set-RioJoyFriendlyName Write-Host "" Write-Host "Done. Open 'joy.cpl' and look for 'RIOJoy Virtual Gamepad'." -ForegroundColor Green Write-Host "If it shows a problem, check Device Manager and 'pnputil /enum-drivers'." diff --git a/driver/sign.ps1 b/driver/sign.ps1 index ac48be4..e424de6 100644 --- a/driver/sign.ps1 +++ b/driver/sign.ps1 @@ -71,5 +71,5 @@ Write-Host "Signed package ready in: $pkg" -ForegroundColor Green Write-Host "Next: run driver\install.ps1 from an elevated shell to trust the cert," -ForegroundColor Yellow Write-Host "enable test signing, and install the device." -ForegroundColor Yellow Write-Host "" -Write-Host "(Note: 'signtool verify /pa' will fail until the cert is trusted by install.ps1 —" -ForegroundColor DarkGray +Write-Host "(Note: 'signtool verify /pa' will fail until the cert is trusted by install.ps1 -" -ForegroundColor DarkGray Write-Host " that is expected for a self-signed test driver.)" -ForegroundColor DarkGray diff --git a/driver/uninstall.ps1 b/driver/uninstall.ps1 index 227ee13..219daf1 100644 --- a/driver/uninstall.ps1 +++ b/driver/uninstall.ps1 @@ -46,6 +46,16 @@ if ($published) { Write-Host "No staged RioGamepad driver package found." } +# 3. Remove the DirectInput / joy.cpl friendly-name registration. +$joyOemRel = "SYSTEM\CurrentControlSet\Control\MediaProperties\PrivateProperties\Joystick\OEM\VID_1209&PID_5249" +foreach ($hive in @("HKLM:", "HKCU:")) { + $key = "$hive\$joyOemRel" + if (Test-Path $key) { + Write-Host "Removing joy.cpl friendly-name key: $key" + Remove-Item -Path $key -Recurse -Force -ErrorAction SilentlyContinue + } +} + if ($RemoveCert) { Write-Host "Removing test certificate from trust stores..." Get-ChildItem Cert:\LocalMachine\Root, Cert:\LocalMachine\TrustedPublisher |