/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxUsbDeviceAPI.cpp

Abstract:


Author:

Environment:

    Both kernel and user mode

Revision History:

--*/

#include "fxusbpch.hpp"

extern "C" {
#include "FxUsbDeviceAPI.tmh"
}

//
// Extern "C" all APIs
//
extern "C" {

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
FxUsbTargetDeviceCreate(
    __in
    PFX_DRIVER_GLOBALS FxDriverGlobals,
    __in
    FxDeviceBase* Device,
    __in
    ULONG USBDClientContractVersion,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES Attributes,
    __out
    WDFUSBDEVICE* UsbDevice
    )
/*++

Routine Description:
    Creates a WDFUSBDEVICE handle for the client.

Arguments:

    Device - FxDeviceBase object

    USBDClientContractVersion - The USBD Client Contract version of the client driver

    UsbDevice - Pointer which will receive the created handle

Return Value:
    STATUS_SUCCESS - success
    STATUS_INSUFFICIENT_RESOURCES - no memory available
    ...

  --*/
{
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    //
    // Basic parameter validation
    //

    FxPointerNotNull(FxDriverGlobals, UsbDevice);
    *UsbDevice = NULL;

    status = FxVerifierCheckIrqlLevel(FxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = FxValidateObjectAttributes(FxDriverGlobals,
                                        Attributes,
                                        FX_VALIDATE_OPTION_PARENT_NOT_ALLOWED);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    pUsbDevice = new(FxDriverGlobals, Attributes) FxUsbDevice(FxDriverGlobals);
    if (pUsbDevice == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Perform all init and handle creation functions.  Check for error at the
    // end and clean up there.
    //
    status = pUsbDevice->Init(Device);

    if (NT_SUCCESS(status)) {
        WDFOBJECT device;

        device = NULL;

        status = pUsbDevice->InitDevice(USBDClientContractVersion);

        if (NT_SUCCESS(status)) {
            status = pUsbDevice->CreateInterfaces();
        }

        if (NT_SUCCESS(status)) {
            status = Device->AddIoTarget(pUsbDevice);
        }

        if (NT_SUCCESS(status)) {
            status = pUsbDevice->Commit(Attributes, &device, Device);
        }

        if (NT_SUCCESS(status)) {
            *UsbDevice = (WDFUSBDEVICE) device;
        }
    }

    if (!NT_SUCCESS(status)) {
        //
        // And now free it
        //
        pUsbDevice->DeleteFromFailedCreate();
    }

    return status;
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceCreate)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDEVICE Device,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES Attributes,
    __out
    WDFUSBDEVICE* UsbDevice
    )
/*++

Routine Description:
    Creates a WDFUSBDEVICE handle for the client.

Arguments:
    Device - WDFDEVICE handle to which we are attaching the WDFUSBDEVICE handle
        to
    PUsbDevice - Pointer which will receive the created handle

Return Value:
    STATUS_SUCCESS - success
    STATUS_INSUFFICIENT_RESOURCES - no memory available
    ...

  --*/
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxDeviceBase* pDevice;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Device,
                                   FX_TYPE_DEVICE_BASE,
                                   (PVOID*)&pDevice,
                                   &pFxDriverGlobals);

    return FxUsbTargetDeviceCreate(pFxDriverGlobals,
                                   pDevice,
                                   USBD_CLIENT_CONTRACT_VERSION_INVALID,
                                   Attributes,
                                   UsbDevice);
}

__checkReturn
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceCreateWithParameters)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFDEVICE Device,
    __in
    PWDF_USB_DEVICE_CREATE_CONFIG Config,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES Attributes,
    __out
    WDFUSBDEVICE* UsbDevice
    )
/*++

Routine Description:
    Creates a WDFUSBDEVICE handle for the client.

Arguments:
    Device - WDFDEVICE handle to which we are attaching the WDFUSBDEVICE handle
        to
    PUsbDevice - Pointer which will receive the created handle

Return Value:
    STATUS_SUCCESS - success
    STATUS_INSUFFICIENT_RESOURCES - no memory available
    ...

  --*/
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxDeviceBase* pDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   Device,
                                   FX_TYPE_DEVICE_BASE,
                                   (PVOID*)&pDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, Config);

    if (Config->Size != sizeof(WDF_USB_DEVICE_CREATE_CONFIG)) {
        status = STATUS_INFO_LENGTH_MISMATCH;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
            "WDF_USB_DEVICE_CREATE_CONFIG Size 0x%x, expected 0x%x, %!STATUS!",
            Config->Size, sizeof(WDF_USB_DEVICE_CREATE_CONFIG), status);

        return status;
    }

    return FxUsbTargetDeviceCreate(pFxDriverGlobals,
                                   pDevice,
                                   Config->USBDClientContractVersion,
                                   Attributes,
                                   UsbDevice);
}

