/*++ RioGamepad — device creation, VHF setup, and the report-submit IOCTL. The driver is a thin relay: it creates a virtual HID device via VHF using the report descriptor in ReportDescriptor.h, exposes a device interface, and on each IOCTL_RIO_SUBMIT_REPORT forwards the caller's report bytes to the HID stack with VhfReadReportSubmit. All report packing lives in the user-mode client. --*/ #include #include // makes DEFINE_GUID in Public.h allocate the GUID storage #include "Driver.h" #include "ReportDescriptor.h" NTSTATUS RioGamepadEvtDeviceAdd( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit ) { NTSTATUS status; WDFDEVICE device; WDF_OBJECT_ATTRIBUTES deviceAttributes; WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; WDF_IO_QUEUE_CONFIG queueConfig; PDEVICE_CONTEXT deviceContext; VHF_CONFIG vhfConfig; UNREFERENCED_PARAMETER(Driver); WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); pnpPowerCallbacks.EvtDeviceD0Entry = RioGamepadEvtDeviceD0Entry; WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT); deviceAttributes.EvtCleanupCallback = RioGamepadEvtDeviceCleanup; status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device); if (!NT_SUCCESS(status)) { return status; } deviceContext = DeviceGetContext(device); deviceContext->VhfHandle = NULL; status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_RIOGAMEPAD, NULL); if (!NT_SUCCESS(status)) { return status; } // // Default parallel queue for the report-submit IOCTL. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); queueConfig.EvtIoDeviceControl = RioGamepadEvtIoDeviceControl; status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, NULL); if (!NT_SUCCESS(status)) { return status; } // // Create the virtual HID device. VhfStart is deferred to D0Entry. // VHF_CONFIG_INIT( &vhfConfig, WdfDeviceWdmGetDeviceObject(device), (USHORT)sizeof(g_RioReportDescriptor), (PUCHAR)g_RioReportDescriptor); vhfConfig.VendorID = RIO_VENDOR_ID; vhfConfig.ProductID = RIO_PRODUCT_ID; vhfConfig.VersionNumber = RIO_VERSION; status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle); if (!NT_SUCCESS(status)) { deviceContext->VhfHandle = NULL; return status; } return STATUS_SUCCESS; } NTSTATUS RioGamepadEvtDeviceD0Entry( _In_ WDFDEVICE Device, _In_ WDF_POWER_DEVICE_STATE PreviousState ) { PDEVICE_CONTEXT deviceContext = DeviceGetContext(Device); UNREFERENCED_PARAMETER(PreviousState); return VhfStart(deviceContext->VhfHandle); } VOID RioGamepadEvtDeviceCleanup( _In_ WDFOBJECT Object ) { PDEVICE_CONTEXT deviceContext = DeviceGetContext((WDFDEVICE)Object); if (deviceContext->VhfHandle != NULL) { VhfDelete(deviceContext->VhfHandle, TRUE); deviceContext->VhfHandle = NULL; } } VOID RioGamepadEvtIoDeviceControl( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) { NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; PDEVICE_CONTEXT deviceContext = DeviceGetContext(WdfIoQueueGetDevice(Queue)); UNREFERENCED_PARAMETER(OutputBufferLength); if (IoControlCode == IOCTL_RIO_SUBMIT_REPORT) { if (InputBufferLength != RIO_REPORT_SIZE) { status = STATUS_INVALID_BUFFER_SIZE; } else { PVOID buffer; size_t bufferSize; status = WdfRequestRetrieveInputBuffer(Request, RIO_REPORT_SIZE, &buffer, &bufferSize); if (NT_SUCCESS(status)) { HID_XFER_PACKET packet; packet.reportId = 0; packet.reportBuffer = (PUCHAR)buffer; packet.reportBufferLen = RIO_REPORT_SIZE; status = VhfReadReportSubmit(deviceContext->VhfHandle, &packet); } } } WdfRequestComplete(Request, status); }