Implemented new plugin request tracking

This commit is contained in:
Benjamin Höglinger-Stelzer
2020-05-13 17:59:18 +02:00
parent 2457076e20
commit 9a2f66048d
5 changed files with 548 additions and 407 deletions

View File

@@ -46,324 +46,331 @@ using ViGEm::Bus::Targets::EmulationTargetDS4;
// Simulates a device plug-in event.
//
EXTERN_C NTSTATUS Bus_PlugInDevice(
_In_ WDFDEVICE Device,
_In_ WDFREQUEST Request,
_In_ BOOLEAN IsInternal,
_Out_ size_t* Transferred)
_In_ WDFDEVICE Device,
_In_ WDFREQUEST Request,
_In_ BOOLEAN IsInternal,
_Out_ size_t* Transferred)
{
PDO_IDENTIFICATION_DESCRIPTION description;
NTSTATUS status;
PVIGEM_PLUGIN_TARGET plugIn;
WDFFILEOBJECT fileObject;
PFDO_FILE_DATA pFileData;
size_t length = 0;
NTSTATUS status;
PVIGEM_PLUGIN_TARGET plugIn;
WDFFILEOBJECT fileObject;
PFDO_FILE_DATA pFileData;
size_t length = 0;
UNREFERENCED_PARAMETER(IsInternal);
PAGED_CODE();
UNREFERENCED_PARAMETER(IsInternal);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
status = WdfRequestRetrieveInputBuffer(
Request,
sizeof(VIGEM_PLUGIN_TARGET),
reinterpret_cast<PVOID*>(&plugIn),
&length
);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!", status);
return status;
}
status = WdfRequestRetrieveInputBuffer(
Request,
sizeof(VIGEM_PLUGIN_TARGET),
reinterpret_cast<PVOID*>(&plugIn),
&length
);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!", status);
return status;
}
if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"sizeof(VIGEM_PLUGIN_TARGET) buffer size mismatch [%d != %d]",
sizeof(VIGEM_PLUGIN_TARGET), plugIn->Size);
return STATUS_INVALID_PARAMETER;
}
if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"sizeof(VIGEM_PLUGIN_TARGET) buffer size mismatch [%d != %d]",
sizeof(VIGEM_PLUGIN_TARGET), plugIn->Size);
return STATUS_INVALID_PARAMETER;
}
if (plugIn->SerialNo == 0)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"Serial no. 0 not allowed");
return STATUS_INVALID_PARAMETER;
}
if (plugIn->SerialNo == 0)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"Serial no. 0 not allowed");
return STATUS_INVALID_PARAMETER;
}
*Transferred = length;
*Transferred = length;
fileObject = WdfRequestGetFileObject(Request);
if (fileObject == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
Request);
return STATUS_INVALID_PARAMETER;
}
fileObject = WdfRequestGetFileObject(Request);
if (fileObject == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
Request);
return STATUS_INVALID_PARAMETER;
}
pFileData = FileObjectGetData(fileObject);
if (pFileData == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"FileObjectGetData failed to get context data for 0x%p",
fileObject);
return STATUS_INVALID_PARAMETER;
}
pFileData = FileObjectGetData(fileObject);
if (pFileData == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"FileObjectGetData failed to get context data for 0x%p",
fileObject);
return STATUS_INVALID_PARAMETER;
}
//
// Initialize the description with the information about the newly
// plugged in device.
//
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
//
// Initialize the description with the information about the newly
// plugged in device.
//
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
description.SerialNo = plugIn->SerialNo;
description.SessionId = pFileData->SessionId;
// Set default IDs if supplied values are invalid
if (plugIn->VendorId == 0 || plugIn->ProductId == 0)
{
switch (plugIn->TargetType)
{
case Xbox360Wired:
description.SerialNo = plugIn->SerialNo;
description.SessionId = pFileData->SessionId;
description.Target = new EmulationTargetXUSB(plugIn->SerialNo, pFileData->SessionId);
// Set default IDs if supplied values are invalid
if (plugIn->VendorId == 0 || plugIn->ProductId == 0)
{
switch (plugIn->TargetType)
{
case Xbox360Wired:
break;
case DualShock4Wired:
description.Target = new EmulationTargetXUSB(plugIn->SerialNo, pFileData->SessionId);
description.Target = new EmulationTargetDS4(plugIn->SerialNo, pFileData->SessionId);
break;
case DualShock4Wired:
break;
default:
return STATUS_NOT_SUPPORTED;
}
}
else
{
switch (plugIn->TargetType)
{
case Xbox360Wired:
description.Target = new EmulationTargetDS4(plugIn->SerialNo, pFileData->SessionId);
description.Target = new EmulationTargetXUSB(
plugIn->SerialNo,
pFileData->SessionId,
plugIn->VendorId,
plugIn->ProductId
);
break;
default:
return STATUS_NOT_SUPPORTED;
}
}
else
{
switch (plugIn->TargetType)
{
case Xbox360Wired:
break;
case DualShock4Wired:
description.Target = new EmulationTargetXUSB(
plugIn->SerialNo,
pFileData->SessionId,
plugIn->VendorId,
plugIn->ProductId
);
description.Target = new EmulationTargetDS4(
plugIn->SerialNo,
pFileData->SessionId,
plugIn->VendorId,
plugIn->ProductId
);
break;
case DualShock4Wired:
break;
default:
return STATUS_NOT_SUPPORTED;
}
}
description.Target = new EmulationTargetDS4(
plugIn->SerialNo,
pFileData->SessionId,
plugIn->VendorId,
plugIn->ProductId
);
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(
WdfFdoGetDefaultChildList(Device),
&description.Header,
NULL
);
break;
default:
return STATUS_NOT_SUPPORTED;
}
}
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status %!STATUS!",
status);
if (!NT_SUCCESS(description.Target->PdoPrepare(Device)))
{
goto pluginEnd;
}
goto pluginEnd;
}
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(
WdfFdoGetDefaultChildList(Device),
&description.Header,
NULL
);
//
// The requested serial number is already in use
//
if (status == STATUS_OBJECT_NAME_EXISTS)
{
status = STATUS_INVALID_PARAMETER;
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfChildListAddOrUpdateChildDescriptionAsPresent failed with status %!STATUS!",
status);
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"The described PDO already exists (%!STATUS!)",
status);
goto pluginEnd;
}
goto pluginEnd;
}
//
// The requested serial number is already in use
//
if (status == STATUS_OBJECT_NAME_EXISTS)
{
status = STATUS_INVALID_PARAMETER;
//status = NT_SUCCESS(status) ? STATUS_PENDING : status;
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"The described PDO already exists (%!STATUS!)",
status);
goto pluginEnd;
}
status = description.Target->EnqueuePlugin(Request);
status = NT_SUCCESS(status) ? STATUS_PENDING : status;
pluginEnd:
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", status);
return status;
return status;
}
//
// Simulates a device unplug event.
//
EXTERN_C NTSTATUS Bus_UnPlugDevice(
_In_ WDFDEVICE Device,
_In_ WDFREQUEST Request,
_In_ BOOLEAN IsInternal,
_Out_ size_t* Transferred)
_In_ WDFDEVICE Device,
_In_ WDFREQUEST Request,
_In_ BOOLEAN IsInternal,
_Out_ size_t* Transferred)
{
NTSTATUS status;
WDFDEVICE hChild;
WDFCHILDLIST list;
WDF_CHILD_LIST_ITERATOR iterator;
WDF_CHILD_RETRIEVE_INFO childInfo;
PDO_IDENTIFICATION_DESCRIPTION description;
BOOLEAN unplugAll;
PVIGEM_UNPLUG_TARGET unPlug;
WDFFILEOBJECT fileObject;
PFDO_FILE_DATA pFileData = NULL;
size_t length = 0;
PAGED_CODE();
NTSTATUS status;
WDFDEVICE hChild;
WDFCHILDLIST list;
WDF_CHILD_LIST_ITERATOR iterator;
WDF_CHILD_RETRIEVE_INFO childInfo;
PDO_IDENTIFICATION_DESCRIPTION description;
BOOLEAN unplugAll;
PVIGEM_UNPLUG_TARGET unPlug;
WDFFILEOBJECT fileObject;
PFDO_FILE_DATA pFileData = NULL;
size_t length = 0;
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
PAGED_CODE();
status = WdfRequestRetrieveInputBuffer(
Request,
sizeof(VIGEM_UNPLUG_TARGET),
(PVOID*)&unPlug,
&length
);
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Entry");
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!",
status);
return status;
}
status = WdfRequestRetrieveInputBuffer(
Request,
sizeof(VIGEM_UNPLUG_TARGET),
(PVOID*)&unPlug,
&length
);
if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"sizeof(VIGEM_UNPLUG_TARGET) buffer size mismatch [%d != %d]",
sizeof(VIGEM_UNPLUG_TARGET), unPlug->Size);
return STATUS_INVALID_PARAMETER;
}
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestRetrieveInputBuffer failed with status %!STATUS!",
status);
return status;
}
*Transferred = length;
unplugAll = (unPlug->SerialNo == 0);
if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"sizeof(VIGEM_UNPLUG_TARGET) buffer size mismatch [%d != %d]",
sizeof(VIGEM_UNPLUG_TARGET), unPlug->Size);
return STATUS_INVALID_PARAMETER;
}
fileObject = WdfRequestGetFileObject(Request);
if (fileObject == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
Request);
return STATUS_INVALID_PARAMETER;
}
*Transferred = length;
unplugAll = (unPlug->SerialNo == 0);
pFileData = FileObjectGetData(fileObject);
if (pFileData == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"FileObjectGetData failed to get context data for 0x%p",
fileObject);
return STATUS_INVALID_PARAMETER;
}
fileObject = WdfRequestGetFileObject(Request);
if (fileObject == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfRequestGetFileObject failed to fetch WDFFILEOBJECT from request 0x%p",
Request);
return STATUS_INVALID_PARAMETER;
}
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"Starting child list traversal");
pFileData = FileObjectGetData(fileObject);
if (pFileData == NULL)
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"FileObjectGetData failed to get context data for 0x%p",
fileObject);
return STATUS_INVALID_PARAMETER;
}
list = WdfFdoGetDefaultChildList(Device);
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"Starting child list traversal");
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
list = WdfFdoGetDefaultChildList(Device);
WdfChildListBeginIteration(list, &iterator);
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
for (;;)
{
WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header);
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
WdfChildListBeginIteration(list, &iterator);
status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo);
for (;;)
{
WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header);
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
// Error or no more children, end loop
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
{
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"WdfChildListRetrieveNextDevice returned with status %!STATUS!",
status);
break;
}
status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo);
// If unable to retrieve device
if (childInfo.Status != WdfChildListRetrieveDeviceSuccess)
{
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"childInfo.Status = %d",
childInfo.Status);
continue;
}
// Error or no more children, end loop
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
{
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"WdfChildListRetrieveNextDevice returned with status %!STATUS!",
status);
break;
}
// Child isn't the one we looked for, skip
if (!unplugAll && description.SerialNo != unPlug->SerialNo)
{
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"Seeking serial mismatch: %d != %d",
description.SerialNo,
unPlug->SerialNo);
continue;
}
// If unable to retrieve device
if (childInfo.Status != WdfChildListRetrieveDeviceSuccess)
{
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"childInfo.Status = %d",
childInfo.Status);
continue;
}
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"description.SessionId = %d, pFileData->SessionId = %d",
description.SessionId,
pFileData->SessionId);
// Child isn't the one we looked for, skip
if (!unplugAll && description.SerialNo != unPlug->SerialNo)
{
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"Seeking serial mismatch: %d != %d",
description.SerialNo,
unPlug->SerialNo);
continue;
}
// Only unplug owned children
if (IsInternal || description.SessionId == pFileData->SessionId)
{
// Unplug child
status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfChildListUpdateChildDescriptionAsMissing failed with status %!STATUS!",
status);
}
}
}
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"description.SessionId = %d, pFileData->SessionId = %d",
description.SessionId,
pFileData->SessionId);
WdfChildListEndIteration(list, &iterator);
// Only unplug owned children
if (IsInternal || description.SessionId == pFileData->SessionId)
{
// Unplug child
status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header);
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR,
TRACE_BUSENUM,
"WdfChildListUpdateChildDescriptionAsMissing failed with status %!STATUS!",
status);
}
}
}
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"Finished child list traversal");
WdfChildListEndIteration(list, &iterator);
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", STATUS_SUCCESS);
TraceEvents(TRACE_LEVEL_VERBOSE,
TRACE_BUSENUM,
"Finished child list traversal");
return STATUS_SUCCESS;
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_BUSENUM, "%!FUNC! Exit with status %!STATUS!", STATUS_SUCCESS);
return STATUS_SUCCESS;
}