_Must_inspect_result_
__drv_maxIRQL(DISPATCH_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceRetrieveInformation)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __out
    PWDF_USB_DEVICE_INFORMATION Information
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, Information);

    if (Information->Size != sizeof(WDF_USB_DEVICE_INFORMATION)) {
        status = STATUS_INFO_LENGTH_MISMATCH;
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                            "Information size %d, expected %d %!STATUS!",
                            Information->Size, sizeof(WDF_USB_DEVICE_INFORMATION),
                            status);
        return status;
    }

    pUsbDevice->GetInformation(Information);

    return STATUS_SUCCESS;
}

__drv_maxIRQL(PASSIVE_LEVEL)
VOID
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceGetDeviceDescriptor)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __out
    PUSB_DEVICE_DESCRIPTOR UsbDeviceDescriptor
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, UsbDeviceDescriptor);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return;
    }

    pUsbDevice->CopyDeviceDescriptor(UsbDeviceDescriptor);
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceRetrieveConfigDescriptor)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __out_bcount_part_opt(*ConfigDescriptorLength, *ConfigDescriptorLength)
    PVOID ConfigDescriptor,
    __inout
    PUSHORT ConfigDescriptorLength
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, ConfigDescriptorLength);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    return pUsbDevice->GetConfigDescriptor(ConfigDescriptor,
                                           ConfigDescriptorLength);
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceQueryString)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in_opt
    WDFREQUEST Request,
    __in_opt
    PWDF_REQUEST_SEND_OPTIONS RequestOptions,
    __out_ecount_opt(*NumCharacters)
    PUSHORT String,
    __inout
    PUSHORT NumCharacters,
    __in
    UCHAR StringIndex,
    __in_opt
    USHORT LangID
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, NumCharacters);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = FxValidateRequestOptions(pFxDriverGlobals, RequestOptions);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = pUsbDevice->GetString(String,
                                   NumCharacters,
                                   StringIndex,
                                   LangID,
                                   Request,
                                   RequestOptions);

    return status;
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceAllocAndQueryString)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES StringMemoryAttributes,
    __out
    WDFMEMORY*   StringMemory,
    __out_opt
    PUSHORT NumCharacters,
    __in
    UCHAR StringIndex,
    __in_opt
    USHORT LangID
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    WDFMEMORY hMemory;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;
    USHORT numChars = 0;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, StringMemory);

    *StringMemory = NULL;
    if (NumCharacters != NULL) {
        *NumCharacters = 0;
    }

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = FxValidateObjectAttributes(pFxDriverGlobals, StringMemoryAttributes);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = pUsbDevice->GetString(NULL, &numChars, StringIndex, LangID);

    if (NT_SUCCESS(status) && numChars > 0) {
        FxMemoryObject* pBuffer;

        status = FxMemoryObject::_Create(pFxDriverGlobals,
                                         StringMemoryAttributes,
                                         NonPagedPool,
                                         pFxDriverGlobals->Tag,
                                         numChars * sizeof(WCHAR),
                                         &pBuffer);

        if (!NT_SUCCESS(status)) {
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        status = pBuffer->Commit(StringMemoryAttributes,
                                 (WDFOBJECT*)&hMemory);

        if (NT_SUCCESS(status)) {
            status = pUsbDevice->GetString((PUSHORT) pBuffer->GetBuffer(),
                                           &numChars,
                                           StringIndex,
                                           LangID);

            if (NT_SUCCESS(status)) {
                if (NumCharacters != NULL) {
                    *NumCharacters  = numChars;
                }
                *StringMemory = hMemory;
            }
        }

        if (!NT_SUCCESS(status)) {
            //
            // There can only be one context on this object right now,
            // so just clear out the one.
            //
            pBuffer->DeleteFromFailedCreate();
        }
    }

    return status;
}

