diff --git a/sys/Context.h b/sys/Context.h index 7174f16..8c1bf55 100644 --- a/sys/Context.h +++ b/sys/Context.h @@ -28,49 +28,7 @@ #pragma once -// -// Used to identify children in the device list of the bus. -// -typedef struct _PDO_IDENTIFICATION_DESCRIPTION -{ - WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header - // - // Unique serial number of the device on the bus - // - ULONG SerialNo; - - // - // PID of the process creating this PDO - // - DWORD OwnerProcessId; - - // - // Device type this PDO is emulating - // - VIGEM_TARGET_TYPE TargetType; - - // - // If set, the vendor ID the emulated device is reporting - // - USHORT VendorId; - - // - // If set, the product ID the emulated device is reporting - // - USHORT ProductId; - - // - // Is the current device owner another driver? - // - BOOLEAN OwnerIsDriver; - - // - // SessionId associated with file handle. Used to map file handles to emulated gamepad devices - // - LONG SessionId; - -} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION; // // The PDO device-extension (context). diff --git a/sys/Driver.c b/sys/Driver.cpp similarity index 59% rename from sys/Driver.c rename to sys/Driver.cpp index 726e604..ca5ae77 100644 --- a/sys/Driver.c +++ b/sys/Driver.cpp @@ -26,8 +26,8 @@ #include "busenum.h" -#include #include "driver.tmh" +#include #ifdef ALLOC_PRAGMA #pragma alloc_text (INIT, DriverEntry) @@ -35,10 +35,21 @@ #pragma alloc_text (PAGE, Bus_DeviceFileCreate) #pragma alloc_text (PAGE, Bus_FileClose) #pragma alloc_text (PAGE, Bus_EvtDriverContextCleanup) -#pragma alloc_text (PAGE, Bus_PdoStageResult) #endif +#include "EmulationTargetPDO.hpp" +#include "XusbPdo.hpp" +#include "Ds4Pdo.hpp" + +using ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION; +using ViGEm::Bus::Core::EmulationTargetPDO; +using ViGEm::Bus::Targets::EmulationTargetXUSB; +using ViGEm::Bus::Targets::EmulationTargetDS4; + + +EXTERN_C_START + // // Driver entry routine. // @@ -94,12 +105,7 @@ NTSTATUS Bus_EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) WDF_FILEOBJECT_CONFIG foConfig; WDF_OBJECT_ATTRIBUTES fdoAttributes; WDF_OBJECT_ATTRIBUTES fileHandleAttributes; - WDF_OBJECT_ATTRIBUTES collectionAttributes; - WDF_OBJECT_ATTRIBUTES timerAttributes; PFDO_DEVICE_DATA pFDOData; - VIGEM_BUS_INTERFACE busInterface; - PINTERFACE interfaceHeader; - WDF_TIMER_CONFIG reqTimerCfg; UNREFERENCED_PARAMETER(Driver); @@ -162,104 +168,11 @@ NTSTATUS Bus_EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit) #pragma endregion -#pragma region Create pending requests collection & lock - - WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttributes); - collectionAttributes.ParentObject = device; - - status = WdfCollectionCreate(&collectionAttributes, &pFDOData->PendingPluginRequests); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DRIVER, - "WdfCollectionCreate failed with status %!STATUS!", - status); - return STATUS_UNSUCCESSFUL; - } - - WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttributes); - collectionAttributes.ParentObject = device; - - status = WdfSpinLockCreate(&collectionAttributes, &pFDOData->PendingPluginRequestsLock); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DRIVER, - "WdfSpinLockCreate failed with status %!STATUS!", - status); - return STATUS_UNSUCCESSFUL; - } - -#pragma endregion - -#pragma region Create timer for sweeping up orphaned requests - - WDF_TIMER_CONFIG_INIT_PERIODIC( - &reqTimerCfg, - Bus_PlugInRequestCleanUpEvtTimerFunc, - ORC_TIMER_START_DELAY - ); - WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); - timerAttributes.ParentObject = device; - - status = WdfTimerCreate(&reqTimerCfg, &timerAttributes, &pFDOData->PendingPluginRequestsCleanupTimer); - if (!NT_SUCCESS(status)) { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DRIVER, - "WdfTimerCreate failed with status %!STATUS!", - status); - return status; - } - -#pragma endregion - -#pragma region Add query interface - - // - // Set up the common interface header - // - interfaceHeader = &busInterface.InterfaceHeader; - - interfaceHeader->Size = sizeof(VIGEM_BUS_INTERFACE); - interfaceHeader->Version = VIGEM_BUS_INTERFACE_VERSION; - interfaceHeader->Context = (PVOID)device; - - // - // We don't pay any particular attention to the reference - // counting of this interface, but we MUST specify routines for - // it. Luckily the framework provides dummy routines - // - interfaceHeader->InterfaceReference = WdfDeviceInterfaceReferenceNoOp; - interfaceHeader->InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; - - busInterface.BusPdoStageResult = Bus_PdoStageResult; - - WDF_QUERY_INTERFACE_CONFIG queryInterfaceConfig; - - WDF_QUERY_INTERFACE_CONFIG_INIT(&queryInterfaceConfig, - interfaceHeader, - &GUID_VIGEM_INTERFACE_PDO, - WDF_NO_EVENT_CALLBACK); - - status = WdfDeviceAddQueryInterface(device, - &queryInterfaceConfig); - - if (!NT_SUCCESS(status)) { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DRIVER, - "WdfDeviceAddQueryInterface failed with status %!STATUS!", - status); - return(status); - } - -#pragma endregion - #pragma region Create default I/O queue for FDO WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl; - queueConfig.EvtIoInternalDeviceControl = Bus_EvtIoInternalDeviceControl; queueConfig.EvtIoDefault = Bus_EvtIoDefault; __analysis_assume(queueConfig.EvtIoStop != 0); @@ -442,21 +355,20 @@ Bus_FileClose( break; } - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_DRIVER, - "PDO properties: status = %!STATUS!, pdoPID = %d, curPID = %d, pdoSID = %d, curSID = %d, internal = %d", - (int)childInfo.Status, - (int)description.OwnerProcessId, - (int)CURRENT_PROCESS_ID(), - (int)description.SessionId, - (int)pFileData->SessionId, - (int)description.OwnerIsDriver - ); + //TraceEvents(TRACE_LEVEL_VERBOSE, + // TRACE_DRIVER, + // "PDO properties: status = %!STATUS!, pdoPID = %d, curPID = %d, pdoSID = %d, curSID = %d, internal = %d", + // (int)childInfo.Status, + // (int)description.OwnerProcessId, + // (int)CURRENT_PROCESS_ID(), + // (int)description.SessionId, + // (int)pFileData->SessionId, + // (int)description.OwnerIsDriver + //); // Only unplug devices with matching session id if (childInfo.Status == WdfChildListRetrieveDeviceSuccess - && description.SessionId == pFileData->SessionId - && !description.OwnerIsDriver) + && description.SessionId == pFileData->SessionId) { TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, @@ -512,151 +424,4 @@ Return Value: } -// -// Called by PDO when a boot-up stage has been completed -// -_Use_decl_annotations_ -VOID -Bus_PdoStageResult( - _In_ PINTERFACE InterfaceHeader, - _In_ VIGEM_PDO_STAGE Stage, - _In_ ULONG Serial, - _In_ NTSTATUS Status -) -{ - ULONG i; - PFDO_DEVICE_DATA pFdoData; - WDFREQUEST curRequest; - ULONG curSerial; - ULONG items; - - UNREFERENCED_PARAMETER(InterfaceHeader); - - PAGED_CODE(); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DRIVER, - "%!FUNC! Entry (stage = %d, serial = %d, status = %!STATUS!)", - Stage, Serial, Status); - - pFdoData = FdoGetData(InterfaceHeader->Context); - - // - // If any stage fails or is last stage, get associated request and complete it - // - if (!NT_SUCCESS(Status) || Stage == ViGEmPdoInitFinished) - { - WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock); - - items = WdfCollectionGetCount(pFdoData->PendingPluginRequests); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DRIVER, - "Items count: %d", - items); - - for (i = 0; i < items; i++) - { - curRequest = WdfCollectionGetItem(pFdoData->PendingPluginRequests, i); - curSerial = PluginRequestGetData(curRequest)->Serial; - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DRIVER, - "Serial: %d, curSerial: %d", - Serial, curSerial); - - if (Serial == curSerial) - { - WdfRequestComplete(curRequest, Status); - - WdfCollectionRemove(pFdoData->PendingPluginRequests, curRequest); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DRIVER, - "Removed item with serial: %d", - curSerial); - - break; - } - } - WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock); - } - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); -} - -_Use_decl_annotations_ -VOID -Bus_PlugInRequestCleanUpEvtTimerFunc( - WDFTIMER Timer -) -{ - ULONG i; - PFDO_DEVICE_DATA pFdoData; - WDFREQUEST curRequest; - ULONG items; - WDFDEVICE device; - PFDO_PLUGIN_REQUEST_DATA pPluginData; - LONGLONG freq; - LARGE_INTEGER pcNow; - LONGLONG ellapsed; - - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); - - device = WdfTimerGetParentObject(Timer); - pFdoData = FdoGetData(device); - - WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock); - - items = WdfCollectionGetCount(pFdoData->PendingPluginRequests); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DRIVER, - "Items count: %d", - items); - - // - // Collection is empty; no need to keep timer running - // - if (items == 0) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_DRIVER, - "Collection is empty, stopping periodic timer"); - WdfTimerStop(Timer, FALSE); - } - - for (i = 0; i < items; i++) - { - curRequest = WdfCollectionGetItem(pFdoData->PendingPluginRequests, i); - pPluginData = PluginRequestGetData(curRequest); - - freq = pPluginData->Frequency.QuadPart / ORC_PC_FREQUENCY_DIVIDER; - pcNow = KeQueryPerformanceCounter(NULL); - ellapsed = (pcNow.QuadPart - pPluginData->Timestamp.QuadPart) / freq; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_DRIVER, - "PDO (serial = %d) plugin request age: %llu ms", - pPluginData->Serial, ellapsed); - - if (ellapsed >= ORC_REQUEST_MAX_AGE) - { - WdfRequestComplete(curRequest, STATUS_SUCCESS); - - WdfCollectionRemove(pFdoData->PendingPluginRequests, curRequest); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DRIVER, - "Removed item with serial: %d", - pPluginData->Serial); - - break; - } - } - WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock); - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit"); -} - +EXTERN_C_END diff --git a/sys/Ds4.c b/sys/Ds4.c deleted file mode 100644 index 493777b..0000000 --- a/sys/Ds4.c +++ /dev/null @@ -1,468 +0,0 @@ -/* -* 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 "ds4.tmh" - -NTSTATUS Ds4_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription) -{ - NTSTATUS status; - UNICODE_STRING buffer; - - // prepare device description - status = RtlUnicodeStringInit(DeviceDescription, L"Virtual DualShock 4 Controller"); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "RtlUnicodeStringInit failed with status %!STATUS!", - status); - return status; - } - - // Set hardware IDs - RtlUnicodeStringInit(&buffer, L"USB\\VID_054C&PID_05C4&REV_0100"); - - status = WdfPdoInitAddHardwareID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfPdoInitAddHardwareID failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringCopy(DeviceId, &buffer); - - RtlUnicodeStringInit(&buffer, L"USB\\VID_054C&PID_05C4"); - - status = WdfPdoInitAddHardwareID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfPdoInitAddHardwareID failed with status %!STATUS!", - status); - return status; - } - - // Set compatible IDs - RtlUnicodeStringInit(&buffer, L"USB\\Class_03&SubClass_00&Prot_00"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfPdoInitAddCompatibleID (#01) failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&buffer, L"USB\\Class_03&SubClass_00"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfPdoInitAddCompatibleID (#02) failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&buffer, L"USB\\Class_03"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfPdoInitAddCompatibleID (#03) failed with status %!STATUS!", - status); - return status; - } - - return STATUS_SUCCESS; -} - -NTSTATUS Ds4_PrepareHardware(WDFDEVICE Device) -{ - NTSTATUS status; - WDF_QUERY_INTERFACE_CONFIG ifaceCfg; - INTERFACE devinterfaceHid; - - devinterfaceHid.Size = sizeof(INTERFACE); - devinterfaceHid.Version = 1; - devinterfaceHid.Context = (PVOID)Device; - - devinterfaceHid.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; - devinterfaceHid.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; - - // Expose GUID_DEVINTERFACE_HID so HIDUSB can initialize - WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&devinterfaceHid, &GUID_DEVINTERFACE_HID, NULL); - - status = WdfDeviceAddQueryInterface(Device, &ifaceCfg); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfDeviceAddQueryInterface failed with status %!STATUS!", - status); - return status; - } - - PDS4_DEVICE_DATA ds4Data = Ds4GetData(Device); - - // Set default HID input report (everything zero`d) - UCHAR DefaultHidReport[DS4_REPORT_SIZE] = - { - 0x01, 0x82, 0x7F, 0x7E, 0x80, 0x08, 0x00, 0x58, - 0x00, 0x00, 0xFD, 0x63, 0x06, 0x03, 0x00, 0xFE, - 0xFF, 0xFC, 0xFF, 0x79, 0xFD, 0x1B, 0x14, 0xD1, - 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, - 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 - }; - - // Initialize HID reports to defaults - RtlCopyBytes(ds4Data->Report, DefaultHidReport, DS4_REPORT_SIZE); - RtlZeroMemory(&ds4Data->OutputReport, sizeof(DS4_OUTPUT_REPORT)); - - // Start pending IRP queue flush timer - WdfTimerStart(ds4Data->PendingUsbInRequestsTimer, DS4_QUEUE_FLUSH_PERIOD); - - return STATUS_SUCCESS; -} - -NTSTATUS Ds4_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description) -{ - NTSTATUS status; - PDS4_DEVICE_DATA ds4 = Ds4GetData(Device); - - // Initialize periodic timer - WDF_TIMER_CONFIG timerConfig; - WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, Ds4_PendingUsbRequestsTimerFunc, DS4_QUEUE_FLUSH_PERIOD); - - // Timer object attributes - WDF_OBJECT_ATTRIBUTES timerAttribs; - WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs); - - // PDO is parent - timerAttribs.ParentObject = Device; - - // Create timer - status = WdfTimerCreate(&timerConfig, &timerAttribs, &ds4->PendingUsbInRequestsTimer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfTimerCreate failed with status %!STATUS!", - status); - return status; - } - - // Load/generate MAC address - - // TODO: tidy up this region - - WDFKEY keyParams, keyTargets, keyDS, keySerial; - UNICODE_STRING keyName, valueName; - - status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), STANDARD_RIGHTS_ALL, WDF_NO_OBJECT_ATTRIBUTES, &keyParams); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfDriverOpenParametersRegistryKey failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&keyName, L"Targets"); - - status = WdfRegistryCreateKey( - keyParams, - &keyName, - KEY_ALL_ACCESS, - REG_OPTION_NON_VOLATILE, - NULL, - WDF_NO_OBJECT_ATTRIBUTES, - &keyTargets - ); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfRegistryCreateKey failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&keyName, L"DualShock"); - - status = WdfRegistryCreateKey( - keyTargets, - &keyName, - KEY_ALL_ACCESS, - REG_OPTION_NON_VOLATILE, - NULL, - WDF_NO_OBJECT_ATTRIBUTES, - &keyDS - ); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfRegistryCreateKey failed with status %!STATUS!", - status); - return status; - } - - DECLARE_UNICODE_STRING_SIZE(serialPath, 4); - RtlUnicodeStringPrintf(&serialPath, L"%04d", Description->SerialNo); - - status = WdfRegistryCreateKey( - keyDS, - &serialPath, - KEY_ALL_ACCESS, - REG_OPTION_NON_VOLATILE, - NULL, - WDF_NO_OBJECT_ATTRIBUTES, - &keySerial - ); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfRegistryCreateKey failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&valueName, L"TargetMacAddress"); - - status = WdfRegistryQueryValue(keySerial, &valueName, sizeof(MAC_ADDRESS), &ds4->TargetMacAddress, NULL, NULL); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_DS4, - "MAC-Address: %02X:%02X:%02X:%02X:%02X:%02X\n", - ds4->TargetMacAddress.Vendor0, - ds4->TargetMacAddress.Vendor1, - ds4->TargetMacAddress.Vendor2, - ds4->TargetMacAddress.Nic0, - ds4->TargetMacAddress.Nic1, - ds4->TargetMacAddress.Nic2); - - if (status == STATUS_OBJECT_NAME_NOT_FOUND) - { - GenerateRandomMacAddress(&ds4->TargetMacAddress); - - status = WdfRegistryAssignValue(keySerial, &valueName, REG_BINARY, sizeof(MAC_ADDRESS), (PVOID)&ds4->TargetMacAddress); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfRegistryAssignValue failed with status %!STATUS!", - status); - return status; - } - } - else if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_DS4, - "WdfRegistryQueryValue failed with status %!STATUS!", - status); - return status; - } - - WdfRegistryClose(keySerial); - WdfRegistryClose(keyDS); - WdfRegistryClose(keyTargets); - WdfRegistryClose(keyParams); - - return STATUS_SUCCESS; -} - -VOID Ds4_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) -{ - UCHAR Ds4DescriptorData[DS4_DESCRIPTOR_SIZE] = - { - 0x09, // bLength - 0x02, // bDescriptorType (Configuration) - 0x29, 0x00, // wTotalLength 41 - 0x01, // bNumInterfaces 1 - 0x01, // bConfigurationValue - 0x00, // iConfiguration (String Index) - 0xC0, // bmAttributes Self Powered - 0xFA, // bMaxPower 500mA - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x00, // bInterfaceNumber 0 - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints 2 - 0x03, // bInterfaceClass - 0x00, // bInterfaceSubClass - 0x00, // bInterfaceProtocol - 0x00, // iInterface (String Index) - - 0x09, // bLength - 0x21, // bDescriptorType (HID) - 0x11, 0x01, // bcdHID 1.11 - 0x00, // bCountryCode - 0x01, // bNumDescriptors - 0x22, // bDescriptorType[0] (HID) - 0xD3, 0x01, // wDescriptorLength[0] 467 - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x84, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x40, 0x00, // wMaxPacketSize 64 - 0x05, // bInterval 5 (unit depends on device speed) - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x03, // bEndpointAddress (OUT/H2D) - 0x03, // bmAttributes (Interrupt) - 0x40, 0x00, // wMaxPacketSize 64 - 0x05, // bInterval 5 (unit depends on device speed) - - // 41 bytes - - // best guess: USB Standard Descriptor - }; - - RtlCopyBytes(Buffer, Ds4DescriptorData, Length); -} - -VOID Ds4_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon) -{ - pDescriptor->bLength = 0x12; - pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; - pDescriptor->bcdUSB = 0x0200; // USB v2.0 - pDescriptor->bDeviceClass = 0x00; // per Interface - pDescriptor->bDeviceSubClass = 0x00; - pDescriptor->bDeviceProtocol = 0x00; - pDescriptor->bMaxPacketSize0 = 0x40; - pDescriptor->idVendor = pCommon->VendorId; - pDescriptor->idProduct = pCommon->ProductId; - pDescriptor->bcdDevice = 0x0100; - pDescriptor->iManufacturer = 0x01; - pDescriptor->iProduct = 0x02; - pDescriptor->iSerialNumber = 0x00; - pDescriptor->bNumConfigurations = 0x01; -} - -VOID Ds4_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo) -{ - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_DS4, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d", - (int)pInfo->Length, - (int)pInfo->InterfaceNumber, - (int)pInfo->AlternateSetting, - pInfo->NumberOfPipes); - - pInfo->Class = 0x03; // HID - pInfo->SubClass = 0x00; - pInfo->Protocol = 0x00; - - pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; - - pInfo->Pipes[0].MaximumTransferSize = 0x00400000; - pInfo->Pipes[0].MaximumPacketSize = 0x40; - pInfo->Pipes[0].EndpointAddress = 0x84; - pInfo->Pipes[0].Interval = 0x05; - pInfo->Pipes[0].PipeType = 0x03; - pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084; - pInfo->Pipes[0].PipeFlags = 0x00; - - pInfo->Pipes[1].MaximumTransferSize = 0x00400000; - pInfo->Pipes[1].MaximumPacketSize = 0x40; - pInfo->Pipes[1].EndpointAddress = 0x03; - pInfo->Pipes[1].Interval = 0x05; - pInfo->Pipes[1].PipeType = 0x03; - pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003; - pInfo->Pipes[1].PipeFlags = 0x00; -} - -// -// Completes pending I/O requests if feeder is too slow. -// -VOID Ds4_PendingUsbRequestsTimerFunc( - _In_ WDFTIMER Timer -) -{ - NTSTATUS status; - WDFREQUEST usbRequest; - WDFDEVICE hChild; - PDS4_DEVICE_DATA ds4Data; - PIRP pendingIrp; - PIO_STACK_LOCATION irpStack; - PPDO_DEVICE_DATA pdoData; - - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DS4, "%!FUNC! Entry"); - - hChild = WdfTimerGetParentObject(Timer); - pdoData = PdoGetData(hChild); - ds4Data = Ds4GetData(hChild); - - // Get pending USB request - status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest); - - if (NT_SUCCESS(status)) - { - // Get pending IRP - pendingIrp = WdfRequestWdmGetIrp(usbRequest); - irpStack = IoGetCurrentIrpStackLocation(pendingIrp); - - // Get USB request block - PURB urb = (PURB)irpStack->Parameters.Others.Argument1; - - // Get transfer buffer - PUCHAR Buffer = (PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer; - // Set buffer length to report size - urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE; - - // Copy cached report to transfer buffer - if (Buffer) - RtlCopyBytes(Buffer, ds4Data->Report, DS4_REPORT_SIZE); - - // Complete pending request - WdfRequestComplete(usbRequest, status); - } - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DS4, "%!FUNC! Exit with status %!STATUS!", status); -} - diff --git a/sys/Ds4.h b/sys/Ds4.h deleted file mode 100644 index b93be80..0000000 --- a/sys/Ds4.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -* 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. -*/ - - -#pragma once - -#define HID_GET_FEATURE_REPORT_SIZE_0 0x31 -#define HID_GET_FEATURE_REPORT_SIZE_1 0x25 -#define HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE 0x10 - -#define HID_SET_FEATURE_REPORT_SIZE_0 0x17 -#define HID_SET_FEATURE_REPORT_SIZE_1 0x11 - -#define HID_REPORT_ID_0 0xA3 -#define HID_REPORT_ID_1 0x02 -#define HID_REPORT_MAC_ADDRESSES_ID 0x12 -#define HID_REPORT_ID_3 0x13 -#define HID_REPORT_ID_4 0x14 - -#define DS4_DESCRIPTOR_SIZE 0x0029 -#if defined(_X86_) -#define DS4_CONFIGURATION_SIZE 0x0050 -#else -#define DS4_CONFIGURATION_SIZE 0x0070 -#endif -#define DS4_HID_REPORT_DESCRIPTOR_SIZE 0x01D3 - -#define DS4_MANUFACTURER_NAME_LENGTH 0x38 -#define DS4_PRODUCT_NAME_LENGTH 0x28 -#define DS4_OUTPUT_BUFFER_OFFSET 0x04 -#define DS4_OUTPUT_BUFFER_LENGTH 0x05 - -#define DS4_REPORT_SIZE 0x40 -#define DS4_QUEUE_FLUSH_PERIOD 0x05 - - -// -// DS4-specific device context data. -// -typedef struct _DS4_DEVICE_DATA -{ - // - // HID Input Report buffer - // - UCHAR Report[DS4_REPORT_SIZE]; - - // - // Output report cache - // - DS4_OUTPUT_REPORT OutputReport; - - // - // Timer for dispatching interrupt transfer - // - WDFTIMER PendingUsbInRequestsTimer; - - // - // Auto-generated MAC address of the target device - // - MAC_ADDRESS TargetMacAddress; - - // - // Default MAC address of the host (not used) - // - MAC_ADDRESS HostMacAddress; - -} DS4_DEVICE_DATA, *PDS4_DEVICE_DATA; - -WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DS4_DEVICE_DATA, Ds4GetData) - - -EVT_WDF_TIMER Ds4_PendingUsbRequestsTimerFunc; - -NTSTATUS -Bus_Ds4SubmitReport( - WDFDEVICE Device, - ULONG SerialNo, - PDS4_SUBMIT_REPORT Report, - _In_ BOOLEAN FromInterface -); - -// -// DS4-specific functions -// -NTSTATUS Ds4_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription); -NTSTATUS Ds4_PrepareHardware(WDFDEVICE Device); -NTSTATUS Ds4_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description); -VOID Ds4_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length); -VOID Ds4_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon); -VOID Ds4_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo); - diff --git a/sys/Ds4Pdo.cpp b/sys/Ds4Pdo.cpp index cdf0d05..f748b03 100644 --- a/sys/Ds4Pdo.cpp +++ b/sys/Ds4Pdo.cpp @@ -9,10 +9,12 @@ PCWSTR ViGEm::Bus::Targets::EmulationTargetDS4::_deviceDescription = L"Virtual DualShock 4 Controller"; -ViGEm::Bus::Targets::EmulationTargetDS4::EmulationTargetDS4() : EmulationTargetPDO(0x054C, 0x05C4) +ViGEm::Bus::Targets::EmulationTargetDS4::EmulationTargetDS4(ULONG Serial, LONG SessionId, USHORT VendorId, + USHORT ProductId) : EmulationTargetPDO( + Serial, SessionId, VendorId, ProductId) { - TargetType = DualShock4Wired; - UsbConfigurationDescriptionSize = DS4_DESCRIPTOR_SIZE; + _TargetType = DualShock4Wired; + _UsbConfigurationDescriptionSize = DS4_DESCRIPTOR_SIZE; } NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, @@ -34,7 +36,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_IN // Set hardware IDs RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X&REV_0100", - this->VendorId, this->ProductId); + this->_VendorId, this->_ProductId); status = WdfPdoInitAddHardwareID(DeviceInit, &buffer); if (!NT_SUCCESS(status)) @@ -49,7 +51,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_IN RtlUnicodeStringCopy(DeviceId, &buffer); RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", - this->VendorId, this->ProductId); + this->_VendorId, this->_ProductId); status = WdfPdoInitAddHardwareID(DeviceInit, &buffer); if (!NT_SUCCESS(status)) @@ -108,7 +110,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareHardware() devinterfaceHid.Size = sizeof(INTERFACE); devinterfaceHid.Version = 1; - devinterfaceHid.Context = static_cast(this->PdoDevice); + devinterfaceHid.Context = static_cast(this->_PdoDevice); devinterfaceHid.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; devinterfaceHid.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; @@ -121,7 +123,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareHardware() NULL ); - NTSTATUS status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg); + NTSTATUS status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, @@ -171,7 +173,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext() WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs); // PDO is parent - timerAttribs.ParentObject = this->PdoDevice; + timerAttribs.ParentObject = this->_PdoDevice; // Create timer status = WdfTimerCreate( @@ -251,7 +253,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext() } DECLARE_UNICODE_STRING_SIZE(serialPath, 4); - RtlUnicodeStringPrintf(&serialPath, L"%04d", this->SerialNo); + RtlUnicodeStringPrintf(&serialPath, L"%04d", this->_SerialNo); status = WdfRegistryCreateKey( keyDS, @@ -391,8 +393,8 @@ VOID ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetDeviceDescriptorType(PUSB_DE pDescriptor->bDeviceSubClass = 0x00; pDescriptor->bDeviceProtocol = 0x00; pDescriptor->bMaxPacketSize0 = 0x40; - pDescriptor->idVendor = this->VendorId; - pDescriptor->idProduct = this->ProductId; + pDescriptor->idVendor = this->_VendorId; + pDescriptor->idProduct = this->_ProductId; pDescriptor->bcdDevice = 0x0100; pDescriptor->iManufacturer = 0x01; pDescriptor->iProduct = 0x02; @@ -872,7 +874,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetDescriptorFromInterface( // // Notify client library that PDO is ready // - KeSetEvent(&this->PdoBootNotificationEvent, 0, FALSE); + KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE); } return status; @@ -990,7 +992,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR /* This request is sent periodically and relies on data the "feeder" has to supply, so we queue this request and return with STATUS_PENDING. The request gets completed as soon as the "feeder" sent an update. */ - status = WdfRequestForwardToIoQueue(Request, this->PendingUsbInRequests); + status = WdfRequestForwardToIoQueue(Request, this->_PendingUsbInRequests); return (NT_SUCCESS(status)) ? STATUS_PENDING : status; } @@ -1001,7 +1003,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR DS4_OUTPUT_BUFFER_LENGTH); // Notify user-mode process that new data is available - status = WdfIoQueueRetrieveNextRequest(this->PendingNotificationRequests, ¬ifyRequest); + status = WdfIoQueueRetrieveNextRequest(this->_PendingNotificationRequests, ¬ifyRequest); if (NT_SUCCESS(status)) { @@ -1018,7 +1020,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR { // Assign values to output buffer notify->Size = sizeof(DS4_REQUEST_NOTIFICATION); - notify->SerialNo = this->SerialNo; + notify->SerialNo = this->_SerialNo; notify->Report = this->OutputReport; WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size); @@ -1063,6 +1065,40 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbControlTransfer(PURB Urb) return status; } +NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::SubmitReport(PVOID NewReport) +{ + NTSTATUS status; + WDFREQUEST usbRequest; + + status = WdfIoQueueRetrieveNextRequest(this->_PendingUsbInRequests, &usbRequest); + + if (!NT_SUCCESS(status)) + return status; + + // Get pending IRP + PIRP pendingIrp = WdfRequestWdmGetIrp(usbRequest); + + // Get USB request block + PURB urb = static_cast(URB_FROM_IRP(pendingIrp)); + + // Get transfer buffer + auto Buffer = static_cast(urb->UrbBulkOrInterruptTransfer.TransferBuffer); + + urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE; + + /* Copy report to cache and transfer buffer + * Skip first byte as it contains the never changing report id */ + RtlCopyBytes(this->Report + 1, &(static_cast(NewReport))->Report, sizeof(DS4_REPORT)); + + if (Buffer) + RtlCopyBytes(Buffer, this->Report, DS4_REPORT_SIZE); + + // Complete pending request + WdfRequestComplete(usbRequest, status); + + return status; +} + VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc( _In_ WDFTIMER Timer ) @@ -1076,7 +1112,7 @@ VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc( TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DS4, "%!FUNC! Entry"); // Get pending USB request - NTSTATUS status = WdfIoQueueRetrieveNextRequest(ctx->PendingUsbInRequests, &usbRequest); + NTSTATUS status = WdfIoQueueRetrieveNextRequest(ctx->_PendingUsbInRequests, &usbRequest); if (NT_SUCCESS(status)) { diff --git a/sys/Ds4Pdo.hpp b/sys/Ds4Pdo.hpp index 4f6c85a..bb1b2ea 100644 --- a/sys/Ds4Pdo.hpp +++ b/sys/Ds4Pdo.hpp @@ -21,7 +21,7 @@ namespace ViGEm::Bus::Targets class EmulationTargetDS4 : public Core::EmulationTargetPDO { public: - EmulationTargetDS4(); + EmulationTargetDS4(ULONG Serial, LONG SessionId, USHORT VendorId = 0x054C, USHORT ProductId = 0x05C4); ~EmulationTargetDS4() = default; NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, @@ -45,6 +45,7 @@ namespace ViGEm::Bus::Targets NTSTATUS UsbGetStringDescriptorType(PURB Urb) override; NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) override; NTSTATUS UsbControlTransfer(PURB Urb) override; + NTSTATUS SubmitReport(PVOID NewReport) override; private: static PCWSTR _deviceDescription; diff --git a/sys/EmulationTargetPDO.cpp b/sys/EmulationTargetPDO.cpp index 707c670..5d785b7 100644 --- a/sys/EmulationTargetPDO.cpp +++ b/sys/EmulationTargetPDO.cpp @@ -10,28 +10,27 @@ PCWSTR ViGEm::Bus::Core::EmulationTargetPDO::_deviceLocation = L"Virtual Gamepad Emulation Bus"; -NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, PWDFDEVICE_INIT DeviceInit, PPDO_IDENTIFICATION_DESCRIPTION Description) +NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentDevice, PWDFDEVICE_INIT DeviceInit) { - NTSTATUS status = STATUS_UNSUCCESSFUL; - 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; - WDF_OBJECT_ATTRIBUTES attributes; - WDF_IO_QUEUE_CONFIG usbInQueueConfig; - WDF_IO_QUEUE_CONFIG notificationsQueueConfig; - PEMULATION_TARGET_PDO_CONTEXT pPdoContext; + NTSTATUS status = STATUS_UNSUCCESSFUL; + 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; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_IO_QUEUE_CONFIG usbInQueueConfig; + WDF_IO_QUEUE_CONFIG notificationsQueueConfig; + PEMULATION_TARGET_PDO_CONTEXT pPdoContext; 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); - UNREFERENCED_PARAMETER(Description); - UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ParentDevice); PAGED_CODE(); @@ -48,9 +47,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfPdoInitAssignRawDevice failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfPdoInitAssignRawDevice failed with status %!STATUS!", + status); break; } @@ -60,9 +59,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfDeviceInitAssignSDDLString failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfDeviceInitAssignSDDLString failed with status %!STATUS!", + status); break; } @@ -80,20 +79,20 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfPdoInitAssignDeviceID failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfPdoInitAssignDeviceID failed with status %!STATUS!", + status); break; } // prepare instance id - status = RtlUnicodeStringPrintf(&buffer, L"%02d", this->SerialNo); + status = RtlUnicodeStringPrintf(&buffer, L"%02d", this->_SerialNo); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "RtlUnicodeStringPrintf failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "RtlUnicodeStringPrintf failed with status %!STATUS!", + status); break; } @@ -102,9 +101,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfPdoInitAssignInstanceID failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfPdoInitAssignInstanceID failed with status %!STATUS!", + status); break; } @@ -113,9 +112,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfPdoInitAddDeviceText failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfPdoInitAddDeviceText failed with status %!STATUS!", + status); break; } @@ -143,36 +142,36 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, // Add common device data context WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, EMULATION_TARGET_PDO_CONTEXT); - status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &this->PdoDevice); + status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &this->_PdoDevice); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfDeviceCreate failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfDeviceCreate failed with status %!STATUS!", + status); break; } TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - "Created PDO 0x%p", - this->PdoDevice); + TRACE_BUSPDO, + "Created PDO 0x%p", + this->_PdoDevice); #pragma endregion #pragma region Expose USB Interface status = WdfDeviceCreateDeviceInterface( - Device, + ParentDevice, const_cast(&GUID_DEVINTERFACE_USB_DEVICE), - NULL + nullptr ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfDeviceCreateDeviceInterface failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfDeviceCreateDeviceInterface failed with status %!STATUS!", + status); break; } @@ -183,17 +182,17 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, // // Bind this object and device context together // - pPdoContext = EmulationTargetPdoGetContext(this->PdoDevice); + pPdoContext = EmulationTargetPdoGetContext(this->_PdoDevice); pPdoContext->Target = this; - + status = this->PdoInitContext(); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "Couldn't initialize additional contexts: %!STATUS!", - status); + TRACE_BUSPDO, + "Couldn't initialize additional contexts: %!STATUS!", + status); break; } @@ -202,23 +201,23 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, #pragma region Create Queues & Locks WDF_OBJECT_ATTRIBUTES_INIT(&attributes); - attributes.ParentObject = this->PdoDevice; + attributes.ParentObject = this->_PdoDevice; // Create and assign queue for incoming interrupt transfer WDF_IO_QUEUE_CONFIG_INIT(&usbInQueueConfig, WdfIoQueueDispatchManual); status = WdfIoQueueCreate( - this->PdoDevice, + this->_PdoDevice, &usbInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, - &this->PendingUsbInRequests + &this->_PendingUsbInRequests ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfIoQueueCreate (PendingUsbInRequests) failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfIoQueueCreate (PendingUsbInRequests) failed with status %!STATUS!", + status); break; } @@ -226,21 +225,21 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual); status = WdfIoQueueCreate( - Device, + ParentDevice, ¬ificationsQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, - &this->PendingNotificationRequests + &this->_PendingNotificationRequests ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfIoQueueCreate (PendingNotificationRequests) failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfIoQueueCreate (PendingNotificationRequests) failed with status %!STATUS!", + status); break; } -#pragma endregion +#pragma endregion #pragma region Default I/O queue setup @@ -249,7 +248,7 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, defaultPdoQueueConfig.EvtIoInternalDeviceControl = EvtIoInternalDeviceControl; status = WdfIoQueueCreate( - this->PdoDevice, + this->_PdoDevice, &defaultPdoQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &defaultPdoQueue @@ -257,9 +256,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSPDO, - "WdfIoQueueCreate (Default) failed with status %!STATUS!", - status); + TRACE_BUSPDO, + "WdfIoQueueCreate (Default) failed with status %!STATUS!", + status); break; } @@ -273,10 +272,10 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, pnpCaps.EjectSupported = WdfTrue; pnpCaps.SurpriseRemovalOK = WdfTrue; - pnpCaps.Address = this->SerialNo; - pnpCaps.UINumber = this->SerialNo; + pnpCaps.Address = this->_SerialNo; + pnpCaps.UINumber = this->_SerialNo; - WdfDeviceSetPnpCapabilities(this->PdoDevice, &pnpCaps); + WdfDeviceSetPnpCapabilities(this->_PdoDevice, &pnpCaps); #pragma endregion @@ -295,11 +294,11 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3; powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3; - WdfDeviceSetPowerCapabilities(this->PdoDevice, &powerCaps); + WdfDeviceSetPowerCapabilities(this->_PdoDevice, &powerCaps); #pragma endregion - - } while (FALSE); + } + while (FALSE); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); @@ -308,12 +307,29 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE Device, VOID ViGEm::Bus::Core::EmulationTargetPDO::SetSerial(ULONG Serial) { - this->SerialNo = Serial; + this->_SerialNo = Serial; } ULONG ViGEm::Bus::Core::EmulationTargetPDO::GetSerial() const { - return this->SerialNo; + return this->_SerialNo; +} + +NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EnqueueNotification(WDFREQUEST Request) const +{ + return (this->IsOwnerProcess()) + ? WdfRequestForwardToIoQueue(Request, this->_PendingNotificationRequests) + : STATUS_ACCESS_DENIED; +} + +bool ViGEm::Bus::Core::EmulationTargetPDO::IsOwnerProcess() const +{ + return this->_OwnerProcessId == current_process_id(); +} + +unsigned long ViGEm::Bus::Core::EmulationTargetPDO::current_process_id() +{ + return static_cast((DWORD_PTR)PsGetCurrentProcessId() & 0xFFFFFFFF); } #pragma region USB Interface Functions @@ -325,7 +341,8 @@ BOOLEAN USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceIsDeviceHi return TRUE; } -NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusInformation(IN PVOID BusContext, IN ULONG Level, +NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusInformation( + IN PVOID BusContext, IN ULONG Level, IN OUT PVOID BusInformationBuffer, IN OUT PULONG BusInformationBufferLength, OUT PULONG BusInformationActualLength) @@ -347,7 +364,8 @@ NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceSubmitIso return STATUS_UNSUCCESSFUL; } -NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame) +NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusTime( + IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame) { UNREFERENCED_PARAMETER(BusContext); UNREFERENCED_PARAMETER(CurrentUsbFrame); @@ -356,8 +374,9 @@ NTSTATUS USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceQueryBusT } VOID USB_BUSIFFN ViGEm::Bus::Core::EmulationTargetPDO::UsbInterfaceGetUSBDIVersion(IN PVOID BusContext, - IN OUT PUSBD_VERSION_INFORMATION VersionInformation, - IN OUT PULONG HcdCapabilities) + IN OUT PUSBD_VERSION_INFORMATION + VersionInformation, + IN OUT PULONG HcdCapabilities) { UNREFERENCED_PARAMETER(BusContext); @@ -380,13 +399,13 @@ void ViGEm::Bus::Core::EmulationTargetPDO::UsbAbortPipe() this->AbortPipe(); // Higher driver shutting down, emptying PDOs queues - WdfIoQueuePurge(this->PendingUsbInRequests, NULL, NULL); - WdfIoQueuePurge(this->PendingNotificationRequests, NULL, NULL); + WdfIoQueuePurge(this->_PendingUsbInRequests, nullptr, nullptr); + WdfIoQueuePurge(this->_PendingNotificationRequests, nullptr, nullptr); } NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbGetConfigurationDescriptorType(PURB Urb) { - const PUCHAR Buffer = (PUCHAR)Urb->UrbControlDescriptorRequest.TransferBuffer; + const PUCHAR Buffer = static_cast(Urb->UrbControlDescriptorRequest.TransferBuffer); // First request just gets required buffer size back if (Urb->UrbControlDescriptorRequest.TransferBufferLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)) @@ -400,9 +419,9 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbGetConfigurationDescriptorType const ULONG length = Urb->UrbControlDescriptorRequest.TransferBufferLength; // Second request can store the whole descriptor - if (length >= UsbConfigurationDescriptionSize) + if (length >= _UsbConfigurationDescriptionSize) { - this->GetConfigurationDescriptorType(Buffer, UsbConfigurationDescriptionSize); + this->GetConfigurationDescriptorType(Buffer, _UsbConfigurationDescriptionSize); return STATUS_SUCCESS; } @@ -412,24 +431,52 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbGetConfigurationDescriptorType NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::UsbSelectConfiguration(PURB Urb) { TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: TotalLength %d", - Urb->UrbHeader.Length); + TRACE_USBPDO, + ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: TotalLength %d", + Urb->UrbHeader.Length); if (Urb->UrbHeader.Length == sizeof(struct _URB_SELECT_CONFIGURATION)) { TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: NULL ConfigurationDescriptor"); + TRACE_USBPDO, + ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: NULL ConfigurationDescriptor"); return STATUS_SUCCESS; } return this->SelectConfiguration(Urb); } -ViGEm::Bus::Core::EmulationTargetPDO::EmulationTargetPDO(USHORT VID, USHORT PID) : VendorId(VID), ProductId(PID) +ViGEm::Bus::Core::EmulationTargetPDO:: +EmulationTargetPDO(ULONG Serial, LONG SessionId, USHORT VendorId, USHORT ProductId) : _SerialNo(Serial), + _SessionId(SessionId), + _VendorId(VendorId), + _ProductId(ProductId) { - KeInitializeEvent(&this->PdoBootNotificationEvent, NotificationEvent, FALSE); + this->_OwnerProcessId = current_process_id(); + KeInitializeEvent(&this->_PdoBootNotificationEvent, NotificationEvent, FALSE); +} + +ViGEm::Bus::Core::EmulationTargetPDO* ViGEm::Bus::Core::EmulationTargetPDO::GetPdoBySerial( + IN WDFDEVICE ParentDevice, IN ULONG SerialNo) +{ + WDF_CHILD_RETRIEVE_INFO info; + + const WDFCHILDLIST list = WdfFdoGetDefaultChildList(ParentDevice); + + PDO_IDENTIFICATION_DESCRIPTION description; + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); + + description.SerialNo = SerialNo; + + WDF_CHILD_RETRIEVE_INFO_INIT(&info, &description.Header); + + const WDFDEVICE pdoDevice = WdfChildListRetrievePdo(list, &info); + + if (pdoDevice == nullptr) + return nullptr; + + return EmulationTargetPdoGetContext(pdoDevice)->Target; } NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EvtDevicePrepareHardware( @@ -455,14 +502,14 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( ) { const auto ctx = EmulationTargetPdoGetContext(WdfIoQueueGetDevice(Queue)); - + UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); - NTSTATUS status = STATUS_INVALID_PARAMETER; - PIRP irp; - PURB urb; - PIO_STACK_LOCATION irpStack; + NTSTATUS status = STATUS_INVALID_PARAMETER; + PIRP irp; + PURB urb; + PIO_STACK_LOCATION irpStack; TraceDbg(TRACE_BUSPDO, "%!FUNC! Entry"); @@ -475,8 +522,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case IOCTL_INTERNAL_USB_SUBMIT_URB: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> IOCTL_INTERNAL_USB_SUBMIT_URB"); + TRACE_BUSPDO, + ">> IOCTL_INTERNAL_USB_SUBMIT_URB"); urb = static_cast(URB_FROM_IRP(irp)); @@ -485,8 +532,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_CONTROL_TRANSFER: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_CONTROL_TRANSFER"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_CONTROL_TRANSFER"); status = ctx->Target->UsbControlTransfer(urb); @@ -495,8 +542,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_CONTROL_TRANSFER_EX: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_CONTROL_TRANSFER_EX"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_CONTROL_TRANSFER_EX"); status = STATUS_UNSUCCESSFUL; @@ -515,8 +562,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_SELECT_CONFIGURATION: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_SELECT_CONFIGURATION"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_SELECT_CONFIGURATION"); status = ctx->Target->UsbSelectConfiguration(urb); @@ -525,8 +572,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_SELECT_INTERFACE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_SELECT_INTERFACE"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_SELECT_INTERFACE"); status = ctx->Target->UsbSelectInterface(urb); @@ -535,26 +582,27 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE"); + 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"); + TRACE_BUSPDO, + ">> >> >> USB_DEVICE_DESCRIPTOR_TYPE"); - ctx->Target->UsbGetDeviceDescriptorType(static_cast(urb->UrbControlDescriptorRequest.TransferBuffer)); + ctx->Target->UsbGetDeviceDescriptorType( + static_cast(urb->UrbControlDescriptorRequest.TransferBuffer)); break; case USB_CONFIGURATION_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE"); + TRACE_BUSPDO, + ">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE"); status = ctx->Target->UsbGetConfigurationDescriptorType(urb); @@ -563,8 +611,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case USB_STRING_DESCRIPTOR_TYPE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> >> USB_STRING_DESCRIPTOR_TYPE"); + TRACE_BUSPDO, + ">> >> >> USB_STRING_DESCRIPTOR_TYPE"); status = ctx->Target->UsbGetStringDescriptorType(urb); @@ -573,23 +621,23 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( default: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> >> Unknown descriptor type"); + TRACE_BUSPDO, + ">> >> >> Unknown descriptor type"); break; } TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - "<< <<"); + TRACE_BUSPDO, + "<< <<"); break; case URB_FUNCTION_GET_STATUS_FROM_DEVICE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE"); // Defaults always succeed status = STATUS_SUCCESS; @@ -599,8 +647,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_ABORT_PIPE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_ABORT_PIPE"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_ABORT_PIPE"); ctx->Target->UsbAbortPipe(); @@ -609,8 +657,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_CLASS_INTERFACE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_CLASS_INTERFACE"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_CLASS_INTERFACE"); status = ctx->Target->UsbClassInterface(urb); @@ -619,8 +667,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE"); + TRACE_BUSPDO, + ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE"); status = ctx->Target->UsbGetDescriptorFromInterface(urb); @@ -629,24 +677,24 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( default: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> >> Unknown function: 0x%X", - urb->UrbHeader.Function); + TRACE_BUSPDO, + ">> >> Unknown function: 0x%X", + urb->UrbHeader.Function); break; } TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - "<<"); + TRACE_BUSPDO, + "<<"); break; case IOCTL_INTERNAL_USB_GET_PORT_STATUS: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> IOCTL_INTERNAL_USB_GET_PORT_STATUS"); + TRACE_BUSPDO, + ">> IOCTL_INTERNAL_USB_GET_PORT_STATUS"); // We report the (virtual) port as always active *static_cast(irpStack->Parameters.Others.Argument1) = USBD_PORT_ENABLED | USBD_PORT_CONNECTED; @@ -658,8 +706,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case IOCTL_INTERNAL_USB_RESET_PORT: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> IOCTL_INTERNAL_USB_RESET_PORT"); + TRACE_BUSPDO, + ">> IOCTL_INTERNAL_USB_RESET_PORT"); // Sure, why not ;) status = STATUS_SUCCESS; @@ -669,8 +717,8 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION"); + TRACE_BUSPDO, + ">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION"); // TODO: implement? // This happens if the I/O latency is too high so HIDUSB aborts communication. @@ -681,9 +729,9 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( default: TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSPDO, - ">> Unknown I/O control code 0x%X", - IoControlCode); + TRACE_BUSPDO, + ">> Unknown I/O control code 0x%X", + IoControlCode); break; } diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp index 59c62f3..d0396b6 100644 --- a/sys/EmulationTargetPDO.hpp +++ b/sys/EmulationTargetPDO.hpp @@ -8,6 +8,7 @@ #include #include +#include // // Some insane macro-magic =3 @@ -27,21 +28,22 @@ namespace ViGEm::Bus::Core class EmulationTargetPDO { public: - EmulationTargetPDO(USHORT VID, USHORT PID); + EmulationTargetPDO(ULONG Serial, LONG SessionId, USHORT VendorId, USHORT ProductId); virtual ~EmulationTargetPDO() = default; + static EmulationTargetPDO* GetPdoBySerial(IN WDFDEVICE ParentDevice, IN ULONG SerialNo); + virtual NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, - PUNICODE_STRING DeviceId, - PUNICODE_STRING DeviceDescription) = 0; + PUNICODE_STRING DeviceId, + PUNICODE_STRING DeviceDescription) = 0; virtual NTSTATUS PdoPrepareHardware() = 0; virtual NTSTATUS PdoInitContext() = 0; - - NTSTATUS PdoCreateDevice(_In_ WDFDEVICE Device, - _In_ PWDFDEVICE_INIT DeviceInit, - _In_ PPDO_IDENTIFICATION_DESCRIPTION Description); + + NTSTATUS PdoCreateDevice(_In_ WDFDEVICE ParentDevice, + _In_ PWDFDEVICE_INIT DeviceInit); VOID SetSerial(ULONG Serial); @@ -49,11 +51,11 @@ namespace ViGEm::Bus::Core bool operator==(EmulationTargetPDO& other) const { - return (other.SerialNo == this->SerialNo); + return (other._SerialNo == this->_SerialNo); } virtual VOID UsbGetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor) = 0; - + NTSTATUS UsbSelectConfiguration(PURB Urb); void UsbAbortPipe(); @@ -68,10 +70,20 @@ namespace ViGEm::Bus::Core virtual NTSTATUS UsbGetStringDescriptorType(PURB Urb) = 0; - virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) = 0; + virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, + WDFREQUEST Request) = 0; virtual NTSTATUS UsbControlTransfer(PURB Urb) = 0; - + + virtual NTSTATUS SubmitReport(PVOID NewReport) = 0; + + NTSTATUS EnqueueNotification(WDFREQUEST Request) const; + + bool IsOwnerProcess() const; + + private: + static unsigned long current_process_id(); + protected: static const ULONG _maxHardwareIdLength = 0xFF; @@ -112,58 +124,79 @@ namespace ViGEm::Bus::Core // // Unique serial number of the device on the bus // - ULONG SerialNo{}; + ULONG _SerialNo{}; // // PID of the process creating this PDO // - DWORD OwnerProcessId{}; + DWORD _OwnerProcessId{}; + + // + // File object session ID + // + LONG _SessionId{}; // // Device type this PDO is emulating // - VIGEM_TARGET_TYPE TargetType; + VIGEM_TARGET_TYPE _TargetType; // // If set, the vendor ID the emulated device is reporting // - USHORT VendorId{}; + USHORT _VendorId{}; // // If set, the product ID the emulated device is reporting // - USHORT ProductId{}; + USHORT _ProductId{}; // // Queue for incoming data interrupt transfer // - WDFQUEUE PendingUsbInRequests{}; + WDFQUEUE _PendingUsbInRequests{}; // // Queue for inverted calls // - WDFQUEUE PendingNotificationRequests{}; + WDFQUEUE _PendingNotificationRequests{}; // // This child objects' device object // - WDFDEVICE PdoDevice{}; + WDFDEVICE _PdoDevice{}; // // Signals the bus that PDO is ready to receive data // - KEVENT PdoBootNotificationEvent; + KEVENT _PdoBootNotificationEvent; // // Configuration descriptor size // - ULONG UsbConfigurationDescriptionSize{}; + ULONG _UsbConfigurationDescriptionSize{}; }; typedef struct _PDO_IDENTIFICATION_DESCRIPTION { + // + // List entity header + // WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; + // + // Primary key to identify PDO + // + ULONG SerialNo; + + // + // Session ID + // + LONG SessionId; + + // + // Context object of PDO + // EmulationTargetPDO* Target; } PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION; diff --git a/sys/Queue.c b/sys/Queue.cpp similarity index 83% rename from sys/Queue.c rename to sys/Queue.cpp index 729443c..3e80dd5 100644 --- a/sys/Queue.c +++ b/sys/Queue.cpp @@ -28,10 +28,24 @@ #include "busenum.h" #include "queue.tmh" +#include "EmulationTargetPDO.hpp" +#include "XusbPdo.hpp" +#include "Ds4Pdo.hpp" + + + #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, Bus_EvtIoDefault) #endif +using ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION; +using ViGEm::Bus::Core::EmulationTargetPDO; +using ViGEm::Bus::Targets::EmulationTargetXUSB; +using ViGEm::Bus::Targets::EmulationTargetDS4; + + +EXTERN_C_START + // // Responds to I/O control requests sent to the FDO. // @@ -52,6 +66,7 @@ VOID Bus_EvtIoDeviceControl( PDS4_REQUEST_NOTIFICATION ds4Notify = NULL; PVIGEM_CHECK_VERSION pCheckVersion = NULL; PXUSB_GET_USER_INDEX pXusbGetUserIndex = NULL; + EmulationTargetPDO *pdo; Device = WdfIoQueueGetDevice(Queue); @@ -66,7 +81,12 @@ VOID Bus_EvtIoDeviceControl( TRACE_QUEUE, "IOCTL_VIGEM_CHECK_VERSION"); - status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_CHECK_VERSION), (PVOID)&pCheckVersion, &length); + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(VIGEM_CHECK_VERSION), + (PVOID*)&pCheckVersion, + &length + ); if (!NT_SUCCESS(status) || length != sizeof(VIGEM_CHECK_VERSION)) { @@ -115,7 +135,12 @@ VOID Bus_EvtIoDeviceControl( TRACE_QUEUE, "IOCTL_XUSB_SUBMIT_REPORT"); - status = WdfRequestRetrieveInputBuffer(Request, sizeof(XUSB_SUBMIT_REPORT), (PVOID)&xusbSubmit, &length); + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(XUSB_SUBMIT_REPORT), + (PVOID*)&xusbSubmit, + &length + ); if (!NT_SUCCESS(status)) { @@ -139,7 +164,12 @@ VOID Bus_EvtIoDeviceControl( break; } - status = Bus_XusbSubmitReport(Device, xusbSubmit->SerialNo, xusbSubmit, FALSE); + pdo = EmulationTargetPDO::GetPdoBySerial(Device, xusbSubmit->SerialNo); + + if (pdo == nullptr) + status = STATUS_DEVICE_DOES_NOT_EXIST; + else + status = pdo->SubmitReport(xusbSubmit); } break; @@ -162,7 +192,12 @@ VOID Bus_EvtIoDeviceControl( break; } - status = WdfRequestRetrieveInputBuffer(Request, sizeof(XUSB_REQUEST_NOTIFICATION), (PVOID)&xusbNotify, &length); + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(XUSB_REQUEST_NOTIFICATION), + (PVOID*)&xusbNotify, + &length + ); if (!NT_SUCCESS(status)) { @@ -199,7 +234,12 @@ VOID Bus_EvtIoDeviceControl( TRACE_QUEUE, "IOCTL_DS4_SUBMIT_REPORT"); - status = WdfRequestRetrieveInputBuffer(Request, sizeof(DS4_SUBMIT_REPORT), (PVOID)&ds4Submit, &length); + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(DS4_SUBMIT_REPORT), + (PVOID*)&ds4Submit, + &length + ); if (!NT_SUCCESS(status)) { @@ -223,7 +263,12 @@ VOID Bus_EvtIoDeviceControl( break; } - status = Bus_Ds4SubmitReport(Device, ds4Submit->SerialNo, ds4Submit, FALSE); + pdo = EmulationTargetPDO::GetPdoBySerial(Device, ds4Submit->SerialNo); + + if (pdo == nullptr) + status = STATUS_DEVICE_DOES_NOT_EXIST; + else + status = pdo->SubmitReport(ds4Submit); } break; @@ -246,7 +291,12 @@ VOID Bus_EvtIoDeviceControl( break; } - status = WdfRequestRetrieveInputBuffer(Request, sizeof(DS4_REQUEST_NOTIFICATION), (PVOID)&ds4Notify, &length); + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(DS4_REQUEST_NOTIFICATION), + (PVOID*)&ds4Notify, + &length + ); if (!NT_SUCCESS(status)) { @@ -291,7 +341,7 @@ VOID Bus_EvtIoDeviceControl( status = WdfRequestRetrieveInputBuffer( Request, sizeof(XUSB_GET_USER_INDEX), - (PVOID)&pXusbGetUserIndex, + (PVOID*)&pXusbGetUserIndex, &length); if (!NT_SUCCESS(status)) @@ -309,7 +359,7 @@ VOID Bus_EvtIoDeviceControl( break; } - status = Xusb_GetUserIndex(Device, pXusbGetUserIndex); + //status = Xusb_GetUserIndex(Device, pXusbGetUserIndex); } break; @@ -332,55 +382,6 @@ VOID Bus_EvtIoDeviceControl( TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_QUEUE, "%!FUNC! Exit with status %!STATUS!", status); } -// -// Gets called upon driver-to-driver communication. -// -// TODO: incomplete and unused currently -// -VOID Bus_EvtIoInternalDeviceControl( - _In_ WDFQUEUE Queue, - _In_ WDFREQUEST Request, - _In_ size_t OutputBufferLength, - _In_ size_t InputBufferLength, - _In_ ULONG IoControlCode -) -{ - NTSTATUS status = STATUS_INVALID_PARAMETER; - WDFDEVICE Device; - size_t length = 0; - - UNREFERENCED_PARAMETER(OutputBufferLength); - UNREFERENCED_PARAMETER(InputBufferLength); - - Device = WdfIoQueueGetDevice(Queue); - - KdPrint((DRIVERNAME "Bus_EvtIoInternalDeviceControl: 0x%p\n", Device)); - - switch (IoControlCode) - { - case IOCTL_VIGEM_PLUGIN_TARGET: - - KdPrint((DRIVERNAME "IOCTL_VIGEM_PLUGIN_TARGET\n")); - - status = Bus_PlugInDevice(Device, Request, TRUE, &length); - - break; - - case IOCTL_VIGEM_UNPLUG_TARGET: - - KdPrint((DRIVERNAME "IOCTL_VIGEM_UNPLUG_TARGET\n")); - - status = Bus_UnPlugDevice(Device, Request, TRUE, &length); - - break; - } - - if (status != STATUS_PENDING) - { - WdfRequestCompleteWithInformation(Request, status, length); - } -} - // // Catches unsupported requests. // @@ -397,4 +398,6 @@ VOID Bus_EvtIoDefault( WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST); TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_QUEUE, "%!FUNC! Exit"); -} \ No newline at end of file +} + +EXTERN_C_END diff --git a/sys/Queue.h b/sys/Queue.h index b2e947d..84eb64d 100644 --- a/sys/Queue.h +++ b/sys/Queue.h @@ -27,6 +27,9 @@ #pragma once +EXTERN_C_START + EVT_WDF_IO_QUEUE_IO_DEFAULT Bus_EvtIoDefault; EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Bus_EvtIoDeviceControl; -EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Bus_EvtIoInternalDeviceControl; + +EXTERN_C_END diff --git a/sys/UsbPdo.h b/sys/UsbPdo.h deleted file mode 100644 index d2ff681..0000000 --- a/sys/UsbPdo.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -* 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. -*/ - - -#pragma once - -BOOLEAN USB_BUSIFFN UsbPdo_IsDeviceHighSpeed(IN PVOID BusContext); -NTSTATUS USB_BUSIFFN UsbPdo_QueryBusInformation( - IN PVOID BusContext, - IN ULONG Level, - IN OUT PVOID BusInformationBuffer, - IN OUT PULONG BusInformationBufferLength, - OUT PULONG BusInformationActualLength -); -NTSTATUS USB_BUSIFFN UsbPdo_SubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb); -NTSTATUS USB_BUSIFFN UsbPdo_QueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame); -VOID USB_BUSIFFN UsbPdo_GetUSBDIVersion( - IN PVOID BusContext, - IN OUT PUSBD_VERSION_INFORMATION VersionInformation, - IN OUT PULONG HcdCapabilities -); -NTSTATUS UsbPdo_GetDeviceDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon); -NTSTATUS UsbPdo_GetConfigurationDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon); -NTSTATUS UsbPdo_GetStringDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon); -NTSTATUS UsbPdo_SelectConfiguration(PURB urb, PPDO_DEVICE_DATA pCommon); -NTSTATUS UsbPdo_SelectInterface(PURB urb, PPDO_DEVICE_DATA pCommon); -NTSTATUS UsbPdo_BulkOrInterruptTransfer(PURB urb, WDFDEVICE Device, WDFREQUEST Request); -NTSTATUS UsbPdo_AbortPipe(WDFDEVICE Device); -NTSTATUS UsbPdo_ClassInterface(PURB urb, WDFDEVICE Device, PPDO_DEVICE_DATA pCommon); -NTSTATUS UsbPdo_GetDescriptorFromInterface(PURB urb, PPDO_DEVICE_DATA pCommon); diff --git a/sys/ViGEmBus.vcxproj b/sys/ViGEmBus.vcxproj index dcafc77..b3bda65 100644 --- a/sys/ViGEmBus.vcxproj +++ b/sys/ViGEmBus.vcxproj @@ -191,32 +191,26 @@ - - - - - + + - - + - - + - diff --git a/sys/ViGEmBus.vcxproj.filters b/sys/ViGEmBus.vcxproj.filters index 10e2f03..8dac57a 100644 --- a/sys/ViGEmBus.vcxproj.filters +++ b/sys/ViGEmBus.vcxproj.filters @@ -42,18 +42,9 @@ Header Files - - Header Files - Header Files - - Header Files - - - Header Files - Header Files @@ -86,30 +77,9 @@ - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files - - Source Files - Source Files @@ -122,6 +92,18 @@ Source Files\Targets + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/sys/Xusb.h b/sys/Xusb.h deleted file mode 100644 index 54c796f..0000000 --- a/sys/Xusb.h +++ /dev/null @@ -1,141 +0,0 @@ -/* -* 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. -*/ - - -// -// For children emulating XUSB devices, the following dummy interfaces -// have to be exposed by the PDO or else the child devices won't start -// -// TODO: that statement might be obsolete, further testing required -// - -// Below these interfaces are from wdmguid.h being used as dummies -// {70211B0E-0AFB-47DB-AFC1-410BF842497A} PNP_LOCATION_INTERFACE -// {B38290E5-3CD0-4F9D-9937-F5FE2B44D47A} D3COLD_SUPPORT_INTERFACE -// {2AEB0243-6A6E-486B-82FC-D815F6B97006} REENUMERATE_SELF_INTERFACE_STANDARD - -#pragma once - -#if defined(_X86_) -#define XUSB_CONFIGURATION_SIZE 0x00E4 -#else -#define XUSB_CONFIGURATION_SIZE 0x0130 -#endif -#define XUSB_DESCRIPTOR_SIZE 0x0099 -#define XUSB_RUMBLE_SIZE 0x08 -#define XUSB_LEDSET_SIZE 0x03 -#define XUSB_LEDNUM_SIZE 0x01 -#define XUSB_INIT_STAGE_SIZE 0x03 -#define XUSB_BLOB_STORAGE_SIZE 0x2A - -#define XUSB_BLOB_00_OFFSET 0x00 -#define XUSB_BLOB_01_OFFSET 0x03 -#define XUSB_BLOB_02_OFFSET 0x06 -#define XUSB_BLOB_03_OFFSET 0x09 -#define XUSB_BLOB_04_OFFSET 0x0C -#define XUSB_BLOB_05_OFFSET 0x20 -#define XUSB_BLOB_06_OFFSET 0x23 -#define XUSB_BLOB_07_OFFSET 0x26 - -#define XUSB_IS_DATA_PIPE(_x_) ((BOOLEAN)(_x_->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0081)) -#define XUSB_IS_CONTROL_PIPE(_x_) ((BOOLEAN)(_x_->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0083)) - -typedef struct _XUSB_INTERRUPT_IN_PACKET -{ - UCHAR Id; - - UCHAR Size; - - XUSB_REPORT Report; - -} XUSB_INTERRUPT_IN_PACKET, *PXUSB_INTERRUPT_IN_PACKET; - -// -// XUSB-specific device context data. -// -typedef struct _XUSB_DEVICE_DATA -{ - // - // Rumble buffer - // - UCHAR Rumble[XUSB_RUMBLE_SIZE]; - - // - // LED number (represents XInput slot index) - // - CHAR LedNumber; - - // - // Report packet - // - XUSB_INTERRUPT_IN_PACKET Packet; - - // - // Queue for incoming control interrupt transfer - // - WDFQUEUE HoldingUsbInRequests; - - // - // Required for XInputGetCapabilities to work - // - BOOLEAN ReportedCapabilities; - - // - // Required for XInputGetCapabilities to work - // - ULONG InterruptInitStage; - - // - // Storage of binary blobs (packets) for PDO initialization - // - WDFMEMORY InterruptBlobStorage; - -} XUSB_DEVICE_DATA, *PXUSB_DEVICE_DATA; - -WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(XUSB_DEVICE_DATA, XusbGetData) - -EXTERN_C_START - -NTSTATUS -Bus_XusbSubmitReport( - WDFDEVICE Device, - ULONG SerialNo, - PXUSB_SUBMIT_REPORT Report, - _In_ BOOLEAN FromInterface -); - -// -// XUSB-specific functions -// -NTSTATUS Xusb_PreparePdo(PWDFDEVICE_INIT DeviceInit, USHORT VendorId, USHORT ProductId, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription); -NTSTATUS Xusb_PrepareHardware(WDFDEVICE Device); -NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device); -VOID Xusb_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length); -VOID Xusb_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon); -VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo); -NTSTATUS Xusb_GetUserIndex(WDFDEVICE Device, PXUSB_GET_USER_INDEX Request); - -EXTERN_C_END diff --git a/sys/XusbPdo.cpp b/sys/XusbPdo.cpp index 428dc14..7f3c55a 100644 --- a/sys/XusbPdo.cpp +++ b/sys/XusbPdo.cpp @@ -11,10 +11,12 @@ PCWSTR ViGEm::Bus::Targets::EmulationTargetXUSB::_deviceDescription = L"Virtual Xbox 360 Controller"; -ViGEm::Bus::Targets::EmulationTargetXUSB::EmulationTargetXUSB() : EmulationTargetPDO(0x045E, 0x028E) +ViGEm::Bus::Targets::EmulationTargetXUSB::EmulationTargetXUSB(ULONG Serial, LONG SessionId, USHORT VendorId, + USHORT ProductId) : EmulationTargetPDO( + Serial, SessionId, VendorId, ProductId) { - TargetType = Xbox360Wired; - UsbConfigurationDescriptionSize = XUSB_DESCRIPTOR_SIZE; + _TargetType = Xbox360Wired; + _UsbConfigurationDescriptionSize = XUSB_DESCRIPTOR_SIZE; } NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, @@ -35,7 +37,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareDevice(PWDFDEVICE_I } // Set hardware ID - RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", this->VendorId, this->ProductId); + RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", this->_VendorId, this->_ProductId); RtlUnicodeStringCopy(DeviceId, &buffer); @@ -110,7 +112,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware() dummyIface.Size = sizeof(INTERFACE); dummyIface.Version = 1; - dummyIface.Context = static_cast(this->PdoDevice); + dummyIface.Context = static_cast(this->_PdoDevice); dummyIface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; dummyIface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; @@ -128,7 +130,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware() nullptr ); - NTSTATUS status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg); + NTSTATUS status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, @@ -149,7 +151,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware() nullptr ); - status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg); + status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, @@ -170,7 +172,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware() nullptr ); - status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg); + status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, @@ -189,7 +191,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware() xusbInterface.Size = sizeof(USB_BUS_INTERFACE_USBDI_V1); xusbInterface.Version = USB_BUSIF_USBDI_VERSION_1; - xusbInterface.BusContext = static_cast(this->PdoDevice); + xusbInterface.BusContext = static_cast(this->_PdoDevice); xusbInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; xusbInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; @@ -207,7 +209,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoPrepareHardware() nullptr ); - status = WdfDeviceAddQueryInterface(this->PdoDevice, &ifaceCfg); + status = WdfDeviceAddQueryInterface(this->_PdoDevice, &ifaceCfg); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, @@ -226,7 +228,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoInitContext() PUCHAR blobBuffer; WDF_OBJECT_ATTRIBUTES_INIT(&attributes); - attributes.ParentObject = this->PdoDevice; + attributes.ParentObject = this->_PdoDevice; TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "Initializing XUSB context..."); @@ -290,7 +292,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::PdoInitContext() WDF_IO_QUEUE_CONFIG_INIT(&holdingInQueueConfig, WdfIoQueueDispatchManual); status = WdfIoQueueCreate( - this->PdoDevice, + this->_PdoDevice, &holdingInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &this->HoldingUsbInRequests @@ -458,8 +460,8 @@ VOID ViGEm::Bus::Targets::EmulationTargetXUSB::UsbGetDeviceDescriptorType(PUSB_D pDescriptor->bDeviceSubClass = 0xFF; pDescriptor->bDeviceProtocol = 0xFF; pDescriptor->bMaxPacketSize0 = 0x08; - pDescriptor->idVendor = this->VendorId; - pDescriptor->idProduct = this->ProductId; + pDescriptor->idVendor = this->_VendorId; + pDescriptor->idProduct = this->_ProductId; pDescriptor->bcdDevice = 0x0114; pDescriptor->iManufacturer = 0x01; pDescriptor->iProduct = 0x02; @@ -790,7 +792,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U /* This request is sent periodically and relies on data the "feeder" * has to supply, so we queue this request and return with STATUS_PENDING. * The request gets completed as soon as the "feeder" sent an update. */ - status = WdfRequestForwardToIoQueue(Request, this->PendingUsbInRequests); + status = WdfRequestForwardToIoQueue(Request, this->_PendingUsbInRequests); return (NT_SUCCESS(status)) ? STATUS_PENDING : status; } @@ -850,7 +852,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U // // Notify client library that PDO is ready // - KeSetEvent(&this->PdoBootNotificationEvent, 0, FALSE); + KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE); } } @@ -875,7 +877,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U } // Notify user-mode process that new data is available - status = WdfIoQueueRetrieveNextRequest(this->PendingNotificationRequests, ¬ifyRequest); + status = WdfIoQueueRetrieveNextRequest(this->_PendingNotificationRequests, ¬ifyRequest); if (NT_SUCCESS(status)) { @@ -892,7 +894,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U { // Assign values to output buffer notify->Size = sizeof(XUSB_REQUEST_NOTIFICATION); - notify->SerialNo = this->SerialNo; + notify->SerialNo = this->_SerialNo; notify->LedNumber = this->LedNumber; notify->LargeMotor = this->Rumble[3]; notify->SmallMotor = this->Rumble[4]; @@ -960,3 +962,54 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbControlTransfer(PURB Urb) return status; } + +NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::SubmitReport(PVOID NewReport) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN changed; + WDFREQUEST usbRequest; + + changed = (RtlCompareMemory(&this->Packet.Report, + &static_cast(NewReport)->Report, + sizeof(XUSB_REPORT)) != sizeof(XUSB_REPORT)); + + // Don't waste pending IRP if input hasn't changed + if (!changed) + { + TraceDbg( + TRACE_BUSENUM, + "Input report hasn't changed since last update, aborting with %!STATUS!", + status); + return status; + } + + TraceDbg( + TRACE_BUSENUM, + "Received new report, processing"); + + status = WdfIoQueueRetrieveNextRequest(this->_PendingUsbInRequests, &usbRequest); + + if (!NT_SUCCESS(status)) + return status; + + // Get pending IRP + PIRP pendingIrp = WdfRequestWdmGetIrp(usbRequest); + + // Get USB request block + PURB urb = static_cast(URB_FROM_IRP(pendingIrp)); + + // Get transfer buffer + auto Buffer = static_cast(urb->UrbBulkOrInterruptTransfer.TransferBuffer); + + urb->UrbBulkOrInterruptTransfer.TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET); + + // Copy submitted report to cache + RtlCopyBytes(&this->Packet.Report, &(static_cast(NewReport))->Report, sizeof(XUSB_REPORT)); + // Copy cached report to URB transfer buffer + RtlCopyBytes(Buffer, &this->Packet, sizeof(XUSB_INTERRUPT_IN_PACKET)); + + // Complete pending request + WdfRequestComplete(usbRequest, status); + + return status; +} diff --git a/sys/XusbPdo.hpp b/sys/XusbPdo.hpp index 31453be..281aefb 100644 --- a/sys/XusbPdo.hpp +++ b/sys/XusbPdo.hpp @@ -24,16 +24,16 @@ namespace ViGEm::Bus::Targets { return (pTransfer->PipeHandle == reinterpret_cast(0xFFFF0083)); } - + class EmulationTargetXUSB : public Core::EmulationTargetPDO { public: - EmulationTargetXUSB(); + EmulationTargetXUSB(ULONG Serial, LONG SessionId, USHORT VendorId = 0x045E, USHORT ProductId = 0x028E); ~EmulationTargetXUSB() = default; NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, - PUNICODE_STRING DeviceId, - PUNICODE_STRING DeviceDescription) override; + PUNICODE_STRING DeviceId, + PUNICODE_STRING DeviceDescription) override; NTSTATUS PdoPrepareHardware() override; @@ -46,13 +46,14 @@ namespace ViGEm::Bus::Targets NTSTATUS SelectConfiguration(PURB Urb) override; void AbortPipe() override; - + NTSTATUS UsbClassInterface(PURB Urb) override; NTSTATUS UsbGetDescriptorFromInterface(PURB Urb) override; NTSTATUS UsbSelectInterface(PURB Urb) override; NTSTATUS UsbGetStringDescriptorType(PURB Urb) override; NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) override; NTSTATUS UsbControlTransfer(PURB Urb) override; + NTSTATUS SubmitReport(PVOID NewReport) override; private: static PCWSTR _deviceDescription; diff --git a/sys/busenum.c b/sys/busenum.c deleted file mode 100644 index 6951bb8..0000000 --- a/sys/busenum.c +++ /dev/null @@ -1,727 +0,0 @@ -/* -* 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 "busenum.tmh" - -#ifdef ALLOC_PRAGMA -#pragma alloc_text (PAGE, Bus_PlugInDevice) -#pragma alloc_text (PAGE, Bus_UnPlugDevice) -#endif - - - -// -// Simulates a device plug-in event. -// -NTSTATUS Bus_PlugInDevice( - _In_ WDFDEVICE Device, - _In_ WDFREQUEST Request, - _In_ BOOLEAN IsInternal, - _Out_ size_t* Transferred) -{ - PDO_IDENTIFICATION_DESCRIPTION description; - NTSTATUS status; - PVIGEM_PLUGIN_TARGET plugIn; - WDFFILEOBJECT fileObject; - PFDO_FILE_DATA pFileData; - size_t length = 0; - WDF_OBJECT_ATTRIBUTES requestAttribs; - PFDO_PLUGIN_REQUEST_DATA pReqData; - PFDO_DEVICE_DATA pFdoData; - - PAGED_CODE(); - - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry"); - - pFdoData = FdoGetData(Device); - - status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_PLUGIN_TARGET), (PVOID)&plugIn, &length); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfRequestRetrieveInputBuffer failed with status %!STATUS!", status); - return status; - } - - if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "sizeof(VIGEM_PLUGIN_TARGET) buffer size mismatch [%d != %d]", - sizeof(VIGEM_PLUGIN_TARGET), plugIn->Size); - return STATUS_INVALID_PARAMETER; - } - - if (plugIn->SerialNo == 0) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "Serial no. 0 not allowed"); - return STATUS_INVALID_PARAMETER; - } - - *Transferred = length; - - fileObject = WdfRequestGetFileObject(Request); - if (fileObject == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p", - Request); - return STATUS_INVALID_PARAMETER; - } - - pFileData = FileObjectGetData(fileObject); - if (pFileData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "FileObjectGetData failed to get context data for 0x%p", - fileObject); - return STATUS_INVALID_PARAMETER; - } - - // - // Initialize the description with the information about the newly - // plugged in device. - // - WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); - - description.SerialNo = plugIn->SerialNo; - description.TargetType = plugIn->TargetType; - description.OwnerProcessId = CURRENT_PROCESS_ID(); - description.SessionId = pFileData->SessionId; - description.OwnerIsDriver = IsInternal; - - // Set default IDs if supplied values are invalid - if (plugIn->VendorId == 0 || plugIn->ProductId == 0) - { - switch (plugIn->TargetType) - { - case Xbox360Wired: - - description.VendorId = 0x045E; - description.ProductId = 0x028E; - - break; - case DualShock4Wired: - - description.VendorId = 0x054C; - description.ProductId = 0x05C4; - - break; - default: - return STATUS_NOT_SUPPORTED; - } - } - else - { - description.VendorId = plugIn->VendorId; - description.ProductId = plugIn->ProductId; - } - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "New PDO properties: serial = %d, type = %d, pid = %d, session = %d, internal = %d, vid = 0x%04X, pid = 0x%04X", - description.SerialNo, - description.TargetType, - description.OwnerProcessId, - description.SessionId, - description.OwnerIsDriver, - description.VendorId, - description.ProductId - ); - - WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Current pending requests count: %d", - WdfCollectionGetCount(pFdoData->PendingPluginRequests)); - - status = WdfChildListAddOrUpdateChildDescriptionAsPresent( - WdfFdoGetDefaultChildList(Device), - &description.Header, - NULL - ); - - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status %!STATUS!", - status); - - goto pluginEnd; - } - - // - // The requested serial number is already in use - // - if (status == STATUS_OBJECT_NAME_EXISTS) - { - status = STATUS_INVALID_PARAMETER; - - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "The described PDO already exists (%!STATUS!)", - status); - - goto pluginEnd; - } - - WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttribs, FDO_PLUGIN_REQUEST_DATA); - - // - // Allocate context data to request - // - status = WdfObjectAllocateContext(Request, &requestAttribs, (PVOID)&pReqData); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfObjectAllocateContext failed with status %!STATUS!", - status); - - goto pluginEnd; - } - - // - // Glue current serial to request - // - pReqData->Serial = plugIn->SerialNo; - - // - // Timestamp the request to track its age - // - pReqData->Timestamp = KeQueryPerformanceCounter(&pReqData->Frequency); - - // - // Keep track of pending request in collection - // - status = WdfCollectionAdd(pFdoData->PendingPluginRequests, Request); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfCollectionAdd failed with status %!STATUS!", - status); - - goto pluginEnd; - } - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_BUSENUM, - "Added item with serial: %d", - plugIn->SerialNo); - - // - // At least one request present in the collection; start clean-up timer - // - WdfTimerStart( - pFdoData->PendingPluginRequestsCleanupTimer, - WDF_REL_TIMEOUT_IN_MS(ORC_TIMER_PERIODIC_DUE_TIME) - ); - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_DRIVER, - "Started periodic timer"); - - status = NT_SUCCESS(status) ? STATUS_PENDING : status; - -pluginEnd: - - WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock); - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status); - - return status; -} - -// -// Simulates a device unplug event. -// -NTSTATUS Bus_UnPlugDevice( - _In_ WDFDEVICE Device, - _In_ WDFREQUEST Request, - _In_ BOOLEAN IsInternal, - _Out_ size_t* Transferred) -{ - NTSTATUS status; - WDFDEVICE hChild; - WDFCHILDLIST list; - WDF_CHILD_LIST_ITERATOR iterator; - WDF_CHILD_RETRIEVE_INFO childInfo; - PDO_IDENTIFICATION_DESCRIPTION description; - BOOLEAN unplugAll; - PVIGEM_UNPLUG_TARGET unPlug; - WDFFILEOBJECT fileObject; - PFDO_FILE_DATA pFileData = NULL; - size_t length = 0; - - PAGED_CODE(); - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry"); - - status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_UNPLUG_TARGET), (PVOID)&unPlug, &length); - - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfRequestRetrieveInputBuffer failed with status %!STATUS!", - status); - return status; - } - - if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "sizeof(VIGEM_UNPLUG_TARGET) buffer size mismatch [%d != %d]", - sizeof(VIGEM_UNPLUG_TARGET), unPlug->Size); - return STATUS_INVALID_PARAMETER; - } - - *Transferred = length; - unplugAll = (unPlug->SerialNo == 0); - - if (!IsInternal) - { - fileObject = WdfRequestGetFileObject(Request); - if (fileObject == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p", - Request); - return STATUS_INVALID_PARAMETER; - } - - pFileData = FileObjectGetData(fileObject); - if (pFileData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "FileObjectGetData failed to get context data for 0x%p", - fileObject); - return STATUS_INVALID_PARAMETER; - } - } - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Starting child list traversal"); - - list = WdfFdoGetDefaultChildList(Device); - - WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren); - - WdfChildListBeginIteration(list, &iterator); - - for (;;) - { - WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header); - WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); - - status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo); - - // Error or no more children, end loop - if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "WdfChildListRetrieveNextDevice returned with status %!STATUS!", - status); - break; - } - - // If unable to retrieve device - if (childInfo.Status != WdfChildListRetrieveDeviceSuccess) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "childInfo.Status = %d", - childInfo.Status); - continue; - } - - // Child isn't the one we looked for, skip - if (!unplugAll && description.SerialNo != unPlug->SerialNo) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Seeking serial mismatch: %d != %d", - description.SerialNo, - unPlug->SerialNo); - continue; - } - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "description.SessionId = %d, pFileData->SessionId = %d", - description.SessionId, - pFileData->SessionId); - - // Only unplug owned children - if (IsInternal || description.SessionId == pFileData->SessionId) - { - // Unplug child - status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfChildListUpdateChildDescriptionAsMissing failed with status %!STATUS!", - status); - } - } - } - - WdfChildListEndIteration(list, &iterator); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Finished child list traversal"); - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", STATUS_SUCCESS); - - return STATUS_SUCCESS; -} - -// -// Sends a report update to an XUSB PDO. -// -NTSTATUS Bus_XusbSubmitReport(WDFDEVICE Device, ULONG SerialNo, PXUSB_SUBMIT_REPORT Report, BOOLEAN FromInterface) -{ - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry"); - - return Bus_SubmitReport(Device, SerialNo, Report, FromInterface); -} - -// -// Queues an inverted call to receive XUSB-specific updates. -// -NTSTATUS Bus_QueueNotification(WDFDEVICE Device, ULONG SerialNo, WDFREQUEST Request) -{ - NTSTATUS status = STATUS_INVALID_PARAMETER; - WDFDEVICE hChild; - PPDO_DEVICE_DATA pdoData; - PXUSB_DEVICE_DATA xusbData; - PDS4_DEVICE_DATA ds4Data; - - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry"); - - hChild = Bus_GetPdo(Device, SerialNo); - - // Validate child - if (hChild == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "Bus_GetPdo: PDO with serial %d not found", - SerialNo); - return STATUS_NO_SUCH_DEVICE; - } - - // Check common context - pdoData = PdoGetData(hChild); - if (pdoData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "PdoGetData failed"); - return STATUS_INVALID_PARAMETER; - } - - // Check if caller owns this PDO - if (!IS_OWNER(pdoData)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "PDO & Request ownership mismatch: %d != %d", - pdoData->OwnerProcessId, - CURRENT_PROCESS_ID()); - return STATUS_ACCESS_DENIED; - } - - // Queue the request for later completion by the PDO and return STATUS_PENDING - switch (pdoData->TargetType) - { - case Xbox360Wired: - - xusbData = XusbGetData(hChild); - - if (xusbData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "XusbGetData failed"); - break; - } - - status = WdfRequestForwardToIoQueue(Request, pdoData->PendingNotificationRequests); - - break; - case DualShock4Wired: - - ds4Data = Ds4GetData(hChild); - - if (ds4Data == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "Ds4GetData failed"); - break; - } - - status = WdfRequestForwardToIoQueue(Request, pdoData->PendingNotificationRequests); - - break; - default: - status = STATUS_NOT_SUPPORTED; - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_BUSENUM, - "Unknown target type: %d (%!STATUS!)", - pdoData->TargetType, - status); - break; - } - - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "WdfRequestForwardToIoQueue failed with status %!STATUS!", - status); - } - - status = (NT_SUCCESS(status)) ? STATUS_PENDING : status; - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status); - - return status; -} - -// -// Sends a report update to a DS4 PDO. -// -NTSTATUS Bus_Ds4SubmitReport(WDFDEVICE Device, ULONG SerialNo, PDS4_SUBMIT_REPORT Report, BOOLEAN FromInterface) -{ - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry"); - - return Bus_SubmitReport(Device, SerialNo, Report, FromInterface); -} - -WDFDEVICE Bus_GetPdo(IN WDFDEVICE Device, IN ULONG SerialNo) -{ - WDFCHILDLIST list; - WDF_CHILD_RETRIEVE_INFO info; - - list = WdfFdoGetDefaultChildList(Device); - - PDO_IDENTIFICATION_DESCRIPTION description; - - WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); - - description.SerialNo = SerialNo; - - WDF_CHILD_RETRIEVE_INFO_INIT(&info, &description.Header); - - return WdfChildListRetrievePdo(list, &info); -} - -NTSTATUS Bus_SubmitReport(WDFDEVICE Device, ULONG SerialNo, PVOID Report, BOOLEAN FromInterface) -{ - NTSTATUS status = STATUS_SUCCESS; - WDFDEVICE hChild; - PPDO_DEVICE_DATA pdoData; - WDFREQUEST usbRequest; - PIRP pendingIrp; - BOOLEAN changed; - - - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry"); - - hChild = Bus_GetPdo(Device, SerialNo); - - // Validate child - if (hChild == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "Bus_GetPdo: PDO with serial %d not found", - SerialNo); - return STATUS_NO_SUCH_DEVICE; - } - - // Check common context - pdoData = PdoGetData(hChild); - if (pdoData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "PdoGetData failed"); - return STATUS_INVALID_PARAMETER; - } - - // Check if caller owns this PDO - if (!FromInterface && !IS_OWNER(pdoData)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_BUSENUM, - "PDO & Request ownership mismatch: %d != %d", - pdoData->OwnerProcessId, - CURRENT_PROCESS_ID()); - return STATUS_ACCESS_DENIED; - } - - // Check if input is different from previous value - switch (pdoData->TargetType) - { - case Xbox360Wired: - - changed = (RtlCompareMemory(&XusbGetData(hChild)->Packet.Report, - &((PXUSB_SUBMIT_REPORT)Report)->Report, - sizeof(XUSB_REPORT)) != sizeof(XUSB_REPORT)); - - break; - case DualShock4Wired: - - changed = TRUE; - - break; - default: - - changed = FALSE; - - break; - } - - // Don't waste pending IRP if input hasn't changed - if (!changed) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Input report hasn't changed since last update, aborting with %!STATUS!", - status); - return status; - } - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Received new report, processing"); - - // Get pending USB request - switch (pdoData->TargetType) - { - case Xbox360Wired: - - status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest); - - break; - case DualShock4Wired: - - status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest); - - break; - default: - - status = STATUS_NOT_SUPPORTED; - - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_BUSENUM, - "Unknown target type: %d (%!STATUS!)", - pdoData->TargetType, - status); - - goto endSubmitReport; - } - - if (status == STATUS_PENDING) - goto endSubmitReport; - else if (!NT_SUCCESS(status)) - goto endSubmitReport; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_BUSENUM, - "Processing pending IRP"); - - // Get pending IRP - pendingIrp = WdfRequestWdmGetIrp(usbRequest); - - // Get USB request block - PURB urb = (PURB)URB_FROM_IRP(pendingIrp); - - // Get transfer buffer - PUCHAR Buffer = (PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer; - - switch (pdoData->TargetType) - { - case Xbox360Wired: - - urb->UrbBulkOrInterruptTransfer.TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET); - - // Copy submitted report to cache - RtlCopyBytes(&XusbGetData(hChild)->Packet.Report, &((PXUSB_SUBMIT_REPORT)Report)->Report, sizeof(XUSB_REPORT)); - // Copy cached report to URB transfer buffer - RtlCopyBytes(Buffer, &XusbGetData(hChild)->Packet, sizeof(XUSB_INTERRUPT_IN_PACKET)); - - break; - case DualShock4Wired: - - urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE; - - /* Copy report to cache and transfer buffer - * Skip first byte as it contains the never changing report id */ - RtlCopyBytes(Ds4GetData(hChild)->Report + 1, &((PDS4_SUBMIT_REPORT)Report)->Report, sizeof(DS4_REPORT)); - - if (Buffer) - RtlCopyBytes(Buffer, Ds4GetData(hChild)->Report, DS4_REPORT_SIZE); - - break; - default: - status = STATUS_INVALID_PARAMETER; - break; - } - - // Complete pending request - WdfRequestComplete(usbRequest, status); - -endSubmitReport: - - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status); - - return status; -} - diff --git a/sys/busenum.cpp b/sys/busenum.cpp new file mode 100644 index 0000000..dd55cce --- /dev/null +++ b/sys/busenum.cpp @@ -0,0 +1,438 @@ +/* +* 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 "busenum.tmh" + +#include "EmulationTargetPDO.hpp" +#include "XusbPdo.hpp" +#include "Ds4Pdo.hpp" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, Bus_PlugInDevice) +#pragma alloc_text (PAGE, Bus_UnPlugDevice) +#endif + +using ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION; +using ViGEm::Bus::Core::EmulationTargetPDO; +using ViGEm::Bus::Targets::EmulationTargetXUSB; +using ViGEm::Bus::Targets::EmulationTargetDS4; + +// +// Simulates a device plug-in event. +// +EXTERN_C NTSTATUS Bus_PlugInDevice( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _In_ BOOLEAN IsInternal, + _Out_ size_t* Transferred) +{ + PDO_IDENTIFICATION_DESCRIPTION description; + NTSTATUS status; + PVIGEM_PLUGIN_TARGET plugIn; + WDFFILEOBJECT fileObject; + PFDO_FILE_DATA pFileData; + size_t length = 0; + + UNREFERENCED_PARAMETER(IsInternal); + + PAGED_CODE(); + + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry"); + + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(VIGEM_PLUGIN_TARGET), + reinterpret_cast(&plugIn), + &length + ); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "WdfRequestRetrieveInputBuffer failed with status %!STATUS!", status); + return status; + } + + if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "sizeof(VIGEM_PLUGIN_TARGET) buffer size mismatch [%d != %d]", + sizeof(VIGEM_PLUGIN_TARGET), plugIn->Size); + return STATUS_INVALID_PARAMETER; + } + + if (plugIn->SerialNo == 0) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "Serial no. 0 not allowed"); + return STATUS_INVALID_PARAMETER; + } + + *Transferred = length; + + fileObject = WdfRequestGetFileObject(Request); + if (fileObject == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p", + Request); + return STATUS_INVALID_PARAMETER; + } + + pFileData = FileObjectGetData(fileObject); + if (pFileData == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "FileObjectGetData failed to get context data for 0x%p", + fileObject); + return STATUS_INVALID_PARAMETER; + } + + // + // Initialize the description with the information about the newly + // plugged in device. + // + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); + + description.SerialNo = plugIn->SerialNo; + description.SessionId = pFileData->SessionId; + + // Set default IDs if supplied values are invalid + if (plugIn->VendorId == 0 || plugIn->ProductId == 0) + { + switch (plugIn->TargetType) + { + case Xbox360Wired: + + description.Target = new EmulationTargetXUSB(plugIn->SerialNo, pFileData->SessionId); + + break; + case DualShock4Wired: + + description.Target = new EmulationTargetDS4(plugIn->SerialNo, pFileData->SessionId); + + break; + default: + return STATUS_NOT_SUPPORTED; + } + } + else + { + switch (plugIn->TargetType) + { + case Xbox360Wired: + + description.Target = new EmulationTargetXUSB( + plugIn->SerialNo, + pFileData->SessionId, + plugIn->VendorId, + plugIn->ProductId + ); + + break; + case DualShock4Wired: + + description.Target = new EmulationTargetDS4( + plugIn->SerialNo, + pFileData->SessionId, + plugIn->VendorId, + plugIn->ProductId + ); + + break; + default: + return STATUS_NOT_SUPPORTED; + } + } + + status = WdfChildListAddOrUpdateChildDescriptionAsPresent( + WdfFdoGetDefaultChildList(Device), + &description.Header, + NULL + ); + + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status %!STATUS!", + status); + + goto pluginEnd; + } + + // + // The requested serial number is already in use + // + if (status == STATUS_OBJECT_NAME_EXISTS) + { + status = STATUS_INVALID_PARAMETER; + + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "The described PDO already exists (%!STATUS!)", + status); + + goto pluginEnd; + } + + //status = NT_SUCCESS(status) ? STATUS_PENDING : status; + +pluginEnd: + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status); + + return status; +} + +// +// Simulates a device unplug event. +// +EXTERN_C NTSTATUS Bus_UnPlugDevice( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _In_ BOOLEAN IsInternal, + _Out_ size_t* Transferred) +{ + NTSTATUS status; + WDFDEVICE hChild; + WDFCHILDLIST list; + WDF_CHILD_LIST_ITERATOR iterator; + WDF_CHILD_RETRIEVE_INFO childInfo; + PDO_IDENTIFICATION_DESCRIPTION description; + BOOLEAN unplugAll; + PVIGEM_UNPLUG_TARGET unPlug; + WDFFILEOBJECT fileObject; + PFDO_FILE_DATA pFileData = NULL; + size_t length = 0; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry"); + + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(VIGEM_UNPLUG_TARGET), + (PVOID*)&unPlug, + &length + ); + + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "WdfRequestRetrieveInputBuffer failed with status %!STATUS!", + status); + return status; + } + + if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "sizeof(VIGEM_UNPLUG_TARGET) buffer size mismatch [%d != %d]", + sizeof(VIGEM_UNPLUG_TARGET), unPlug->Size); + return STATUS_INVALID_PARAMETER; + } + + *Transferred = length; + unplugAll = (unPlug->SerialNo == 0); + + fileObject = WdfRequestGetFileObject(Request); + if (fileObject == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p", + Request); + return STATUS_INVALID_PARAMETER; + } + + pFileData = FileObjectGetData(fileObject); + if (pFileData == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "FileObjectGetData failed to get context data for 0x%p", + fileObject); + return STATUS_INVALID_PARAMETER; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_BUSENUM, + "Starting child list traversal"); + + list = WdfFdoGetDefaultChildList(Device); + + WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren); + + WdfChildListBeginIteration(list, &iterator); + + for (;;) + { + WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header); + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); + + status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo); + + // Error or no more children, end loop + if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES) + { + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_BUSENUM, + "WdfChildListRetrieveNextDevice returned with status %!STATUS!", + status); + break; + } + + // If unable to retrieve device + if (childInfo.Status != WdfChildListRetrieveDeviceSuccess) + { + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_BUSENUM, + "childInfo.Status = %d", + childInfo.Status); + continue; + } + + // Child isn't the one we looked for, skip + if (!unplugAll && description.SerialNo != unPlug->SerialNo) + { + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_BUSENUM, + "Seeking serial mismatch: %d != %d", + description.SerialNo, + unPlug->SerialNo); + continue; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_BUSENUM, + "description.SessionId = %d, pFileData->SessionId = %d", + description.SessionId, + pFileData->SessionId); + + // Only unplug owned children + if (IsInternal || description.SessionId == pFileData->SessionId) + { + // Unplug child + status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "WdfChildListUpdateChildDescriptionAsMissing failed with status %!STATUS!", + status); + } + } + } + + WdfChildListEndIteration(list, &iterator); + + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_BUSENUM, + "Finished child list traversal"); + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", STATUS_SUCCESS); + + return STATUS_SUCCESS; +} + +// +// Sends a report update to an XUSB PDO. +// +EXTERN_C NTSTATUS Bus_XusbSubmitReport(WDFDEVICE Device, ULONG SerialNo, PXUSB_SUBMIT_REPORT Report, BOOLEAN FromInterface) +{ + TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry"); + + return Bus_SubmitReport(Device, SerialNo, Report, FromInterface); +} + +// +// Queues an inverted call to receive XUSB-specific updates. +// +EXTERN_C NTSTATUS Bus_QueueNotification(WDFDEVICE Device, ULONG SerialNo, WDFREQUEST Request) +{ + NTSTATUS status; + + TraceDbg(TRACE_BUSENUM, "%!FUNC! Entry"); + + const auto pdo = EmulationTargetPDO::GetPdoBySerial(Device, SerialNo); + + // Validate child + if (pdo == nullptr) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "PDO with serial %d not found", + SerialNo); + return STATUS_NO_SUCH_DEVICE; + } + + status = pdo->EnqueueNotification(Request); + + status = (NT_SUCCESS(status)) ? STATUS_PENDING : status; + + TraceDbg(TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status); + + return status; +} + +// +// Sends a report update to a DS4 PDO. +// +NTSTATUS Bus_Ds4SubmitReport(WDFDEVICE Device, ULONG SerialNo, PDS4_SUBMIT_REPORT Report, BOOLEAN FromInterface) +{ + TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_BUSENUM, "%!FUNC! Entry"); + + return Bus_SubmitReport(Device, SerialNo, Report, FromInterface); +} + +EXTERN_C NTSTATUS Bus_SubmitReport(WDFDEVICE Device, ULONG SerialNo, PVOID Report, BOOLEAN FromInterface) +{ + UNREFERENCED_PARAMETER(FromInterface); + + const auto pdo = EmulationTargetPDO::GetPdoBySerial(Device, SerialNo); + + // Validate child + if (pdo == nullptr) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSENUM, + "PDO with serial %d not found", + SerialNo); + return STATUS_NO_SUCH_DEVICE; + } + + return pdo->SubmitReport(Report); +} diff --git a/sys/busenum.h b/sys/busenum.h index af1cdbe..1c8c1a1 100644 --- a/sys/busenum.h +++ b/sys/busenum.h @@ -41,24 +41,17 @@ #include #include "Context.h" #include "Util.h" -#include "UsbPdo.h" -#include "Xusb.h" -#include "Ds4.h" + #pragma region Macros -#define MAX_INSTANCE_ID_LEN 80 #define HID_LANGUAGE_ID_LENGTH 0x04 -#define HID_REQUEST_GET_REPORT 0x01 -#define HID_REQUEST_SET_REPORT 0x09 -#define HID_REPORT_TYPE_FEATURE 0x03 + #define VIGEM_POOL_TAG 0x45476956 // "EGiV" -#define XUSB_POOL_TAG 'BSUX' #define DRIVERNAME "ViGEm: " -#define MAX_HARDWARE_ID_LENGTH 0xFF #define ORC_PC_FREQUENCY_DIVIDER 1000 #define ORC_TIMER_START_DELAY 500 // ms @@ -67,27 +60,9 @@ #pragma endregion -#pragma region Helpers -// -// Extracts the HID Report ID from the supplied class request. -// -#define HID_GET_REPORT_ID(_req_) ((_req_->Value) & 0xFF) - -// -// Extracts the HID Report type from the supplied class request. -// -#define HID_GET_REPORT_TYPE(_req_) ((_req_->Value >> 8) & 0xFF) - -// -// Some insane macro-magic =3 -// -#define P99_PROTECT(...) __VA_ARGS__ -#define COPY_BYTE_ARRAY(_dst_, _bytes_) do {BYTE b[] = _bytes_; \ - RtlCopyMemory(_dst_, b, RTL_NUMBER_OF_V1(b)); } while (0) - -#pragma endregion +EXTERN_C_START #pragma region WDF callback prototypes @@ -107,8 +82,6 @@ EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Pdo_EvtIoInternalDeviceControl; EVT_WDF_OBJECT_CONTEXT_CLEANUP Bus_EvtDriverContextCleanup; -EVT_WDF_TIMER Bus_PlugInRequestCleanUpEvtTimerFunc; - #pragma endregion #pragma region Bus enumeration-specific functions @@ -129,13 +102,6 @@ Bus_UnPlugDevice( _Out_ size_t* Transferred ); -NTSTATUS -Bus_CreatePdo( - _In_ WDFDEVICE Device, - _In_ PWDFDEVICE_INIT ChildInit, - _In_ PPDO_IDENTIFICATION_DESCRIPTION Description -); - NTSTATUS Bus_QueueNotification( WDFDEVICE Device, @@ -151,11 +117,6 @@ Bus_SubmitReport( _In_ BOOLEAN FromInterface ); -WDFDEVICE -Bus_GetPdo( - IN WDFDEVICE Device, - IN ULONG SerialNo); - VOID Bus_PdoStageResult( _In_ PINTERFACE InterfaceHeader, @@ -166,3 +127,4 @@ Bus_PdoStageResult( #pragma endregion +EXTERN_C_END diff --git a/sys/buspdo.c b/sys/buspdo.c deleted file mode 100644 index 05cb933..0000000 --- a/sys/buspdo.c +++ /dev/null @@ -1,870 +0,0 @@ -/* -* 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; - - 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; - } - 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; - 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; - - 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); -} - diff --git a/sys/buspdo.cpp b/sys/buspdo.cpp new file mode 100644 index 0000000..89a83ea --- /dev/null +++ b/sys/buspdo.cpp @@ -0,0 +1,68 @@ +/* +* 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 "EmulationTargetPDO.hpp" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo) +#endif + +EXTERN_C NTSTATUS Bus_EvtDeviceListCreatePdo( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, + PWDFDEVICE_INIT ChildInit) +{ + ViGEm::Bus::Core::PPDO_IDENTIFICATION_DESCRIPTION pDesc; + + PAGED_CODE(); + + pDesc = CONTAINING_RECORD(IdentificationDescription, ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION, Header); + + return pDesc->Target->PdoCreateDevice(WdfChildListGetDevice(DeviceList), ChildInit); +} + +// +// Compares two children on the bus based on their serial numbers. +// +EXTERN_C BOOLEAN Bus_EvtChildListIdentificationDescriptionCompare( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription) +{ + ViGEm::Bus::Core::PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs; + + UNREFERENCED_PARAMETER(DeviceList); + + lhs = CONTAINING_RECORD(FirstIdentificationDescription, + ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION, + Header); + rhs = CONTAINING_RECORD(SecondIdentificationDescription, + ViGEm::Bus::Core::PDO_IDENTIFICATION_DESCRIPTION, + Header); + + return (lhs->Target == rhs->Target) ? TRUE : FALSE; +} diff --git a/sys/usbpdo.c b/sys/usbpdo.c deleted file mode 100644 index a7cb329..0000000 --- a/sys/usbpdo.c +++ /dev/null @@ -1,1263 +0,0 @@ -/* -* 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 "usbpdo.tmh" - - -// -// Dummy function to satisfy USB interface -// -BOOLEAN USB_BUSIFFN UsbPdo_IsDeviceHighSpeed(IN PVOID BusContext) -{ - UNREFERENCED_PARAMETER(BusContext); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_USBPDO, - "IsDeviceHighSpeed: TRUE"); - - return TRUE; -} - -// -// Dummy function to satisfy USB interface -// -NTSTATUS USB_BUSIFFN UsbPdo_QueryBusInformation( - IN PVOID BusContext, - IN ULONG Level, - IN OUT PVOID BusInformationBuffer, - IN OUT PULONG BusInformationBufferLength, - OUT PULONG BusInformationActualLength -) -{ - UNREFERENCED_PARAMETER(BusContext); - UNREFERENCED_PARAMETER(Level); - UNREFERENCED_PARAMETER(BusInformationBuffer); - UNREFERENCED_PARAMETER(BusInformationBufferLength); - UNREFERENCED_PARAMETER(BusInformationActualLength); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_USBPDO, - "QueryBusInformation: %!STATUS!", STATUS_UNSUCCESSFUL); - - return STATUS_UNSUCCESSFUL; -} - -// -// Dummy function to satisfy USB interface -// -NTSTATUS USB_BUSIFFN UsbPdo_SubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb) -{ - UNREFERENCED_PARAMETER(BusContext); - UNREFERENCED_PARAMETER(Urb); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_USBPDO, - "SubmitIsoOutUrb: %!STATUS!", STATUS_UNSUCCESSFUL); - - return STATUS_UNSUCCESSFUL; -} - -// -// Dummy function to satisfy USB interface -// -NTSTATUS USB_BUSIFFN UsbPdo_QueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame) -{ - UNREFERENCED_PARAMETER(BusContext); - UNREFERENCED_PARAMETER(CurrentUsbFrame); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_USBPDO, - "QueryBusTime: %!STATUS!", STATUS_UNSUCCESSFUL); - - return STATUS_UNSUCCESSFUL; -} - -// -// Dummy function to satisfy USB interface -// -VOID USB_BUSIFFN UsbPdo_GetUSBDIVersion( - IN PVOID BusContext, - IN OUT PUSBD_VERSION_INFORMATION VersionInformation, - IN OUT PULONG HcdCapabilities -) -{ - UNREFERENCED_PARAMETER(BusContext); - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_USBPDO, - "GetUSBDIVersion: 0x500, 0x200"); - - if (VersionInformation != NULL) - { - VersionInformation->USBDI_Version = 0x500; /* Usbport */ - VersionInformation->Supported_USB_Version = 0x200; /* USB 2.0 */ - } - - if (HcdCapabilities != NULL) - { - *HcdCapabilities = 0; - } -} - -// -// Set device descriptor to identify the current USB device. -// -NTSTATUS UsbPdo_GetDeviceDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon) -{ - PUSB_DEVICE_DESCRIPTOR pDescriptor = (PUSB_DEVICE_DESCRIPTOR)urb->UrbControlDescriptorRequest.TransferBuffer; - - switch (pCommon->TargetType) - { - case Xbox360Wired: - - Xusb_GetDeviceDescriptorType(pDescriptor, pCommon); - - break; - - case DualShock4Wired: - - Ds4_GetDeviceDescriptorType(pDescriptor, pCommon); - - break; - - default: - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -// -// Set configuration descriptor, expose interfaces and endpoints. -// -NTSTATUS UsbPdo_GetConfigurationDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon) -{ - PUCHAR Buffer = (PUCHAR)urb->UrbControlDescriptorRequest.TransferBuffer; - - // First request just gets required buffer size back - if (urb->UrbControlDescriptorRequest.TransferBufferLength == sizeof(USB_CONFIGURATION_DESCRIPTOR)) - { - ULONG length = sizeof(USB_CONFIGURATION_DESCRIPTOR); - - switch (pCommon->TargetType) - { - case Xbox360Wired: - - Xusb_GetConfigurationDescriptorType(Buffer, length); - - break; - case DualShock4Wired: - - Ds4_GetConfigurationDescriptorType(Buffer, length); - - break; - default: - return STATUS_UNSUCCESSFUL; - } - } - - ULONG length = urb->UrbControlDescriptorRequest.TransferBufferLength; - - // Second request can store the whole descriptor - switch (pCommon->TargetType) - { - case Xbox360Wired: - - if (length >= XUSB_DESCRIPTOR_SIZE) - { - Xusb_GetConfigurationDescriptorType(Buffer, XUSB_DESCRIPTOR_SIZE); - } - - break; - case DualShock4Wired: - - if (length >= DS4_DESCRIPTOR_SIZE) - { - Ds4_GetConfigurationDescriptorType(Buffer, DS4_DESCRIPTOR_SIZE); - } - - break; - default: - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -// -// Set device string descriptors (currently only used in DS4 emulation). -// -NTSTATUS UsbPdo_GetStringDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon) -{ - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - "Index = %d", - urb->UrbControlDescriptorRequest.Index); - - switch (pCommon->TargetType) - { - case DualShock4Wired: - { - switch (urb->UrbControlDescriptorRequest.Index) - { - case 0: - { - // "American English" - UCHAR LangId[HID_LANGUAGE_ID_LENGTH] = - { - 0x04, 0x03, 0x09, 0x04 - }; - - urb->UrbControlDescriptorRequest.TransferBufferLength = HID_LANGUAGE_ID_LENGTH; - RtlCopyBytes(urb->UrbControlDescriptorRequest.TransferBuffer, LangId, HID_LANGUAGE_ID_LENGTH); - - break; - } - case 1: - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - "LanguageId = 0x%X", - urb->UrbControlDescriptorRequest.LanguageId); - - if (urb->UrbControlDescriptorRequest.TransferBufferLength < DS4_MANUFACTURER_NAME_LENGTH) - { - PUSB_STRING_DESCRIPTOR pDesc = (PUSB_STRING_DESCRIPTOR)urb->UrbControlDescriptorRequest.TransferBuffer; - pDesc->bLength = DS4_MANUFACTURER_NAME_LENGTH; - break; - } - - // "Sony Computer Entertainment" - UCHAR ManufacturerString[DS4_MANUFACTURER_NAME_LENGTH] = - { - 0x38, 0x03, 0x53, 0x00, 0x6F, 0x00, 0x6E, 0x00, - 0x79, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, - 0x6D, 0x00, 0x70, 0x00, 0x75, 0x00, 0x74, 0x00, - 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x45, 0x00, - 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, - 0x74, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6E, 0x00, - 0x6D, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x74, 0x00 - }; - - urb->UrbControlDescriptorRequest.TransferBufferLength = DS4_MANUFACTURER_NAME_LENGTH; - RtlCopyBytes(urb->UrbControlDescriptorRequest.TransferBuffer, ManufacturerString, DS4_MANUFACTURER_NAME_LENGTH); - - break; - } - case 2: - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - "LanguageId = 0x%X", - urb->UrbControlDescriptorRequest.LanguageId); - - if (urb->UrbControlDescriptorRequest.TransferBufferLength < DS4_PRODUCT_NAME_LENGTH) - { - PUSB_STRING_DESCRIPTOR pDesc = (PUSB_STRING_DESCRIPTOR)urb->UrbControlDescriptorRequest.TransferBuffer; - pDesc->bLength = DS4_PRODUCT_NAME_LENGTH; - break; - } - - // "Wireless Controller" - UCHAR ProductString[DS4_PRODUCT_NAME_LENGTH] = - { - 0x28, 0x03, 0x57, 0x00, 0x69, 0x00, 0x72, 0x00, - 0x65, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x73, 0x00, - 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, - 0x6E, 0x00, 0x74, 0x00, 0x72, 0x00, 0x6F, 0x00, - 0x6C, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x72, 0x00 - }; - - urb->UrbControlDescriptorRequest.TransferBufferLength = DS4_PRODUCT_NAME_LENGTH; - RtlCopyBytes(urb->UrbControlDescriptorRequest.TransferBuffer, ProductString, DS4_PRODUCT_NAME_LENGTH); - - break; - } - default: - break; - } - - break; - } - default: - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -// -// Fakes a successfully selected configuration. -// -NTSTATUS UsbPdo_SelectConfiguration(PURB urb, PPDO_DEVICE_DATA pCommon) -{ - PUSBD_INTERFACE_INFORMATION pInfo; - - pInfo = &urb->UrbSelectConfiguration.Interface; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: TotalLength %d", - urb->UrbHeader.Length); - - if (urb->UrbHeader.Length == sizeof(struct _URB_SELECT_CONFIGURATION)) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: NULL ConfigurationDescriptor"); - return STATUS_SUCCESS; - } - - switch (pCommon->TargetType) - { - case Xbox360Wired: - - if (urb->UrbHeader.Length < XUSB_CONFIGURATION_SIZE) - { - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Invalid ConfigurationDescriptor"); - return STATUS_INVALID_PARAMETER; - } - - Xusb_SelectConfiguration(pInfo); - - break; - - case DualShock4Wired: - - if (urb->UrbHeader.Length < DS4_CONFIGURATION_SIZE) - { - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Invalid ConfigurationDescriptor"); - return STATUS_INVALID_PARAMETER; - } - - Ds4_SelectConfiguration(pInfo); - - break; - - default: - return STATUS_UNSUCCESSFUL; - } - - return STATUS_SUCCESS; -} - -// -// Fakes a successfully selected interface. -// -NTSTATUS UsbPdo_SelectInterface(PURB urb, PPDO_DEVICE_DATA pCommon) -{ - PUSBD_INTERFACE_INFORMATION pInfo = &urb->UrbSelectInterface.Interface; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_INTERFACE: Length %d, Interface %d, Alternate %d, Pipes %d", - (int)pInfo->Length, - (int)pInfo->InterfaceNumber, - (int)pInfo->AlternateSetting, - pInfo->NumberOfPipes); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_SELECT_INTERFACE: Class %d, SubClass %d, Protocol %d", - (int)pInfo->Class, - (int)pInfo->SubClass, - (int)pInfo->Protocol); - - switch (pCommon->TargetType) - { - case Xbox360Wired: - { - if (pInfo->InterfaceNumber == 1) - { - pInfo[0].Class = 0xFF; - pInfo[0].SubClass = 0x5D; - pInfo[0].Protocol = 0x03; - pInfo[0].NumberOfPipes = 0x04; - - pInfo[0].InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; - - pInfo[0].Pipes[0].MaximumTransferSize = 0x00400000; - pInfo[0].Pipes[0].MaximumPacketSize = 0x20; - pInfo[0].Pipes[0].EndpointAddress = 0x82; - pInfo[0].Pipes[0].Interval = 0x04; - pInfo[0].Pipes[0].PipeType = 0x03; - pInfo[0].Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0082; - pInfo[0].Pipes[0].PipeFlags = 0x00; - - pInfo[0].Pipes[1].MaximumTransferSize = 0x00400000; - pInfo[0].Pipes[1].MaximumPacketSize = 0x20; - pInfo[0].Pipes[1].EndpointAddress = 0x02; - pInfo[0].Pipes[1].Interval = 0x08; - pInfo[0].Pipes[1].PipeType = 0x03; - pInfo[0].Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0002; - pInfo[0].Pipes[1].PipeFlags = 0x00; - - pInfo[0].Pipes[2].MaximumTransferSize = 0x00400000; - pInfo[0].Pipes[2].MaximumPacketSize = 0x20; - pInfo[0].Pipes[2].EndpointAddress = 0x83; - pInfo[0].Pipes[2].Interval = 0x08; - pInfo[0].Pipes[2].PipeType = 0x03; - pInfo[0].Pipes[2].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0083; - pInfo[0].Pipes[2].PipeFlags = 0x00; - - pInfo[0].Pipes[3].MaximumTransferSize = 0x00400000; - pInfo[0].Pipes[3].MaximumPacketSize = 0x20; - pInfo[0].Pipes[3].EndpointAddress = 0x03; - pInfo[0].Pipes[3].Interval = 0x08; - pInfo[0].Pipes[3].PipeType = 0x03; - pInfo[0].Pipes[3].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003; - pInfo[0].Pipes[3].PipeFlags = 0x00; - - return STATUS_SUCCESS; - } - - if (pInfo->InterfaceNumber == 2) - { - pInfo[0].Class = 0xFF; - pInfo[0].SubClass = 0x5D; - pInfo[0].Protocol = 0x02; - pInfo[0].NumberOfPipes = 0x01; - - pInfo[0].InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; - - pInfo[0].Pipes[0].MaximumTransferSize = 0x00400000; - pInfo[0].Pipes[0].MaximumPacketSize = 0x20; - pInfo[0].Pipes[0].EndpointAddress = 0x84; - pInfo[0].Pipes[0].Interval = 0x04; - pInfo[0].Pipes[0].PipeType = 0x03; - pInfo[0].Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084; - pInfo[0].Pipes[0].PipeFlags = 0x00; - - return STATUS_SUCCESS; - } - } - case DualShock4Wired: - { - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_USBPDO, - "Not implemented"); - - break; - } - default: - break; - } - - return STATUS_INVALID_PARAMETER; -} - -// -// Dispatch interrupt transfers. -// -NTSTATUS UsbPdo_BulkOrInterruptTransfer(PURB urb, WDFDEVICE Device, WDFREQUEST Request) -{ - struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer = &urb->UrbBulkOrInterruptTransfer; - NTSTATUS status; - PPDO_DEVICE_DATA pdoData; - WDFREQUEST notifyRequest; - PUCHAR blobBuffer; - - pdoData = PdoGetData(Device); - - if (pdoData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_USBPDO, - ">> >> >> PdoGetData failed"); - return STATUS_INVALID_PARAMETER; - } - - switch (pdoData->TargetType) - { - case Xbox360Wired: - { - PXUSB_DEVICE_DATA xusb = XusbGetData(Device); - - // Check context - if (xusb == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_USBPDO, - "No XUSB context found on device %p", - Device); - - return STATUS_UNSUCCESSFUL; - } - - // Data coming FROM us TO higher driver - if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> Incoming request, queuing..."); - - blobBuffer = WdfMemoryGetBuffer(xusb->InterruptBlobStorage, NULL); - - if (XUSB_IS_DATA_PIPE(pTransfer)) - { - // - // Send "boot sequence" first, then the actual inputs - // - switch (xusb->InterruptInitStage) - { - case 0: - pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; - xusb->InterruptInitStage++; - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_00_OFFSET], - XUSB_INIT_STAGE_SIZE - ); - return STATUS_SUCCESS; - case 1: - pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; - xusb->InterruptInitStage++; - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_01_OFFSET], - XUSB_INIT_STAGE_SIZE - ); - return STATUS_SUCCESS; - case 2: - pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; - xusb->InterruptInitStage++; - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_02_OFFSET], - XUSB_INIT_STAGE_SIZE - ); - return STATUS_SUCCESS; - case 3: - pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; - xusb->InterruptInitStage++; - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_03_OFFSET], - XUSB_INIT_STAGE_SIZE - ); - return STATUS_SUCCESS; - case 4: - pTransfer->TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET); - xusb->InterruptInitStage++; - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_04_OFFSET], - sizeof(XUSB_INTERRUPT_IN_PACKET) - ); - return STATUS_SUCCESS; - case 5: - pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; - xusb->InterruptInitStage++; - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_05_OFFSET], - XUSB_INIT_STAGE_SIZE - ); - return STATUS_SUCCESS; - default: - /* This request is sent periodically and relies on data the "feeder" - * has to supply, so we queue this request and return with STATUS_PENDING. - * The request gets completed as soon as the "feeder" sent an update. */ - status = WdfRequestForwardToIoQueue(Request, pdoData->PendingUsbInRequests); - - return (NT_SUCCESS(status)) ? STATUS_PENDING : status; - } - } - - if (XUSB_IS_CONTROL_PIPE(pTransfer)) - { - if (!xusb->ReportedCapabilities && pTransfer->TransferBufferLength >= XUSB_INIT_STAGE_SIZE) - { - RtlCopyMemory( - pTransfer->TransferBuffer, - &blobBuffer[XUSB_BLOB_06_OFFSET], - XUSB_INIT_STAGE_SIZE - ); - - xusb->ReportedCapabilities = TRUE; - - return STATUS_SUCCESS; - } - - status = WdfRequestForwardToIoQueue(Request, xusb->HoldingUsbInRequests); - - return (NT_SUCCESS(status)) ? STATUS_PENDING : status; - } - } - - // Data coming FROM the higher driver TO us - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: Handle %p, Flags %X, Length %d", - pTransfer->PipeHandle, - pTransfer->TransferFlags, - pTransfer->TransferBufferLength); - - if (pTransfer->TransferBufferLength == XUSB_LEDSET_SIZE) // Led - { - PUCHAR Buffer = pTransfer->TransferBuffer; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - "-- LED Buffer: %02X %02X %02X", - Buffer[0], Buffer[1], Buffer[2]); - - // extract LED byte to get controller slot - if (Buffer[0] == 0x01 && Buffer[1] == 0x03 && Buffer[2] >= 0x02) - { - if (Buffer[2] == 0x02) xusb->LedNumber = 0; - if (Buffer[2] == 0x03) xusb->LedNumber = 1; - if (Buffer[2] == 0x04) xusb->LedNumber = 2; - if (Buffer[2] == 0x05) xusb->LedNumber = 3; - - TraceEvents(TRACE_LEVEL_INFORMATION, - TRACE_USBPDO, - "-- LED Number: %d", - xusb->LedNumber); - // - // Report back to FDO that we are ready to operate - // - BUS_PDO_REPORT_STAGE_RESULT( - pdoData->BusInterface, - ViGEmPdoInitFinished, - pdoData->SerialNo, - STATUS_SUCCESS - ); - } - } - - // Extract rumble (vibration) information - if (pTransfer->TransferBufferLength == XUSB_RUMBLE_SIZE) - { - PUCHAR Buffer = pTransfer->TransferBuffer; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - "-- Rumble Buffer: %02X %02X %02X %02X %02X %02X %02X %02X", - Buffer[0], - Buffer[1], - Buffer[2], - Buffer[3], - Buffer[4], - Buffer[5], - Buffer[6], - Buffer[7]); - - RtlCopyBytes(xusb->Rumble, Buffer, pTransfer->TransferBufferLength); - } - - // Notify user-mode process that new data is available - status = WdfIoQueueRetrieveNextRequest(pdoData->PendingNotificationRequests, ¬ifyRequest); - - if (NT_SUCCESS(status)) - { - PXUSB_REQUEST_NOTIFICATION notify = NULL; - - status = WdfRequestRetrieveOutputBuffer(notifyRequest, sizeof(XUSB_REQUEST_NOTIFICATION), (PVOID)¬ify, NULL); - - if (NT_SUCCESS(status)) - { - // Assign values to output buffer - notify->Size = sizeof(XUSB_REQUEST_NOTIFICATION); - notify->SerialNo = pdoData->SerialNo; - notify->LedNumber = xusb->LedNumber; - notify->LargeMotor = xusb->Rumble[3]; - notify->SmallMotor = xusb->Rumble[4]; - - WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size); - } - else - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_USBPDO, - "WdfRequestRetrieveOutputBuffer failed with status %!STATUS!", - status); - } - } - else - { - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_USBPDO, - "!! [XUSB] WdfIoQueueRetrieveNextRequest failed with status %!STATUS!", - status); - } - - break; - } - case DualShock4Wired: - { - PDS4_DEVICE_DATA ds4Data = Ds4GetData(Device); - - // Data coming FROM us TO higher driver - if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN - && pTransfer->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0084) - { - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> Incoming request, queuing..."); - - /* This request is sent periodically and relies on data the "feeder" - has to supply, so we queue this request and return with STATUS_PENDING. - The request gets completed as soon as the "feeder" sent an update. */ - status = WdfRequestForwardToIoQueue(Request, pdoData->PendingUsbInRequests); - - return (NT_SUCCESS(status)) ? STATUS_PENDING : status; - } - - // Store relevant bytes of buffer in PDO context - RtlCopyBytes(&ds4Data->OutputReport, - (PUCHAR)pTransfer->TransferBuffer + DS4_OUTPUT_BUFFER_OFFSET, - DS4_OUTPUT_BUFFER_LENGTH); - - // Notify user-mode process that new data is available - status = WdfIoQueueRetrieveNextRequest(pdoData->PendingNotificationRequests, ¬ifyRequest); - - if (NT_SUCCESS(status)) - { - PDS4_REQUEST_NOTIFICATION notify = NULL; - - status = WdfRequestRetrieveOutputBuffer(notifyRequest, sizeof(DS4_REQUEST_NOTIFICATION), (PVOID)¬ify, NULL); - - if (NT_SUCCESS(status)) - { - // Assign values to output buffer - notify->Size = sizeof(DS4_REQUEST_NOTIFICATION); - notify->SerialNo = pdoData->SerialNo; - notify->Report = ds4Data->OutputReport; - - WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size); - } - else - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_USBPDO, - "WdfRequestRetrieveOutputBuffer failed with status %!STATUS!", - status); - } - } - - break; - } - default: - break; - } - - return STATUS_SUCCESS; -} - -// -// Clean-up actions on shutdown. -// -NTSTATUS UsbPdo_AbortPipe(WDFDEVICE Device) -{ - PPDO_DEVICE_DATA pdoData = PdoGetData(Device); - - switch (pdoData->TargetType) - { - case DualShock4Wired: - { - PDS4_DEVICE_DATA ds4 = Ds4GetData(Device); - - // Check context - if (ds4 == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_USBPDO, - "No DS4 context found on device %p", Device); - - return STATUS_UNSUCCESSFUL; - } - - // Higher driver shutting down, emptying PDOs queues - WdfTimerStop(ds4->PendingUsbInRequestsTimer, TRUE); - - break; - } - default: - break; - } - - // Higher driver shutting down, emptying PDOs queues - WdfIoQueuePurge(pdoData->PendingUsbInRequests, NULL, NULL); - WdfIoQueuePurge(pdoData->PendingNotificationRequests, NULL, NULL); - - return STATUS_SUCCESS; -} - -// -// Processes URBs containing HID-related requests. -// -NTSTATUS UsbPdo_ClassInterface(PURB urb, WDFDEVICE Device, PPDO_DEVICE_DATA pCommon) -{ - struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST* pRequest = &urb->UrbControlVendorClassRequest; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> URB_FUNCTION_CLASS_INTERFACE"); - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> TransferFlags = 0x%X, Request = 0x%X, Value = 0x%X, Index = 0x%X, BufLen = %d", - pRequest->TransferFlags, - pRequest->Request, - pRequest->Value, - pRequest->Index, - pRequest->TransferBufferLength); - - switch (pCommon->TargetType) - { - case DualShock4Wired: - { - PDS4_DEVICE_DATA ds4 = Ds4GetData(Device); - - switch (pRequest->Request) - { - case HID_REQUEST_GET_REPORT: - { - UCHAR reportId = HID_GET_REPORT_ID(pRequest); - UCHAR reportType = HID_GET_REPORT_TYPE(pRequest); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> >> GET_REPORT(%d): %d", - reportType, reportId); - - switch (reportType) - { - case HID_REPORT_TYPE_FEATURE: - { - switch (reportId) - { - case HID_REPORT_ID_0: - { - // Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests - UCHAR Response[HID_GET_FEATURE_REPORT_SIZE_0] = - { - 0xA3, 0x41, 0x75, 0x67, 0x20, 0x20, 0x33, 0x20, - 0x32, 0x30, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x37, 0x3A, 0x30, 0x31, 0x3A, 0x31, - 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x31, 0x03, 0x00, 0x00, - 0x00, 0x49, 0x00, 0x05, 0x00, 0x00, 0x80, 0x03, - 0x00 - }; - - pRequest->TransferBufferLength = HID_GET_FEATURE_REPORT_SIZE_0; - RtlCopyBytes(pRequest->TransferBuffer, Response, HID_GET_FEATURE_REPORT_SIZE_0); - - break; - } - case HID_REPORT_ID_1: - { - // Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests - UCHAR Response[HID_GET_FEATURE_REPORT_SIZE_1] = - { - 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, - 0x22, 0x7B, 0xDD, 0xB2, 0x22, 0x47, 0xDD, 0xBD, - 0x22, 0x43, 0xDD, 0x1C, 0x02, 0x1C, 0x02, 0x7F, - 0x1E, 0x2E, 0xDF, 0x60, 0x1F, 0x4C, 0xE0, 0x3A, - 0x1D, 0xC6, 0xDE, 0x08, 0x00 - }; - - pRequest->TransferBufferLength = HID_GET_FEATURE_REPORT_SIZE_1; - RtlCopyBytes(pRequest->TransferBuffer, Response, HID_GET_FEATURE_REPORT_SIZE_1); - - break; - } - case HID_REPORT_MAC_ADDRESSES_ID: - { - // Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests - UCHAR Response[HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE] = - { - 0x12, 0x8B, 0x09, 0x07, 0x6D, 0x66, 0x1C, 0x08, - 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - // Insert (auto-generated) target MAC address into response - RtlCopyBytes(Response + 1, &ds4->TargetMacAddress, sizeof(MAC_ADDRESS)); - // Adjust byte order - ReverseByteArray(Response + 1, sizeof(MAC_ADDRESS)); - - // Insert (auto-generated) host MAC address into response - RtlCopyBytes(Response + 10, &ds4->HostMacAddress, sizeof(MAC_ADDRESS)); - // Adjust byte order - ReverseByteArray(Response + 10, sizeof(MAC_ADDRESS)); - - pRequest->TransferBufferLength = HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE; - RtlCopyBytes(pRequest->TransferBuffer, Response, HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE); - - break; - } - default: - break; - } - break; - } - default: - break; - } - - break; - } - case HID_REQUEST_SET_REPORT: - { - UCHAR reportId = HID_GET_REPORT_ID(pRequest); - UCHAR reportType = HID_GET_REPORT_TYPE(pRequest); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> >> SET_REPORT(%d): %d", - reportType, reportId); - - switch (reportType) - { - case HID_REPORT_TYPE_FEATURE: - { - switch (reportId) - { - case HID_REPORT_ID_3: - { - // Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests - UCHAR Response[HID_SET_FEATURE_REPORT_SIZE_0] = - { - 0x13, 0xAC, 0x9E, 0x17, 0x94, 0x05, 0xB0, 0x56, - 0xE8, 0x81, 0x38, 0x08, 0x06, 0x51, 0x41, 0xC0, - 0x7F, 0x12, 0xAA, 0xD9, 0x66, 0x3C, 0xCE - }; - - pRequest->TransferBufferLength = HID_SET_FEATURE_REPORT_SIZE_0; - RtlCopyBytes(pRequest->TransferBuffer, Response, HID_SET_FEATURE_REPORT_SIZE_0); - - break; - } - case HID_REPORT_ID_4: - { - // Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests - UCHAR Response[HID_SET_FEATURE_REPORT_SIZE_1] = - { - 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00 - }; - - pRequest->TransferBufferLength = HID_SET_FEATURE_REPORT_SIZE_1; - RtlCopyBytes(pRequest->TransferBuffer, Response, HID_SET_FEATURE_REPORT_SIZE_1); - - break; - } - default: - break; - } - break; - } - default: - break; - } - - break; - } - default: - break; - } - - break; - } - } - - return STATUS_SUCCESS; -} - -// -// Returns interface HID report descriptor. -// -NTSTATUS UsbPdo_GetDescriptorFromInterface(PURB urb, PPDO_DEVICE_DATA pCommon) -{ - NTSTATUS status = STATUS_INVALID_PARAMETER; - UCHAR Ds4HidReportDescriptor[DS4_HID_REPORT_DESCRIPTOR_SIZE] = - { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x05, // Usage (Game Pad) - 0xA1, 0x01, // Collection (Application) - 0x85, 0x01, // Report ID (1) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x09, 0x32, // Usage (Z) - 0x09, 0x35, // Usage (Rz) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x04, // Report Count (4) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x09, 0x39, // Usage (Hat switch) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x07, // Logical Maximum (7) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0x3B, 0x01, // Physical Maximum (315) - 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) - 0x75, 0x04, // Report Size (4) - 0x95, 0x01, // Report Count (1) - 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) - 0x65, 0x00, // Unit (None) - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (0x01) - 0x29, 0x0E, // Usage Maximum (0x0E) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x0E, // Report Count (14) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) - 0x09, 0x20, // Usage (0x20) - 0x75, 0x06, // Report Size (6) - 0x95, 0x01, // Report Count (1) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x7F, // Logical Maximum (127) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x33, // Usage (Rx) - 0x09, 0x34, // Usage (Ry) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x00, // Logical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x02, // Report Count (2) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) - 0x09, 0x21, // Usage (0x21) - 0x95, 0x36, // Report Count (54) - 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x85, 0x05, // Report ID (5) - 0x09, 0x22, // Usage (0x22) - 0x95, 0x1F, // Report Count (31) - 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x04, // Report ID (4) - 0x09, 0x23, // Usage (0x23) - 0x95, 0x24, // Report Count (36) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x02, // Report ID (2) - 0x09, 0x24, // Usage (0x24) - 0x95, 0x24, // Report Count (36) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x08, // Report ID (8) - 0x09, 0x25, // Usage (0x25) - 0x95, 0x03, // Report Count (3) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x10, // Report ID (16) - 0x09, 0x26, // Usage (0x26) - 0x95, 0x04, // Report Count (4) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x11, // Report ID (17) - 0x09, 0x27, // Usage (0x27) - 0x95, 0x02, // Report Count (2) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x12, // Report ID (18) - 0x06, 0x02, 0xFF, // Usage Page (Vendor Defined 0xFF02) - 0x09, 0x21, // Usage (0x21) - 0x95, 0x0F, // Report Count (15) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x13, // Report ID (19) - 0x09, 0x22, // Usage (0x22) - 0x95, 0x16, // Report Count (22) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x14, // Report ID (20) - 0x06, 0x05, 0xFF, // Usage Page (Vendor Defined 0xFF05) - 0x09, 0x20, // Usage (0x20) - 0x95, 0x10, // Report Count (16) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x15, // Report ID (21) - 0x09, 0x21, // Usage (0x21) - 0x95, 0x2C, // Report Count (44) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80) - 0x85, 0x80, // Report ID (128) - 0x09, 0x20, // Usage (0x20) - 0x95, 0x06, // Report Count (6) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x81, // Report ID (129) - 0x09, 0x21, // Usage (0x21) - 0x95, 0x06, // Report Count (6) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x82, // Report ID (130) - 0x09, 0x22, // Usage (0x22) - 0x95, 0x05, // Report Count (5) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x83, // Report ID (131) - 0x09, 0x23, // Usage (0x23) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x84, // Report ID (132) - 0x09, 0x24, // Usage (0x24) - 0x95, 0x04, // Report Count (4) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x85, // Report ID (133) - 0x09, 0x25, // Usage (0x25) - 0x95, 0x06, // Report Count (6) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x86, // Report ID (134) - 0x09, 0x26, // Usage (0x26) - 0x95, 0x06, // Report Count (6) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x87, // Report ID (135) - 0x09, 0x27, // Usage (0x27) - 0x95, 0x23, // Report Count (35) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x88, // Report ID (136) - 0x09, 0x28, // Usage (0x28) - 0x95, 0x22, // Report Count (34) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x89, // Report ID (137) - 0x09, 0x29, // Usage (0x29) - 0x95, 0x02, // Report Count (2) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x90, // Report ID (144) - 0x09, 0x30, // Usage (0x30) - 0x95, 0x05, // Report Count (5) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x91, // Report ID (145) - 0x09, 0x31, // Usage (0x31) - 0x95, 0x03, // Report Count (3) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x92, // Report ID (146) - 0x09, 0x32, // Usage (0x32) - 0x95, 0x03, // Report Count (3) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0x93, // Report ID (147) - 0x09, 0x33, // Usage (0x33) - 0x95, 0x0C, // Report Count (12) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA0, // Report ID (160) - 0x09, 0x40, // Usage (0x40) - 0x95, 0x06, // Report Count (6) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA1, // Report ID (161) - 0x09, 0x41, // Usage (0x41) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA2, // Report ID (162) - 0x09, 0x42, // Usage (0x42) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA3, // Report ID (163) - 0x09, 0x43, // Usage (0x43) - 0x95, 0x30, // Report Count (48) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA4, // Report ID (164) - 0x09, 0x44, // Usage (0x44) - 0x95, 0x0D, // Report Count (13) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA5, // Report ID (165) - 0x09, 0x45, // Usage (0x45) - 0x95, 0x15, // Report Count (21) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA6, // Report ID (166) - 0x09, 0x46, // Usage (0x46) - 0x95, 0x15, // Report Count (21) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xF0, // Report ID (240) - 0x09, 0x47, // Usage (0x47) - 0x95, 0x3F, // Report Count (63) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xF1, // Report ID (241) - 0x09, 0x48, // Usage (0x48) - 0x95, 0x3F, // Report Count (63) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xF2, // Report ID (242) - 0x09, 0x49, // Usage (0x49) - 0x95, 0x0F, // Report Count (15) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA7, // Report ID (167) - 0x09, 0x4A, // Usage (0x4A) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA8, // Report ID (168) - 0x09, 0x4B, // Usage (0x4B) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xA9, // Report ID (169) - 0x09, 0x4C, // Usage (0x4C) - 0x95, 0x08, // Report Count (8) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xAA, // Report ID (170) - 0x09, 0x4E, // Usage (0x4E) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xAB, // Report ID (171) - 0x09, 0x4F, // Usage (0x4F) - 0x95, 0x39, // Report Count (57) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xAC, // Report ID (172) - 0x09, 0x50, // Usage (0x50) - 0x95, 0x39, // Report Count (57) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xAD, // Report ID (173) - 0x09, 0x51, // Usage (0x51) - 0x95, 0x0B, // Report Count (11) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xAE, // Report ID (174) - 0x09, 0x52, // Usage (0x52) - 0x95, 0x01, // Report Count (1) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xAF, // Report ID (175) - 0x09, 0x53, // Usage (0x53) - 0x95, 0x02, // Report Count (2) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x85, 0xB0, // Report ID (176) - 0x09, 0x54, // Usage (0x54) - 0x95, 0x3F, // Report Count (63) - 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0xC0, // End Collection - }; - - struct _URB_CONTROL_DESCRIPTOR_REQUEST* pRequest = &urb->UrbControlDescriptorRequest; - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_USBPDO, - ">> >> >> _URB_CONTROL_DESCRIPTOR_REQUEST: Buffer Length %d", - pRequest->TransferBufferLength); - - switch (pCommon->TargetType) - { - case DualShock4Wired: - { - if (pRequest->TransferBufferLength >= DS4_HID_REPORT_DESCRIPTOR_SIZE) - { - RtlCopyMemory(pRequest->TransferBuffer, Ds4HidReportDescriptor, DS4_HID_REPORT_DESCRIPTOR_SIZE); - status = STATUS_SUCCESS; - } - - break; - } - default: - break; - } - - return status; -} - diff --git a/sys/xusb.c b/sys/xusb.c deleted file mode 100644 index 6e3ce29..0000000 --- a/sys/xusb.c +++ /dev/null @@ -1,643 +0,0 @@ -/* -* 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 "xusb.tmh" - -EXTERN_C_START - -NTSTATUS Xusb_PreparePdo( - PWDFDEVICE_INIT DeviceInit, - USHORT VendorId, - USHORT ProductId, - PUNICODE_STRING DeviceId, - PUNICODE_STRING DeviceDescription) -{ - NTSTATUS status; - DECLARE_UNICODE_STRING_SIZE(buffer, MAX_HARDWARE_ID_LENGTH); - - // prepare device description - status = RtlUnicodeStringInit(DeviceDescription, L"Virtual Xbox 360 Controller"); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "RtlUnicodeStringInit failed with status %!STATUS!", - status); - return status; - } - - // Set hardware ID - RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", VendorId, ProductId); - - RtlUnicodeStringCopy(DeviceId, &buffer); - - status = WdfPdoInitAddHardwareID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfPdoInitAddHardwareID failed with status %!STATUS!", - status); - return status; - } - - - // Set compatible IDs - RtlUnicodeStringInit(&buffer, L"USB\\MS_COMP_XUSB10"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfPdoInitAddCompatibleID #1 failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_5D&Prot_01"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfPdoInitAddCompatibleID #2 failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_5D"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfPdoInitAddCompatibleID #3 failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&buffer, L"USB\\Class_FF"); - - status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfPdoInitAddCompatibleID #4 failed with status %!STATUS!", - status); - return status; - } - - return STATUS_SUCCESS; -} - -NTSTATUS Xusb_PrepareHardware(WDFDEVICE Device) -{ - NTSTATUS status; - WDF_QUERY_INTERFACE_CONFIG ifaceCfg; - - INTERFACE dummyIface; - - dummyIface.Size = sizeof(INTERFACE); - dummyIface.Version = 1; - dummyIface.Context = (PVOID)Device; - - dummyIface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; - dummyIface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; - - /* XUSB.sys will query for the following three "dummy" interfaces - * BUT WONT USE IT so we just expose them to satisfy initialization. (TODO: Check if still valid!) - */ - - // Dummy PNP_LOCATION - - WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_PNP_LOCATION_INTERFACE, NULL); - - status = WdfDeviceAddQueryInterface(Device, &ifaceCfg); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "Couldn't register PNP_LOCATION dummy interface %!GUID! (WdfDeviceAddQueryInterface failed with status %!STATUS!)", - &GUID_PNP_LOCATION_INTERFACE, - status); - - return status; - } - - // Dummy D3COLD_SUPPORT - - WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_D3COLD_SUPPORT_INTERFACE, NULL); - - status = WdfDeviceAddQueryInterface(Device, &ifaceCfg); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "Couldn't register D3COLD_SUPPORT dummy interface %!GUID! (WdfDeviceAddQueryInterface failed with status %!STATUS!)", - &GUID_D3COLD_SUPPORT_INTERFACE, - status); - - return status; - } - - // Dummy REENUMERATE_SELF_INTERFACE_STANDARD - - WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_REENUMERATE_SELF_INTERFACE_STANDARD, NULL); - - status = WdfDeviceAddQueryInterface(Device, &ifaceCfg); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "Couldn't register REENUM_SELF_STD dummy interface %!GUID! (WdfDeviceAddQueryInterface failed with status %!STATUS!)", - &GUID_REENUMERATE_SELF_INTERFACE_STANDARD, - status); - - return status; - } - - // Expose USB_BUS_INTERFACE_USBDI_GUID - - // This interface actually IS used - USB_BUS_INTERFACE_USBDI_V1 xusbInterface; - - xusbInterface.Size = sizeof(USB_BUS_INTERFACE_USBDI_V1); - xusbInterface.Version = USB_BUSIF_USBDI_VERSION_1; - xusbInterface.BusContext = (PVOID)Device; - - xusbInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; - xusbInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; - - xusbInterface.SubmitIsoOutUrb = UsbPdo_SubmitIsoOutUrb; - xusbInterface.GetUSBDIVersion = UsbPdo_GetUSBDIVersion; - xusbInterface.QueryBusTime = UsbPdo_QueryBusTime; - xusbInterface.QueryBusInformation = UsbPdo_QueryBusInformation; - xusbInterface.IsDeviceHighSpeed = UsbPdo_IsDeviceHighSpeed; - - WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&xusbInterface, &USB_BUS_INTERFACE_USBDI_GUID, NULL); - - status = WdfDeviceAddQueryInterface(Device, &ifaceCfg); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfDeviceAddQueryInterface failed with status %!STATUS!", - status); - return status; - } - - return STATUS_SUCCESS; -} - -NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device) -{ - NTSTATUS status; - WDF_OBJECT_ATTRIBUTES attributes; - PUCHAR blobBuffer; - - WDF_OBJECT_ATTRIBUTES_INIT(&attributes); - attributes.ParentObject = Device; - - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "Initializing XUSB context..."); - - PXUSB_DEVICE_DATA xusb = XusbGetData(Device); - - RtlZeroMemory(xusb, sizeof(XUSB_DEVICE_DATA)); - - // Is later overwritten by actual XInput slot - xusb->LedNumber = -1; - // Packet size (20 bytes = 0x14) - xusb->Packet.Size = 0x14; - - // Allocate blob storage - status = WdfMemoryCreate( - &attributes, - NonPagedPoolNx, - XUSB_POOL_TAG, - XUSB_BLOB_STORAGE_SIZE, - &xusb->InterruptBlobStorage, - &blobBuffer - ); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfMemoryCreate failed with status %!STATUS!", - status); - return status; - } - - // Fill blob storage - COPY_BYTE_ARRAY(blobBuffer, P99_PROTECT({ - // 0 - 0x01, 0x03, 0x0E, - // 1 - 0x02, 0x03, 0x00, - // 2 - 0x03, 0x03, 0x03, - // 3 - 0x08, 0x03, 0x00, - // 4 - 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xf2, - 0xb3, 0xf8, 0x49, 0xf3, 0xb0, 0xfc, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - // 5 - 0x01, 0x03, 0x03, - // 6 - 0x05, 0x03, 0x00, - // 7 - 0x31, 0x3F, 0xCF, 0xDC - })); - - // I/O Queue for pending IRPs - WDF_IO_QUEUE_CONFIG holdingInQueueConfig; - - // Create and assign queue for unhandled interrupt requests - WDF_IO_QUEUE_CONFIG_INIT(&holdingInQueueConfig, WdfIoQueueDispatchManual); - - status = WdfIoQueueCreate(Device, &holdingInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &xusb->HoldingUsbInRequests); - if (!NT_SUCCESS(status)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "WdfIoQueueCreate (HoldingUsbInRequests) failed with status %!STATUS!", - status); - return status; - } - - return STATUS_SUCCESS; -} - -VOID Xusb_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) -{ - UCHAR XusbDescriptorData[XUSB_DESCRIPTOR_SIZE] = - { - 0x09, // bLength - 0x02, // bDescriptorType (Configuration) - 0x99, 0x00, // wTotalLength 153 - 0x04, // bNumInterfaces 4 - 0x01, // bConfigurationValue - 0x00, // iConfiguration (String Index) - 0xA0, // bmAttributes Remote Wakeup - 0xFA, // bMaxPower 500mA - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x00, // bInterfaceNumber 0 - 0x00, // bAlternateSetting - 0x02, // bNumEndpoints 2 - 0xFF, // bInterfaceClass - 0x5D, // bInterfaceSubClass - 0x01, // bInterfaceProtocol - 0x00, // iInterface (String Index) - - 0x11, // bLength - 0x21, // bDescriptorType (HID) - 0x00, 0x01, // bcdHID 1.00 - 0x01, // bCountryCode - 0x25, // bNumDescriptors - 0x81, // bDescriptorType[0] (Unknown 0x81) - 0x14, 0x00, // wDescriptorLength[0] 20 - 0x00, // bDescriptorType[1] (Unknown 0x00) - 0x00, 0x00, // wDescriptorLength[1] 0 - 0x13, // bDescriptorType[2] (Unknown 0x13) - 0x01, 0x08, // wDescriptorLength[2] 2049 - 0x00, // bDescriptorType[3] (Unknown 0x00) - 0x00, - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x81, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x04, // bInterval 4 (unit depends on device speed) - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x01, // bEndpointAddress (OUT/H2D) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x08, // bInterval 8 (unit depends on device speed) - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x01, // bInterfaceNumber 1 - 0x00, // bAlternateSetting - 0x04, // bNumEndpoints 4 - 0xFF, // bInterfaceClass - 0x5D, // bInterfaceSubClass - 0x03, // bInterfaceProtocol - 0x00, // iInterface (String Index) - - 0x1B, // bLength - 0x21, // bDescriptorType (HID) - 0x00, 0x01, // bcdHID 1.00 - 0x01, // bCountryCode - 0x01, // bNumDescriptors - 0x82, // bDescriptorType[0] (Unknown 0x82) - 0x40, 0x01, // wDescriptorLength[0] 320 - 0x02, 0x20, 0x16, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x82, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x02, // bInterval 2 (unit depends on device speed) - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x02, // bEndpointAddress (OUT/H2D) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x04, // bInterval 4 (unit depends on device speed) - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x83, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x40, // bInterval 64 (unit depends on device speed) - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x03, // bEndpointAddress (OUT/H2D) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x10, // bInterval 16 (unit depends on device speed) - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x02, // bInterfaceNumber 2 - 0x00, // bAlternateSetting - 0x01, // bNumEndpoints 1 - 0xFF, // bInterfaceClass - 0x5D, // bInterfaceSubClass - 0x02, // bInterfaceProtocol - 0x00, // iInterface (String Index) - - 0x09, // bLength - 0x21, // bDescriptorType (HID) - 0x00, 0x01, // bcdHID 1.00 - 0x01, // bCountryCode - 0x22, // bNumDescriptors - 0x84, // bDescriptorType[0] (Unknown 0x84) - 0x07, 0x00, // wDescriptorLength[0] 7 - - 0x07, // bLength - 0x05, // bDescriptorType (Endpoint) - 0x84, // bEndpointAddress (IN/D2H) - 0x03, // bmAttributes (Interrupt) - 0x20, 0x00, // wMaxPacketSize 32 - 0x10, // bInterval 16 (unit depends on device speed) - - 0x09, // bLength - 0x04, // bDescriptorType (Interface) - 0x03, // bInterfaceNumber 3 - 0x00, // bAlternateSetting - 0x00, // bNumEndpoints 0 - 0xFF, // bInterfaceClass - 0xFD, // bInterfaceSubClass - 0x13, // bInterfaceProtocol - 0x04, // iInterface (String Index) - - 0x06, // bLength - 0x41, // bDescriptorType (Unknown) - 0x00, 0x01, 0x01, 0x03, - // 153 bytes - - // best guess: USB Standard Descriptor - }; - - RtlCopyBytes(Buffer, XusbDescriptorData, Length); -} - -VOID Xusb_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon) -{ - pDescriptor->bLength = 0x12; - pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; - pDescriptor->bcdUSB = 0x0200; // USB v2.0 - pDescriptor->bDeviceClass = 0xFF; - pDescriptor->bDeviceSubClass = 0xFF; - pDescriptor->bDeviceProtocol = 0xFF; - pDescriptor->bMaxPacketSize0 = 0x08; - pDescriptor->idVendor = pCommon->VendorId; - pDescriptor->idProduct = pCommon->ProductId; - pDescriptor->bcdDevice = 0x0114; - pDescriptor->iManufacturer = 0x01; - pDescriptor->iProduct = 0x02; - pDescriptor->iSerialNumber = 0x03; - pDescriptor->bNumConfigurations = 0x01; -} - -VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo) -{ - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_XUSB, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d", - (int)pInfo->Length, - (int)pInfo->InterfaceNumber, - (int)pInfo->AlternateSetting, - pInfo->NumberOfPipes); - - pInfo->Class = 0xFF; - pInfo->SubClass = 0x5D; - pInfo->Protocol = 0x01; - - pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; - - pInfo->Pipes[0].MaximumTransferSize = 0x00400000; - pInfo->Pipes[0].MaximumPacketSize = 0x20; - pInfo->Pipes[0].EndpointAddress = 0x81; - pInfo->Pipes[0].Interval = 0x04; - pInfo->Pipes[0].PipeType = 0x03; - pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0081; - pInfo->Pipes[0].PipeFlags = 0x00; - - pInfo->Pipes[1].MaximumTransferSize = 0x00400000; - pInfo->Pipes[1].MaximumPacketSize = 0x20; - pInfo->Pipes[1].EndpointAddress = 0x01; - pInfo->Pipes[1].Interval = 0x08; - pInfo->Pipes[1].PipeType = 0x03; - pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0001; - pInfo->Pipes[1].PipeFlags = 0x00; - - pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_XUSB, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d", - (int)pInfo->Length, - (int)pInfo->InterfaceNumber, - (int)pInfo->AlternateSetting, - pInfo->NumberOfPipes); - - pInfo->Class = 0xFF; - pInfo->SubClass = 0x5D; - pInfo->Protocol = 0x03; - - pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; - - pInfo->Pipes[0].MaximumTransferSize = 0x00400000; - pInfo->Pipes[0].MaximumPacketSize = 0x20; - pInfo->Pipes[0].EndpointAddress = 0x82; - pInfo->Pipes[0].Interval = 0x04; - pInfo->Pipes[0].PipeType = 0x03; - pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0082; - pInfo->Pipes[0].PipeFlags = 0x00; - - pInfo->Pipes[1].MaximumTransferSize = 0x00400000; - pInfo->Pipes[1].MaximumPacketSize = 0x20; - pInfo->Pipes[1].EndpointAddress = 0x02; - pInfo->Pipes[1].Interval = 0x08; - pInfo->Pipes[1].PipeType = 0x03; - pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0002; - pInfo->Pipes[1].PipeFlags = 0x00; - - pInfo->Pipes[2].MaximumTransferSize = 0x00400000; - pInfo->Pipes[2].MaximumPacketSize = 0x20; - pInfo->Pipes[2].EndpointAddress = 0x83; - pInfo->Pipes[2].Interval = 0x08; - pInfo->Pipes[2].PipeType = 0x03; - pInfo->Pipes[2].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0083; - pInfo->Pipes[2].PipeFlags = 0x00; - - pInfo->Pipes[3].MaximumTransferSize = 0x00400000; - pInfo->Pipes[3].MaximumPacketSize = 0x20; - pInfo->Pipes[3].EndpointAddress = 0x03; - pInfo->Pipes[3].Interval = 0x08; - pInfo->Pipes[3].PipeType = 0x03; - pInfo->Pipes[3].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003; - pInfo->Pipes[3].PipeFlags = 0x00; - - pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_XUSB, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d", - (int)pInfo->Length, - (int)pInfo->InterfaceNumber, - (int)pInfo->AlternateSetting, - pInfo->NumberOfPipes); - - pInfo->Class = 0xFF; - pInfo->SubClass = 0x5D; - pInfo->Protocol = 0x02; - - pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; - - pInfo->Pipes[0].MaximumTransferSize = 0x00400000; - pInfo->Pipes[0].MaximumPacketSize = 0x20; - pInfo->Pipes[0].EndpointAddress = 0x84; - pInfo->Pipes[0].Interval = 0x04; - pInfo->Pipes[0].PipeType = 0x03; - pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084; - pInfo->Pipes[0].PipeFlags = 0x00; - - pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length); - - TraceEvents(TRACE_LEVEL_VERBOSE, - TRACE_XUSB, - ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d", - (int)pInfo->Length, - (int)pInfo->InterfaceNumber, - (int)pInfo->AlternateSetting, - pInfo->NumberOfPipes); - - pInfo->Class = 0xFF; - pInfo->SubClass = 0xFD; - pInfo->Protocol = 0x13; - - pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; -} - -NTSTATUS Xusb_GetUserIndex(WDFDEVICE Device, PXUSB_GET_USER_INDEX Request) -{ - NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; - WDFDEVICE hChild; - PPDO_DEVICE_DATA pdoData; - CHAR userIndex; - - - TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_XUSB, "%!FUNC! Entry"); - - hChild = Bus_GetPdo(Device, Request->SerialNo); - - // Validate child - if (hChild == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "Bus_GetPdo for serial %d failed", Request->SerialNo); - return STATUS_NO_SUCH_DEVICE; - } - - // Check common context - pdoData = PdoGetData(hChild); - if (pdoData == NULL) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "PdoGetData failed"); - return STATUS_INVALID_PARAMETER; - } - - // Check if caller owns this PDO - if (!IS_OWNER(pdoData)) - { - TraceEvents(TRACE_LEVEL_ERROR, - TRACE_XUSB, - "PID mismatch: %d != %d", - pdoData->OwnerProcessId, - CURRENT_PROCESS_ID()); - return STATUS_ACCESS_DENIED; - } - - userIndex = XusbGetData(hChild)->LedNumber; - - if (userIndex >= 0) - { - Request->UserIndex = (ULONG)userIndex; - status = STATUS_SUCCESS; - } - else - { - // If the index is negative at this stage, we've exceeded XUSER_MAX_COUNT - // and need to fail this request with a distinct status. - status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER; - } - - TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "%!FUNC! Exit with status %!STATUS!", status); - - return status; -} - -EXTERN_C_END