mirror of
https://github.com/nefarius/ViGEmBus.git
synced 2025-08-10 00:52:17 +00:00
729 lines
21 KiB
C
729 lines
21 KiB
C
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2016 Benjamin "Nefarius" Höglinger
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include "busenum.h"
|
|
#include <wdmsec.h>
|
|
#include <usbioctl.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, Bus_CreatePdo)
|
|
#pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo)
|
|
#pragma alloc_text(PAGE, Bus_EvtDevicePrepareHardware)
|
|
#endif
|
|
|
|
NTSTATUS Bus_EvtDeviceListCreatePdo(
|
|
WDFCHILDLIST DeviceList,
|
|
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
|
|
PWDFDEVICE_INIT ChildInit)
|
|
{
|
|
PPDO_IDENTIFICATION_DESCRIPTION pDesc;
|
|
|
|
PAGED_CODE();
|
|
|
|
pDesc = CONTAINING_RECORD(IdentificationDescription, PDO_IDENTIFICATION_DESCRIPTION, Header);
|
|
|
|
return Bus_CreatePdo(WdfChildListGetDevice(DeviceList), ChildInit, pDesc);
|
|
}
|
|
|
|
//
|
|
// Compares two children on the bus based on their serial numbers.
|
|
//
|
|
BOOLEAN Bus_EvtChildListIdentificationDescriptionCompare(
|
|
WDFCHILDLIST DeviceList,
|
|
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription,
|
|
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription)
|
|
{
|
|
PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceList);
|
|
|
|
lhs = CONTAINING_RECORD(FirstIdentificationDescription,
|
|
PDO_IDENTIFICATION_DESCRIPTION,
|
|
Header);
|
|
rhs = CONTAINING_RECORD(SecondIdentificationDescription,
|
|
PDO_IDENTIFICATION_DESCRIPTION,
|
|
Header);
|
|
|
|
return (lhs->SerialNo == rhs->SerialNo) ? TRUE : FALSE;
|
|
}
|
|
|
|
//
|
|
// Creates and initializes a PDO (child).
|
|
//
|
|
NTSTATUS Bus_CreatePdo(
|
|
_In_ WDFDEVICE Device,
|
|
_In_ PWDFDEVICE_INIT DeviceInit,
|
|
_In_ PPDO_IDENTIFICATION_DESCRIPTION Description)
|
|
{
|
|
NTSTATUS status;
|
|
PPDO_DEVICE_DATA pdoData;
|
|
WDFDEVICE hChild = NULL;
|
|
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
|
|
WDF_DEVICE_POWER_CAPABILITIES powerCaps;
|
|
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
|
WDF_OBJECT_ATTRIBUTES pdoAttributes;
|
|
WDF_IO_QUEUE_CONFIG defaultPdoQueueConfig;
|
|
WDFQUEUE defaultPdoQueue;
|
|
UNICODE_STRING deviceDescription;
|
|
VIGEM_BUS_INTERFACE busInterface;
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
WDF_IO_QUEUE_CONFIG usbInQueueConfig;
|
|
WDF_IO_QUEUE_CONFIG notificationsQueueConfig;
|
|
|
|
DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Virtual Gamepad Emulation Bus");
|
|
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN);
|
|
// reserve space for device id
|
|
DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN);
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
KdPrint((DRIVERNAME "Entered Bus_CreatePdo\n"));
|
|
|
|
//
|
|
// Get the FDO interface ASAP to report progress to bus
|
|
//
|
|
status = WdfFdoQueryForInterface(Device,
|
|
&GUID_VIGEM_INTERFACE_PDO,
|
|
(PINTERFACE)&busInterface,
|
|
sizeof(VIGEM_BUS_INTERFACE),
|
|
VIGEM_BUS_INTERFACE_VERSION,
|
|
NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfFdoQueryForInterface failed status 0x%x\n", status));
|
|
return status;
|
|
}
|
|
|
|
// set device type
|
|
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
|
|
// Bus is power policy owner
|
|
WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
|
|
|
|
#pragma region Enter RAW device mode
|
|
|
|
status = WdfPdoInitAssignRawDevice(DeviceInit, &GUID_DEVCLASS_VIGEM_RAWPDO);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfPdoInitAssignRawDevice failed status 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
WdfDeviceInitSetCharacteristics(DeviceInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE);
|
|
|
|
status = WdfDeviceInitAssignSDDLString(DeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfDeviceInitAssignSDDLString failed status 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Prepare PDO
|
|
|
|
// set parameters matching desired target device
|
|
switch (Description->TargetType)
|
|
{
|
|
//
|
|
// A Xbox 360 device was requested
|
|
//
|
|
case Xbox360Wired:
|
|
|
|
status = Xusb_PreparePdo(
|
|
DeviceInit,
|
|
Description->VendorId,
|
|
Description->ProductId,
|
|
&deviceId,
|
|
&deviceDescription);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
break;
|
|
|
|
//
|
|
// A Sony DualShock 4 device was requested
|
|
//
|
|
case DualShock4Wired:
|
|
|
|
status = Ds4_PreparePdo(DeviceInit, &deviceId, &deviceDescription);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
break;
|
|
|
|
//
|
|
// A Xbox One device was requested
|
|
//
|
|
case XboxOneWired:
|
|
|
|
status = Xgip_PreparePdo(DeviceInit, &deviceId, &deviceDescription);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
KdPrint((DRIVERNAME "Unsupported target type\n"));
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
// set device id
|
|
status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId);
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
// prepare instance id
|
|
status = RtlUnicodeStringPrintf(&buffer, L"%02d", Description->SerialNo);
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
// set instance id
|
|
status = WdfPdoInitAssignInstanceID(DeviceInit, &buffer);
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
// set device description (for English operating systems)
|
|
status = WdfPdoInitAddDeviceText(DeviceInit, &deviceDescription, &deviceLocation, 0x409);
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
// default locale is English
|
|
// TODO: add more locales
|
|
WdfPdoInitSetDefaultLocale(DeviceInit, 0x409);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region PNP/Power event callbacks
|
|
|
|
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
|
|
|
|
pnpPowerCallbacks.EvtDevicePrepareHardware = Bus_EvtDevicePrepareHardware;
|
|
|
|
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
|
|
|
|
#pragma endregion
|
|
|
|
// NOTE: not utilized at the moment
|
|
WdfPdoInitAllowForwardingRequestToParent(DeviceInit);
|
|
|
|
#pragma region Create PDO
|
|
|
|
// Add common device data context
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_DEVICE_DATA);
|
|
|
|
status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild);
|
|
if (!NT_SUCCESS(status))
|
|
goto endCreatePdo;
|
|
|
|
KdPrint((DRIVERNAME "Created PDO: 0x%X\n", hChild));
|
|
|
|
switch (Description->TargetType)
|
|
{
|
|
// Add XUSB-specific device data context
|
|
case Xbox360Wired:
|
|
{
|
|
PXUSB_DEVICE_DATA xusbData = NULL;
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XUSB_DEVICE_DATA);
|
|
|
|
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xusbData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfObjectAllocateContext failed status 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DualShock4Wired:
|
|
{
|
|
PDS4_DEVICE_DATA ds4Data = NULL;
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, DS4_DEVICE_DATA);
|
|
|
|
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&ds4Data);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfObjectAllocateContext failed status 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case XboxOneWired:
|
|
{
|
|
PXGIP_DEVICE_DATA xgipData = NULL;
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XGIP_DEVICE_DATA);
|
|
|
|
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xgipData);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfObjectAllocateContext failed status 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Expose USB Interface
|
|
|
|
status = WdfDeviceCreateDeviceInterface(Device, (LPGUID)&GUID_DEVINTERFACE_USB_DEVICE, NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfDeviceCreateDeviceInterface failed status 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Set PDO contexts
|
|
|
|
pdoData = PdoGetData(hChild);
|
|
|
|
pdoData->BusInterface = busInterface;
|
|
|
|
pdoData->SerialNo = Description->SerialNo;
|
|
pdoData->TargetType = Description->TargetType;
|
|
pdoData->OwnerProcessId = Description->OwnerProcessId;
|
|
pdoData->VendorId = Description->VendorId;
|
|
pdoData->ProductId = Description->ProductId;
|
|
|
|
// Initialize additional contexts (if available)
|
|
switch (Description->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
|
|
status = Xusb_AssignPdoContext(hChild, Description);
|
|
|
|
break;
|
|
|
|
case DualShock4Wired:
|
|
|
|
status = Ds4_AssignPdoContext(hChild, Description);
|
|
|
|
break;
|
|
|
|
case XboxOneWired:
|
|
|
|
status = Xgip_AssignPdoContext(hChild);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "Couldn't initialize additional contexts\n"));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Create Queues & Locks
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
|
attributes.ParentObject = hChild;
|
|
|
|
// Create and assign queue for incoming interrupt transfer
|
|
WDF_IO_QUEUE_CONFIG_INIT(&usbInQueueConfig, WdfIoQueueDispatchManual);
|
|
|
|
status = WdfIoQueueCreate(Device, &usbInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingUsbInRequests);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfIoQueueCreate (PendingUsbInRequests) failed 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
// Create and assign queue for user-land notification requests
|
|
WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual);
|
|
|
|
status = WdfIoQueueCreate(Device, ¬ificationsQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingNotificationRequests);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfIoQueueCreate (PendingNotificationRequests) failed 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Default I/O queue setup
|
|
|
|
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&defaultPdoQueueConfig, WdfIoQueueDispatchParallel);
|
|
|
|
defaultPdoQueueConfig.EvtIoInternalDeviceControl = Pdo_EvtIoInternalDeviceControl;
|
|
|
|
status = WdfIoQueueCreate(hChild, &defaultPdoQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &defaultPdoQueue);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
KdPrint((DRIVERNAME "WdfIoQueueCreate failed 0x%x\n", status));
|
|
goto endCreatePdo;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region PNP capabilities
|
|
|
|
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
|
|
|
|
pnpCaps.Removable = WdfTrue;
|
|
pnpCaps.EjectSupported = WdfTrue;
|
|
pnpCaps.SurpriseRemovalOK = WdfTrue;
|
|
|
|
pnpCaps.Address = Description->SerialNo;
|
|
pnpCaps.UINumber = Description->SerialNo;
|
|
|
|
WdfDeviceSetPnpCapabilities(hChild, &pnpCaps);
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Power capabilities
|
|
|
|
WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps);
|
|
|
|
powerCaps.DeviceD1 = WdfTrue;
|
|
powerCaps.WakeFromD1 = WdfTrue;
|
|
powerCaps.DeviceWake = PowerDeviceD1;
|
|
|
|
powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
|
powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1;
|
|
powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
|
|
powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
|
|
powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
|
|
powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
|
|
|
WdfDeviceSetPowerCapabilities(hChild, &powerCaps);
|
|
|
|
#pragma endregion
|
|
|
|
endCreatePdo:
|
|
KdPrint((DRIVERNAME "BUS_PDO_REPORT_STAGE_RESULT Stage: ViGEmPdoCreate, Serial: 0x%X, Status: 0x%X (%d)\n",
|
|
Description->SerialNo, status, NT_SUCCESS(status)));
|
|
|
|
BUS_PDO_REPORT_STAGE_RESULT(busInterface, ViGEmPdoCreate, Description->SerialNo, status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Exposes necessary interfaces on PDO power-up.
|
|
//
|
|
NTSTATUS Bus_EvtDevicePrepareHardware(
|
|
_In_ WDFDEVICE Device,
|
|
_In_ WDFCMRESLIST ResourcesRaw,
|
|
_In_ WDFCMRESLIST ResourcesTranslated
|
|
)
|
|
{
|
|
PPDO_DEVICE_DATA pdoData;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER(ResourcesRaw);
|
|
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
|
|
|
KdPrint((DRIVERNAME "Bus_EvtDevicePrepareHardware: 0x%p\n", Device));
|
|
|
|
pdoData = PdoGetData(Device);
|
|
|
|
switch (pdoData->TargetType)
|
|
{
|
|
// Expose XUSB interfaces
|
|
case Xbox360Wired:
|
|
|
|
status = Xusb_PrepareHardware(Device);
|
|
|
|
break;
|
|
|
|
case DualShock4Wired:
|
|
|
|
status = Ds4_PrepareHardware(Device);
|
|
|
|
break;
|
|
|
|
case XboxOneWired:
|
|
|
|
status = Xgip_PrepareHardware(Device);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoPrepareHardware, pdoData->SerialNo, status);
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Responds to IRP_MJ_INTERNAL_DEVICE_CONTROL requests sent to PDO.
|
|
//
|
|
VOID Pdo_EvtIoInternalDeviceControl(
|
|
_In_ WDFQUEUE Queue,
|
|
_In_ WDFREQUEST Request,
|
|
_In_ size_t OutputBufferLength,
|
|
_In_ size_t InputBufferLength,
|
|
_In_ ULONG IoControlCode
|
|
)
|
|
{
|
|
// Regular buffers not used in USB communication
|
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|
|
|
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
|
WDFDEVICE hDevice;
|
|
PIRP irp;
|
|
PURB urb;
|
|
PPDO_DEVICE_DATA pdoData;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
hDevice = WdfIoQueueGetDevice(Queue);
|
|
pdoData = PdoGetData(hDevice);
|
|
// No help from the framework available from here on
|
|
irp = WdfRequestWdmGetIrp(Request);
|
|
irpStack = IoGetCurrentIrpStackLocation(irp);
|
|
|
|
switch (IoControlCode)
|
|
{
|
|
case IOCTL_INTERNAL_USB_SUBMIT_URB:
|
|
|
|
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_SUBMIT_URB\n"));
|
|
|
|
urb = (PURB)URB_FROM_IRP(irp);
|
|
|
|
switch (urb->UrbHeader.Function)
|
|
{
|
|
case URB_FUNCTION_CONTROL_TRANSFER:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_CONTROL_TRANSFER\n"));
|
|
|
|
// Control transfer can safely be ignored
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_CONTROL_TRANSFER_EX:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_CONTROL_TRANSFER_EX\n"));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER\n"));
|
|
|
|
status = UsbPdo_BulkOrInterruptTransfer(urb, hDevice, Request);
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_SELECT_CONFIGURATION:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_SELECT_CONFIGURATION\n"));
|
|
|
|
status = UsbPdo_SelectConfiguration(urb, pdoData);
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_SELECT_INTERFACE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_SELECT_INTERFACE\n"));
|
|
|
|
status = UsbPdo_SelectInterface(urb, pdoData);
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n"));
|
|
|
|
switch (urb->UrbControlDescriptorRequest.DescriptorType)
|
|
{
|
|
case USB_DEVICE_DESCRIPTOR_TYPE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> >> USB_DEVICE_DESCRIPTOR_TYPE\n"));
|
|
|
|
status = UsbPdo_GetDeviceDescriptorType(urb, pdoData);
|
|
|
|
break;
|
|
|
|
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE\n"));
|
|
|
|
status = UsbPdo_GetConfigurationDescriptorType(urb, pdoData);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
|
}
|
|
|
|
break;
|
|
|
|
case USB_STRING_DESCRIPTOR_TYPE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> >> USB_STRING_DESCRIPTOR_TYPE\n"));
|
|
|
|
status = UsbPdo_GetStringDescriptorType(urb, pdoData);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
|
}
|
|
|
|
break;
|
|
case USB_INTERFACE_DESCRIPTOR_TYPE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> >> USB_INTERFACE_DESCRIPTOR_TYPE\n"));
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_DESCRIPTOR_TYPE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> >> USB_ENDPOINT_DESCRIPTOR_TYPE\n"));
|
|
|
|
break;
|
|
|
|
default:
|
|
KdPrint((DRIVERNAME ">> >> >> Unknown descriptor type\n"));
|
|
break;
|
|
}
|
|
|
|
KdPrint((DRIVERNAME "<< <<\n"));
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE\n"));
|
|
|
|
// Defaults always succeed
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// This IOCTL code is a nice indicator that the virtual XUSB device is
|
|
// "powered up" and ready to get interacted with so we can report
|
|
// success to the parent bus.
|
|
//
|
|
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_ABORT_PIPE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_ABORT_PIPE\n"));
|
|
|
|
status = UsbPdo_AbortPipe(hDevice);
|
|
|
|
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_CLASS_INTERFACE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_CLASS_INTERFACE\n"));
|
|
|
|
status = UsbPdo_ClassInterface(urb, hDevice, pdoData);
|
|
|
|
break;
|
|
|
|
case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
|
|
|
|
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE\n"));
|
|
|
|
status = UsbPdo_GetDescriptorFromInterface(urb, pdoData);
|
|
|
|
//
|
|
// This IOCTL code is a nice indicator that the virtual HIDUSB device is
|
|
// "powered up" and ready to get interacted with so we can report
|
|
// success to the parent bus.
|
|
//
|
|
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
|
|
|
break;
|
|
|
|
default:
|
|
KdPrint((DRIVERNAME ">> >> Unknown function: 0x%X\n", urb->UrbHeader.Function));
|
|
break;
|
|
}
|
|
|
|
KdPrint((DRIVERNAME "<<\n"));
|
|
|
|
break;
|
|
|
|
case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
|
|
|
|
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_GET_PORT_STATUS\n"));
|
|
|
|
// We report the (virtual) port as always active
|
|
*(unsigned long *)irpStack->Parameters.Others.Argument1 = USBD_PORT_ENABLED | USBD_PORT_CONNECTED;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_INTERNAL_USB_RESET_PORT:
|
|
|
|
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_RESET_PORT\n"));
|
|
|
|
// Sure, why not ;)
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
|
|
|
|
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION\n"));
|
|
|
|
// TODO: implement
|
|
// This happens if the I/O latency is too high so HIDUSB aborts communication.
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
default:
|
|
KdPrint((DRIVERNAME ">> Unknown I/O control code 0x%X\n", IoControlCode));
|
|
break;
|
|
}
|
|
|
|
if (status != STATUS_PENDING)
|
|
{
|
|
WdfRequestComplete(Request, status);
|
|
}
|
|
}
|
|
|