_Must_inspect_result_
__drv_maxIRQL(DISPATCH_LEVEL)
NTSTATUS
WDFEXPORT(WdfUsbTargetDeviceFormatRequestForString)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in
    WDFREQUEST Request,
    __in
    WDFMEMORY Memory,
    __in_opt
    PWDFMEMORY_OFFSET Offset,
    __in
    UCHAR StringIndex,
    __in_opt
    USHORT LangID
    )
/*++

Routine Description:
    Formats a request so that it can be used to query for a string  from the
    device.

Arguments:
    UsbDevice - device to be queried

    Request - request to format

    Memory - memory to write the string into


Return Value:


  --*/

{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    IFxMemory* pMemory;
    FxUsbDevice* pUsbDevice;
    FxRequestBuffer buf;
    FxRequest* pRequest;
    NTSTATUS status;
    size_t bufferSize;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    DoTraceLevelMessage(
        pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
        "WDFUSBDEVICE %p, WDFREQUEST %p, WDFMEMORY %p, StringIndex %d, LandID 0x%x",
        UsbDevice, Request, Memory, StringIndex, LangID);

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         Memory,
                         IFX_TYPE_MEMORY,
                         (PVOID*) &pMemory);

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         Request,
                         FX_TYPE_REQUEST,
                         (PVOID*) &pRequest);

    status = pMemory->ValidateMemoryOffsets(Offset);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    buf.SetMemory(pMemory, Offset);

    bufferSize = buf.GetBufferLength();

    //
    // the string descriptor is array of WCHARs so the buffer being used must be
    // of an integral number of them.
    //
    if ((bufferSize % sizeof(WCHAR)) != 0) {
        status = STATUS_INVALID_PARAMETER;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
            "WDFMEMORY %p length must be even number of WCHARs, but is %I64d in "
            "length, %!STATUS!",
            Memory, bufferSize, status);

        return status;
    }

    //
    // While the length in the descriptor (bLength) is a byte, so the requested
    // buffer cannot be bigger then that, do not check the bufferSize < 0xFF.
    // We don't check because the driver can be using the WDFMEMORY as a part
    // of a larger structure.
    //

    //
    // Format the request
    //
    status = pUsbDevice->FormatStringRequest(pRequest, &buf, StringIndex, LangID);

    if (NT_SUCCESS(status)) {
        FxUsbDeviceStringContext* pContext;

        pContext = (FxUsbDeviceStringContext*) pRequest->GetContext();
        pContext->m_UsbParameters.Parameters.DeviceString.Buffer = Memory;
        pContext->m_UsbParameters.Parameters.DeviceString.StringIndex = StringIndex;
        pContext->m_UsbParameters.Parameters.DeviceString.LangID = LangID;
    }

    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                        "WDFUSBDEVICE %p, WDFREQUEST %p, WDFMEMORY %p, %!STATUS!",
                        UsbDevice, Request, Memory, status);

    return status;
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceSelectConfig)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES PipesAttributes,
    __inout
    PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxPointerNotNull(pFxDriverGlobals, Params);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    if (Params->Size != sizeof(WDF_USB_DEVICE_SELECT_CONFIG_PARAMS)) {
        status = STATUS_INFO_LENGTH_MISMATCH;
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                            "Params size %d, expected %d %!STATUS!",
                            Params->Size, sizeof(WDF_USB_DEVICE_SELECT_CONFIG_PARAMS),
                            status);
        return status;
    }

    //
    // Sanity check the Type value as well to be in range.
    //
    if (Params->Type < WdfUsbTargetDeviceSelectConfigTypeDeconfig
        ||
        Params->Type > WdfUsbTargetDeviceSelectConfigTypeUrb) {

        status = STATUS_INVALID_PARAMETER;

        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                            "Params Type %d not a valid value, %!STATUS!",
                            Params->Size, status);

        return status;
    }

#if (FX_CORE_MODE == FX_CORE_USER_MODE)
    if ((Params->Type == WdfUsbTargetDeviceSelectConfigTypeDeconfig) ||
        (Params->Type == WdfUsbTargetDeviceSelectConfigTypeUrb) ||
        (Params->Type == WdfUsbTargetDeviceSelectConfigTypeInterfacesDescriptor)) {
        status = STATUS_NOT_SUPPORTED;

        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                            "Params Type %d not supported for UMDF, %!STATUS!",
                            Params->Type, status);

        return status;

    }
