# 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/`](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`](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: ```cmd RioGamepad\build.cmd E: ``` This sources the EWDK env (`\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-build `DrvCat` task can't load `Microsoft.Kits.Logger`, so the > `.cat` is produced separately with `inf2cat.exe` and signed with `signtool.exe` > as 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. ```cmd :: 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_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), 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).