From 030715d5c3f969cd21e9ee216e12fc8142266133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger-Stelzer?= Date: Sat, 29 Aug 2020 15:52:39 +0200 Subject: [PATCH] Redesigned blocking device plugin --- sys/EmulationTargetPDO.cpp | 97 +++++++++++++++++++++++++++++++++++--- sys/EmulationTargetPDO.hpp | 51 +++++++++++--------- sys/Queue.cpp | 11 +++-- 3 files changed, 125 insertions(+), 34 deletions(-) diff --git a/sys/EmulationTargetPDO.cpp b/sys/EmulationTargetPDO.cpp index 2fb539a..9d658e2 100644 --- a/sys/EmulationTargetPDO.cpp +++ b/sys/EmulationTargetPDO.cpp @@ -559,7 +559,7 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::WaitDeviceReadyCompletionWorkerRoutin { const auto ctx = static_cast(StartContext); - WDFREQUEST pluginRequest; + WDFREQUEST waitRequest; LARGE_INTEGER timeout; timeout.QuadPart = WDF_REL_TIMEOUT_IN_SEC(1); @@ -581,11 +581,11 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::WaitDeviceReadyCompletionWorkerRoutin // // Fetch pending request (there has to be one at this point) // - if (!NT_SUCCESS(WdfIoQueueRetrieveNextRequest(ctx->_WaitDeviceReadyRequests, &pluginRequest))) + if (!NT_SUCCESS(WdfIoQueueRetrieveNextRequest(ctx->_WaitDeviceReadyRequests, &waitRequest))) { TraceEvents(TRACE_LEVEL_WARNING, TRACE_BUSPDO, - "No pending plugin request available" + "No pending device wait request available" ); break; } @@ -594,13 +594,13 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::WaitDeviceReadyCompletionWorkerRoutin { TraceEvents(TRACE_LEVEL_WARNING, TRACE_BUSPDO, - "Plugin request timed out, completing with error" + "Device wait request timed out, completing with error" ); // // We haven't hit a path where the event gets signaled, report error // - WdfRequestComplete(pluginRequest, STATUS_DEVICE_HARDWARE_ERROR); + WdfRequestComplete(waitRequest, STATUS_DEVICE_HARDWARE_ERROR); break; } @@ -608,13 +608,13 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::WaitDeviceReadyCompletionWorkerRoutin { TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSPDO, - "Plugin request completed successfully" + "Device wait request completed successfully" ); // // Event triggered in time, complete with success // - WdfRequestComplete(pluginRequest, STATUS_SUCCESS); + WdfRequestComplete(waitRequest, STATUS_SUCCESS); break; } } @@ -761,6 +761,89 @@ bool ViGEm::Bus::Core::EmulationTargetPDO::GetPdoByTypeAndSerial(IN WDFDEVICE Pa return (GetPdoBySerial(ParentDevice, SerialNo, Object) && (*Object)->GetType() == Type); } +BOOLEAN ViGEm::Bus::Core::EmulationTargetPDO::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->SerialNo == rhs->SerialNo) ? TRUE : FALSE; +} + + +NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EnqueueWaitDeviceReady(WDFDEVICE ParentDevice, ULONG SerialNo, + WDFREQUEST Request) +{ + NTSTATUS status; + PDO_IDENTIFICATION_DESCRIPTION description; + WDF_CHILD_LIST_ITERATOR iterator; + WDF_CHILD_RETRIEVE_INFO childInfo; + WDFDEVICE childDevice; + + TraceDbg(TRACE_BUSPDO, "%!FUNC! Entry"); + + const WDFCHILDLIST list = WdfFdoGetDefaultChildList(ParentDevice); + + WDF_CHILD_LIST_ITERATOR_INIT( + &iterator, + WdfRetrievePendingChildren // might not be online yet + ); + WdfChildListBeginIteration( + list, + &iterator + ); + + WDF_CHILD_RETRIEVE_INFO_INIT( + &childInfo, + &description.Header + ); + + // + // Compare to find explicit PDO asked for (by serial) + // + childInfo.EvtChildListIdentificationDescriptionCompare = EvtChildListIdentificationDescriptionCompare; + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( + &description.Header, + sizeof(description) + ); + + description.SerialNo = SerialNo; + + status = WdfChildListRetrieveNextDevice( + list, + &iterator, + &childDevice, // is NULL due to WdfRetrievePendingChildren + &childInfo + ); + if (NT_SUCCESS(status)) + { + // + // Object pointer filled after successful retrieval + // + status = description.Target->EnqueueWaitDeviceReady(Request); + } + + WdfChildListEndIteration( + list, + &iterator + ); + + TraceDbg(TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); + + return status; +} + NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::EvtDevicePrepareHardware( _In_ WDFDEVICE Device, _In_ WDFCMRESLIST ResourcesRaw, diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp index c630ca2..e71dfde 100644 --- a/sys/EmulationTargetPDO.hpp +++ b/sys/EmulationTargetPDO.hpp @@ -62,30 +62,31 @@ namespace ViGEm::Bus::Core EmulationTargetPDO(ULONG Serial, LONG SessionId, USHORT VendorId, USHORT ProductId); virtual ~EmulationTargetPDO() = default; - + static bool GetPdoByTypeAndSerial( IN WDFDEVICE ParentDevice, IN VIGEM_TARGET_TYPE Type, - IN ULONG SerialNo, - OUT EmulationTargetPDO** Object - ); - - static bool GetPdoBySerial( - IN WDFDEVICE ParentDevice, IN ULONG SerialNo, OUT EmulationTargetPDO** Object ); + static NTSTATUS EnqueueWaitDeviceReady( + WDFDEVICE ParentDevice, + ULONG SerialNo, + WDFREQUEST Request); + + static EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_COMPARE EvtChildListIdentificationDescriptionCompare; + 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 ParentDevice, - _In_ PWDFDEVICE_INIT DeviceInit); + _In_ PWDFDEVICE_INIT DeviceInit); bool operator==(EmulationTargetPDO& other) const { @@ -109,7 +110,7 @@ namespace ViGEm::Bus::Core virtual NTSTATUS UsbGetStringDescriptorType(PURB Urb) = 0; virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, - WDFREQUEST Request) = 0; + WDFREQUEST Request) = 0; virtual NTSTATUS UsbControlTransfer(PURB Urb) = 0; @@ -121,22 +122,28 @@ namespace ViGEm::Bus::Core VIGEM_TARGET_TYPE GetType() const; - NTSTATUS EnqueueWaitDeviceReady(WDFREQUEST Request); - NTSTATUS PdoPrepare(WDFDEVICE ParentDevice); - + private: static unsigned long current_process_id(); static EVT_WDF_DEVICE_CONTEXT_CLEANUP EvtDeviceContextCleanup; - HANDLE _WaitDeviceReadyCompletionWorkerThreadHandle{}; + static bool GetPdoBySerial( + IN WDFDEVICE ParentDevice, + IN ULONG SerialNo, + OUT EmulationTargetPDO** Object + ); + + NTSTATUS EnqueueWaitDeviceReady(WDFREQUEST Request); + HANDLE _WaitDeviceReadyCompletionWorkerThreadHandle{}; + protected: static const ULONG _maxHardwareIdLength = 0xFF; static const int MAX_INSTANCE_ID_LEN = 80; - + static PCWSTR _deviceLocation; static BOOLEAN USB_BUSIFFN UsbInterfaceIsDeviceHighSpeed(IN PVOID BusContext); @@ -164,7 +171,7 @@ namespace ViGEm::Bus::Core static EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoInternalDeviceControl; static VOID WaitDeviceReadyCompletionWorkerRoutine(IN PVOID StartContext); - + virtual VOID GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) = 0; virtual NTSTATUS SelectConfiguration(PURB Urb) = 0; @@ -184,7 +191,7 @@ namespace ViGEm::Bus::Core // Power Capabilities may differ from device to device // WDF_DEVICE_POWER_CAPABILITIES _PowerCapabilities; - + // // Unique serial number of the device on the bus // @@ -219,7 +226,7 @@ namespace ViGEm::Bus::Core // Queue for blocking plugin requests // WDFQUEUE _WaitDeviceReadyRequests{}; - + // // Queue for incoming data interrupt transfer // @@ -234,7 +241,7 @@ namespace ViGEm::Bus::Core // This child objects' device object // WDFDEVICE _PdoDevice{}; - + // // Configuration descriptor size (populated by derived class) // @@ -267,12 +274,12 @@ namespace ViGEm::Bus::Core // Context object of PDO // EmulationTargetPDO* Target; - } PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION; + } PDO_IDENTIFICATION_DESCRIPTION, * PPDO_IDENTIFICATION_DESCRIPTION; typedef struct _EMULATION_TARGET_PDO_CONTEXT { EmulationTargetPDO* Target; - } EMULATION_TARGET_PDO_CONTEXT, *PEMULATION_TARGET_PDO_CONTEXT; + } EMULATION_TARGET_PDO_CONTEXT, * PEMULATION_TARGET_PDO_CONTEXT; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(EMULATION_TARGET_PDO_CONTEXT, EmulationTargetPdoGetContext) } diff --git a/sys/Queue.cpp b/sys/Queue.cpp index 571c380..22cec1e 100644 --- a/sys/Queue.cpp +++ b/sys/Queue.cpp @@ -138,12 +138,13 @@ VOID Bus_EvtIoDeviceControl( break; } - if (!EmulationTargetPDO::GetPdoBySerial(Device, xusbSubmit->SerialNo, &pdo)) - status = STATUS_DEVICE_DOES_NOT_EXIST; - else - status = pdo->EnqueueWaitDeviceReady(Request); + status = EmulationTargetPDO::EnqueueWaitDeviceReady( + Device, + pWaitDeviceReady->SerialNo, + Request + ); - status = NT_SUCCESS(status) ? STATUS_PENDING : status; + status = NT_SUCCESS(status) ? STATUS_PENDING : STATUS_DEVICE_DOES_NOT_EXIST; break;