feat(host/windows,drivers): gamepad driver attach/heartbeat health surfaced in logs
The gamepad drivers have no IOCTL plane (hidclass gates the stack), so until now the host had ZERO visibility into whether a driver ever bound: a pad could be "created" with no driver installed and nothing was logged. Two health fields are carved from reserved shm space (layout-compatible; pf-driver-proto pins the offsets): driver_proto — stamped by pf-xusb at device add + per serviced XInput IOCTL (movement = the game-visible path) and by pf-dualsense/DS4 from its ~125Hz timer — and driver_heartbeat. Host-side, every pad owns a DriverAttach watcher fed from the existing service() poll: INFO on attach (WARN on proto mismatch), and after 3s of silence ONE diagnosis WARN combining a cached pnputil /enum-drivers store check, the devnode's CM problem code (CM_Locate_DevNodeW/CM_Get_DevNode_Status on the instance id now captured from the create callback, with plain-language hints: 28 = not installed, 52 = signature/Memory Integrity, …) and the driver's debug log path. Also fixes a real bug both SwDeviceCreate wrappers shared: the 10s WaitForSingleObject result was ignored and the callback HRESULT zero-initialised, so a PnP timeout read as SUCCESS (now E_FAIL init + explicit timeout error). Failure-mode table: design/gamepad-driver-health.md. Linux workspace green; Windows host + drivers CI-compile only, on-box recipe at the bottom of the design doc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -229,11 +229,14 @@ static INPUT_REPORT: std::sync::Mutex<[u8; 64]> = std::sync::Mutex::new(NEUTRAL_
|
||||
// UMDF runs in WUDFHost.exe (user-mode) and hidclass blocks a control channel on the device stack
|
||||
// (custom interface CreateFile → err 31; custom IOCTL on the HID handle → err 1) and UMDF has no
|
||||
// control device, so the host channel is a named section the (privileged) host CREATES and the driver
|
||||
// OPENS. Layout (256 B): magic u32 @0 ("PFDS"), input_seq u32 @4, input_report[64] @8,
|
||||
// output_seq u32 @72, output_report[64] @76.
|
||||
// OPENS. Layout (256 B, must match pf_driver_proto::gamepad::PadShm): magic u32 @0 ("PFDS"),
|
||||
// input_seq u32 @4, input_report[64] @8, output_seq u32 @72, output_report[64] @76,
|
||||
// device_type u8 @140, driver_proto u32 @144 (we stamp GAMEPAD_PROTO_VERSION = the host's
|
||||
// driver-attach health signal), driver_heartbeat u32 @148 (we bump per timer tick = liveness).
|
||||
const FILE_MAP_RW: u32 = 0x0002 | 0x0004; // FILE_MAP_WRITE | FILE_MAP_READ
|
||||
const SHM_MAGIC: u32 = 0x5046_4453; // "PFDS" little-endian
|
||||
const SHM_SIZE: usize = 256;
|
||||
const GAMEPAD_PROTO_VERSION: u32 = 1; // must match pf_driver_proto::gamepad::GAMEPAD_PROTO_VERSION
|
||||
static LOGGED_SHM: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false);
|
||||
|
||||
// kernel32 file-mapping APIs (resolved via std's kernel32 import; UMDF permits file mapping).
|
||||
@@ -770,6 +773,15 @@ extern "C" fn evt_timer(timer: WDFTIMER) {
|
||||
*g = buf;
|
||||
}
|
||||
}
|
||||
// Health marks the host watches: driver_proto @144 (attach signal, idempotent) and
|
||||
// driver_heartbeat @148 (+1 per ~8 ms tick = liveness). Lets the host tell "driver bound
|
||||
// and alive" apart from "driver package missing/failed to bind".
|
||||
// SAFETY: view points at a mapped 256-byte section; proto @144, heartbeat @148.
|
||||
unsafe {
|
||||
core::ptr::write_unaligned(view.add(144) as *mut u32, GAMEPAD_PROTO_VERSION);
|
||||
let hb = view.add(148) as *mut u32;
|
||||
core::ptr::write_unaligned(hb, core::ptr::read_unaligned(hb).wrapping_add(1));
|
||||
}
|
||||
});
|
||||
// SAFETY: timer valid; parent is the manual queue.
|
||||
let queue =
|
||||
|
||||
Reference in New Issue
Block a user