From 9441b25e0c3ab65a02acab788a86d7c8bba05cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger-Stelzer?= Date: Sat, 6 Aug 2022 11:50:26 +0200 Subject: [PATCH] Added IOCTL_DS4_AWAIT_OUTPUT Added DMF support to PDO --- ViGEmBus.sln.DotSettings | 2 + sdk/include/ViGEm/km/BusShared.h | 414 +++++++++++++++++-------------- sys/Driver.cpp | 1 + sys/Ds4Pdo.cpp | 373 +++++++++++++++++----------- sys/Ds4Pdo.hpp | 35 ++- sys/EmulationTargetPDO.cpp | 61 ++++- sys/EmulationTargetPDO.hpp | 15 +- sys/Queue.cpp | 50 ++++ sys/Queue.hpp | 1 + sys/XusbPdo.cpp | 5 + sys/XusbPdo.hpp | 1 + 11 files changed, 605 insertions(+), 353 deletions(-) diff --git a/ViGEmBus.sln.DotSettings b/ViGEmBus.sln.DotSettings index b110456..dbd4b03 100644 --- a/ViGEmBus.sln.DotSettings +++ b/ViGEmBus.sln.DotSettings @@ -1,2 +1,4 @@  + True + True True \ No newline at end of file diff --git a/sdk/include/ViGEm/km/BusShared.h b/sdk/include/ViGEm/km/BusShared.h index 92ee477..3b9fd1d 100644 --- a/sdk/include/ViGEm/km/BusShared.h +++ b/sdk/include/ViGEm/km/BusShared.h @@ -31,7 +31,7 @@ SOFTWARE. // // {96E42B22-F5E9-42F8-B043-ED0F932F014F} DEFINE_GUID(GUID_DEVINTERFACE_BUSENUM_VIGEM, - 0x96E42B22, 0xF5E9, 0x42F8, 0xB0, 0x43, 0xED, 0x0F, 0x93, 0x2F, 0x01, 0x4F); + 0x96E42B22, 0xF5E9, 0x42F8, 0xB0, 0x43, 0xED, 0x0F, 0x93, 0x2F, 0x01, 0x4F); #pragma once @@ -71,6 +71,7 @@ DEFINE_GUID(GUID_DEVINTERFACE_BUSENUM_VIGEM, //#define IOCTL_XGIP_SUBMIT_REPORT BUSENUM_W_IOCTL (IOCTL_VIGEM_BASE + 0x204) //#define IOCTL_XGIP_SUBMIT_INTERRUPT BUSENUM_W_IOCTL (IOCTL_VIGEM_BASE + 0x205) #define IOCTL_XUSB_GET_USER_INDEX BUSENUM_RW_IOCTL(IOCTL_VIGEM_BASE + 0x206) +#define IOCTL_DS4_AWAIT_OUTPUT BUSENUM_RW_IOCTL(IOCTL_VIGEM_BASE + 0x207) // @@ -84,47 +85,47 @@ DEFINE_GUID(GUID_DEVINTERFACE_BUSENUM_VIGEM, // typedef struct _VIGEM_PLUGIN_TARGET { - // - // sizeof (struct _BUSENUM_HARDWARE) - // - IN ULONG Size; + // + // sizeof (struct _BUSENUM_HARDWARE) + // + IN ULONG Size; - // - // Serial number of target device. - // - IN ULONG SerialNo; + // + // Serial number of target device. + // + IN ULONG SerialNo; - // - // Type of the target device to emulate. - // - VIGEM_TARGET_TYPE TargetType; + // + // Type of the target device to emulate. + // + VIGEM_TARGET_TYPE TargetType; - // - // If set, the vendor ID the emulated device is reporting - // - USHORT VendorId; + // + // If set, the vendor ID the emulated device is reporting + // + USHORT VendorId; - // - // If set, the product ID the emulated device is reporting - // - USHORT ProductId; + // + // If set, the product ID the emulated device is reporting + // + USHORT ProductId; -} VIGEM_PLUGIN_TARGET, *PVIGEM_PLUGIN_TARGET; +} VIGEM_PLUGIN_TARGET, * PVIGEM_PLUGIN_TARGET; // // Initializes a VIGEM_PLUGIN_TARGET structure. // VOID FORCEINLINE VIGEM_PLUGIN_TARGET_INIT( - _Out_ PVIGEM_PLUGIN_TARGET PlugIn, - _In_ ULONG SerialNo, - _In_ VIGEM_TARGET_TYPE TargetType + _Out_ PVIGEM_PLUGIN_TARGET PlugIn, + _In_ ULONG SerialNo, + _In_ VIGEM_TARGET_TYPE TargetType ) { - RtlZeroMemory(PlugIn, sizeof(VIGEM_PLUGIN_TARGET)); + RtlZeroMemory(PlugIn, sizeof(VIGEM_PLUGIN_TARGET)); - PlugIn->Size = sizeof(VIGEM_PLUGIN_TARGET); - PlugIn->SerialNo = SerialNo; - PlugIn->TargetType = TargetType; + PlugIn->Size = sizeof(VIGEM_PLUGIN_TARGET); + PlugIn->SerialNo = SerialNo; + PlugIn->TargetType = TargetType; } #pragma endregion @@ -136,30 +137,30 @@ VOID FORCEINLINE VIGEM_PLUGIN_TARGET_INIT( // typedef struct _VIGEM_UNPLUG_TARGET { - // - // sizeof (struct _REMOVE_HARDWARE) - // - IN ULONG Size; + // + // sizeof (struct _REMOVE_HARDWARE) + // + IN ULONG Size; - // - // Serial number of target device. - // - ULONG SerialNo; + // + // Serial number of target device. + // + ULONG SerialNo; -} VIGEM_UNPLUG_TARGET, *PVIGEM_UNPLUG_TARGET; +} VIGEM_UNPLUG_TARGET, * PVIGEM_UNPLUG_TARGET; // // Initializes a VIGEM_UNPLUG_TARGET structure. // VOID FORCEINLINE VIGEM_UNPLUG_TARGET_INIT( - _Out_ PVIGEM_UNPLUG_TARGET UnPlug, - _In_ ULONG SerialNo + _Out_ PVIGEM_UNPLUG_TARGET UnPlug, + _In_ ULONG SerialNo ) { - RtlZeroMemory(UnPlug, sizeof(VIGEM_UNPLUG_TARGET)); + RtlZeroMemory(UnPlug, sizeof(VIGEM_UNPLUG_TARGET)); - UnPlug->Size = sizeof(VIGEM_UNPLUG_TARGET); - UnPlug->SerialNo = SerialNo; + UnPlug->Size = sizeof(VIGEM_UNPLUG_TARGET); + UnPlug->SerialNo = SerialNo; } #pragma endregion @@ -168,21 +169,21 @@ VOID FORCEINLINE VIGEM_UNPLUG_TARGET_INIT( typedef struct _VIGEM_CHECK_VERSION { - IN ULONG Size; + IN ULONG Size; - IN ULONG Version; + IN ULONG Version; -} VIGEM_CHECK_VERSION, *PVIGEM_CHECK_VERSION; +} VIGEM_CHECK_VERSION, * PVIGEM_CHECK_VERSION; VOID FORCEINLINE VIGEM_CHECK_VERSION_INIT( - _Out_ PVIGEM_CHECK_VERSION CheckVersion, - _In_ ULONG Version + _Out_ PVIGEM_CHECK_VERSION CheckVersion, + _In_ ULONG Version ) { - RtlZeroMemory(CheckVersion, sizeof(VIGEM_CHECK_VERSION)); + RtlZeroMemory(CheckVersion, sizeof(VIGEM_CHECK_VERSION)); - CheckVersion->Size = sizeof(VIGEM_CHECK_VERSION); - CheckVersion->Version = Version; + CheckVersion->Size = sizeof(VIGEM_CHECK_VERSION); + CheckVersion->Version = Version; } #pragma endregion @@ -191,21 +192,21 @@ VOID FORCEINLINE VIGEM_CHECK_VERSION_INIT( typedef struct _VIGEM_WAIT_DEVICE_READY { - IN ULONG Size; + IN ULONG Size; - IN ULONG SerialNo; + IN ULONG SerialNo; } VIGEM_WAIT_DEVICE_READY, * PVIGEM_WAIT_DEVICE_READY; VOID FORCEINLINE VIGEM_WAIT_DEVICE_READY_INIT( - _Out_ PVIGEM_WAIT_DEVICE_READY WaitReady, - _In_ ULONG SerialNo + _Out_ PVIGEM_WAIT_DEVICE_READY WaitReady, + _In_ ULONG SerialNo ) { - RtlZeroMemory(WaitReady, sizeof(VIGEM_WAIT_DEVICE_READY)); + RtlZeroMemory(WaitReady, sizeof(VIGEM_WAIT_DEVICE_READY)); - WaitReady->Size = sizeof(VIGEM_WAIT_DEVICE_READY); - WaitReady->SerialNo = SerialNo; + WaitReady->Size = sizeof(VIGEM_WAIT_DEVICE_READY); + WaitReady->SerialNo = SerialNo; } #pragma endregion @@ -217,45 +218,45 @@ VOID FORCEINLINE VIGEM_WAIT_DEVICE_READY_INIT( // typedef struct _XUSB_REQUEST_NOTIFICATION { - // - // sizeof(struct _XUSB_REQUEST_NOTIFICATION) - // - ULONG Size; + // + // sizeof(struct _XUSB_REQUEST_NOTIFICATION) + // + ULONG Size; - // - // Serial number of target device. - // - ULONG SerialNo; + // + // Serial number of target device. + // + ULONG SerialNo; - // - // Vibration intensity value of the large motor (0-255). - // - UCHAR LargeMotor; + // + // Vibration intensity value of the large motor (0-255). + // + UCHAR LargeMotor; - // - // Vibration intensity value of the small motor (0-255). - // - UCHAR SmallMotor; + // + // Vibration intensity value of the small motor (0-255). + // + UCHAR SmallMotor; - // - // Index number of the slot/LED that XUSB.sys has assigned. - // - UCHAR LedNumber; + // + // Index number of the slot/LED that XUSB.sys has assigned. + // + UCHAR LedNumber; -} XUSB_REQUEST_NOTIFICATION, *PXUSB_REQUEST_NOTIFICATION; +} XUSB_REQUEST_NOTIFICATION, * PXUSB_REQUEST_NOTIFICATION; // // Initializes a XUSB_REQUEST_NOTIFICATION structure. // VOID FORCEINLINE XUSB_REQUEST_NOTIFICATION_INIT( - _Out_ PXUSB_REQUEST_NOTIFICATION Request, - _In_ ULONG SerialNo + _Out_ PXUSB_REQUEST_NOTIFICATION Request, + _In_ ULONG SerialNo ) { - RtlZeroMemory(Request, sizeof(XUSB_REQUEST_NOTIFICATION)); + RtlZeroMemory(Request, sizeof(XUSB_REQUEST_NOTIFICATION)); - Request->Size = sizeof(XUSB_REQUEST_NOTIFICATION); - Request->SerialNo = SerialNo; + Request->Size = sizeof(XUSB_REQUEST_NOTIFICATION); + Request->SerialNo = SerialNo; } // @@ -263,68 +264,68 @@ VOID FORCEINLINE XUSB_REQUEST_NOTIFICATION_INIT( // typedef struct _XUSB_SUBMIT_REPORT { - // - // sizeof(struct _XUSB_SUBMIT_REPORT) - // - ULONG Size; + // + // sizeof(struct _XUSB_SUBMIT_REPORT) + // + ULONG Size; - // - // Serial number of target device. - // - ULONG SerialNo; + // + // Serial number of target device. + // + ULONG SerialNo; - // - // Report to submit to the target device. - // - XUSB_REPORT Report; + // + // Report to submit to the target device. + // + XUSB_REPORT Report; -} XUSB_SUBMIT_REPORT, *PXUSB_SUBMIT_REPORT; +} XUSB_SUBMIT_REPORT, * PXUSB_SUBMIT_REPORT; // // Initializes an XUSB report. // VOID FORCEINLINE XUSB_SUBMIT_REPORT_INIT( - _Out_ PXUSB_SUBMIT_REPORT Report, - _In_ ULONG SerialNo + _Out_ PXUSB_SUBMIT_REPORT Report, + _In_ ULONG SerialNo ) { - RtlZeroMemory(Report, sizeof(XUSB_SUBMIT_REPORT)); + RtlZeroMemory(Report, sizeof(XUSB_SUBMIT_REPORT)); - Report->Size = sizeof(XUSB_SUBMIT_REPORT); - Report->SerialNo = SerialNo; + Report->Size = sizeof(XUSB_SUBMIT_REPORT); + Report->SerialNo = SerialNo; } typedef struct _XUSB_GET_USER_INDEX { - // - // sizeof(struct _XUSB_GET_USER_INDEX) - // - ULONG Size; + // + // sizeof(struct _XUSB_GET_USER_INDEX) + // + ULONG Size; - // - // Serial number of target device. - // - ULONG SerialNo; + // + // Serial number of target device. + // + ULONG SerialNo; - // - // User index of target device. - // - OUT ULONG UserIndex; + // + // User index of target device. + // + OUT ULONG UserIndex; -} XUSB_GET_USER_INDEX, *PXUSB_GET_USER_INDEX; +} XUSB_GET_USER_INDEX, * PXUSB_GET_USER_INDEX; // // Initializes XUSB_GET_USER_INDEX structure. // VOID FORCEINLINE XUSB_GET_USER_INDEX_INIT( - _Out_ PXUSB_GET_USER_INDEX GetRequest, - _In_ ULONG SerialNo + _Out_ PXUSB_GET_USER_INDEX GetRequest, + _In_ ULONG SerialNo ) { - RtlZeroMemory(GetRequest, sizeof(XUSB_GET_USER_INDEX)); + RtlZeroMemory(GetRequest, sizeof(XUSB_GET_USER_INDEX)); - GetRequest->Size = sizeof(XUSB_GET_USER_INDEX); - GetRequest->SerialNo = SerialNo; + GetRequest->Size = sizeof(XUSB_GET_USER_INDEX); + GetRequest->SerialNo = SerialNo; } #pragma endregion @@ -333,57 +334,57 @@ VOID FORCEINLINE XUSB_GET_USER_INDEX_INIT( typedef struct _DS4_OUTPUT_REPORT { - // - // Vibration intensity value of the small motor (0-255). - // - UCHAR SmallMotor; + // + // Vibration intensity value of the small motor (0-255). + // + UCHAR SmallMotor; - // - // Vibration intensity value of the large motor (0-255). - // - UCHAR LargeMotor; + // + // Vibration intensity value of the large motor (0-255). + // + UCHAR LargeMotor; - // - // Color values of the Lightbar. - // - DS4_LIGHTBAR_COLOR LightbarColor; + // + // Color values of the Lightbar. + // + DS4_LIGHTBAR_COLOR LightbarColor; -} DS4_OUTPUT_REPORT, *PDS4_OUTPUT_REPORT; +} DS4_OUTPUT_REPORT, * PDS4_OUTPUT_REPORT; // // Data structure used in IOCTL_DS4_REQUEST_NOTIFICATION requests. // typedef struct _DS4_REQUEST_NOTIFICATION { - // - // sizeof(struct _XUSB_REQUEST_NOTIFICATION) - // - ULONG Size; + // + // sizeof(struct _XUSB_REQUEST_NOTIFICATION) + // + ULONG Size; - // - // Serial number of target device. - // - ULONG SerialNo; + // + // Serial number of target device. + // + ULONG SerialNo; - // - // The HID output report - // - DS4_OUTPUT_REPORT Report; + // + // The HID output report + // + DS4_OUTPUT_REPORT Report; -} DS4_REQUEST_NOTIFICATION, *PDS4_REQUEST_NOTIFICATION; +} DS4_REQUEST_NOTIFICATION, * PDS4_REQUEST_NOTIFICATION; // // Initializes a DS4_REQUEST_NOTIFICATION structure. // VOID FORCEINLINE DS4_REQUEST_NOTIFICATION_INIT( - _Out_ PDS4_REQUEST_NOTIFICATION Request, - _In_ ULONG SerialNo + _Out_ PDS4_REQUEST_NOTIFICATION Request, + _In_ ULONG SerialNo ) { - RtlZeroMemory(Request, sizeof(DS4_REQUEST_NOTIFICATION)); + RtlZeroMemory(Request, sizeof(DS4_REQUEST_NOTIFICATION)); - Request->Size = sizeof(DS4_REQUEST_NOTIFICATION); - Request->SerialNo = SerialNo; + Request->Size = sizeof(DS4_REQUEST_NOTIFICATION); + Request->SerialNo = SerialNo; } // @@ -391,37 +392,37 @@ VOID FORCEINLINE DS4_REQUEST_NOTIFICATION_INIT( // typedef struct _DS4_SUBMIT_REPORT { - // - // sizeof(struct _DS4_SUBMIT_REPORT) - // - ULONG Size; + // + // sizeof(struct _DS4_SUBMIT_REPORT) + // + ULONG Size; - // - // Serial number of target device. - // - ULONG SerialNo; + // + // Serial number of target device. + // + ULONG SerialNo; - // - // HID Input report - // - DS4_REPORT Report; + // + // HID Input report + // + DS4_REPORT Report; -} DS4_SUBMIT_REPORT, *PDS4_SUBMIT_REPORT; +} DS4_SUBMIT_REPORT, * PDS4_SUBMIT_REPORT; // // Initializes a DualShock 4 report. // VOID FORCEINLINE DS4_SUBMIT_REPORT_INIT( - _Out_ PDS4_SUBMIT_REPORT Report, - _In_ ULONG SerialNo + _Out_ PDS4_SUBMIT_REPORT Report, + _In_ ULONG SerialNo ) { - RtlZeroMemory(Report, sizeof(DS4_SUBMIT_REPORT)); + RtlZeroMemory(Report, sizeof(DS4_SUBMIT_REPORT)); - Report->Size = sizeof(DS4_SUBMIT_REPORT); - Report->SerialNo = SerialNo; + Report->Size = sizeof(DS4_SUBMIT_REPORT); + Report->SerialNo = SerialNo; - DS4_REPORT_INIT(&Report->Report); + DS4_REPORT_INIT(&Report->Report); } #include @@ -431,20 +432,20 @@ VOID FORCEINLINE DS4_SUBMIT_REPORT_INIT( // typedef struct _DS4_SUBMIT_REPORT_EX { - // - // sizeof(struct _DS4_SUBMIT_REPORT_EX) - // - _In_ ULONG Size; + // + // sizeof(struct _DS4_SUBMIT_REPORT_EX) + // + _In_ ULONG Size; - // - // Serial number of target device. - // - _In_ ULONG SerialNo; + // + // Serial number of target device. + // + _In_ ULONG SerialNo; - // - // Full size HID report excluding fixed Report ID. - // - _In_ DS4_REPORT_EX Report; + // + // Full size HID report excluding fixed Report ID. + // + _In_ DS4_REPORT_EX Report; } DS4_SUBMIT_REPORT_EX, * PDS4_SUBMIT_REPORT_EX; @@ -454,14 +455,53 @@ typedef struct _DS4_SUBMIT_REPORT_EX // Initializes a DualShock 4 extended report. // VOID FORCEINLINE DS4_SUBMIT_REPORT_EX_INIT( - _Out_ PDS4_SUBMIT_REPORT_EX Report, - _In_ ULONG SerialNo + _Out_ PDS4_SUBMIT_REPORT_EX Report, + _In_ ULONG SerialNo ) { - RtlZeroMemory(Report, sizeof(DS4_SUBMIT_REPORT_EX)); + RtlZeroMemory(Report, sizeof(DS4_SUBMIT_REPORT_EX)); - Report->Size = sizeof(DS4_SUBMIT_REPORT_EX); - Report->SerialNo = SerialNo; + Report->Size = sizeof(DS4_SUBMIT_REPORT_EX); + Report->SerialNo = SerialNo; } #pragma endregion + +typedef struct _DS4_AWAIT_OUTPUT_BUFFER +{ + // + // The output report buffer + // + _Out_ UCHAR Buffer[64]; + +} DS4_AWAIT_OUTPUT_BUFFER, *PDS4_AWAIT_OUTPUT_BUFFER; + +typedef struct _DS4_AWAIT_OUTPUT +{ + // + // sizeof(struct _DS4_SUBMIT_REPORT_EX) + // + _In_ ULONG Size; + + // + // Serial number of target device. + // + _In_ ULONG SerialNo; + + // + // The output report buffer + // + _Out_ DS4_AWAIT_OUTPUT_BUFFER Report; + +} DS4_AWAIT_OUTPUT, * PDS4_AWAIT_OUTPUT; + +VOID FORCEINLINE DS4_AWAIT_OUTPUT_INIT( + _Out_ PDS4_AWAIT_OUTPUT Output, + _In_ ULONG SerialNo +) +{ + RtlZeroMemory(Output, sizeof(DS4_AWAIT_OUTPUT)); + + Output->Size = sizeof(DS4_AWAIT_OUTPUT); + Output->SerialNo = SerialNo; +} diff --git a/sys/Driver.cpp b/sys/Driver.cpp index e6ccd8e..6a23390 100644 --- a/sys/Driver.cpp +++ b/sys/Driver.cpp @@ -70,6 +70,7 @@ IoctlHandler_IoctlRecord ViGEmBus_IoctlSpecification[] = {IOCTL_DS4_SUBMIT_REPORT, sizeof(DS4_SUBMIT_REPORT), 0, Bus_Ds4SubmitReportHandler}, {IOCTL_DS4_REQUEST_NOTIFICATION, sizeof(DS4_REQUEST_NOTIFICATION), sizeof(DS4_REQUEST_NOTIFICATION), Bus_Ds4RequestNotificationHandler}, {IOCTL_XUSB_GET_USER_INDEX, sizeof(XUSB_GET_USER_INDEX), sizeof(XUSB_GET_USER_INDEX), Bus_XusbGetUserIndexHandler}, + {IOCTL_DS4_AWAIT_OUTPUT, sizeof(DS4_AWAIT_OUTPUT), sizeof(DS4_AWAIT_OUTPUT), Bus_Ds4AwaitOutputHandler}, }; // diff --git a/sys/Ds4Pdo.cpp b/sys/Ds4Pdo.cpp index e4bc1f1..8b66622 100644 --- a/sys/Ds4Pdo.cpp +++ b/sys/Ds4Pdo.cpp @@ -144,7 +144,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoPrepareDevice(PWDFDEVICE_IN status); return status; } - + return STATUS_SUCCESS; } @@ -192,162 +192,160 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext() // PDO is parent timerAttribs.ParentObject = this->_PdoDevice; - // Create timer - status = WdfTimerCreate( - &timerConfig, - &timerAttribs, - &this->_PendingUsbInRequestsTimer - ); - if (!NT_SUCCESS(status)) + do { - TraceError( - 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)) - { - TraceError( - 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, - nullptr, - WDF_NO_OBJECT_ATTRIBUTES, - &keyTargets - ); - if (!NT_SUCCESS(status)) - { - TraceError( - 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, - nullptr, - WDF_NO_OBJECT_ATTRIBUTES, - &keyDS - ); - if (!NT_SUCCESS(status)) - { - TraceError( - 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, - nullptr, - WDF_NO_OBJECT_ATTRIBUTES, - &keySerial - ); - if (!NT_SUCCESS(status)) - { - TraceError( - TRACE_DS4, - "WdfRegistryCreateKey failed with status %!STATUS!", - status); - return status; - } - - RtlUnicodeStringInit(&valueName, L"TargetMacAddress"); - - status = WdfRegistryQueryValue( - keySerial, - &valueName, - sizeof(MAC_ADDRESS), - &this->_TargetMacAddress, - nullptr, - nullptr - ); - - 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)) + // Create timer + if (!NT_SUCCESS(status = WdfTimerCreate( + &timerConfig, + &timerAttribs, + &this->_PendingUsbInRequestsTimer + ))) { TraceError( TRACE_DS4, - "WdfRegistryAssignValue failed with status %!STATUS!", + "WdfTimerCreate failed with status %!STATUS!", status); - return status; + break; } - } - else if (!NT_SUCCESS(status)) - { - TraceError( + + // Load/generate MAC address + + // + // TODO: tidy up this region + // + + WDFKEY keyParams, keyTargets, keyDS, keySerial; + UNICODE_STRING keyName, valueName; + + if (!NT_SUCCESS(status = WdfDriverOpenParametersRegistryKey( + WdfGetDriver(), + STANDARD_RIGHTS_ALL, + WDF_NO_OBJECT_ATTRIBUTES, + &keyParams + ))) + { + TraceError( + TRACE_DS4, + "WdfDriverOpenParametersRegistryKey failed with status %!STATUS!", + status); + break; + } + + RtlUnicodeStringInit(&keyName, L"Targets"); + + if (!NT_SUCCESS(status = WdfRegistryCreateKey( + keyParams, + &keyName, + KEY_ALL_ACCESS, + REG_OPTION_NON_VOLATILE, + nullptr, + WDF_NO_OBJECT_ATTRIBUTES, + &keyTargets + ))) + { + TraceError( + TRACE_DS4, + "WdfRegistryCreateKey failed with status %!STATUS!", + status); + break; + } + + RtlUnicodeStringInit(&keyName, L"DualShock"); + + if (!NT_SUCCESS(status = WdfRegistryCreateKey( + keyTargets, + &keyName, + KEY_ALL_ACCESS, + REG_OPTION_NON_VOLATILE, + nullptr, + WDF_NO_OBJECT_ATTRIBUTES, + &keyDS + ))) + { + TraceError( + TRACE_DS4, + "WdfRegistryCreateKey failed with status %!STATUS!", + status); + break; + } + + DECLARE_UNICODE_STRING_SIZE(serialPath, 4); + RtlUnicodeStringPrintf(&serialPath, L"%04d", this->_SerialNo); + + if (!NT_SUCCESS(status = WdfRegistryCreateKey( + keyDS, + &serialPath, + KEY_ALL_ACCESS, + REG_OPTION_NON_VOLATILE, + nullptr, + WDF_NO_OBJECT_ATTRIBUTES, + &keySerial + ))) + { + TraceError( + TRACE_DS4, + "WdfRegistryCreateKey failed with status %!STATUS!", + status); + break; + } + + RtlUnicodeStringInit(&valueName, L"TargetMacAddress"); + + status = WdfRegistryQueryValue( + keySerial, + &valueName, + sizeof(MAC_ADDRESS), + &this->_TargetMacAddress, + nullptr, + nullptr + ); + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DS4, - "WdfRegistryQueryValue failed with status %!STATUS!", - status); - return status; - } + "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); - WdfRegistryClose(keySerial); - WdfRegistryClose(keyDS); - WdfRegistryClose(keyTargets); - WdfRegistryClose(keyParams); + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + GenerateRandomMacAddress(&this->_TargetMacAddress); - return STATUS_SUCCESS; + if (!NT_SUCCESS(status = WdfRegistryAssignValue( + keySerial, + &valueName, + REG_BINARY, + sizeof(MAC_ADDRESS), + static_cast(&this->_TargetMacAddress) + ))) + { + TraceError( + TRACE_DS4, + "WdfRegistryAssignValue failed with status %!STATUS!", + status); + break; + } + } + else if (!NT_SUCCESS(status)) + { + TraceError( + TRACE_DS4, + "WdfRegistryQueryValue failed with status %!STATUS!", + status); + break; + } + + WdfRegistryClose(keySerial); + WdfRegistryClose(keyDS); + WdfRegistryClose(keyTargets); + WdfRegistryClose(keyParams); + + } while (FALSE); + + return status; } VOID ViGEm::Bus::Targets::EmulationTargetDS4::GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) @@ -999,8 +997,8 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetStringDescriptorType(PUR NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) { - NTSTATUS status = STATUS_SUCCESS; - WDFREQUEST notifyRequest; + NTSTATUS status = STATUS_SUCCESS; + WDFREQUEST notifyRequest; // Data coming FROM us TO higher driver if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN @@ -1023,6 +1021,20 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR static_cast(pTransfer->TransferBuffer) + DS4_OUTPUT_BUFFER_OFFSET, DS4_OUTPUT_BUFFER_LENGTH); + if (!NT_SUCCESS(status = DMF_NotifyUserWithRequestMultiple_DataBroadcast( + this->_OutputReportNotify, + pTransfer->TransferBuffer, + sizeof(DS4_AWAIT_OUTPUT_BUFFER), + STATUS_SUCCESS + ))) + { + TraceError( + TRACE_USBPDO, + "DMF_NotifyUserWithRequestMultiple_DataBroadcast failed with status %!STATUS!", + status + ); + } + if (NT_SUCCESS(WdfIoQueueRetrieveNextRequest( this->_PendingNotificationRequests, ¬ifyRequest))) @@ -1328,3 +1340,64 @@ VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc( TraceVerbose(TRACE_DS4, "%!FUNC! Exit with status %!STATUS!", status); } + +VOID ViGEm::Bus::Targets::EmulationTargetDS4::EvtUserNotifyRequestComplete( + _In_ DMFMODULE DmfModule, + _In_ WDFREQUEST Request, + _In_opt_ ULONG_PTR Context, + _In_ NTSTATUS NtStatus +) +{ + UNREFERENCED_PARAMETER(DmfModule); + + FuncEntry(TRACE_DS4); + + PDS4_AWAIT_OUTPUT pNotify = NULL; + size_t bufLen = 0; + + const PUCHAR pBuffer = reinterpret_cast(Context); + + if (NT_SUCCESS(WdfRequestRetrieveOutputBuffer( + Request, + sizeof(DS4_AWAIT_OUTPUT), + reinterpret_cast(&pNotify), + &bufLen + ))) + { + RtlCopyMemory(pNotify->Report.Buffer, pBuffer, sizeof(DS4_AWAIT_OUTPUT_BUFFER)); + WdfRequestSetInformation(Request, sizeof(DS4_AWAIT_OUTPUT)); + } + + WdfRequestComplete(Request, NtStatus); + + FuncExitNoReturn(TRACE_DS4); +} + +NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::OutputReportRequestProcess(WDFREQUEST Request) const +{ + return DMF_NotifyUserWithRequestMultiple_RequestProcess( + this->_OutputReportNotify, + Request + ); +} + +VOID ViGEm::Bus::Targets::EmulationTargetDS4::DmfDeviceModulesAdd(_In_ PDMFMODULE_INIT DmfModuleInit) +{ + DMF_CONFIG_NotifyUserWithRequestMultiple notifyConfig; + DMF_MODULE_ATTRIBUTES moduleAttributes; + + DMF_CONFIG_NotifyUserWithRequestMultiple_AND_ATTRIBUTES_INIT(¬ifyConfig, &moduleAttributes); + + notifyConfig.MaximumNumberOfPendingRequests = 64 * 2; + notifyConfig.SizeOfDataBuffer = sizeof(DS4_AWAIT_OUTPUT_BUFFER); + notifyConfig.MaximumNumberOfPendingDataBuffers = 64; + notifyConfig.ModeType.Modes.ReplayLastMessageToNewClients = TRUE; + notifyConfig.CompletionCallback = EvtUserNotifyRequestComplete; + + DMF_DmfModuleAdd( + DmfModuleInit, + &moduleAttributes, + WDF_NO_OBJECT_ATTRIBUTES, + &this->_OutputReportNotify + ); +} diff --git a/sys/Ds4Pdo.hpp b/sys/Ds4Pdo.hpp index 7094d9d..d2371cb 100644 --- a/sys/Ds4Pdo.hpp +++ b/sys/Ds4Pdo.hpp @@ -53,7 +53,7 @@ namespace ViGEm::Bus::Targets UCHAR Nic1; UCHAR Nic2; } MAC_ADDRESS, * PMAC_ADDRESS; - + constexpr unsigned char hid_get_report_id(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST* pReq) { return pReq->Value & 0xFF; @@ -70,8 +70,8 @@ namespace ViGEm::Bus::Targets EmulationTargetDS4(ULONG Serial, LONG SessionId, USHORT VendorId = 0x054C, USHORT ProductId = 0x05C4); NTSTATUS PdoPrepareDevice(PWDFDEVICE_INIT DeviceInit, - PUNICODE_STRING DeviceId, - PUNICODE_STRING DeviceDescription) override; + PUNICODE_STRING DeviceId, + PUNICODE_STRING DeviceDescription) override; NTSTATUS PdoPrepareHardware() override; @@ -84,21 +84,23 @@ 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 SubmitReportImpl(PVOID NewReport) override; - + + NTSTATUS OutputReportRequestProcess(WDFREQUEST Request) const; + private: static EVT_WDF_TIMER PendingUsbRequestsTimerFunc; @@ -106,8 +108,12 @@ namespace ViGEm::Bus::Targets static VOID GenerateRandomMacAddress(PMAC_ADDRESS Address); + static EVT_DMF_NotifyUserWithRequest_Complete EvtUserNotifyRequestComplete; + protected: void ProcessPendingNotification(WDFQUEUE Queue) override; + + void DmfDeviceModulesAdd(_In_ PDMFMODULE_INIT DmfModuleInit) override; private: static PCWSTR _deviceDescription; @@ -159,6 +165,11 @@ namespace ViGEm::Bus::Targets // // Default MAC address of the host (not used) // - MAC_ADDRESS _HostMacAddress; + MAC_ADDRESS _HostMacAddress; + + // + // User-mode notification on new output report + // + DMFMODULE _OutputReportNotify; }; } diff --git a/sys/EmulationTargetPDO.cpp b/sys/EmulationTargetPDO.cpp index a878f51..604421c 100644 --- a/sys/EmulationTargetPDO.cpp +++ b/sys/EmulationTargetPDO.cpp @@ -57,6 +57,8 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentD WDF_IO_QUEUE_CONFIG usbInQueueConfig; WDF_IO_QUEUE_CONFIG notificationsQueueConfig; PEMULATION_TARGET_PDO_CONTEXT pPdoContext; + PDMFDEVICE_INIT dmfDeviceInit = NULL; + DMF_EVENT_CALLBACKS dmfEventCallbacks; TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Entry"); @@ -74,6 +76,22 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentD do { + dmfDeviceInit = DMF_DmfDeviceInitAllocate(DeviceInit); + + if (dmfDeviceInit == NULL) + { + TraceError( + TRACE_BUSPDO, + "DMF_DmfDeviceInitAllocate failed" + ); + status = STATUS_NO_MEMORY; + break; + } + + DMF_DmfDeviceInitHookPnpPowerEventCallbacks(dmfDeviceInit, NULL); + DMF_DmfDeviceInitHookFileObjectConfig(dmfDeviceInit, NULL); + DMF_DmfDeviceInitHookPowerPolicyEventCallbacks(dmfDeviceInit, NULL); + #pragma region Prepare PDO status = this->PdoPrepareDevice(DeviceInit, &deviceId, &deviceDescription); @@ -272,6 +290,8 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentD defaultPdoQueueConfig.EvtIoInternalDeviceControl = EvtIoInternalDeviceControl; + DMF_DmfDeviceInitHookQueueConfig(dmfDeviceInit, &defaultPdoQueueConfig); + status = WdfIoQueueCreate( this->_PdoDevice, &defaultPdoQueueConfig, @@ -311,8 +331,41 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentD WdfDeviceSetPowerCapabilities(this->_PdoDevice, &this->_PowerCapabilities); #pragma endregion + +#pragma region DMF Initialization + + DMF_EVENT_CALLBACKS_INIT(&dmfEventCallbacks); + dmfEventCallbacks.EvtDmfDeviceModulesAdd = DmfDeviceModulesAdd; + DMF_DmfDeviceInitSetEventCallbacks(dmfDeviceInit, &dmfEventCallbacks); + + if (!NT_SUCCESS(status = DMF_ModulesCreate( + this->_PdoDevice, + &dmfDeviceInit + ))) + { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_DS4, + "DMF_ModulesCreate failed with status %!STATUS!", + status + ); + break; + } + +#pragma endregion + } while (FALSE); + if (dmfDeviceInit) + { + DMF_DmfDeviceInitFree(&dmfDeviceInit); + } + + if (!NT_SUCCESS(status) && this->_PdoDevice != nullptr) + { + WdfObjectDelete(this->_PdoDevice); + } + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); return status; @@ -824,7 +877,6 @@ BOOLEAN ViGEm::Bus::Core::EmulationTargetPDO::EvtChildListIdentificationDescript return (lhs->SerialNo == rhs->SerialNo) ? TRUE : FALSE; } - NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EnqueueWaitDeviceReady(WDFDEVICE ParentDevice, ULONG SerialNo, WDFREQUEST Request) { @@ -1177,3 +1229,10 @@ void ViGEm::Bus::Core::EmulationTargetPDO::EvtWdfIoPendingNotificationQueueState pThis->ProcessPendingNotification(Queue); } + +VOID ViGEm::Bus::Core::EmulationTargetPDO::DmfDeviceModulesAdd(_In_ WDFDEVICE Device, _In_ PDMFMODULE_INIT DmfModuleInit) +{ + const auto pThis = static_cast(EmulationTargetPdoGetContext(Device)->Target); + + pThis->DmfDeviceModulesAdd(DmfModuleInit); +} diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp index 6c019d8..926c698 100644 --- a/sys/EmulationTargetPDO.hpp +++ b/sys/EmulationTargetPDO.hpp @@ -138,7 +138,7 @@ namespace ViGEm::Bus::Core ); NTSTATUS EnqueueWaitDeviceReady(WDFREQUEST Request); - + HANDLE _WaitDeviceReadyCompletionWorkerThreadHandle{}; protected: @@ -147,7 +147,7 @@ namespace ViGEm::Bus::Core static const int MAX_INSTANCE_ID_LEN = 80; static const size_t MAX_OUT_BUFFER_QUEUE_COUNT = 64; - + static const size_t MAX_OUT_BUFFER_QUEUE_SIZE = 128; static PCWSTR _deviceLocation; @@ -181,7 +181,9 @@ namespace ViGEm::Bus::Core static VOID WaitDeviceReadyCompletionWorkerRoutine(IN PVOID StartContext); static VOID DumpAsHex(PCSTR Prefix, PVOID Buffer, ULONG BufferLength); - + + static VOID DmfDeviceModulesAdd(_In_ WDFDEVICE Device, _In_ PDMFMODULE_INIT DmfModuleInit); + virtual VOID GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) = 0; virtual NTSTATUS SelectConfiguration(PURB Urb) = 0; @@ -192,6 +194,8 @@ namespace ViGEm::Bus::Core virtual VOID ProcessPendingNotification(WDFQUEUE Queue) = 0; + virtual void DmfDeviceModulesAdd(_In_ PDMFMODULE_INIT DmfModuleInit) = 0; + // // PNP Capabilities may differ from device to device // @@ -266,6 +270,11 @@ namespace ViGEm::Bus::Core // Queue for interrupt out requests delivered to user-land // DMFMODULE _UsbInterruptOutBufferQueue{}; + + // + // DMF event callbacks + // + DMF_EVENT_CALLBACKS _DmfEventCallbacks; }; typedef struct _PDO_IDENTIFICATION_DESCRIPTION diff --git a/sys/Queue.cpp b/sys/Queue.cpp index 9563489..7f531dc 100644 --- a/sys/Queue.cpp +++ b/sys/Queue.cpp @@ -486,4 +486,54 @@ exit: return status; } +NTSTATUS +Bus_Ds4AwaitOutputHandler( + _In_ DMFMODULE DmfModule, + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG IoctlCode, + _In_reads_(InputBufferSize) VOID* InputBuffer, + _In_ size_t InputBufferSize, + _Out_writes_(OutputBufferSize) VOID* OutputBuffer, + _In_ size_t OutputBufferSize, + _Out_ size_t* BytesReturned +) +{ + UNREFERENCED_PARAMETER(DmfModule); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(IoctlCode); + UNREFERENCED_PARAMETER(OutputBufferSize); + UNREFERENCED_PARAMETER(InputBufferSize); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(BytesReturned); + + FuncEntry(TRACE_QUEUE); + + NTSTATUS status; + EmulationTargetPDO* pdo; + PDS4_AWAIT_OUTPUT pDs4AwaitOut = (PDS4_AWAIT_OUTPUT)InputBuffer; + + // This request only supports a single PDO at a time + if (pDs4AwaitOut->SerialNo == 0) + { + status = STATUS_INVALID_PARAMETER; + goto exit; + } + + if (!EmulationTargetPDO::GetPdoByTypeAndSerial(WdfIoQueueGetDevice(Queue), DualShock4Wired, pDs4AwaitOut->SerialNo, &pdo)) + { + status = STATUS_DEVICE_DOES_NOT_EXIST; + goto exit; + } + + status = static_cast(pdo)->OutputReportRequestProcess(Request); + + status = NT_SUCCESS(status) ? STATUS_PENDING : status; + +exit: + FuncExit(TRACE_QUEUE, "status=%!STATUS!", status); + + return status; +} + EXTERN_C_END diff --git a/sys/Queue.hpp b/sys/Queue.hpp index 4b0fea6..48c941d 100644 --- a/sys/Queue.hpp +++ b/sys/Queue.hpp @@ -46,5 +46,6 @@ EVT_DMF_IoctlHandler_Callback Bus_XusbRequestNotificationHandler; EVT_DMF_IoctlHandler_Callback Bus_Ds4SubmitReportHandler; EVT_DMF_IoctlHandler_Callback Bus_Ds4RequestNotificationHandler; EVT_DMF_IoctlHandler_Callback Bus_XusbGetUserIndexHandler; +EVT_DMF_IoctlHandler_Callback Bus_Ds4AwaitOutputHandler; EXTERN_C_END diff --git a/sys/XusbPdo.cpp b/sys/XusbPdo.cpp index 7355fe6..614922e 100644 --- a/sys/XusbPdo.cpp +++ b/sys/XusbPdo.cpp @@ -1138,3 +1138,8 @@ void ViGEm::Bus::Targets::EmulationTargetXUSB::ProcessPendingNotification(WDFQUE TraceVerbose(TRACE_BUSENUM, "%!FUNC! Exit"); } + +VOID ViGEm::Bus::Targets::EmulationTargetXUSB::DmfDeviceModulesAdd(_In_ PDMFMODULE_INIT DmfModuleInit) +{ + UNREFERENCED_PARAMETER(DmfModuleInit); +} diff --git a/sys/XusbPdo.hpp b/sys/XusbPdo.hpp index 4bc2e64..9afb022 100644 --- a/sys/XusbPdo.hpp +++ b/sys/XusbPdo.hpp @@ -99,6 +99,7 @@ namespace ViGEm::Bus::Targets protected: void ProcessPendingNotification(WDFQUEUE Queue) override; + void DmfDeviceModulesAdd(_In_ PDMFMODULE_INIT DmfModuleInit) override; private: static PCWSTR _deviceDescription;