From 7d81cf3d768baff6859ab265e6bcdc61a8ee1de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger-Stelzer?= Date: Sat, 6 Aug 2022 16:37:45 +0200 Subject: [PATCH] Implemented new DS4 output report feature --- sdk | 2 +- sys/Driver.cpp | 49 ++++++++++++++++++++++++++-- sys/Driver.h | 15 ++++++++- sys/Ds4Pdo.cpp | 85 +++++++++++++------------------------------------ sys/Ds4Pdo.hpp | 9 ++++-- sys/Queue.cpp | 12 +++---- sys/busenum.cpp | 5 +++ 7 files changed, 101 insertions(+), 76 deletions(-) diff --git a/sdk b/sdk index da1ed30..16d9c40 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit da1ed30414b8d4ec3f7d034fc76a11302b427a3d +Subproject commit 16d9c40aae4793c06ff065c1238a8e77f50544fc diff --git a/sys/Driver.cpp b/sys/Driver.cpp index f13541a..d60f3d9 100644 --- a/sys/Driver.cpp +++ b/sys/Driver.cpp @@ -70,7 +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}, + {IOCTL_DS4_AWAIT_OUTPUT_AVAILABLE, sizeof(DS4_AWAIT_OUTPUT), sizeof(DS4_AWAIT_OUTPUT), Bus_Ds4AwaitOutputHandler}, }; // @@ -319,10 +319,10 @@ DmfDeviceModulesAdd( _In_ PDMFMODULE_INIT DmfModuleInit ) { - UNREFERENCED_PARAMETER(Device); - FuncEntry(TRACE_DRIVER); + PFDO_DEVICE_DATA pDevCtx = FdoGetData(Device); + DMF_MODULE_ATTRIBUTES moduleAttributes; DMF_CONFIG_IoctlHandler ioctlHandlerConfig; DMF_CONFIG_IoctlHandler_AND_ATTRIBUTES_INIT(&ioctlHandlerConfig, &moduleAttributes); @@ -339,6 +339,22 @@ DmfDeviceModulesAdd( NULL ); + DMF_CONFIG_NotifyUserWithRequestMultiple notifyConfig; + DMF_CONFIG_NotifyUserWithRequestMultiple_AND_ATTRIBUTES_INIT(¬ifyConfig, &moduleAttributes); + + notifyConfig.MaximumNumberOfPendingRequests = 64 * 2; + notifyConfig.SizeOfDataBuffer = sizeof(DS4_AWAIT_OUTPUT); + notifyConfig.MaximumNumberOfPendingDataBuffers = 64; + notifyConfig.ModeType.Modes.ReplayLastMessageToNewClients = FALSE; + notifyConfig.CompletionCallback = Bus_EvtUserNotifyRequestComplete; + + DMF_DmfModuleAdd( + DmfModuleInit, + &moduleAttributes, + WDF_NO_OBJECT_ATTRIBUTES, + &pDevCtx->UserNotification + ); + FuncExitNoReturn(TRACE_DRIVER); } #pragma code_seg() @@ -547,4 +563,31 @@ Return Value: } +void Bus_EvtUserNotifyRequestComplete( + _In_ DMFMODULE DmfModule, + _In_ WDFREQUEST Request, + _In_opt_ ULONG_PTR Context, + _In_ NTSTATUS NtStatus +) +{ + UNREFERENCED_PARAMETER(DmfModule); + + auto pOutput = reinterpret_cast(Context); + PDS4_AWAIT_OUTPUT pNotify = NULL; + size_t length = 0; + + if (NT_SUCCESS(WdfRequestRetrieveOutputBuffer( + Request, + sizeof(DS4_AWAIT_OUTPUT), + reinterpret_cast(&pNotify), + &length))) + { + RtlCopyMemory(pNotify, pOutput, sizeof(DS4_AWAIT_OUTPUT)); + + WdfRequestSetInformation(Request, sizeof(DS4_AWAIT_OUTPUT)); + } + + WdfRequestComplete(Request, NtStatus); +} + EXTERN_C_END diff --git a/sys/Driver.h b/sys/Driver.h index 78bf7f6..989e1f6 100644 --- a/sys/Driver.h +++ b/sys/Driver.h @@ -65,6 +65,11 @@ typedef struct _FDO_DEVICE_DATA // LONG NextSessionId; + // + // Notification DMF module + // + DMFMODULE UserNotification; + } FDO_DEVICE_DATA, * PFDO_DEVICE_DATA; #define FDO_FIRST_SESSION_ID 100 @@ -77,7 +82,8 @@ WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DEVICE_DATA, FdoGetData) typedef struct _FDO_FILE_DATA { // - // SessionId associated with file handle. Used to map file handles to emulated gamepad devices + // SessionId associated with file handle. + // Used to map file handles to emulated gamepad devices. // LONG SessionId; @@ -111,6 +117,13 @@ DmfDeviceModulesAdd( _In_ PDMFMODULE_INIT DmfModuleInit ); +void Bus_EvtUserNotifyRequestComplete( + _In_ DMFMODULE DmfModule, + _In_ WDFREQUEST Request, + _In_opt_ ULONG_PTR Context, + _In_ NTSTATUS NtStatus +); + #pragma region Bus enumeration-specific functions NTSTATUS diff --git a/sys/Ds4Pdo.cpp b/sys/Ds4Pdo.cpp index fa0f509..280da8f 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; } @@ -342,9 +342,9 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::PdoInitContext() WdfRegistryClose(keyDS); WdfRegistryClose(keyTargets); WdfRegistryClose(keyParams); - + } while (FALSE); - + return status; } @@ -1021,10 +1021,21 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR static_cast(pTransfer->TransferBuffer) + DS4_OUTPUT_BUFFER_OFFSET, DS4_OUTPUT_BUFFER_LENGTH); + + this->_AwaitOutputCache.Size = sizeof(DS4_AWAIT_OUTPUT); + this->_AwaitOutputCache.SerialNo = this->_SerialNo; + RtlCopyMemory( + this->_AwaitOutputCache.Report.Buffer, + pTransfer->TransferBuffer, + pTransfer->TransferBufferLength <= sizeof(DS4_OUTPUT_BUFFER) + ? pTransfer->TransferBufferLength + : sizeof(DS4_OUTPUT_BUFFER) + ); + if (!NT_SUCCESS(status = DMF_NotifyUserWithRequestMultiple_DataBroadcast( this->_OutputReportNotify, - pTransfer->TransferBuffer, - sizeof(DS4_AWAIT_OUTPUT_BUFFER), + &this->_AwaitOutputCache, + sizeof(DS4_AWAIT_OUTPUT), STATUS_SUCCESS ))) { @@ -1035,6 +1046,7 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR ); } + if (NT_SUCCESS(WdfIoQueueRetrieveNextRequest( this->_PendingNotificationRequests, ¬ifyRequest))) @@ -1341,63 +1353,12 @@ 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 auto 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 - ); + UNREFERENCED_PARAMETER(DmfModuleInit); +} + +VOID ViGEm::Bus::Targets::EmulationTargetDS4::SetOutputReportNotifyModule(DMFMODULE Module) +{ + this->_OutputReportNotify = Module; } diff --git a/sys/Ds4Pdo.hpp b/sys/Ds4Pdo.hpp index d2371cb..3ae9cf7 100644 --- a/sys/Ds4Pdo.hpp +++ b/sys/Ds4Pdo.hpp @@ -99,7 +99,7 @@ namespace ViGEm::Bus::Targets NTSTATUS SubmitReportImpl(PVOID NewReport) override; - NTSTATUS OutputReportRequestProcess(WDFREQUEST Request) const; + VOID SetOutputReportNotifyModule(DMFMODULE Module); private: static EVT_WDF_TIMER PendingUsbRequestsTimerFunc; @@ -108,8 +108,6 @@ namespace ViGEm::Bus::Targets static VOID GenerateRandomMacAddress(PMAC_ADDRESS Address); - static EVT_DMF_NotifyUserWithRequest_Complete EvtUserNotifyRequestComplete; - protected: void ProcessPendingNotification(WDFQUEUE Queue) override; @@ -171,5 +169,10 @@ namespace ViGEm::Bus::Targets // User-mode notification on new output report // DMFMODULE _OutputReportNotify; + + // + // Memory for full output report request + // + DS4_AWAIT_OUTPUT _AwaitOutputCache; }; } diff --git a/sys/Queue.cpp b/sys/Queue.cpp index 5a45d3d..7122e41 100644 --- a/sys/Queue.cpp +++ b/sys/Queue.cpp @@ -499,7 +499,7 @@ Bus_Ds4AwaitOutputHandler( _Out_ size_t* BytesReturned ) { - UNREFERENCED_PARAMETER(DmfModule); + UNREFERENCED_PARAMETER(Queue); UNREFERENCED_PARAMETER(IoctlCode); UNREFERENCED_PARAMETER(OutputBufferSize); UNREFERENCED_PARAMETER(InputBufferSize); @@ -509,8 +509,8 @@ Bus_Ds4AwaitOutputHandler( FuncEntry(TRACE_QUEUE); NTSTATUS status; - EmulationTargetPDO* pdo; PDS4_AWAIT_OUTPUT pDs4AwaitOut = (PDS4_AWAIT_OUTPUT)InputBuffer; + PFDO_DEVICE_DATA pDevCtx = FdoGetData(DMF_ParentDeviceGet(DmfModule)); // This request only supports a single PDO at a time if (pDs4AwaitOut->SerialNo == 0) @@ -519,14 +519,14 @@ Bus_Ds4AwaitOutputHandler( goto exit; } - if (!EmulationTargetPDO::GetPdoByTypeAndSerial(WdfIoQueueGetDevice(Queue), DualShock4Wired, pDs4AwaitOut->SerialNo, &pdo)) + if (!NT_SUCCESS(status = DMF_NotifyUserWithRequestMultiple_RequestProcess( + pDevCtx->UserNotification, + Request + ))) { - status = STATUS_DEVICE_DOES_NOT_EXIST; goto exit; } - status = static_cast(pdo)->OutputReportRequestProcess(Request); - status = NT_SUCCESS(status) ? STATUS_PENDING : status; exit: diff --git a/sys/busenum.cpp b/sys/busenum.cpp index 83b662e..71a3ec6 100644 --- a/sys/busenum.cpp +++ b/sys/busenum.cpp @@ -189,6 +189,11 @@ EXTERN_C NTSTATUS Bus_PlugInDevice( goto pluginEnd; } + if (plugIn->TargetType == DualShock4Wired) + { + static_cast(description.Target)->SetOutputReportNotifyModule(FdoGetData(Device)->UserNotification); + } + status = WdfChildListAddOrUpdateChildDescriptionAsPresent( WdfFdoGetDefaultChildList(Device), &description.Header,