#endif

    status = FxValidateObjectAttributes(pFxDriverGlobals,
                                        PipesAttributes,
                                        FX_VALIDATE_OPTION_PARENT_NOT_ALLOWED);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    if (pUsbDevice->HasMismatchedInterfacesInConfigDescriptor()) {
        //
        // Config descriptor reported zero interfaces, but we found an
        // interface descriptor in it.
        //
        status = STATUS_INVALID_DEVICE_REQUEST;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
            "WDFUSBDEVICE %p number of interfaces found in the config descriptor "
            "does not match bNumInterfaces in config descriptor, failing config "
            "operation %!WdfUsbTargetDeviceSelectConfigType!, %!STATUS!",
            UsbDevice, Params->Type, status);

        return status;
    }
    else if (pUsbDevice->GetNumInterfaces() == 0) {
        //
        // Special case the zero interface case and exit early
        //
        status = STATUS_SUCCESS;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
            "WDFUSBDEVICE %p succeeding config operation "
            "%!WdfUsbTargetDeviceSelectConfigType! on zero interfaces "
            "immediately, %!STATUS!", UsbDevice, Params->Type, status);

        return status;
    }

    switch (Params->Type) {

#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)

    case WdfUsbTargetDeviceSelectConfigTypeDeconfig:
        status = pUsbDevice->Deconfig();
        break;

    case WdfUsbTargetDeviceSelectConfigTypeInterfacesDescriptor:
        if (Params->Types.Descriptor.InterfaceDescriptors == NULL ||
            Params->Types.Descriptor.NumInterfaceDescriptors == 0) {
            status = STATUS_INVALID_PARAMETER;
            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                "Either  InterfaceDescriptor is NULL or  NumInterfaceDescriptors is zero "
                "WDFUSBDEVICE %p InterfaceDescriptor %p NumInterfaceDescriptors 0x%x"
                "%!WdfUsbTargetDeviceSelectConfigType! %!STATUS!", UsbDevice,
                Params->Types.Descriptor.InterfaceDescriptors,
                Params->Types.Descriptor.NumInterfaceDescriptors,
                Params->Type,
                status);

        }
        else {
            status = pUsbDevice->SelectConfigDescriptor(
                PipesAttributes,
                Params);
        }
        break;

    case WdfUsbTargetDeviceSelectConfigTypeUrb:
        //
        // Since the USBD macro's dont include the  USBD_PIPE_INFORMATION for 0 EP's,
        // make the length check to use
        // sizeof(struct _URB_SELECT_CONFIGURATION) - sizeof(USBD_PIPE_INFORMATION)
        //
        if (Params->Types.Urb.Urb == NULL ||
            Params->Types.Urb.Urb->UrbHeader.Function != URB_FUNCTION_SELECT_CONFIGURATION ||
            Params->Types.Urb.Urb->UrbHeader.Length <
               (sizeof(struct _URB_SELECT_CONFIGURATION) - sizeof(USBD_PIPE_INFORMATION))) {
            status = STATUS_INVALID_PARAMETER;
            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                "Either URB passed in was NULL or the URB Function or Length was invalid "
                " WDFUSBDEVICE %p Urb 0x%p "
                "%!WdfUsbTargetDeviceSelectConfigType!"
                " %!STATUS!", UsbDevice,
                Params->Types.Urb.Urb,
                Params->Type,
                status);

        }
        else {
            status = pUsbDevice->SelectConfig(
                                              PipesAttributes,
                                              Params->Types.Urb.Urb,
                                              FxUrbTypeLegacy,
                                              NULL);
        }
        break;

