diff --git a/sys/Ds4Pdo.cpp b/sys/Ds4Pdo.cpp index bbfb35e..8c7cee8 100644 --- a/sys/Ds4Pdo.cpp +++ b/sys/Ds4Pdo.cpp @@ -1056,6 +1056,29 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_UR status); } } + else + { + PVOID clientBuffer, contextBuffer; + + if (NT_SUCCESS(DMF_BufferQueue_Fetch( + this->_UsbInterruptOutBufferQueue, + &clientBuffer, + &contextBuffer + ))) + { + RtlCopyMemory( + clientBuffer, + &this->_OutputReport, + DS4_OUTPUT_BUFFER_LENGTH + ); + + *static_cast(contextBuffer) = DS4_OUTPUT_BUFFER_LENGTH; + + TraceDbg(TRACE_USBPDO, "Queued %Iu bytes", DS4_OUTPUT_BUFFER_LENGTH); + + DMF_BufferQueue_Enqueue(this->_UsbInterruptOutBufferQueue, clientBuffer); + } + } return status; } @@ -1195,6 +1218,10 @@ VOID ViGEm::Bus::Targets::EmulationTargetDS4::GenerateRandomMacAddress(PMAC_ADDR Address->Nic2 = RtlRandomEx(&seed) % 0xFF; } +void ViGEm::Bus::Targets::EmulationTargetDS4::ProcessPendingNotification(WDFQUEUE Queue) +{ +} + VOID ViGEm::Bus::Targets::EmulationTargetDS4::PendingUsbRequestsTimerFunc( _In_ WDFTIMER Timer ) diff --git a/sys/Ds4Pdo.hpp b/sys/Ds4Pdo.hpp index 3cead67..7094d9d 100644 --- a/sys/Ds4Pdo.hpp +++ b/sys/Ds4Pdo.hpp @@ -105,7 +105,10 @@ namespace ViGEm::Bus::Targets static VOID ReverseByteArray(PUCHAR Array, INT Length); static VOID GenerateRandomMacAddress(PMAC_ADDRESS Address); - + + protected: + void ProcessPendingNotification(WDFQUEUE Queue) override; + private: static PCWSTR _deviceDescription; static const int HID_REQUEST_GET_REPORT = 0x01; diff --git a/sys/EmulationTargetPDO.cpp b/sys/EmulationTargetPDO.cpp index 00a5eac..571e9da 100644 --- a/sys/EmulationTargetPDO.cpp +++ b/sys/EmulationTargetPDO.cpp @@ -255,6 +255,20 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoCreateDevice(WDFDEVICE ParentD break; } + status = WdfIoQueueReadyNotify( + this->_PendingNotificationRequests, + EvtWdfIoPendingNotificationQueueState, + this + ); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSPDO, + "WdfIoQueueReadyNotify (PendingNotificationRequests) failed with status %!STATUS!", + status); + break; + } + #pragma endregion #pragma region Default I/O queue setup @@ -465,6 +479,8 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoPrepare(WDFDEVICE ParentDevice NTSTATUS status; WDF_OBJECT_ATTRIBUTES attributes; WDF_IO_QUEUE_CONFIG plugInQueueConfig; + DMF_MODULE_ATTRIBUTES moduleAttributes; + DMF_CONFIG_BufferQueue dmfBufferCfg; WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = ParentDevice; @@ -486,6 +502,41 @@ NTSTATUS ViGEm::Bus::Core::EmulationTargetPDO::PdoPrepare(WDFDEVICE ParentDevice status); } + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = ParentDevice; + + DMF_CONFIG_BufferQueue_AND_ATTRIBUTES_INIT( + &dmfBufferCfg, + &moduleAttributes + ); + + // Don't auto-grow; start dropping packets on overrun + dmfBufferCfg.SourceSettings.EnableLookAside = FALSE; + // Maximum number of buffers to be filled and kept queued + dmfBufferCfg.SourceSettings.BufferCount = MAX_OUT_BUFFER_QUEUE_COUNT; + // Maximum byte count per buffer + dmfBufferCfg.SourceSettings.BufferSize = MAX_OUT_BUFFER_QUEUE_SIZE; + // Field to store real buffer content length + dmfBufferCfg.SourceSettings.BufferContextSize = sizeof(size_t); + // "Expensive" memory ;) + dmfBufferCfg.SourceSettings.PoolType = NonPagedPoolNx; + + status = DMF_BufferQueue_Create( + ParentDevice, + &moduleAttributes, + &attributes, + &this->_UsbInterruptOutBufferQueue + ); + + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_BUSPDO, + "DMF_BufferQueue_Create failed with status %!STATUS!", + status + ); + } + return status; } @@ -1117,3 +1168,13 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( TraceDbg(TRACE_BUSPDO, "%!FUNC! Exit with status %!STATUS!", status); } + +void ViGEm::Bus::Core::EmulationTargetPDO::EvtWdfIoPendingNotificationQueueState( + WDFQUEUE Queue, + WDFCONTEXT Context +) +{ + const auto pThis = static_cast(Context); + + pThis->ProcessPendingNotification(Queue); +} diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp index e71dfde..6c019d8 100644 --- a/sys/EmulationTargetPDO.hpp +++ b/sys/EmulationTargetPDO.hpp @@ -35,6 +35,9 @@ #pragma once +#pragma warning(disable:5040) +#include +#pragma warning(default:5040) #include #include #include @@ -43,7 +46,6 @@ #include #include -#include // // Some insane macro-magic =3 @@ -144,6 +146,10 @@ 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; static BOOLEAN USB_BUSIFFN UsbInterfaceIsDeviceHighSpeed(IN PVOID BusContext); @@ -170,8 +176,12 @@ namespace ViGEm::Bus::Core static EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoInternalDeviceControl; + static EVT_WDF_IO_QUEUE_STATE EvtWdfIoPendingNotificationQueueState; + static VOID WaitDeviceReadyCompletionWorkerRoutine(IN PVOID StartContext); + static VOID DumpAsHex(PCSTR Prefix, PVOID Buffer, ULONG BufferLength); + virtual VOID GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length) = 0; virtual NTSTATUS SelectConfiguration(PURB Urb) = 0; @@ -180,7 +190,7 @@ namespace ViGEm::Bus::Core virtual NTSTATUS SubmitReportImpl(PVOID NewReport) = 0; - static VOID DumpAsHex(PCSTR Prefix, PVOID Buffer, ULONG BufferLength); + virtual VOID ProcessPendingNotification(WDFQUEUE Queue) = 0; // // PNP Capabilities may differ from device to device @@ -251,6 +261,11 @@ namespace ViGEm::Bus::Core // Signals the bus that PDO is ready to receive data // KEVENT _PdoBootNotificationEvent; + + // + // Queue for interrupt out requests delivered to user-land + // + DMFMODULE _UsbInterruptOutBufferQueue{}; }; typedef struct _PDO_IDENTIFICATION_DESCRIPTION diff --git a/sys/XusbPdo.cpp b/sys/XusbPdo.cpp index e518cce..a3a569e 100644 --- a/sys/XusbPdo.cpp +++ b/sys/XusbPdo.cpp @@ -814,6 +814,8 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U pTransfer->TransferFlags, pTransfer->TransferBufferLength); +#pragma region Cache values + if (pTransfer->TransferBufferLength == XUSB_LEDSET_SIZE) // Led { auto Buffer = static_cast(pTransfer->TransferBuffer); @@ -835,12 +837,12 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U TRACE_USBPDO, "-- LED Number: %d", this->_LedNumber); - - // - // Notify client library that PDO is ready - // - KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE); } + + // + // Notify client library that PDO is ready + // + KeSetEvent(&this->_PdoBootNotificationEvent, 0, FALSE); } // Extract rumble (vibration) information @@ -863,12 +865,17 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U RtlCopyBytes(this->_Rumble, Buffer, pTransfer->TransferBufferLength); } +#pragma endregion + // Notify user-mode process that new data is available - status = WdfIoQueueRetrieveNextRequest(this->_PendingNotificationRequests, ¬ifyRequest); + status = WdfIoQueueRetrieveNextRequest( + this->_PendingNotificationRequests, + ¬ifyRequest + ); if (NT_SUCCESS(status)) { - PXUSB_REQUEST_NOTIFICATION notify = NULL; + PXUSB_REQUEST_NOTIFICATION notify = nullptr; status = WdfRequestRetrieveOutputBuffer( notifyRequest, @@ -891,17 +898,33 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_U else { TraceEvents(TRACE_LEVEL_ERROR, - TRACE_USBPDO, - "WdfRequestRetrieveOutputBuffer failed with status %!STATUS!", - status); + TRACE_USBPDO, + "WdfRequestRetrieveOutputBuffer failed with status %!STATUS!", + status); } } else { - TraceEvents(TRACE_LEVEL_WARNING, - TRACE_USBPDO, - "!! WdfIoQueueRetrieveNextRequest failed with status %!STATUS!", - status); + PVOID clientBuffer, contextBuffer; + + if (NT_SUCCESS(DMF_BufferQueue_Fetch( + this->_UsbInterruptOutBufferQueue, + &clientBuffer, + &contextBuffer + )) && pTransfer->TransferBufferLength <= MAX_OUT_BUFFER_QUEUE_SIZE) + { + RtlCopyMemory( + clientBuffer, + pTransfer->TransferBuffer, + pTransfer->TransferBufferLength + ); + + *static_cast(contextBuffer) = pTransfer->TransferBufferLength; + + TraceDbg(TRACE_USBPDO, "Queued %Iu bytes", pTransfer->TransferBufferLength); + + DMF_BufferQueue_Enqueue(this->_UsbInterruptOutBufferQueue, clientBuffer); + } } return status; @@ -1023,3 +1046,40 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::GetUserIndex(PULONG UserIndex // and need to fail this request with a distinct status. return STATUS_INVALID_DEVICE_OBJECT_PARAMETER; } + +void ViGEm::Bus::Targets::EmulationTargetXUSB::ProcessPendingNotification(WDFQUEUE Queue) +{ + NTSTATUS status; + WDFREQUEST request; + PVOID clientBuffer, contextBuffer; + + // + // No buffer available to answer the request with, leave queued + // + if (DMF_BufferQueue_Count(this->_UsbInterruptOutBufferQueue) == 0) + { + return; + } + + while (NT_SUCCESS(WdfIoQueueRetrieveNextRequest(Queue, &request))) + { + status = DMF_BufferQueue_Dequeue( + this->_UsbInterruptOutBufferQueue, + &clientBuffer, + &contextBuffer + ); + + // + // Shouldn't happen, but if so, error out + // + if(!NT_SUCCESS(status)) + { + WdfRequestComplete(request, status); + continue; + } + + // + // TODO: finish implementation + // + } +} diff --git a/sys/XusbPdo.hpp b/sys/XusbPdo.hpp index d7b6f10..4bc2e64 100644 --- a/sys/XusbPdo.hpp +++ b/sys/XusbPdo.hpp @@ -96,7 +96,9 @@ namespace ViGEm::Bus::Targets NTSTATUS SubmitReportImpl(PVOID NewReport) override; NTSTATUS GetUserIndex(PULONG UserIndex) const; - + + protected: + void ProcessPendingNotification(WDFQUEUE Queue) override; private: static PCWSTR _deviceDescription;