Phase 8B: RioGamepadXP.sys — WDM HID minidriver for Windows XP

The XP flavor of our own virtual joystick (no third-party driver), built
with WDK 7.1.0 to x86/subsystem-5.01. Exposes the identical Public.h
contract as the modern KMDF+VHF driver — same GUID, IOCTL_RIO_SUBMIT_REPORT,
25-byte report, VID/PID — so HidFeederJoystickSink drives both.

Design (driver/RioGamepadXP/rioxp.c):
- WDM HID minidriver via HidRegisterMinidriver presenting the 6-axis/hat/
  96-button joystick. POLLED mode (DevicesArePolled=TRUE): hidclass paces
  IOCTL_HID_READ_REPORT and we complete each synchronously from a cached
  report — no pending-IRP or cancel-routine machinery (the usual crash
  surface in a virtual HID driver).
- Named sideband control device (\Device\RioGamepadXP + \DosDevices symlink)
  takes IOCTL_RIO_SUBMIT_REPORT and updates the cache under a spinlock.
- hidclass overwrites our CREATE/CLOSE/DEVICE_CONTROL during registration;
  we save its pointers and reinstall wrappers that route the control device's
  IRPs to us and forward the HID FDO's to hidclass.

Packaging/install:
- Root-enumerated, so install uses devcon (built from the same WDK) —
  InstallHinfSection can't create the devnode. install-core.bat runs
  "devcon install RioGamepadXP.inf root\RioGamepadXP"; unsigned is fine
  (XP x86 enforces no kernel signing). Bundled into vendor\xp\.
- net40 feeder opens the driver by name (\.\RioGamepadXP); XP can't put a
  device interface on a bare control device (no PDO) — the one nuance.

Static build only: compiles clean but runtime bring-up (joy.cpl enumeration,
report flow) needs a real XP target — that's 8E. 275 tests still green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-17 14:01:16 -05:00
co-authored by Claude Fable 5
parent 2ecb617c09
commit 52712f8409
12 changed files with 613 additions and 25 deletions
+24 -20
View File
@@ -322,26 +322,30 @@ XP consumes pre-rendered wallpapers.
(`ViGEmJoystickSink`, `HidFeederJoystickSink`, `Hid/`).
*Risk fallback:* if Bcl.Async misbehaves on real XP, the receive loop
reverts to a dedicated thread (the legacy `CommWatchProc` shape) for net40.
- **8B — RioGamepadXP.sys, the XP flavor of our own driver.** Third-party
virtual-joystick drivers are ruled out (decided: **no vJoy** — the project
is unmaintained; PPJoy likewise). Instead, rebuild the thin driver side of
our existing split for XP: a **WDM HID minidriver** in the shape of the
DDK `vhidmini` sample (`HidRegisterMinidriver`, x86), exposing the **same
`Public.h` contract** as the modern driver — identical
`IOCTL_RIO_SUBMIT_REPORT`, identical 25-byte report, same descriptor
(6×16-bit axes, hat, 96 buttons) — so the existing `HidFeederJoystickSink`
drives it unchanged (only the device path differs). Precedent: the
original FASA `tasgame.sys` was exactly an XP HID minidriver
(docs/Win32RIO/, analyzed); ours stays thin with serial in user mode.
Toolchain: **WDK 7.1.0** (last XP-capable kit) under `driver/RioGamepadXP/`;
XP x86 enforces no kernel signing, so install is just the INF — none of
the Phase 1 test-signing/Secure Boot friction exists there.
Acquisition order (decided): **the Xbox 360 pad (ViGEm) stays preferred
on 10/11** — net48: ViGEm → RioGamepad → Null (unchanged);
net40: RioGamepadXP → Null.
*Staging:* the XP app is useful before the driver lands — milestone 1
ships keyboard/mouse + lamps + plasma (joystick = Null sink), the driver
follows as milestone 2.
- **8B — RioGamepadXP.sys — built ✅ (static; XP bring-up pending in 8E).**
Source in [`driver/RioGamepadXP/`](../driver/RioGamepadXP/): a WDM HID
minidriver (`HidRegisterMinidriver`, **polled mode** so there's no pending-
IRP/cancel machinery) presenting the 6-axis/hat/96-button joystick, plus a
named sideband control device (`\\.\RioGamepadXP`) that takes
`IOCTL_RIO_SUBMIT_REPORT` — the **identical `Public.h` contract** as the
modern driver (same 25-byte report, VID/PID). A dispatch wrapper routes the
control device's CREATE/CLOSE/DEVICE_CONTROL to us and forwards the HID FDO's
to hidclass. Builds with WDK 7.1.0 (`build.cmd``setenv … fre x86 WXP
no_oacr`) to `RioGamepadXP.sys` (x86, subsystem 5.01). XP enforces no kernel
signing, so install is unsigned; the device is root-enumerated so it needs
**devcon** (`devcon install RioGamepadXP.inf root\RioGamepadXP`, also built
from the WDK) rather than InstallHinfSection. The net40 feeder opens the
driver by name (`#if NET40` branch in `HidFeederJoystickSink`); XP can't
register a device interface on a bare control device (no PDO), the one
contract nuance. Bundled into the package's `vendor\xp\`. Precedent: the
original FASA `tasgame.sys` was itself an XP HID minidriver (docs/Win32RIO/,
analyzed); ours stays thin with serial in user mode. No third-party virtual
joystick (vJoy/PPJoy unmaintained). Acquisition order: net48 ViGEm →
RioGamepad → Null (unchanged); net40 RioGamepadXP → Null. ⏳ **8E:** the
driver compiles clean but is **unverified at runtime** — needs an XP target
to confirm joy.cpl enumeration + report flow. *Staging:* the app is useful
before the driver — milestone 1 ships keyboard/mouse + lamps + plasma
(joystick = Null sink) if the driver isn't present.
- **8C — Tray on net40/x86 — done ✅:** multi-target `RioJoy.Tray` (net40 drops the
ViGEm + RioJoy.Overlay references); gate `WallpaperMakerForm` + overlay
generation; `WallpaperApplier` converts PNG→BMP via GDI+ before