#endif





    case WdfUsbTargetDeviceSelectConfigTypeInterfacesPairs:
        if (Params->Types.MultiInterface.Pairs == NULL) {
            status = STATUS_INVALID_PARAMETER;

            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                "WDFUSBDEVICE %p SettingPairs Array passed is NULL, %!STATUS!",
                pUsbDevice->GetHandle(),status);

            break;
        }
        else if (Params->Types.MultiInterface.NumberInterfaces !=
                                        pUsbDevice->GetNumInterfaces()) {
            status = STATUS_INVALID_PARAMETER;

            DoTraceLevelMessage(
                pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                "WDFUSBDEVICE %p MultiInterface.NumberInterfaces %d != %d "
                "(reported num interfaces), %!STATUS!",
                pUsbDevice->GetHandle(),
                Params->Types.MultiInterface.NumberInterfaces,
                pUsbDevice->GetNumInterfaces(), status);

            break;
        }

        //    ||      ||   Fall through     ||        ||
        //    \/      \/                    \/        \/
    case WdfUsbTargetDeviceSelectConfigTypeMultiInterface:

        //
        // Validate SettingIndexes passed-in
        //
        for (ULONG i = 0;
             i < Params->Types.MultiInterface.NumberInterfaces;
             i++) {

            PWDF_USB_INTERFACE_SETTING_PAIR  pair;
            FxUsbInterface * pUsbInterface;
            UCHAR numSettings;

            pair = &(Params->Types.MultiInterface.Pairs[i]);

            FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                                 pair->UsbInterface,
                                 FX_TYPE_USB_INTERFACE,
                                 (PVOID*) &pUsbInterface);

            numSettings = pUsbInterface->GetNumSettings();

            if (pair->SettingIndex >= numSettings) {
                status = STATUS_INVALID_PARAMETER;

                DoTraceLevelMessage(
                    pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                    "WDFUSBDEVICE %p SettingPairs contains invalid SettingIndex"
                    " for WDFUSBINTERFACE %p. Setting index passed in: %d, "
                    "max index: %d, returning %!STATUS!",
                    pUsbDevice->GetHandle(),
                    pair->UsbInterface,
                    pair->SettingIndex,
                    pUsbInterface->GetNumSettings() - 1,
                    status);

                return status;
            }
        }

        status = pUsbDevice->SelectConfigMulti(
            PipesAttributes,
            Params);
        break;

    case WdfUsbTargetDeviceSelectConfigTypeSingleInterface:
        status = pUsbDevice->SelectConfigSingle(   //vm changed name from  SelectConfigAuto
            PipesAttributes,
            Params);
        break;

    default:
        status = STATUS_INVALID_PARAMETER;
    }

    return status;
}


__drv_maxIRQL(DISPATCH_LEVEL)
UCHAR
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceGetNumInterfaces)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice
    )
{
    DDI_ENTRY();

    FxUsbDevice* pUsbDevice;

    FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                         UsbDevice,
                         FX_TYPE_IO_TARGET_USB_DEVICE,
                         (PVOID*) &pUsbDevice);

    return pUsbDevice->GetNumInterfaces();
}


__drv_maxIRQL(DISPATCH_LEVEL)
USBD_CONFIGURATION_HANDLE
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceWdmGetConfigurationHandle)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice
    )
{
    DDI_ENTRY();

    FxUsbDevice* pUsbDevice;

    FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
                         UsbDevice,
                         FX_TYPE_IO_TARGET_USB_DEVICE,
                         (PVOID*) &pUsbDevice);

    return pUsbDevice->GetConfigHandle();
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFEXPORT(WdfUsbTargetDeviceSendControlTransferSynchronously)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in_opt
    WDFREQUEST Request,
    __in_opt
    PWDF_REQUEST_SEND_OPTIONS RequestOptions,
    __in
    PWDF_USB_CONTROL_SETUP_PACKET SetupPacket,
    __in_opt
    PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,
    __out_opt
    PULONG BytesTransferred
    )
