From 6ebef069d5de89797839b555577cefa80a7ee06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger-Stelzer?= Date: Sun, 10 May 2020 17:03:01 +0200 Subject: [PATCH] Further porting over stuff --- sys/Ds4Pdo.cpp | 234 ++++++++++++++++++++++++++++++++++++- sys/Ds4Pdo.hpp | 46 +++++++- sys/EmulationTargetPDO.hpp | 38 ++++++ sys/Util.h | 4 + sys/XusbPdo.cpp | 25 ++-- sys/XusbPdo.hpp | 103 ++++++++-------- 6 files changed, 377 insertions(+), 73 deletions(-) diff --git a/sys/Ds4Pdo.cpp b/sys/Ds4Pdo.cpp index ea10704..bc4455d 100644 --- a/sys/Ds4Pdo.cpp +++ b/sys/Ds4Pdo.cpp @@ -3,6 +3,7 @@ #include "Ds4Pdo.tmh" #define NTSTRSAFE_LIB #include +#include using namespace ViGEm::Bus::Targets; @@ -101,16 +102,230 @@ NTSTATUS EmulationTargetDS4::PrepareDevice(PWDFDEVICE_INIT DeviceInit, USHORT Ve NTSTATUS EmulationTargetDS4::PrepareHardware(WDFDEVICE Device) { - UNREFERENCED_PARAMETER(Device); - - return NTSTATUS(); + 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 + ); + + NTSTATUS status = WdfDeviceAddQueryInterface(Device, &ifaceCfg); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_DS4, + "WdfDeviceAddQueryInterface failed with status %!STATUS!", + status); + return status; + } + + // 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(this->Report, DefaultHidReport, DS4_REPORT_SIZE); + RtlZeroMemory(&this->OutputReport, sizeof(DS4_OUTPUT_REPORT)); + + // Start pending IRP queue flush timer + WdfTimerStart(this->PendingUsbInRequestsTimer, DS4_QUEUE_FLUSH_PERIOD); + + return STATUS_SUCCESS; } NTSTATUS EmulationTargetDS4::InitContext(WDFDEVICE Device) { - UNREFERENCED_PARAMETER(Device); - - return NTSTATUS(); + NTSTATUS status; + + // Initialize periodic timer + WDF_TIMER_CONFIG timerConfig; + WDF_TIMER_CONFIG_INIT_PERIODIC( + &timerConfig, + 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, + &this->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", this->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), + &this->TargetMacAddress, + NULL, + NULL + ); + + TraceEvents(TRACE_LEVEL_INFORMATION, + TRACE_DS4, + "MAC-Address: %02X:%02X:%02X:%02X:%02X:%02X\n", + this->TargetMacAddress.Vendor0, + this->TargetMacAddress.Vendor1, + this->TargetMacAddress.Vendor2, + this->TargetMacAddress.Nic0, + this->TargetMacAddress.Nic1, + this->TargetMacAddress.Nic2); + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + GenerateRandomMacAddress(&this->TargetMacAddress); + + status = WdfRegistryAssignValue( + keySerial, + &valueName, + REG_BINARY, + sizeof(MAC_ADDRESS), + static_cast(&this->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 EmulationTargetDS4::GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) @@ -216,3 +431,10 @@ VOID EmulationTargetDS4::SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo) pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003; pInfo->Pipes[1].PipeFlags = 0x00; } + +VOID EmulationTargetDS4::PendingUsbRequestsTimerFunc( + _In_ WDFTIMER Timer +) +{ + UNREFERENCED_PARAMETER(Timer); +} diff --git a/sys/Ds4Pdo.hpp b/sys/Ds4Pdo.hpp index 939b2b8..ad49fe3 100644 --- a/sys/Ds4Pdo.hpp +++ b/sys/Ds4Pdo.hpp @@ -2,11 +2,11 @@ #include "EmulationTargetPDO.hpp" +#include +#include "Util.h" namespace ViGEm::Bus::Targets -{ - - +{ class EmulationTargetDS4 : public Core::EmulationTargetPDO { public: @@ -28,7 +28,7 @@ namespace ViGEm::Bus::Targets private: static PCWSTR _deviceDescription; - + #if defined(_X86_) static const int XUSB_CONFIGURATION_SIZE = 0x00E4; #else @@ -70,5 +70,43 @@ namespace ViGEm::Bus::Targets static const int DS4_REPORT_SIZE = 0x40; static const int DS4_QUEUE_FLUSH_PERIOD = 0x05; + + // + // 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; + + static EVT_WDF_TIMER PendingUsbRequestsTimerFunc; }; + + // + // DS4-specific device context data. + // + typedef struct _DS4_PDO_CONTEXT + { + EmulationTargetDS4* Context; + + } DS4_PDO_CONTEXT, * PDS4_PDO_CONTEXT; + + WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DS4_PDO_CONTEXT, Ds4PdoGetContext) } diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp index eed7ca7..3e60b31 100644 --- a/sys/EmulationTargetPDO.hpp +++ b/sys/EmulationTargetPDO.hpp @@ -2,10 +2,13 @@ #include #include +#include #include #include +#include + // // Some insane macro-magic =3 // @@ -56,5 +59,40 @@ namespace ViGEm::Bus::Core IN OUT PUSBD_VERSION_INFORMATION VersionInformation, IN OUT PULONG HcdCapabilities ); + + // + // 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; + + // + // Queue for incoming data interrupt transfer + // + WDFQUEUE PendingUsbInRequests; + + // + // Queue for inverted calls + // + WDFQUEUE PendingNotificationRequests; }; } diff --git a/sys/Util.h b/sys/Util.h index b6b8a1b..956eec8 100644 --- a/sys/Util.h +++ b/sys/Util.h @@ -48,5 +48,9 @@ typedef struct _MAC_ADDRESS } MAC_ADDRESS, *PMAC_ADDRESS; +EXTERN_C_START + VOID ReverseByteArray(PUCHAR Array, INT Length); VOID GenerateRandomMacAddress(PMAC_ADDRESS Address); + +EXTERN_C_END diff --git a/sys/XusbPdo.cpp b/sys/XusbPdo.cpp index d9a11a2..991f5ec 100644 --- a/sys/XusbPdo.cpp +++ b/sys/XusbPdo.cpp @@ -224,22 +224,26 @@ NTSTATUS EmulationTargetXUSB::InitContext(WDFDEVICE Device) TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_XUSB, "Initializing XUSB context..."); - PXUSB_DEVICE_DATA xusb = XusbPdoGetContext(Device); - - RtlZeroMemory(xusb, sizeof(XUSB_DEVICE_DATA)); - + RtlZeroMemory(this->Rumble, ARRAYSIZE(this->Rumble)); + // Is later overwritten by actual XInput slot - xusb->LedNumber = -1; + this->LedNumber = -1; + + RtlZeroMemory(&this->Packet, sizeof(XUSB_INTERRUPT_IN_PACKET)); // Packet size (20 bytes = 0x14) - xusb->Packet.Size = 0x14; + this->Packet.Size = 0x14; + this->ReportedCapabilities = FALSE; + + this->InterruptInitStage = 0; + // Allocate blob storage NTSTATUS status = WdfMemoryCreate( &attributes, NonPagedPoolNx, XUSB_POOL_TAG, XUSB_BLOB_STORAGE_SIZE, - &xusb->InterruptBlobStorage, + &this->InterruptBlobStorage, reinterpret_cast(&blobBuffer) ); if (!NT_SUCCESS(status)) @@ -279,7 +283,12 @@ NTSTATUS EmulationTargetXUSB::InitContext(WDFDEVICE Device) // 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); + status = WdfIoQueueCreate( + Device, + &holdingInQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &this->HoldingUsbInRequests + ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, diff --git a/sys/XusbPdo.hpp b/sys/XusbPdo.hpp index 289712a..6aac312 100644 --- a/sys/XusbPdo.hpp +++ b/sys/XusbPdo.hpp @@ -2,23 +2,12 @@ #include "EmulationTargetPDO.hpp" +#include namespace ViGEm::Bus::Targets { constexpr auto XUSB_POOL_TAG = 'EGiV'; - - typedef struct _XUSB_REPORT - { - USHORT wButtons; - BYTE bLeftTrigger; - BYTE bRightTrigger; - SHORT sThumbLX; - SHORT sThumbLY; - SHORT sThumbRX; - SHORT sThumbRY; - - } XUSB_REPORT, * PXUSB_REPORT; - + typedef struct _XUSB_INTERRUPT_IN_PACKET { UCHAR Id; @@ -29,49 +18,7 @@ namespace ViGEm::Bus::Targets } XUSB_INTERRUPT_IN_PACKET, * PXUSB_INTERRUPT_IN_PACKET; - // - // XUSB-specific device context data. - // - typedef struct _XUSB_DEVICE_DATA - { - // - // Rumble buffer - // - UCHAR Rumble[0x08]; - // - // 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, XusbPdoGetContext) class EmulationTargetXUSB : public Core::EmulationTargetPDO { @@ -115,5 +62,51 @@ namespace ViGEm::Bus::Targets static const int XUSB_BLOB_05_OFFSET = 0x20; static const int XUSB_BLOB_06_OFFSET = 0x23; static const int XUSB_BLOB_07_OFFSET = 0x26; + + // + // 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-specific device context data. + // + typedef struct _XUSB_PDO_CONTEXT + { + EmulationTargetXUSB* Context; + + } XUSB_PDO_CONTEXT, * PXUSB_PDO_CONTEXT; + + WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(XUSB_PDO_CONTEXT, XusbPdoGetContext) }