/* * 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 #include #include #include #include #include #include #include #include #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; }