/* * Virtual Gamepad Emulation Framework - Windows kernel-mode bus driver * * MIT License * * Copyright (c) 2016-2019 Nefarius Software Solutions e.U. and Contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "busenum.h" #include #include #include "buspdo.tmh" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, Bus_CreatePdo) #pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo) #pragma alloc_text(PAGE, Pdo_EvtDevicePrepareHardware) #endif NTSTATUS Bus_EvtDeviceListCreatePdo( WDFCHILDLIST DeviceList, PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, PWDFDEVICE_INIT ChildInit) { PPDO_IDENTIFICATION_DESCRIPTION pDesc; PAGED_CODE(); pDesc = CONTAINING_RECORD(IdentificationDescription, PDO_IDENTIFICATION_DESCRIPTION, Header); return Bus_CreatePdo(WdfChildListGetDevice(DeviceList), ChildInit, pDesc); } // // Compares two children on the bus based on their serial numbers. // BOOLEAN Bus_EvtChildListIdentificationDescriptionCompare( WDFCHILDLIST DeviceList, PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription, PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription) { PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs; UNREFERENCED_PARAMETER(DeviceList); lhs = CONTAINING_RECORD(FirstIdentificationDescription, PDO_IDENTIFICATION_DESCRIPTION, Header); rhs = CONTAINING_RECORD(SecondIdentificationDescription, PDO_IDENTIFICATION_DESCRIPTION, Header); return (lhs->SerialNo == rhs->SerialNo) ? TRUE : FALSE; } // // Creates and initializes a PDO (child). // NTSTATUS Bus_CreatePdo( _In_ WDFDEVICE Device, _In_ PWDFDEVICE_INIT DeviceInit, _In_ PPDO_IDENTIFICATION_DESCRIPTION Description) { NTSTATUS status; PPDO_DEVICE_DATA pdoData; WDFDEVICE hChild = NULL; WDF_DEVICE_PNP_CAPABILITIES pnpCaps; WDF_DEVICE_POWER_CAPABILITIES powerCaps; WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; WDF_OBJECT_ATTRIBUTES pdoAttributes; WDF_IO_QUEUE_CONFIG defaultPdoQueueConfig; WDFQUEUE defaultPdoQueue; UNICODE_STRING deviceDescription; VIGEM_BUS_INTERFACE busInterface; WDF_OBJECT_ATTRIBUTES attributes; WDF_IO_QUEUE_CONFIG usbInQueueConfig; WDF_IO_QUEUE_CONFIG notificationsQueueConfig; DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Virtual Gamepad Emulation Bus"); DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN); // reserve space for device id DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN); PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); // // Get the FDO interface ASAP to report progress to bus // status = WdfFdoQueryForInterface(Device, &GUID_VIGEM_INTERFACE_PDO, (PINTERFACE)&busInterface, sizeof(VIGEM_BUS_INTERFACE), VIGEM_BUS_INTERFACE_VERSION, NULL); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfFdoQueryForInterface failed with status %!STATUS!", status); return status; } // set device type WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER); // Bus is power policy owner WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE); #pragma region Enter RAW device mode status = WdfPdoInitAssignRawDevice(DeviceInit, &GUID_DEVCLASS_VIGEM_RAWPDO); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfPdoInitAssignRawDevice failed with status %!STATUS!", status); goto endCreatePdo; } WdfDeviceInitSetCharacteristics(DeviceInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE); status = WdfDeviceInitAssignSDDLString(DeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfDeviceInitAssignSDDLString failed with status %!STATUS!", status); goto endCreatePdo; } #pragma endregion #pragma region Prepare PDO // set parameters matching desired target device switch (Description->TargetType) { // // A Xbox 360 device was requested // case Xbox360Wired: status = Xusb_PreparePdo( DeviceInit, Description->VendorId, Description->ProductId, &deviceId, &deviceDescription); if (!NT_SUCCESS(status)) goto endCreatePdo; break; // // A Sony DualShock 4 device was requested // case DualShock4Wired: status = Ds4_PreparePdo(DeviceInit, &deviceId, &deviceDescription); if (!NT_SUCCESS(status)) goto endCreatePdo; break; // // A Xbox One device was requested // case XboxOneWired: status = Xgip_PreparePdo(DeviceInit, &deviceId, &deviceDescription); if (!NT_SUCCESS(status)) goto endCreatePdo; break; default: status = STATUS_INVALID_PARAMETER; TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "Unknown target type: %d (%!STATUS!)", Description->TargetType, status); goto endCreatePdo; } // set device id status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfPdoInitAssignDeviceID failed with status %!STATUS!", status); goto endCreatePdo; } // prepare instance id status = RtlUnicodeStringPrintf(&buffer, L"%02d", Description->SerialNo); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "RtlUnicodeStringPrintf failed with status %!STATUS!", status); goto endCreatePdo; } // set instance id status = WdfPdoInitAssignInstanceID(DeviceInit, &buffer); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfPdoInitAssignInstanceID failed with status %!STATUS!", status); goto endCreatePdo; } // set device description (for English operating systems) status = WdfPdoInitAddDeviceText(DeviceInit, &deviceDescription, &deviceLocation, 0x409); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfPdoInitAddDeviceText failed with status %!STATUS!", status); goto endCreatePdo; } // default locale is English // TODO: add more locales WdfPdoInitSetDefaultLocale(DeviceInit, 0x409); #pragma endregion #pragma region PNP/Power event callbacks WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); pnpPowerCallbacks.EvtDevicePrepareHardware = Pdo_EvtDevicePrepareHardware; WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); #pragma endregion // NOTE: not utilized at the moment WdfPdoInitAllowForwardingRequestToParent(DeviceInit); #pragma region Create PDO // Add common device data context WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_DEVICE_DATA); status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfDeviceCreate failed with status %!STATUS!", status); goto endCreatePdo; } TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "Created PDO 0x%p", hChild); switch (Description->TargetType) { // Add XUSB-specific device data context case Xbox360Wired: { PXUSB_DEVICE_DATA xusbData = NULL; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XUSB_DEVICE_DATA); status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xusbData); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfObjectAllocateContext failed with status %!STATUS!", status); goto endCreatePdo; } break; } case DualShock4Wired: { PDS4_DEVICE_DATA ds4Data = NULL; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, DS4_DEVICE_DATA); status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&ds4Data); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfObjectAllocateContext failed with status %!STATUS!", status); goto endCreatePdo; } break; } case XboxOneWired: { PXGIP_DEVICE_DATA xgipData = NULL; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XGIP_DEVICE_DATA); status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xgipData); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfObjectAllocateContext failed with status %!STATUS!", status); goto endCreatePdo; } break; } default: break; } #pragma endregion #pragma region Expose USB Interface status = WdfDeviceCreateDeviceInterface(Device, (LPGUID)&GUID_DEVINTERFACE_USB_DEVICE, NULL); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfDeviceCreateDeviceInterface failed with status %!STATUS!", status); goto endCreatePdo; } #pragma endregion #pragma region Set PDO contexts pdoData = PdoGetData(hChild); pdoData->BusInterface = busInterface; pdoData->SerialNo = Description->SerialNo; pdoData->TargetType = Description->TargetType; pdoData->OwnerProcessId = Description->OwnerProcessId; pdoData->VendorId = Description->VendorId; pdoData->ProductId = Description->ProductId; TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "PDO Context properties: serial = %d, type = %d, pid = %d, vid = 0x%04X, pid = 0x%04X", pdoData->SerialNo, pdoData->TargetType, pdoData->OwnerProcessId, pdoData->VendorId, pdoData->ProductId); // Initialize additional contexts (if available) switch (Description->TargetType) { case Xbox360Wired: status = Xusb_AssignPdoContext(hChild); break; case DualShock4Wired: status = Ds4_AssignPdoContext(hChild, Description); break; case XboxOneWired: status = Xgip_AssignPdoContext(hChild); break; default: break; } if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "Couldn't initialize additional contexts: %!STATUS!", status); goto endCreatePdo; } #pragma endregion #pragma region Create Queues & Locks WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = hChild; // Create and assign queue for incoming interrupt transfer WDF_IO_QUEUE_CONFIG_INIT(&usbInQueueConfig, WdfIoQueueDispatchManual); status = WdfIoQueueCreate(hChild, &usbInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingUsbInRequests); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfIoQueueCreate (PendingUsbInRequests) failed with status %!STATUS!", status); goto endCreatePdo; } // Create and assign queue for user-land notification requests WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual); status = WdfIoQueueCreate(Device, ¬ificationsQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingNotificationRequests); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfIoQueueCreate (PendingNotificationRequests) failed with status %!STATUS!", status); goto endCreatePdo; } #pragma endregion #pragma region Default I/O queue setup WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&defaultPdoQueueConfig, WdfIoQueueDispatchParallel); defaultPdoQueueConfig.EvtIoInternalDeviceControl = Pdo_EvtIoInternalDeviceControl; status = WdfIoQueueCreate(hChild, &defaultPdoQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &defaultPdoQueue); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_BUSPDO, "WdfIoQueueCreate (Default) failed with status %!STATUS!", status); goto endCreatePdo; } #pragma endregion #pragma region PNP capabilities WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); pnpCaps.Removable = WdfTrue; pnpCaps.EjectSupported = WdfTrue; pnpCaps.SurpriseRemovalOK = WdfTrue; pnpCaps.Address = Description->SerialNo; pnpCaps.UINumber = Description->SerialNo; WdfDeviceSetPnpCapabilities(hChild, &pnpCaps); #pragma endregion #pragma region Power capabilities WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps); powerCaps.DeviceD1 = WdfTrue; powerCaps.WakeFromD1 = WdfTrue; powerCaps.DeviceWake = PowerDeviceD1; powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0; powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1; powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD3; powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD3; powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3; powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3; WdfDeviceSetPowerCapabilities(hChild, &powerCaps); #pragma endregion endCreatePdo: TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "BUS_PDO_REPORT_STAGE_RESULT Stage: ViGEmPdoCreate [serial: %d, status: %!STATUS!]", Description->SerialNo, status); BUS_PDO_REPORT_STAGE_RESULT(busInterface, ViGEmPdoCreate, Description->SerialNo, status); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); return status; } // // PDO power-up. // NTSTATUS Pdo_EvtDevicePrepareHardware( _In_ WDFDEVICE Device, _In_ WDFCMRESLIST ResourcesRaw, _In_ WDFCMRESLIST ResourcesTranslated ) { PPDO_DEVICE_DATA pdoData; NTSTATUS status = STATUS_UNSUCCESSFUL; PAGED_CODE(); UNREFERENCED_PARAMETER(ResourcesRaw); UNREFERENCED_PARAMETER(ResourcesTranslated); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry"); pdoData = PdoGetData(Device); switch (pdoData->TargetType) { // Expose XUSB interfaces case Xbox360Wired: status = Xusb_PrepareHardware(Device); break; case DualShock4Wired: status = Ds4_PrepareHardware(Device); break; case XboxOneWired: status = Xgip_PrepareHardware(Device); break; default: break; } TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "BUS_PDO_REPORT_STAGE_RESULT Stage: ViGEmPdoCreate [serial: %d, status: %!STATUS!]", pdoData->SerialNo, status); BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoPrepareHardware, pdoData->SerialNo, status); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); return status; } // // Responds to IRP_MJ_INTERNAL_DEVICE_CONTROL requests sent to PDO. // VOID Pdo_EvtIoInternalDeviceControl( _In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode ) { // Regular buffers not used in USB communication UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); NTSTATUS status = STATUS_INVALID_PARAMETER; WDFDEVICE hDevice; PIRP irp; PURB urb; PPDO_DEVICE_DATA pdoData; PIO_STACK_LOCATION irpStack; PXUSB_DEVICE_DATA pXusbData; PUCHAR blobBuffer; TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "%!FUNC! Entry"); hDevice = WdfIoQueueGetDevice(Queue); pdoData = PdoGetData(hDevice); // No help from the framework available from here on irp = WdfRequestWdmGetIrp(Request); irpStack = IoGetCurrentIrpStackLocation(irp); switch (IoControlCode) { case IOCTL_INTERNAL_USB_SUBMIT_URB: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> IOCTL_INTERNAL_USB_SUBMIT_URB"); urb = (PURB)URB_FROM_IRP(irp); switch (urb->UrbHeader.Function) { case URB_FUNCTION_CONTROL_TRANSFER: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_CONTROL_TRANSFER"); switch (urb->UrbControlTransfer.SetupPacket[6]) { case 0x04: if (pdoData->TargetType == Xbox360Wired) { pXusbData = XusbGetData(hDevice); blobBuffer = WdfMemoryGetBuffer(pXusbData->InterruptBlobStorage, NULL); // // Xenon magic // RtlCopyMemory( urb->UrbControlTransfer.TransferBuffer, &blobBuffer[XUSB_BLOB_07_OFFSET], 0x04 ); status = STATUS_SUCCESS; } break; case 0x14: // // This is some weird USB 1.0 condition and _must fail_ // urb->UrbControlTransfer.Hdr.Status = USBD_STATUS_STALL_PID; status = STATUS_UNSUCCESSFUL; break; case 0x08: // // This is some weird USB 1.0 condition and _must fail_ // urb->UrbControlTransfer.Hdr.Status = USBD_STATUS_STALL_PID; status = STATUS_UNSUCCESSFUL; break; default: status = STATUS_SUCCESS; break; } break; case URB_FUNCTION_CONTROL_TRANSFER_EX: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_CONTROL_TRANSFER_EX"); status = STATUS_UNSUCCESSFUL; break; case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER"); status = UsbPdo_BulkOrInterruptTransfer(urb, hDevice, Request); break; case URB_FUNCTION_SELECT_CONFIGURATION: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_SELECT_CONFIGURATION"); status = UsbPdo_SelectConfiguration(urb, pdoData); break; case URB_FUNCTION_SELECT_INTERFACE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_SELECT_INTERFACE"); status = UsbPdo_SelectInterface(urb, pdoData); break; case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE"); switch (urb->UrbControlDescriptorRequest.DescriptorType) { case USB_DEVICE_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> >> USB_DEVICE_DESCRIPTOR_TYPE"); status = UsbPdo_GetDeviceDescriptorType(urb, pdoData); break; case USB_CONFIGURATION_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE"); status = UsbPdo_GetConfigurationDescriptorType(urb, pdoData); break; case USB_STRING_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> >> USB_STRING_DESCRIPTOR_TYPE"); status = UsbPdo_GetStringDescriptorType(urb, pdoData); break; case USB_INTERFACE_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> >> USB_INTERFACE_DESCRIPTOR_TYPE"); break; case USB_ENDPOINT_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> >> USB_ENDPOINT_DESCRIPTOR_TYPE"); break; default: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> >> Unknown descriptor type"); break; } TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "<< <<"); break; case URB_FUNCTION_GET_STATUS_FROM_DEVICE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE"); // Defaults always succeed status = STATUS_SUCCESS; break; case URB_FUNCTION_ABORT_PIPE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_ABORT_PIPE"); status = UsbPdo_AbortPipe(hDevice); break; case URB_FUNCTION_CLASS_INTERFACE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_CLASS_INTERFACE"); status = UsbPdo_ClassInterface(urb, hDevice, pdoData); break; case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE"); status = UsbPdo_GetDescriptorFromInterface(urb, pdoData); // // The DS4 is basically ready to operate at this stage // if (pdoData->TargetType == DualShock4Wired) { // // Report back to FDO that we are ready to operate // BUS_PDO_REPORT_STAGE_RESULT( pdoData->BusInterface, ViGEmPdoInitFinished, pdoData->SerialNo, STATUS_SUCCESS ); } break; default: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> >> Unknown function: 0x%X", urb->UrbHeader.Function); break; } TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "<<"); break; case IOCTL_INTERNAL_USB_GET_PORT_STATUS: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> IOCTL_INTERNAL_USB_GET_PORT_STATUS"); // We report the (virtual) port as always active *(unsigned long *)irpStack->Parameters.Others.Argument1 = USBD_PORT_ENABLED | USBD_PORT_CONNECTED; status = STATUS_SUCCESS; break; case IOCTL_INTERNAL_USB_RESET_PORT: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> IOCTL_INTERNAL_USB_RESET_PORT"); // Sure, why not ;) status = STATUS_SUCCESS; break; case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION"); // TODO: implement // This happens if the I/O latency is too high so HIDUSB aborts communication. status = STATUS_SUCCESS; break; default: TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, ">> Unknown I/O control code 0x%X", IoControlCode); break; } if (status != STATUS_PENDING) { WdfRequestComplete(Request, status); } TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); }