/*++

Routine Description:
    Synchronously sends a control transfer to the default control pipe on the
    device.

Arguments:
    UsbDevice - the target representing the device

    Request - Request whose PIRP to use

    RequestOptions - options to use when sending the request

    SetupPacket - control setup packet to be used in the transfer

    MemoryDescriptor - memory to use in the transfer after the setup packet

    BytesTransferred - number of bytes sent to or by the device

Return Value:
    NTSTATUS

  --*/
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;
    FxRequestBuffer buf;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    FxUsbDeviceControlContext context(FxUrbTypeLegacy);

    FxSyncRequest request(pFxDriverGlobals, &context, Request);

    //
    // FxSyncRequest always succeesds for KM but can fail for UM.
    //
    status = request.Initialize();
    if (!NT_SUCCESS(status)) {
        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
                            "Failed to initialize FxSyncRequest");
        return status;
    }

    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                        "WDFUSBDEVICE %p control transfer sync", UsbDevice);

    FxPointerNotNull(pFxDriverGlobals, SetupPacket);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = FxValidateRequestOptions(pFxDriverGlobals, RequestOptions);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = buf.ValidateMemoryDescriptor(
        pFxDriverGlobals,
        MemoryDescriptor,
        MemoryDescriptorNullAllowed | MemoryDescriptorNoBufferAllowed
        );

    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = pUsbDevice->FormatControlRequest(request.m_TrueRequest,
                                              SetupPacket,
                                              &buf);

    if (NT_SUCCESS(status)) {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
            "WDFUSBDEVICE %p, WDFREQUEST %p being submitted",
            UsbDevice, request.m_TrueRequest->GetTraceObjectHandle());

        status = pUsbDevice->SubmitSync(request.m_TrueRequest, RequestOptions);

        if (BytesTransferred != NULL) {
            if (NT_SUCCESS(status)) {
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
                *BytesTransferred = context.m_Urb->TransferBufferLength;
#elif (FX_CORE_MODE == FX_CORE_USER_MODE)
                *BytesTransferred = context.m_UmUrb.UmUrbControlTransfer.TransferBufferLength;
#endif
            }
            else {
                *BytesTransferred = 0;
            }
        }
    }

    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                        "WDFUSBDEVICE %p, %!STATUS!", UsbDevice, status);

    return status;
}

_Must_inspect_result_
__drv_maxIRQL(DISPATCH_LEVEL)
NTSTATUS
WDFEXPORT(WdfUsbTargetDeviceFormatRequestForControlTransfer)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in
    WDFREQUEST Request,
    __in
    PWDF_USB_CONTROL_SETUP_PACKET SetupPacket,
    __in_opt
    WDFMEMORY TransferMemory,
    __in_opt
    PWDFMEMORY_OFFSET TransferOffset
    )
/*++

Routine Description:
    Formats a request so that a control transfer can be sent to the device
    after this call returns successfully.

Arguments:
    UsbDevice - the target representing the device

    Request - Request to format

    SetupPacket - control setup packet to be used in the transfer

    TransferMemory - memory to use in the transfer after the setup packet

    TransferOffset - offset into TransferMemory and size override for transfer
                     length

Return Value:
    NTSTATUS

  --*/
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    IFxMemory* pMemory;
    FxUsbDevice* pUsbDevice;
    FxRequestBuffer buf;
    FxRequest* pRequest;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                        "WDFUSBDEVICE %p, WDFREQUEST %p, WDFMEMORY %p",
                        UsbDevice, Request, TransferMemory);

    FxPointerNotNull(pFxDriverGlobals, SetupPacket);

    if (TransferMemory != NULL) {
        FxObjectHandleGetPtr(pFxDriverGlobals,
                             TransferMemory,
                             IFX_TYPE_MEMORY,
                             (PVOID*) &pMemory);

        status = pMemory->ValidateMemoryOffsets(TransferOffset);
        if (!NT_SUCCESS(status)) {
            return status;
        }

        buf.SetMemory(pMemory, TransferOffset);
    }
    else {
        pMemory = NULL;
    }

    FxObjectHandleGetPtr(pFxDriverGlobals,
                         Request,
                         FX_TYPE_REQUEST,
                         (PVOID*) &pRequest);

    status = pUsbDevice->FormatControlRequest(pRequest, SetupPacket, &buf);

    if (NT_SUCCESS(status)) {
        FxUsbDeviceControlContext* pContext;

        pContext = (FxUsbDeviceControlContext*) pRequest->GetContext();

        RtlCopyMemory(
            &pContext->m_UsbParameters.Parameters.DeviceControlTransfer.SetupPacket,
            SetupPacket,
            sizeof(*SetupPacket)
            );

        if (pMemory != NULL) {
            pContext->m_UsbParameters.Parameters.DeviceControlTransfer.Buffer =
                TransferMemory;
        }
    }

    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
                        "format control request WDFUSBDEVICE %p, WDFREQWUEST %p, WDFMEMORY %p, %!STATUS!",
                        UsbDevice, Request, TransferMemory, status);

    return status;
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceResetPortSynchronously)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice
    )
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = pUsbDevice->Reset();

    return status;
}

