Implemented new DS4 output report feature

This commit is contained in:
Benjamin Höglinger-Stelzer
2022-08-06 16:37:45 +02:00
parent 11603f6b13
commit 7d81cf3d76
7 changed files with 101 additions and 76 deletions

2
sdk

Submodule sdk updated: da1ed30414...16d9c40aae

View File

@@ -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(&notifyConfig, &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<PDS4_AWAIT_OUTPUT>(Context);
PDS4_AWAIT_OUTPUT pNotify = NULL;
size_t length = 0;
if (NT_SUCCESS(WdfRequestRetrieveOutputBuffer(
Request,
sizeof(DS4_AWAIT_OUTPUT),
reinterpret_cast<PVOID*>(&pNotify),
&length)))
{
RtlCopyMemory(pNotify, pOutput, sizeof(DS4_AWAIT_OUTPUT));
WdfRequestSetInformation(Request, sizeof(DS4_AWAIT_OUTPUT));
}
WdfRequestComplete(Request, NtStatus);
}
EXTERN_C_END

View File

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

View File

@@ -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<PUCHAR>(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,
&notifyRequest)))
@@ -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<PUCHAR>(Context);
if (NT_SUCCESS(WdfRequestRetrieveOutputBuffer(
Request,
sizeof(DS4_AWAIT_OUTPUT),
reinterpret_cast<PVOID*>(&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(&notifyConfig, &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;
}

View File

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

View File

@@ -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<EmulationTargetDS4*>(pdo)->OutputReportRequestProcess(Request);
status = NT_SUCCESS(status) ? STATUS_PENDING : status;
exit:

View File

@@ -189,6 +189,11 @@ EXTERN_C NTSTATUS Bus_PlugInDevice(
goto pluginEnd;
}
if (plugIn->TargetType == DualShock4Wired)
{
static_cast<EmulationTargetDS4*>(description.Target)->SetOutputReportNotifyModule(FdoGetData(Device)->UserNotification);
}
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(
WdfFdoGetDefaultChildList(Device),
&description.Header,