Files
QEMU-S5L8950X/windows/QemuA6Ude/driver.c
T

770 lines
23 KiB
C

/*
* QemuA6Ude - Windows USB Device Emulation controller for the QEMU A6 model.
*
* The UDE child is deliberately exposed as Apple's normal DFU PID (0x1227),
* which is what unmodified libirecovery/idevicerestore discovers on Windows.
* Class and vendor control transfers are relayed through \\.\QemuA6Ude0.
*/
#include <ntddk.h>
#include <wdf.h>
#include <usb.h>
#include <wdfusb.h>
#include <usbdlib.h>
#include <ude/1.0/UdeCx.h>
#include <initguid.h>
#include <usbioctl.h>
#include <wdmsec.h>
#include "../include/qemu_a6_usb_protocol.h"
#define QA6_POOL_TAG 'U6AQ'
#define QA6_DEVICE_NAME L"\\Device\\QemuA6Ude0"
#define QA6_SYMBOLIC_LINK L"\\DosDevices\\QemuA6Ude0"
#define QA6_HOST_INTERFACE_REF L"QemuA6UdeHost"
#define QA6_LANGUAGE_ID 0x0409
#define QA6_LOG_ERROR(...) \
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, \
"QemuA6Ude: " __VA_ARGS__)
#define QA6_LOG_INFO(...) \
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, \
"QemuA6Ude: " __VA_ARGS__)
typedef struct _QA6_CONTROLLER_CONTEXT {
WDFQUEUE DefaultQueue;
WDFQUEUE WaitingUserReads;
WDFSPINLOCK BridgeLock;
PUDECXUSBDEVICE_INIT ChildInit;
UDECXUSBDEVICE ChildDevice;
UDECXUSBENDPOINT ControlEndpoint;
WDFQUEUE ControlQueue;
BOOLEAN PluggedIn;
WDFREQUEST PendingUrb;
ULONG PendingRequestId;
ULONG PendingTransferLength;
BOOLEAN PendingDirectionIn;
BOOLEAN RequestFrameReady;
ULONG RequestFrameLength;
UCHAR RequestFrame[QA6_USB_MAX_FRAME_SIZE];
} QA6_CONTROLLER_CONTEXT, *PQA6_CONTROLLER_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QA6_CONTROLLER_CONTEXT,
Qa6GetControllerContext);
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD Qa6EvtDeviceAdd;
EVT_WDF_DEVICE_D0_ENTRY Qa6EvtDeviceD0Entry;
EVT_WDF_DEVICE_D0_EXIT Qa6EvtDeviceD0Exit;
EVT_WDF_OBJECT_CONTEXT_CLEANUP Qa6EvtControllerCleanup;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Qa6EvtControllerIoctl;
EVT_WDF_IO_QUEUE_IO_READ Qa6EvtBridgeRead;
EVT_WDF_IO_QUEUE_IO_WRITE Qa6EvtBridgeWrite;
EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE Qa6EvtCanceledUserRead;
EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Qa6EvtControlUrb;
EVT_UDECX_USB_ENDPOINT_RESET Qa6EvtEndpointReset;
EVT_UDECX_WDF_DEVICE_QUERY_USB_CAPABILITY Qa6EvtQueryUsbCapability;
static const USB_DEVICE_DESCRIPTOR Qa6DeviceDescriptor = {
sizeof(USB_DEVICE_DESCRIPTOR),
USB_DEVICE_DESCRIPTOR_TYPE,
0x0200,
0x00,
0x00,
0x00,
0x40,
0x05ac,
0x1227,
0x0000,
2,
3,
4,
1
};
static const UCHAR Qa6ConfigurationDescriptor[] = {
0x09, USB_CONFIGURATION_DESCRIPTOR_TYPE,
0x19, 0x00,
0x01,
0x01,
0x05,
0x80,
0xfa,
0x09, USB_INTERFACE_DESCRIPTOR_TYPE,
0x00,
0x00,
0x00,
0xfe,
0x01,
0x00,
0x00,
0x07, 0x21,
0x01,
0x0a, 0x00,
0x00,
0x08
};
static const UCHAR Qa6LanguageDescriptor[] = { 4, USB_STRING_DESCRIPTOR_TYPE,
0x09, 0x04 };
DECLARE_CONST_UNICODE_STRING(Qa6ManufacturerString, L"Apple Inc.");
DECLARE_CONST_UNICODE_STRING(Qa6ProductString,
L"Apple Mobile Device (DFU Mode)");
DECLARE_CONST_UNICODE_STRING(
Qa6SerialString,
L"CPID:8950 CPRV:20 CPFM:03 SCEP:10 BDID:00 "
L"ECID:0000000000200000 IBFL:00 SRTG:[iBoot-1145.3]");
DECLARE_CONST_UNICODE_STRING(Qa6ConfigurationString, L"Apple Mobile Device");
static VOID
Qa6CompleteUserRead(
_In_ WDFREQUEST Request,
_In_reads_bytes_(Length) const UCHAR *Data,
_In_ ULONG Length
)
{
PVOID buffer;
size_t bufferLength;
NTSTATUS status;
status = WdfRequestRetrieveOutputBuffer(Request, Length, &buffer,
&bufferLength);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
return;
}
if (bufferLength < Length) {
WdfRequestComplete(Request, STATUS_BUFFER_TOO_SMALL);
return;
}
RtlCopyMemory(buffer, Data, Length);
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, Length);
}
static VOID
Qa6PublishRequestFrame(
_In_ WDFDEVICE Controller
)
{
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(Controller);
WDFREQUEST reader = NULL;
NTSTATUS status;
WdfSpinLockAcquire(context->BridgeLock);
status = WdfIoQueueRetrieveNextRequest(context->WaitingUserReads, &reader);
if (NT_SUCCESS(status)) {
context->RequestFrameReady = FALSE;
} else {
context->RequestFrameReady = TRUE;
}
WdfSpinLockRelease(context->BridgeLock);
if (reader != NULL) {
Qa6CompleteUserRead(reader, context->RequestFrame,
context->RequestFrameLength);
}
}
static NTSTATUS
Qa6InitializeUsbDescriptors(
_In_ WDFDEVICE Controller
)
{
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(Controller);
UDECX_USB_DEVICE_STATE_CHANGE_CALLBACKS callbacks;
NTSTATUS status;
context->ChildInit = UdecxUsbDeviceInitAllocate(Controller);
if (context->ChildInit == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
UDECX_USB_DEVICE_CALLBACKS_INIT(&callbacks);
UdecxUsbDeviceInitSetStateChangeCallbacks(context->ChildInit, &callbacks);
UdecxUsbDeviceInitSetSpeed(context->ChildInit, UdecxUsbHighSpeed);
UdecxUsbDeviceInitSetEndpointsType(context->ChildInit,
UdecxEndpointTypeSimple);
status = UdecxUsbDeviceInitAddDescriptor(
context->ChildInit, (PUCHAR)&Qa6DeviceDescriptor,
(USHORT)sizeof(Qa6DeviceDescriptor));
if (!NT_SUCCESS(status)) {
return status;
}
status = UdecxUsbDeviceInitAddDescriptorWithIndex(
context->ChildInit, (PUCHAR)Qa6LanguageDescriptor,
(USHORT)sizeof(Qa6LanguageDescriptor), 0);
if (!NT_SUCCESS(status)) {
return status;
}
status = UdecxUsbDeviceInitAddStringDescriptor(
context->ChildInit, &Qa6ManufacturerString, 2, QA6_LANGUAGE_ID);
if (!NT_SUCCESS(status)) {
return status;
}
status = UdecxUsbDeviceInitAddStringDescriptor(
context->ChildInit, &Qa6ProductString, 3, QA6_LANGUAGE_ID);
if (!NT_SUCCESS(status)) {
return status;
}
status = UdecxUsbDeviceInitAddStringDescriptor(
context->ChildInit, &Qa6SerialString, 4, QA6_LANGUAGE_ID);
if (!NT_SUCCESS(status)) {
return status;
}
return UdecxUsbDeviceInitAddStringDescriptor(
context->ChildInit, &Qa6ConfigurationString, 5, QA6_LANGUAGE_ID);
}
static NTSTATUS
Qa6CreateControlEndpoint(
_In_ WDFDEVICE Controller
)
{
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(Controller);
WDF_IO_QUEUE_CONFIG queueConfig;
PUDECXUSBENDPOINT_INIT endpointInit = NULL;
UDECX_USB_ENDPOINT_CALLBACKS callbacks;
NTSTATUS status;
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchSequential);
queueConfig.EvtIoInternalDeviceControl = Qa6EvtControlUrb;
queueConfig.PowerManaged = WdfFalse;
status = WdfIoQueueCreate(Controller, &queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&context->ControlQueue);
if (!NT_SUCCESS(status)) {
return status;
}
endpointInit = UdecxUsbSimpleEndpointInitAllocate(context->ChildDevice);
if (endpointInit == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
UdecxUsbEndpointInitSetEndpointAddress(endpointInit,
USB_DEFAULT_ENDPOINT_ADDRESS);
UDECX_USB_ENDPOINT_CALLBACKS_INIT(&callbacks, Qa6EvtEndpointReset);
UdecxUsbEndpointInitSetCallbacks(endpointInit, &callbacks);
status = UdecxUsbEndpointCreate(&endpointInit,
WDF_NO_OBJECT_ATTRIBUTES,
&context->ControlEndpoint);
if (!NT_SUCCESS(status)) {
if (endpointInit != NULL) {
UdecxUsbEndpointInitFree(endpointInit);
}
return status;
}
UdecxUsbEndpointSetWdfIoQueue(context->ControlEndpoint,
context->ControlQueue);
return STATUS_SUCCESS;
}
static NTSTATUS
Qa6PlugInUsbDevice(
_In_ WDFDEVICE Controller
)
{
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(Controller);
UDECX_USB_DEVICE_PLUG_IN_OPTIONS plugOptions;
WDF_OBJECT_ATTRIBUTES attributes;
NTSTATUS status;
if (context->PluggedIn) {
return STATUS_SUCCESS;
}
status = UdecxUsbDeviceInitAddDescriptor(
context->ChildInit, (PUCHAR)Qa6ConfigurationDescriptor,
(USHORT)sizeof(Qa6ConfigurationDescriptor));
if (!NT_SUCCESS(status)) {
return status;
}
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
status = UdecxUsbDeviceCreate(&context->ChildInit, &attributes,
&context->ChildDevice);
if (!NT_SUCCESS(status)) {
return status;
}
status = Qa6CreateControlEndpoint(Controller);
if (!NT_SUCCESS(status)) {
return status;
}
UDECX_USB_DEVICE_PLUG_IN_OPTIONS_INIT(&plugOptions);
plugOptions.Usb20PortNumber = 1;
status = UdecxUsbDevicePlugIn(context->ChildDevice, &plugOptions);
if (NT_SUCCESS(status)) {
context->PluggedIn = TRUE;
QA6_LOG_INFO("Apple DFU child plugged in as USB\\VID_05AC&PID_1227\n");
}
return status;
}
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_DRIVER_CONFIG_INIT(&config, Qa6EvtDeviceAdd);
config.DriverPoolTag = QA6_POOL_TAG;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
return WdfDriverCreate(DriverObject, RegistryPath, &attributes, &config,
WDF_NO_HANDLE);
}
NTSTATUS
Qa6EvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
WDF_PNPPOWER_EVENT_CALLBACKS powerCallbacks;
WDF_FILEOBJECT_CONFIG fileConfig;
WDF_OBJECT_ATTRIBUTES attributes;
UDECX_WDF_DEVICE_CONFIG udeConfig;
WDF_IO_QUEUE_CONFIG queueConfig;
WDF_IO_QUEUE_CONFIG manualQueueConfig;
PQA6_CONTROLLER_CONTEXT context;
WDFDEVICE controller;
UNICODE_STRING deviceName;
UNICODE_STRING symbolicLink;
UNICODE_STRING reference;
NTSTATUS status;
UNREFERENCED_PARAMETER(Driver);
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&powerCallbacks);
powerCallbacks.EvtDeviceD0Entry = Qa6EvtDeviceD0Entry;
powerCallbacks.EvtDeviceD0Exit = Qa6EvtDeviceD0Exit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &powerCallbacks);
WDF_FILEOBJECT_CONFIG_INIT(&fileConfig, WDF_NO_EVENT_CALLBACK,
WDF_NO_EVENT_CALLBACK,
WDF_NO_EVENT_CALLBACK);
fileConfig.FileObjectClass = WdfFileObjectWdfCannotUseFsContexts;
WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig,
WDF_NO_OBJECT_ATTRIBUTES);
status = WdfDeviceInitAssignSDDLString(
DeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);
if (!NT_SUCCESS(status)) {
return status;
}
status = UdecxInitializeWdfDeviceInit(DeviceInit);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString(&deviceName, QA6_DEVICE_NAME);
status = WdfDeviceInitAssignName(DeviceInit, &deviceName);
if (!NT_SUCCESS(status)) {
return status;
}
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
QA6_CONTROLLER_CONTEXT);
attributes.EvtCleanupCallback = Qa6EvtControllerCleanup;
attributes.ExecutionLevel = WdfExecutionLevelPassive;
status = WdfDeviceCreate(&DeviceInit, &attributes, &controller);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString(&symbolicLink, QA6_SYMBOLIC_LINK);
status = WdfDeviceCreateSymbolicLink(controller, &symbolicLink);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString(&reference, QA6_HOST_INTERFACE_REF);
status = WdfDeviceCreateDeviceInterface(
controller, (LPGUID)&GUID_DEVINTERFACE_USB_HOST_CONTROLLER,
&reference);
if (!NT_SUCCESS(status)) {
return status;
}
UDECX_WDF_DEVICE_CONFIG_INIT(&udeConfig, Qa6EvtQueryUsbCapability);
status = UdecxWdfDeviceAddUsbDeviceEmulation(controller, &udeConfig);
if (!NT_SUCCESS(status)) {
return status;
}
context = Qa6GetControllerContext(controller);
RtlZeroMemory(context, sizeof(*context));
status = WdfSpinLockCreate(WDF_NO_OBJECT_ATTRIBUTES,
&context->BridgeLock);
if (!NT_SUCCESS(status)) {
return status;
}
WDF_IO_QUEUE_CONFIG_INIT(&manualQueueConfig, WdfIoQueueDispatchManual);
manualQueueConfig.EvtIoCanceledOnQueue = Qa6EvtCanceledUserRead;
manualQueueConfig.PowerManaged = WdfFalse;
status = WdfIoQueueCreate(controller, &manualQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&context->WaitingUserReads);
if (!NT_SUCCESS(status)) {
return status;
}
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,
WdfIoQueueDispatchSequential);
queueConfig.EvtIoDeviceControl = Qa6EvtControllerIoctl;
queueConfig.EvtIoRead = Qa6EvtBridgeRead;
queueConfig.EvtIoWrite = Qa6EvtBridgeWrite;
queueConfig.PowerManaged = WdfFalse;
status = WdfIoQueueCreate(controller, &queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&context->DefaultQueue);
if (!NT_SUCCESS(status)) {
return status;
}
return Qa6InitializeUsbDescriptors(controller);
}
NTSTATUS
Qa6EvtDeviceD0Entry(
_In_ WDFDEVICE Device,
_In_ WDF_POWER_DEVICE_STATE PreviousState
)
{
UNREFERENCED_PARAMETER(PreviousState);
return Qa6PlugInUsbDevice(Device);
}
NTSTATUS
Qa6EvtDeviceD0Exit(
_In_ WDFDEVICE Device,
_In_ WDF_POWER_DEVICE_STATE TargetState
)
{
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(Device);
if (TargetState == WdfPowerDeviceD3Final && context->PluggedIn) {
context->PluggedIn = FALSE;
return UdecxUsbDevicePlugOutAndDelete(context->ChildDevice);
}
return STATUS_SUCCESS;
}
VOID
Qa6EvtControllerCleanup(
_In_ WDFOBJECT Object
)
{
PQA6_CONTROLLER_CONTEXT context =
Qa6GetControllerContext((WDFDEVICE)Object);
if (context->ChildInit != NULL) {
UdecxUsbDeviceInitFree(context->ChildInit);
context->ChildInit = NULL;
}
}
VOID
Qa6EvtControllerIoctl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
WDFDEVICE controller = WdfIoQueueGetDevice(Queue);
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(IoControlCode);
if (!UdecxWdfDeviceTryHandleUserIoctl(controller, Request)) {
WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST);
}
}
VOID
Qa6EvtBridgeRead(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t Length
)
{
WDFDEVICE controller = WdfIoQueueGetDevice(Queue);
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(controller);
BOOLEAN ready;
ULONG frameLength;
NTSTATUS status;
UNREFERENCED_PARAMETER(Length);
WdfSpinLockAcquire(context->BridgeLock);
ready = context->RequestFrameReady;
frameLength = context->RequestFrameLength;
if (ready) {
context->RequestFrameReady = FALSE;
}
WdfSpinLockRelease(context->BridgeLock);
if (ready) {
Qa6CompleteUserRead(Request, context->RequestFrame, frameLength);
return;
}
status = WdfRequestForwardToIoQueue(Request, context->WaitingUserReads);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
}
}
VOID
Qa6EvtBridgeWrite(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t Length
)
{
WDFDEVICE controller = WdfIoQueueGetDevice(Queue);
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(controller);
const Qa6UsbFrameHeader *header;
PVOID rawBuffer;
size_t rawLength;
WDFREQUEST urb = NULL;
ULONG originalTransferLength = 0;
BOOLEAN directionIn = FALSE;
NTSTATUS status;
UNREFERENCED_PARAMETER(Length);
status = WdfRequestRetrieveInputBuffer(Request, QA6_USB_HEADER_SIZE,
&rawBuffer, &rawLength);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
return;
}
header = (const Qa6UsbFrameHeader *)rawBuffer;
if (header->magic != QA6_USB_MAGIC ||
header->version != QA6_USB_PROTOCOL_VERSION ||
header->type != QA6_USB_MESSAGE_RESPONSE ||
header->payload_length > QA6_USB_MAX_PAYLOAD ||
rawLength != QA6_USB_HEADER_SIZE + header->payload_length) {
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
WdfSpinLockAcquire(context->BridgeLock);
if (context->PendingUrb != NULL &&
context->PendingRequestId == header->request_id) {
urb = context->PendingUrb;
originalTransferLength = context->PendingTransferLength;
directionIn = context->PendingDirectionIn;
context->PendingUrb = NULL;
}
WdfSpinLockRelease(context->BridgeLock);
if (urb == NULL) {
WdfRequestComplete(Request, STATUS_NOT_FOUND);
return;
}
if (header->status == QA6_USB_STATUS_SUCCESS) {
ULONG completed;
if (directionIn) {
PUCHAR transferBuffer;
ULONG transferBufferLength;
status = UdecxUrbRetrieveBuffer(urb, &transferBuffer,
&transferBufferLength);
if (NT_SUCCESS(status)) {
completed = header->payload_length;
if (completed > transferBufferLength) {
completed = transferBufferLength;
}
RtlCopyMemory(transferBuffer,
(const UCHAR *)rawBuffer + QA6_USB_HEADER_SIZE,
completed);
} else {
completed = 0;
}
} else {
completed = header->transfer_length;
if (completed > originalTransferLength) {
completed = originalTransferLength;
}
status = STATUS_SUCCESS;
}
UdecxUrbSetBytesCompleted(urb, completed);
UdecxUrbCompleteWithNtStatus(urb, status);
} else if (header->status == QA6_USB_STATUS_STALL) {
UdecxUrbComplete(urb, USBD_STATUS_STALL_PID);
} else {
UdecxUrbCompleteWithNtStatus(urb, STATUS_DEVICE_NOT_CONNECTED);
}
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, rawLength);
}
VOID
Qa6EvtCanceledUserRead(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request
)
{
UNREFERENCED_PARAMETER(Queue);
WdfRequestComplete(Request, STATUS_CANCELLED);
}
VOID
Qa6EvtControlUrb(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
WDFDEVICE controller = WdfIoQueueGetDevice(Queue);
PQA6_CONTROLLER_CONTEXT context = Qa6GetControllerContext(controller);
Qa6UsbFrameHeader *header =
(Qa6UsbFrameHeader *)context->RequestFrame;
WDF_USB_CONTROL_SETUP_PACKET setup;
PUCHAR transferBuffer = NULL;
ULONG transferBufferLength = 0;
ULONG payloadLength = 0;
ULONG requestId;
NTSTATUS status;
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(InputBufferLength);
if (IoControlCode != IOCTL_INTERNAL_USB_SUBMIT_URB) {
UdecxUrbCompleteWithNtStatus(Request, STATUS_INVALID_DEVICE_REQUEST);
return;
}
status = UdecxUrbRetrieveControlSetupPacket(Request, &setup);
if (!NT_SUCCESS(status)) {
UdecxUrbCompleteWithNtStatus(Request, status);
return;
}
/* UDE answers descriptor/enumeration requests from the registered sets. */
if ((setup.Generic.Bytes[0] & 0x60) == 0) {
UdecxUrbSetBytesCompleted(Request, 0);
UdecxUrbCompleteWithNtStatus(Request, STATUS_SUCCESS);
return;
}
if (setup.Packet.wLength != 0) {
status = UdecxUrbRetrieveBuffer(Request, &transferBuffer,
&transferBufferLength);
if (!NT_SUCCESS(status)) {
UdecxUrbCompleteWithNtStatus(Request, status);
return;
}
if (transferBufferLength > QA6_USB_MAX_PAYLOAD) {
UdecxUrbCompleteWithNtStatus(Request,
STATUS_INVALID_BUFFER_SIZE);
return;
}
}
if ((setup.Generic.Bytes[0] & 0x80) == 0) {
payloadLength = transferBufferLength;
}
WdfSpinLockAcquire(context->BridgeLock);
if (context->PendingUrb != NULL) {
WdfSpinLockRelease(context->BridgeLock);
UdecxUrbCompleteWithNtStatus(Request, STATUS_DEVICE_BUSY);
return;
}
requestId = ++context->PendingRequestId;
if (requestId == 0) {
requestId = ++context->PendingRequestId;
}
RtlZeroMemory(header, sizeof(*header));
header->magic = QA6_USB_MAGIC;
header->version = QA6_USB_PROTOCOL_VERSION;
header->type = QA6_USB_MESSAGE_REQUEST;
header->request_id = requestId;
header->status = QA6_USB_STATUS_SUCCESS;
header->payload_length = payloadLength;
header->transfer_length = transferBufferLength;
RtlCopyMemory(header->setup, setup.Generic.Bytes, sizeof(header->setup));
if (payloadLength != 0) {
RtlCopyMemory(context->RequestFrame + QA6_USB_HEADER_SIZE,
transferBuffer, payloadLength);
}
context->RequestFrameLength = QA6_USB_HEADER_SIZE + payloadLength;
context->PendingUrb = Request;
context->PendingTransferLength = transferBufferLength;
context->PendingDirectionIn =
(setup.Generic.Bytes[0] & 0x80) != 0;
WdfSpinLockRelease(context->BridgeLock);
QA6_LOG_INFO("EP0 request %lu bm=%02x req=%02x len=%u\n",
requestId, setup.Generic.Bytes[0], setup.Generic.Bytes[1],
setup.Packet.wLength);
Qa6PublishRequestFrame(controller);
}
VOID
Qa6EvtEndpointReset(
_In_ UDECXUSBENDPOINT Endpoint,
_In_ WDFREQUEST Request
)
{
UNREFERENCED_PARAMETER(Endpoint);
WdfRequestComplete(Request, STATUS_SUCCESS);
}
NTSTATUS
Qa6EvtQueryUsbCapability(
_In_ WDFDEVICE Device,
_In_ PGUID CapabilityType,
_In_ ULONG OutputBufferLength,
_Out_writes_to_opt_(OutputBufferLength, *ResultLength) PVOID OutputBuffer,
_Out_ PULONG ResultLength
)
{
UNREFERENCED_PARAMETER(Device);
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(OutputBuffer);
*ResultLength = 0;
if (RtlCompareMemory(
CapabilityType,
&GUID_USB_CAPABILITY_DEVICE_CONNECTION_HIGH_SPEED_COMPATIBLE,
sizeof(GUID)) == sizeof(GUID)) {
return STATUS_SUCCESS;
}
return STATUS_NOT_SUPPORTED;
}