#pragma warning(disable:28285)
__checkReturn
__drv_maxIRQL(DISPATCH_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceCreateIsochUrb)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in_opt
    PWDF_OBJECT_ATTRIBUTES Attributes,
    __in
    ULONG NumberOfIsochPackets,
    __out
    WDFMEMORY* UrbMemory,
    __deref_opt_out_bcount(GET_ISOCH_URB_SIZE(NumberOfIsochPackets))
    PURB* Urb
    )
/*++

Routine Description:
    Creates a WDFUSBDEVICE handle for the client.

Arguments:
    Attributes - Attributes associated with this object

    NumberOfIsochPacket - Maximum number of Isoch packets that will be programmed into this Urb

    UrbMemory - The returned handle to the caller for the allocated Urb

    Urb - (opt) Pointer to the associated urb buffer.

Return Value:
    STATUS_INVALID_PARAMETER - any required parameters are not present/invalid

    STATUS_INVALID_DEVICE_STATE - If the client did not specify a client contract verion while
        creating the WDFUSBDEVICE

    STATUS_INSUFFICIENT_RESOURCES - could not allocated the object that backs
        the handle

    STATUS_SUCCESS - success

    ...

  --*/
{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    NTSTATUS status;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    //
    // Basic parameter validation
    //
    FxPointerNotNull(pFxDriverGlobals, UrbMemory);

    if (pUsbDevice->GetUSBDHandle() == NULL) {
        status = STATUS_INVALID_DEVICE_STATE;

        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
            "USBDEVICE Must have been created with Client Contract Verion Info, %!STATUS!",
            status);

        return status;
    }

    status = pUsbDevice->CreateIsochUrb(Attributes,
                                        NumberOfIsochPackets,
                                        UrbMemory,
                                        Urb);

    return status;
}

__drv_maxIRQL(DISPATCH_LEVEL)
WDFUSBINTERFACE
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceGetInterface)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __in
    UCHAR InterfaceIndex
    )
/*++

Routine Description:


Arguments:

Return Value:


  --*/

{
    DDI_ENTRY();

    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;
    FxUsbInterface *pUsbInterface;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    pUsbInterface = pUsbDevice->GetInterfaceFromIndex(InterfaceIndex);

    if (pUsbInterface != NULL) {
        return pUsbInterface->GetHandle();
    }
    else {
        DoTraceLevelMessage(
            pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
            "WDFUSBDEVICE %p has %d interfaces, index %d requested, returning "
            "NULL handle",
            UsbDevice, pUsbDevice->GetNumInterfaces(), InterfaceIndex);

        return NULL;
    }
}

_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
WDFAPI
WDFEXPORT(WdfUsbTargetDeviceQueryUsbCapability)(
    __in
    PWDF_DRIVER_GLOBALS DriverGlobals,
    __in
    WDFUSBDEVICE UsbDevice,
    __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
    )
/*++

Routine Description:
    Queries USB capability for the device. Such capabilities are available
    only with USB 3.0 stack. On earlier stacks this API will fail with
    STATUS_NOT_IMPLEMENTED

Arguments:
    UsbDevice - Device whose capability is to be queried.

    CapabilityType - Type of capability as defined by
                     IOCTL_INTERNAL_USB_GET_USB_CAPABILITY

    CapabilityBufferLength - Length of Capability buffer

    CapabilityBuffer - Buffer for capability. Can be NULL if
                       CapabilitiyBufferLength is 0

    ResultLength - Actual length of the capability. This parameter is optional.

Return Value:
    STATUS_SUCCESS - success
    STATUS_NOT_IMPLEMENTED - Capabilties are not supported by USB stack
    STATUS_INSUFFICIENT_RESOURCES - no memory available
    ...

  --*/
{
    DDI_ENTRY();

    NTSTATUS status;
    PFX_DRIVER_GLOBALS pFxDriverGlobals;
    FxUsbDevice* pUsbDevice;

    FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
                                   UsbDevice,
                                   FX_TYPE_IO_TARGET_USB_DEVICE,
                                   (PVOID*) &pUsbDevice,
                                   &pFxDriverGlobals);

    if (CapabilityBufferLength > 0) {
        FxPointerNotNull(pFxDriverGlobals, CapabilityBuffer);
    }

    status = pUsbDevice->QueryUsbCapability(CapabilityType,
                                            CapabilityBufferLength,
                                            CapabilityBuffer,
                                            ResultLength);
    return status;
}

} // extern "C"