The RioGamepad driver now installs and enumerates, so the user-mode feeder path is verifiable. Add tools/RioJoySmokeTest, a standalone on-cabinet utility that drives the real HidFeederJoystickSink (open the device, submit reports via IOCTL_RIO_SUBMIT_REPORT) and reads the gamepad back through winmm joyGetPosEx, asserting axes (min/mid/max), buttons, and the POV hat all surface to the OS. Verified: all checks pass against the installed driver. Update docs to match reality (PLAN.md predated the HidFeederJoystickSink commit): Phase 1 is now test-signed/installed/verified with the VHF LowerFilters requirement noted; the stale "NullJoystickSink placeholder" remainders in Phases 3 and 5 are corrected to reflect the wired, verified feeder. driver/README.md notes the end-to-end verification. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.7 KiB
RioGamepad — virtual HID driver
The RioGamepad virtual HID device replaces the legacy vJoy dependency. It
is a KMDF driver built on the Windows Virtual HID Framework (VHF, vhf.sys)
that presents a single HID game controller to Windows matching the layout the
old app drove through vJoy:
| Report field | Count | Notes |
|---|---|---|
| Axes | 6 | X, Y, Z, Rx, Ry, Rz — 16-bit |
| Hat switch | 1 | 4-direction POV with null state |
| Buttons | 96 | 12 bytes of button bits |
The C# tray app feeds input reports to the driver through a custom
DeviceIoControl IOCTL on the driver's control device; the driver relays them
to Windows via VhfReadReportSubmit.
Status
Phase 1 — implemented, installed, and enumerating. The driver source under
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
caller's 25-byte report to VhfReadReportSubmit. All report packing lives in the
C# client (RioJoy.Core.Hid.RioHidReport, unit-tested) so the wire format is
pinned on both sides (RioGamepad/Public.h).
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.
The user-mode feeder (RioJoy.Core.Output.HidFeederJoystickSink) is wired in and
verified end-to-end: driving it moves the six axes, 96 buttons, and POV hat,
read back through winmm joyGetPosEx / joy.cpl exactly as a game would see
them. The tray app selects it automatically when the driver is present and falls
back to a no-op sink when it is not.
Building
With the EWDK mounted (e.g. drive E:), from this folder:
RioGamepad\build.cmd E:
This sources the EWDK env (<EWDK>\BuildEnv\SetupBuildEnv.cmd) and runs MSBuild,
producing RioGamepad\x64\Release\RioGamepad.sys. The project is a WDK/MSBuild
.vcxproj; it is not part of RioJoy.sln (different toolchain).
EWDK note: the build disables the managed catalog task (
/p:DriverCatalog_Enable=false) and auto-signing (/p:SignMode=Off). On this EWDK image the in-buildDrvCattask can't loadMicrosoft.Kits.Logger, so the.catis produced separately withinf2cat.exeand signed withsigntool.exeas part of install (below), rather than during compilation.
Test-signing & install (cabinets you own)
Scripts in this folder automate the test-signing flow. Steps marked (admin) need an elevated shell; everything else is non-admin.
:: 1. Build the driver (non-admin), EWDK mounted at E:
RioGamepad\build.cmd E:
:: 2. Test-sign: makes a self-signed cert, builds + signs the .cat and .sys,
:: and exports RIOJoyTest.cer (non-admin)
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. 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. 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_CONFIGhas no string field and VHF handlesIOCTL_HID_GET_STRINGitself 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 registryOEMNamevalue keyed by VID/PID, soinstall.ps1 -CreateDevicewritesRIOJoy Virtual Gamepadto…\MediaProperties\PrivateProperties\Joystick\OEM\VID_1209&PID_5249\OEMName(under both HKLM and HKCU);uninstall.ps1removes it.joy.cplmust 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 /enumshould listtestsigning Yes), re-trust the cert and re-stage withinstall.ps1, and reboot once more — thetestsigningflag 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) butAddDevicefails. For a VHF driver this is almost alwaysVhfCreatereturningSTATUS_INVALID_DEVICE_REQUEST(0xC0000010) becausevhf.sysis not attached beneath the device as a lower filter. The INF must add it:[RioGamepad_Device.NT.HW] AddReg = RioGamepad_Device.NT.AddReg [RioGamepad_Device.NT.AddReg] HKR,, "LowerFilters", 0x00010000, "vhf"Confirm with
Get-PnpDeviceProperty -InstanceId <id> -KeyName DEVPKEY_Device_LowerFilters(should returnvhf). The exact failing NTSTATUS for any Code 31/52 is in the devnode'sDEVPKEY_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,
Phase 6).
The user-mode side opens this device by GUID_DEVINTERFACE_RIOGAMEPAD and feeds
reports via IOCTL_RIO_SUBMIT_REPORT — implemented by
RioJoy.Core.Output.HidFeederJoystickSink, which the tray app uses automatically
when the driver is present (falling back to a no-op sink when it is not).