[WDF] Add Windows Driver Framework files
[reactos.git] / sdk / lib / drivers / wdf / shared / targets / usb / km / fxusbdevicekm.cpp
diff --git a/sdk/lib/drivers/wdf/shared/targets/usb/km/fxusbdevicekm.cpp b/sdk/lib/drivers/wdf/shared/targets/usb/km/fxusbdevicekm.cpp
new file mode 100644 (file)
index 0000000..1fa9964
--- /dev/null
@@ -0,0 +1,1107 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+    FxUsbDeviceKm.cpp
+
+Abstract:
+
+Author:
+
+Environment:
+
+    kernel mode only
+
+Revision History:
+
+--*/
+
+extern "C" {
+#include <initguid.h>
+}
+
+#include "fxusbpch.hpp"
+
+
+extern "C" {
+#include "FxUsbDeviceKm.tmh"
+}
+
+
+
+
+
+
+
+#define UCHAR_MAX (0xff)
+
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::InitDevice(
+    __in ULONG USBDClientContractVersionForWdfClient
+    )
+{
+    URB urb;
+    FxSyncRequest request(GetDriverGlobals(), NULL);
+    WDF_REQUEST_SEND_OPTIONS options;
+    NTSTATUS status;
+    ULONG size;
+
+    RtlZeroMemory(&urb, sizeof(urb));
+
+    if (USBDClientContractVersionForWdfClient != USBD_CLIENT_CONTRACT_VERSION_INVALID) {
+
+        //
+        // Register with USBDEX.lib
+        //
+        status = USBD_CreateHandle(m_InStackDevice,
+                                   m_TargetDevice,
+                                   USBDClientContractVersionForWdfClient,
+                                   GetDriverGlobals()->Tag,
+                                   &m_USBDHandle);
+
+        if (!NT_SUCCESS(status)) {
+            DoTraceLevelMessage(
+                GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                "USBD_CreateHandle failed, %!STATUS!", status);
+            goto Done;
+        }
+
+        m_UrbType = FxUrbTypeUsbdAllocated;
+    }
+
+    status = request.m_TrueRequest->ValidateTarget(this);
+    if (!NT_SUCCESS(status)) {
+        goto Done;
+    }
+
+    UsbBuildGetDescriptorRequest(&urb,
+                                 sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),
+                                 USB_DEVICE_DESCRIPTOR_TYPE,
+                                 0,
+                                 0,
+                                 &m_DeviceDescriptor,
+                                 NULL,
+                                 sizeof(m_DeviceDescriptor),
+                                 NULL);
+
+    FxFormatUsbRequest(request.m_TrueRequest, &urb, FxUrbTypeLegacy, NULL);
+
+    WDF_REQUEST_SEND_OPTIONS_INIT(&options, 0);
+    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&options, WDF_REL_TIMEOUT_IN_SEC(5));
+
+    status = SubmitSync(request.m_TrueRequest, &options);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not retrieve device descriptor, %!STATUS!", status);
+        goto Done;
+    }
+
+    //
+    // After successfully completing any default control URB, USBD/usbport fills
+    // in the PipeHandle field of the URB (it is the same offset in every URB,
+    // which this CASSERT verifies.  Since USBD_DEFAULT_PIPE_TRANSFER is not
+    // supported by USBD (it is by USBPORT), this is the prescribed way of getting
+    // the default control pipe so that you can set the PipeHandle field in
+    // _URB_CONTROL_TRANSFER.
+    //
+    WDFCASSERT(FIELD_OFFSET(_URB_CONTROL_TRANSFER, PipeHandle) ==
+               FIELD_OFFSET(_URB_CONTROL_DESCRIPTOR_REQUEST, Reserved));
+
+    m_ControlPipe = urb.UrbControlDescriptorRequest.Reserved;
+
+    USB_CONFIGURATION_DESCRIPTOR config;
+
+    RtlZeroMemory(&config, sizeof(config));
+    size = sizeof(config);
+
+    UsbBuildGetDescriptorRequest(&urb,
+                                 sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST),
+                                 USB_CONFIGURATION_DESCRIPTOR_TYPE,
+                                 0,
+                                 0,
+                                 &config,
+                                 NULL,
+                                 size,
+                                 NULL);
+
+    request.m_TrueRequest->GetSubmitFxIrp()->Reuse(STATUS_SUCCESS);
+    request.m_TrueRequest->ClearFieldsForReuse();
+    FxFormatUsbRequest(request.m_TrueRequest, &urb, FxUrbTypeLegacy, NULL);
+
+    status = SubmitSync(request.m_TrueRequest, &options);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not retrieve config descriptor size, %!STATUS!", status);
+        goto Done;
+    }
+    else if (urb.UrbControlDescriptorRequest.TransferBufferLength == 0) {
+        //
+        // Not enough info returned
+        //
+        status = STATUS_UNSUCCESSFUL;
+
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not retrieve config descriptor size, zero bytes transferred, "
+            " %!STATUS!", status);
+
+        goto Done;
+    }
+    else if (config.wTotalLength < size) {
+        //
+        // Not enough info returned
+        //
+        status = STATUS_UNSUCCESSFUL;
+
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not retrieve config descriptor size, config.wTotalLength %d < "
+            "sizeof(config descriptor) (%d), %!STATUS!",
+            config.wTotalLength, size, status);
+
+        goto Done;
+    }
+
+    //
+    // Allocate an additional memory at the end of the buffer so if we
+    // accidentily access fields beyond the end of the buffer we don't crash
+
+    //
+
+
+
+
+
+    size = config.wTotalLength;
+    ULONG paddedSize = size + sizeof(USB_DEVICE_DESCRIPTOR);
+
+    m_ConfigDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)
+        FxPoolAllocate(GetDriverGlobals(),
+                       NonPagedPool,
+                       paddedSize);
+
+    if (m_ConfigDescriptor == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not allocate %d bytes for config descriptor, %!STATUS!",
+            paddedSize, status);
+
+        goto Done;
+    }
+
+    RtlZeroMemory(m_ConfigDescriptor, paddedSize);
+
+    UsbBuildGetDescriptorRequest(&urb,
+                                 sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST),
+                                 USB_CONFIGURATION_DESCRIPTOR_TYPE,
+                                 0,
+                                 0,
+                                 m_ConfigDescriptor,
+                                 NULL,
+                                 size,
+                                 NULL);
+
+    request.m_TrueRequest->GetSubmitFxIrp()->Reuse(STATUS_SUCCESS);
+    request.m_TrueRequest->ClearFieldsForReuse();
+    FxFormatUsbRequest(request.m_TrueRequest, &urb, FxUrbTypeLegacy, NULL);
+
+    status = SubmitSync(request.m_TrueRequest, &options);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not retrieve config descriptor, %!STATUS!", status);
+        goto Done;
+    } else if(m_ConfigDescriptor->wTotalLength != size) {
+        //
+        // Invalid wTotalLength
+        //
+        status = STATUS_DEVICE_DATA_ERROR;
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Defective USB device reported two different config descriptor "
+            "wTotalLength values: %d and %d, %!STATUS!",
+            size, m_ConfigDescriptor->wTotalLength, status);
+        goto Done;
+    }
+
+    //
+    // Check to see if we are wait wake capable
+    //
+    if (m_ConfigDescriptor->bmAttributes & USB_CONFIG_REMOTE_WAKEUP) {
+        m_Traits |= WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;
+    }
+
+    //
+    // Check to see if we are self or bus powered
+    //
+    USHORT deviceStatus;
+
+    UsbBuildGetStatusRequest(&urb,
+                             URB_FUNCTION_GET_STATUS_FROM_DEVICE,
+                             0,
+                             &deviceStatus,
+                             NULL,
+                             NULL);
+
+    request.m_TrueRequest->GetSubmitFxIrp()->Reuse(STATUS_SUCCESS);
+    request.m_TrueRequest->ClearFieldsForReuse();
+    FxFormatUsbRequest(request.m_TrueRequest, &urb, FxUrbTypeLegacy, NULL);
+
+    status = SubmitSync(request.m_TrueRequest, &options);
+    if (NT_SUCCESS(status) && (deviceStatus & USB_GETSTATUS_SELF_POWERED)) {
+        m_Traits |= WDF_USB_DEVICE_TRAIT_SELF_POWERED;
+    }
+
+    //
+    // Revert back to success b/c we don't care if the usb device get status
+    // fails
+    //
+    status = STATUS_SUCCESS;
+
+    USB_BUS_INTERFACE_USBDI_V1 busIf;
+
+    RtlZeroMemory(&busIf, sizeof(busIf));
+
+    //
+    // All PNP irps must have this initial status
+    //
+    request.m_TrueRequest->GetSubmitFxIrp()->Reuse(STATUS_NOT_SUPPORTED);
+    request.m_TrueRequest->ClearFieldsForReuse();
+
+    FxQueryInterface::_FormatIrp(
+        request.m_TrueRequest->GetSubmitFxIrp()->GetIrp(),
+        &USB_BUS_INTERFACE_USBDI_GUID,
+        (PINTERFACE) &busIf,
+        sizeof(busIf),
+        USB_BUSIF_USBDI_VERSION_1);
+
+    request.m_TrueRequest->VerifierSetFormatted();
+
+    status = SubmitSync(request.m_TrueRequest);
+
+    if (!NT_SUCCESS(status)) {
+        //
+        // Retry with the older interface
+        //
+        RtlZeroMemory(&busIf, sizeof(busIf));
+
+        //
+        // All PNP irps must have this initial status
+        //
+
+        request.m_TrueRequest->GetSubmitFxIrp()->Reuse(STATUS_NOT_SUPPORTED);
+        request.m_TrueRequest->ClearFieldsForReuse();
+
+        FxQueryInterface::_FormatIrp(
+            request.m_TrueRequest->GetSubmitFxIrp()->GetIrp(),
+            &USB_BUS_INTERFACE_USBDI_GUID,
+            (PINTERFACE) &busIf,
+            sizeof(USB_BUS_INTERFACE_USBDI_V0),
+            USB_BUSIF_USBDI_VERSION_0);
+
+        request.m_TrueRequest->VerifierSetFormatted();
+
+        status = SubmitSync(request.m_TrueRequest);
+    }
+
+    if (NT_SUCCESS(status)) {
+        //
+        // Need to check for NULL b/c we may have only retrieved the V0 interface
+        //
+        if (busIf.IsDeviceHighSpeed != NULL &&
+            busIf.IsDeviceHighSpeed(busIf.BusContext)) {
+            m_Traits |= WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED;
+        }
+
+        //
+        // Stash these off for later
+        //
+        m_QueryBusTime = busIf.QueryBusTime;
+        m_BusInterfaceContext = busIf.BusContext;
+        m_BusInterfaceDereference = busIf.InterfaceDereference;
+
+        ASSERT(busIf.GetUSBDIVersion != NULL);
+        busIf.GetUSBDIVersion(busIf.BusContext,
+                              &m_UsbdVersionInformation,
+                              &m_HcdPortCapabilities);
+    }
+    else if (status == STATUS_NOT_SUPPORTED) {
+        //
+        // We will only use m_ControlPipe on stacks which do not support
+        // USBD_DEFAULT_PIPE_TRANSFER.  If all the QIs failed, then we know
+        // definitively that we are running on USBD and we need m_ControlPipe
+        // to be != NULL for later control transfers
+        //
+        ASSERT(m_ControlPipe != NULL);
+
+        m_OnUSBD = TRUE;
+
+        //
+        // The QI failed with not supported, do not return error
+        //
+        status = STATUS_SUCCESS;
+    }
+    else {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Query Interface for bus returned error, %!STATUS!", status);
+    }
+
+Done:
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::GetString(
+    __in_ecount(*NumCharacters) PUSHORT String,
+    __in PUSHORT NumCharacters,
+    __in UCHAR StringIndex,
+    __in_opt USHORT LangID,
+    __in_opt WDFREQUEST Request,
+    __in_opt PWDF_REQUEST_SEND_OPTIONS Options
+    )
+{
+    PUSB_STRING_DESCRIPTOR pDescriptor;
+    PVOID buffer;
+    _URB_CONTROL_DESCRIPTOR_REQUEST urb;
+    WDF_REQUEST_SEND_OPTIONS options, *pOptions;
+    USB_COMMON_DESCRIPTOR common;
+    ULONG length;
+    NTSTATUS status;
+
+    FxSyncRequest request(GetDriverGlobals(), NULL, Request);
+
+    //
+    // FxSyncRequest always succeesds for KM.
+    //
+    status = request.Initialize();
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "Failed to initialize FxSyncRequest");
+        return status;
+    }
+
+    buffer = NULL;
+
+    status = request.m_TrueRequest->ValidateTarget(this);
+    if (!NT_SUCCESS(status)) {
+        goto Done;
+    }
+
+    RtlZeroMemory(&urb, sizeof(urb));
+
+    if (String != NULL) {
+        length = sizeof(USB_STRING_DESCRIPTOR) + (*NumCharacters - 1) * sizeof(WCHAR);
+
+        buffer = FxPoolAllocate(GetDriverGlobals(),
+                                NonPagedPool,
+                                length);
+
+        if (buffer == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto Done;
+        }
+
+        RtlZeroMemory(buffer, length);
+        pDescriptor = (PUSB_STRING_DESCRIPTOR) buffer;
+    }
+    else {
+        RtlZeroMemory(&common, sizeof(common));
+
+        length = sizeof(USB_COMMON_DESCRIPTOR);
+        pDescriptor = (PUSB_STRING_DESCRIPTOR) &common;
+    }
+
+    UsbBuildGetDescriptorRequest((PURB) &urb,
+                                 sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST),
+                                 USB_STRING_DESCRIPTOR_TYPE,
+                                 StringIndex,
+                                 LangID,
+                                 pDescriptor,
+                                 NULL,
+                                 length,
+                                 NULL);
+
+    if (Options != NULL) {
+        pOptions = Options;
+    }
+    else {
+        WDF_REQUEST_SEND_OPTIONS_INIT(&options, 0);
+        WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&options,
+                                             WDF_REL_TIMEOUT_IN_SEC(2));
+
+        pOptions = &options;
+    }
+#pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "this annotation change in usb.h is communicated to usb team")
+    FxFormatUsbRequest(request.m_TrueRequest, (PURB) &urb, FxUrbTypeLegacy, NULL);
+    status = SubmitSync(request.m_TrueRequest, pOptions);
+
+    if (NT_SUCCESS(status)) {
+        USHORT numChars;
+
+        //
+        // Make sure we got an even number of bytes and that we got a header
+        //
+        if ((pDescriptor->bLength & 0x1) ||
+            pDescriptor->bLength < sizeof(USB_COMMON_DESCRIPTOR)) {
+            status = STATUS_DEVICE_DATA_ERROR;
+        }
+        else {
+            //
+            // bLength is the length of the entire descriptor.  Subtract off
+            // the descriptor header and then divide by the size of a WCHAR.
+            //
+            numChars =
+                (pDescriptor->bLength - sizeof(USB_COMMON_DESCRIPTOR)) / sizeof(WCHAR);
+
+            if (String != NULL) {
+                if (*NumCharacters >= numChars) {
+                    length = numChars * sizeof(WCHAR);
+                }
+                else {
+                    length = *NumCharacters * sizeof(WCHAR);
+                    status = STATUS_BUFFER_OVERFLOW;
+                }
+
+                *NumCharacters = numChars;
+                RtlCopyMemory(String, pDescriptor->bString, length);
+            }
+            else {
+                *NumCharacters = numChars;
+            }
+        }
+    }
+
+    if (buffer != NULL) {
+        FxPoolFree(buffer);
+    }
+
+Done:
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::FormatStringRequest(
+    __in FxRequestBase* Request,
+    __in FxRequestBuffer *RequestBuffer,
+    __in UCHAR StringIndex,
+    __in USHORT LangID
+    )
+/*++
+
+Routine Description:
+    Formats a request to retrieve a string from a string descriptor
+
+Arguments:
+    Request - request to format
+
+    RequestBuffer - Buffer to be filled in when the request has completed
+
+    StringIndex - index of the string
+
+    LandID - language ID of the string to be retrieved
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    FxUsbDeviceStringContext* pContext;
+    NTSTATUS status;
+    FX_URB_TYPE urbType;
+
+    status = Request->ValidateTarget(this);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "WDFUSBDEVICE %p, Request %p, setting target failed, "
+                            "%!STATUS!", GetHandle(), Request, status);
+
+        return status;
+    }
+
+    if (Request->HasContextType(FX_RCT_USB_STRING_REQUEST)) {
+        pContext = (FxUsbDeviceStringContext*) Request->GetContext();
+    }
+    else {
+
+        urbType = GetFxUrbTypeForRequest(Request);
+        pContext = new(GetDriverGlobals()) FxUsbDeviceStringContext(urbType);
+        if (pContext == NULL) {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        if (urbType == FxUrbTypeUsbdAllocated) {
+            status = pContext->AllocateUrb(m_USBDHandle);
+            if (!NT_SUCCESS(status)) {
+                DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                    "FxUsbDeviceStringContext::AllocateUrb failed, %!STATUS!", status);
+                delete pContext;
+                return status;
+            }
+
+            //
+            // Since the AllocateUrb routine calls USBD_xxxUrbAllocate APIs to allocate an Urb, it is
+            // important to release those resorces before the devnode is removed. Those
+            // resoruces are removed at the time Request is disposed.
+            //
+            Request->EnableContextDisposeNotification();
+        }
+
+        Request->SetContext(pContext);
+    }
+
+    status = pContext->AllocateDescriptor(GetDriverGlobals(),
+                                          RequestBuffer->GetBufferLength());
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    pContext->StoreAndReferenceMemory(RequestBuffer);
+    pContext->SetUrbInfo(StringIndex, LangID);
+
+    if (pContext->m_Urb == &pContext->m_UrbLegacy) {
+        urbType = FxUrbTypeLegacy;
+    }
+    else {
+        urbType = FxUrbTypeUsbdAllocated;
+    }
+
+    FxFormatUsbRequest(Request, (PURB)pContext->m_Urb, urbType, m_USBDHandle);
+
+    return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::FormatControlRequest(
+    __in FxRequestBase* Request,
+    __in PWDF_USB_CONTROL_SETUP_PACKET SetupPacket,
+    __in FxRequestBuffer *RequestBuffer
+    )
+{
+    FxUsbDeviceControlContext* pContext;
+    NTSTATUS status;
+    size_t bufferSize;
+    FX_URB_TYPE urbType;
+
+    bufferSize = RequestBuffer->GetBufferLength();
+
+    //
+    // We can only transfer 2 bytes worth of data, so if the buffer is larger,
+    // fail here.
+    //
+    if (bufferSize > 0xFFFF) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Control transfer buffer is limited to 0xFFFF bytes in size, "
+            "%I64d requested ", bufferSize);
+
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    status = Request->ValidateTarget(this);
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "WDFUSBDEVICE %p, Request %p, setting target failed, "
+                            "%!STATUS!", GetHandle(), Request, status);
+
+        return status;
+    }
+
+    if (Request->HasContextType(FX_RCT_USB_CONTROL_REQUEST)) {
+        pContext = (FxUsbDeviceControlContext*) Request->GetContext();
+    }
+    else {
+
+        urbType = GetFxUrbTypeForRequest(Request);
+        pContext = new(GetDriverGlobals()) FxUsbDeviceControlContext(urbType);
+        if (pContext == NULL) {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        if (urbType == FxUrbTypeUsbdAllocated) {
+            status = pContext->AllocateUrb(m_USBDHandle);
+            if (!NT_SUCCESS(status)) {
+                DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                                    "FxUsbDeviceControlContext::AllocateUrb Failed, %!STATUS!", status);
+
+                delete pContext;
+                return status;
+            }
+            //
+            // Since the AllocateUrb routine calls USBD_xxxUrbAllocate APIs to allocate an Urb, it is
+            // important to release those resorces before the devnode is removed. Those
+            // resoruces are removed at the time Request is disposed.
+            //
+            Request->EnableContextDisposeNotification();
+        }
+
+        Request->SetContext(pContext);
+    }
+
+    if (RequestBuffer->HasMdl()) {
+        PMDL pMdl;
+
+        pMdl = NULL;
+        ASSERT(pContext->m_PartialMdl == NULL);
+
+        status = RequestBuffer->GetOrAllocateMdl(GetDriverGlobals(),
+                                                 &pMdl,
+                                                 &pContext->m_PartialMdl,
+                                                 &pContext->m_UnlockPages,
+                                                 IoModifyAccess);
+
+        if (!NT_SUCCESS(status)) {
+            return status;
+        }
+
+        ASSERT(pMdl != NULL);
+    }
+
+    pContext->StoreAndReferenceMemory(this, RequestBuffer, SetupPacket);
+
+    if (pContext->m_Urb == &pContext->m_UrbLegacy) {
+        urbType = FxUrbTypeLegacy;
+    }
+    else {
+        urbType = FxUrbTypeUsbdAllocated;
+    }
+
+    FxFormatUsbRequest(Request, (PURB)pContext->m_Urb, urbType, m_USBDHandle);
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+FxUsbDeviceControlContext::StoreAndReferenceMemory(
+    __in FxUsbDevice* Device,
+    __in FxRequestBuffer* Buffer,
+    __in PWDF_USB_CONTROL_SETUP_PACKET SetupPacket
+    )
+{
+    SetUsbType(WdfUsbRequestTypeDeviceControlTransfer);
+
+    RtlZeroMemory(m_Urb, sizeof(*m_Urb));
+
+    m_Urb->Hdr.Function = URB_FUNCTION_CONTROL_TRANSFER;
+    m_Urb->Hdr.Length = sizeof(*m_Urb);
+
+    __super::StoreAndReferenceMemory(Buffer);
+
+    //
+    // Set the values using what is stored in the buffer
+    //
+    Buffer->AssignValues(&m_Urb->TransferBuffer,
+                         &m_Urb->TransferBufferMDL,
+                         &m_Urb->TransferBufferLength);
+
+    RtlCopyMemory(&m_Urb->SetupPacket[0],
+                  &SetupPacket->Generic.Bytes[0],
+                  sizeof(m_Urb->SetupPacket));
+
+    //
+    // also indicate the length of the buffer in the header
+    //
+    ((PWDF_USB_CONTROL_SETUP_PACKET) &m_Urb->SetupPacket[0])->Packet.wLength =
+        (USHORT) m_Urb->TransferBufferLength;
+
+    //
+    // Control transfers are always short OK.  USBD_TRANSFER_DIRECTION_IN may
+    // be OR'ed in later.
+    //
+    m_Urb->TransferFlags = USBD_SHORT_TRANSFER_OK;
+
+    //
+    // Get the direction out of the setup packet
+    //
+    if (SetupPacket->Packet.bm.Request.Dir == BMREQUEST_DEVICE_TO_HOST) {
+        m_Urb->TransferFlags |= USBD_TRANSFER_DIRECTION_IN;
+    }
+
+    if (Device->OnUSBD()) {
+        m_Urb->PipeHandle = Device->GetControlPipeHandle();
+    }
+    else {
+        //
+        // USBPORT supports this flag
+        //
+        m_Urb->TransferFlags |= USBD_DEFAULT_PIPE_TRANSFER;
+    }
+
+    //
+    // If we have built a partial MDL, use that instead.  TransferBufferLength
+    // is still valid because the Offsets or length in Buffer will have been
+    // used to create this PartialMdl by the caller.
+    //
+    if (m_PartialMdl != NULL) {
+        m_Urb->TransferBufferMDL = m_PartialMdl;
+    }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::QueryUsbCapability(
+    __in
+    CONST GUID* CapabilityType,
+    __in
+    ULONG CapabilityBufferLength,
+    __drv_when(CapabilityBufferLength == 0, __out_opt)
+    __drv_when(CapabilityBufferLength != 0 && ResultLength == NULL, __out_bcount(CapabilityBufferLength))
+    __drv_when(CapabilityBufferLength != 0 && ResultLength != NULL, __out_bcount_part_opt(CapabilityBufferLength, *ResultLength))
+    PVOID CapabilityBuffer,
+    __out_opt
+    __drv_when(ResultLength != NULL,__deref_out_range(<=,CapabilityBufferLength))
+    PULONG ResultLength
+   )
+{
+    NTSTATUS status;
+
+    if (ResultLength != NULL) {
+        *ResultLength = 0;
+    }
+
+    if (GetUSBDHandle() == NULL) {
+        status = STATUS_INVALID_DEVICE_STATE;
+
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "WDFUSBDEVICE must have been created using WdfUsbTargetDeviceCreateWithParameters, %!STATUS!",
+            status);
+
+        return status;
+    }
+
+    status = USBD_QueryUsbCapability(m_USBDHandle,
+                                     CapabilityType,
+                                     CapabilityBufferLength,
+                                     (PUCHAR) CapabilityBuffer,
+                                     ResultLength);
+
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "Could not retrieve capability %!GUID!, %!STATUS!",
+            CapabilityType, status);
+        goto exit;
+    }
+
+exit:
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::SelectConfigSingle(
+    __in PWDF_OBJECT_ATTRIBUTES PipeAttributes,
+    __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
+    )
+/*++
+
+Routine Description:
+    This will configure the single inteface case and pick up the first available
+    setting. If there are multiple settings on  a single interface device
+    and the driver wants to pick one then the driver should use the multinterface
+    option to initialize.
+
+    This takes care of the simplest case only.  Configuring a multi interface
+    device as a single interface device would be treated as an error.  There is
+    duplication of code with the multi case but it is better to keep these two
+    separate especially if more gets added.
+
+Arguments:
+
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    //
+    // The array needs an extra element which is zero'd out to mark the end
+    //
+    USBD_INTERFACE_LIST_ENTRY listEntry[2];
+    PURB urb;
+    NTSTATUS status;
+
+    RtlZeroMemory(&Params->Types.SingleInterface,
+                  sizeof(Params->Types.SingleInterface));
+
+    if (m_NumInterfaces > 1) {
+        status = STATUS_INVALID_PARAMETER;
+
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "WDFUSBDEVICE %p cannot be auto configured for a single interface "
+            "since there are %d interfaces on the device, %!STATUS!",
+            GetHandle(), m_NumInterfaces, status);
+
+        return status;
+    }
+
+    RtlZeroMemory(&listEntry[0], sizeof(listEntry));
+
+    //
+    // Use AlternateSetting 0 by default
+    //
+    listEntry[0].InterfaceDescriptor = m_Interfaces[0]->GetSettingDescriptor(0);
+
+    if (listEntry[0].InterfaceDescriptor == NULL) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+            "WDFUSBDEVICE %p could not retrieve AlternateSetting 0 for "
+            "bInterfaceNumber %d", GetHandle(),
+            m_Interfaces[0]->m_InterfaceNumber);
+
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    urb = FxUsbCreateConfigRequest(GetDriverGlobals(),
+                                   m_ConfigDescriptor,
+                                   &listEntry[0],
+                                   GetDefaultMaxTransferSize());
+
+    if (urb == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else {
+        status = SelectConfig(PipeAttributes, urb, FxUrbTypeLegacy, NULL);
+
+        if (NT_SUCCESS(status)) {
+            Params->Types.SingleInterface.NumberConfiguredPipes  =
+                 m_Interfaces[0]->GetNumConfiguredPipes();
+
+            Params->Types.SingleInterface.ConfiguredUsbInterface =
+                m_Interfaces[0]->GetHandle();
+        }
+
+        FxPoolFree(urb);
+        urb = NULL;
+    }
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::SelectConfigMulti(
+    __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
+    __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
+    )
+/*++
+
+Routine Description:
+    Selects the configuration as described by the parameter Params.  If there is a
+    previous active configuration, the WDFUSBPIPEs for it are stopped and
+    destroyed before the new configuration is selected
+
+Arguments:
+    PipesAttributes - object attributes to apply to each created WDFUSBPIPE
+
+    Params -
+
+Return Value:
+    NTSTATUS
+
+  --*/
+{
+    PUSBD_INTERFACE_LIST_ENTRY pList;
+    PURB urb;
+    NTSTATUS status;
+    ULONG size;
+    UCHAR i;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals;
+
+    pFxDriverGlobals = GetDriverGlobals();
+
+    Params->Types.MultiInterface.NumberOfConfiguredInterfaces = 0;
+
+    //
+    // The array needs an extra element which is zero'd out to mark the end
+    //
+    size = sizeof(USBD_INTERFACE_LIST_ENTRY) * (m_NumInterfaces + 1);
+    pList = (PUSBD_INTERFACE_LIST_ENTRY) FxPoolAllocate(
+        pFxDriverGlobals,
+        NonPagedPool,
+        size
+        );
+
+    if (pList == NULL) {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    RtlZeroMemory(pList, size);
+
+    if (Params->Type == WdfUsbTargetDeviceSelectConfigTypeMultiInterface) {
+        for (i = 0; i < m_NumInterfaces; i++) {
+            pList[i].InterfaceDescriptor =
+                m_Interfaces[i]->GetSettingDescriptor(0);
+
+            if (pList[i].InterfaceDescriptor == NULL) {
+                DoTraceLevelMessage(
+                    GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                    "WDFUSBDEVICE %p could not retrieve AlternateSetting 0 for "
+                    "bInterfaceNumber %d", GetHandle(),
+                    m_Interfaces[i]->m_InterfaceNumber);
+
+                status = STATUS_INVALID_PARAMETER;
+                goto Done;
+            }
+        }
+    }
+    else {
+        //
+        // Type is WdfUsbTargetDeviceSelectConfigTypeInterfacesPairs
+        //
+        UCHAR interfacePairsNum = 0;
+        UCHAR bitArray[UCHAR_MAX/sizeof(UCHAR)];
+
+        //
+        // initialize the bit array
+        //
+        RtlZeroMemory(bitArray, sizeof(bitArray));
+        //
+        // Build a list of descriptors from the Setting pairs
+        // passed in by the user. There could be interfaces not
+        // covered in the setting/interface pairs array passed.
+        // If that is the case return STATUS_INVALID_PARAMETER
+        //
+        for (i = 0; i < Params->Types.MultiInterface.NumberInterfaces ; i++) {
+            PWDF_USB_INTERFACE_SETTING_PAIR settingPair;
+            UCHAR interfaceNumber;
+            UCHAR altSettingIndex;
+
+            settingPair = &Params->Types.MultiInterface.Pairs[i];
+
+            status = GetInterfaceNumberFromInterface(
+                settingPair->UsbInterface,
+                &interfaceNumber
+                );
+
+            //
+            //convert the interface handle to interface number
+            //
+            if (NT_SUCCESS(status)) {
+                altSettingIndex = settingPair->SettingIndex;
+
+                //
+                // do the following only if the bit is not already set
+                //
+                if (FxBitArraySet(&bitArray[0], interfaceNumber) == FALSE) {
+                    pList[interfacePairsNum].InterfaceDescriptor =
+                         FxUsbParseConfigurationDescriptor(
+                             m_ConfigDescriptor,
+                             interfaceNumber,
+                             altSettingIndex
+                             );
+
+                    if (pList[interfacePairsNum].InterfaceDescriptor == NULL) {
+                        status = STATUS_INVALID_PARAMETER;
+                        DoTraceLevelMessage(
+                            GetDriverGlobals(), TRACE_LEVEL_ERROR,
+                            TRACINGIOTARGET,
+                            "WDFUSBDEVICE %p could not retrieve "
+                            "AlternateSetting %d for "
+                            "bInterfaceNumber %d, returning %!STATUS!",
+                            GetHandle(),
+                            altSettingIndex, interfaceNumber, status);
+                        goto Done;
+                    }
+
+                    interfacePairsNum++;
+                }
+            }
+            else {
+                goto Done;
+            }
+        }
+
+        //
+        // Check if there are any interfaces not specified by the array. If
+        // there are, then select setting 0 for them.
+        //
+        if (m_NumInterfaces > interfacePairsNum) {
+            status = STATUS_INVALID_PARAMETER;
+            DoTraceLevelMessage(
+                GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                "WDFUSBDEVICE %p interface pairs set (%d) is not equal to actual "
+                "# of interfaces (%d) reported by the device, %!STATUS!",
+                GetObjectHandle(), interfacePairsNum, m_NumInterfaces, status);
+            goto Done;
+        }
+    } //WdfUsbTargetDeviceSelectConfigTypeInterfacesPairs
+
+    urb = FxUsbCreateConfigRequest(
+        GetDriverGlobals(),
+        m_ConfigDescriptor,
+        pList,
+        GetDefaultMaxTransferSize()
+        );
+
+    if (urb == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+    }
+    else {
+        status = SelectConfig(
+            PipesAttributes,
+            urb,
+            FxUrbTypeLegacy,
+            &Params->Types.MultiInterface.NumberOfConfiguredInterfaces);
+
+        FxPoolFree(urb);
+        urb = NULL;
+    }
+
+Done:
+    FxPoolFree(pList);
+    pList = NULL;
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxUsbDevice::Reset(
+    VOID
+    )
+{
+    FxIoContext context;
+    FxSyncRequest request(GetDriverGlobals(), &context);
+    FxRequestBuffer emptyBuffer;
+    NTSTATUS status;
+
+    //
+    // FxSyncRequest always succeesds for KM.
+    //
+    status = request.Initialize();
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
+                            "Failed to initialize FxSyncRequest");
+        return status;
+    }
+
+    status = FormatIoctlRequest(request.m_TrueRequest,
+                                IOCTL_INTERNAL_USB_RESET_PORT,
+                                TRUE,
+                                &emptyBuffer,
+                                &emptyBuffer);
+    if (NT_SUCCESS(status)) {
+        CancelSentIo();
+        status = SubmitSyncRequestIgnoreTargetState(request.m_TrueRequest, NULL);
+    }
+
+    return status;
+}
+
+