Implementing new notification logic

This commit is contained in:
Benjamin Höglinger-Stelzer
2020-11-23 22:23:48 +01:00
parent 703842c753
commit c3c4047cfa
6 changed files with 186 additions and 18 deletions

View File

@@ -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<size_t*>(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
)

View File

@@ -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;

View File

@@ -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<EmulationTargetPDO*>(Context);
pThis->ProcessPendingNotification(Queue);
}

View File

@@ -35,6 +35,9 @@
#pragma once
#pragma warning(disable:5040)
#include <DmfModules.Library.h>
#pragma warning(default:5040)
#include <ntddk.h>
#include <wdf.h>
#include <ntintsafe.h>
@@ -43,7 +46,6 @@
#include <usbbusif.h>
#include <ViGEm/Common.h>
#include <initguid.h>
//
// 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

View File

@@ -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<PUCHAR>(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, &notifyRequest);
status = WdfIoQueueRetrieveNextRequest(
this->_PendingNotificationRequests,
&notifyRequest
);
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<size_t*>(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
//
}
}

View File

@@ -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;