Sample Code

Windows Driver Samples/ Usbsamp Generic USB Driver/ C++/ sys/ device.c/

/*++

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    Device.c

Abstract:

    Bulk USB device driver for Intel 82930 USB test board
    Plug and Play module. This file contains routines to handle pnp requests.

Environment:

    Kernel mode

--*/

#include "private.h"

#ifdef ALLOC_PRAGMA

#pragma alloc_text(PAGE, UsbSamp_EvtDeviceAdd)
#pragma alloc_text(PAGE, UsbSamp_EvtDevicePrepareHardware)
#pragma alloc_text(PAGE, UsbSamp_EvtDeviceContextCleanup)
#pragma alloc_text(PAGE, ReadAndSelectDescriptors)
#pragma alloc_text(PAGE, ConfigureDevice)
#pragma alloc_text(PAGE, SelectInterfaces)
#pragma alloc_text(PAGE, SetPowerPolicy)
#pragma alloc_text(PAGE, AbortPipes)
#pragma alloc_text(PAGE, ReadFdoRegistryKeyValue)
#pragma alloc_text(PAGE, RetrieveDeviceInformation)
#pragma alloc_text(PAGE, UsbSamp_ValidateConfigurationDescriptor)
#if (NTDDI_VERSION >= NTDDI_WIN8)
#pragma alloc_text(PAGE, UsbSamp_EvtPipeContextCleanup)
#pragma alloc_text(PAGE, InitializePipeContextForSuperSpeedDevice)
#pragma alloc_text(PAGE, GetEndpointDescriptorForEndpointAddress)
#pragma alloc_text(PAGE, InitializePipeContextForSuperSpeedIsochPipe)
#endif
#pragma alloc_text(PAGE, InitializePipeContextForHighSpeedDevice)
#pragma alloc_text(PAGE, InitializePipeContextForFullSpeedDevice)
#endif

NTSTATUS
UsbSamp_EvtDeviceAdd(
    WDFDRIVER        Driver,
    PWDFDEVICE_INIT  DeviceInit
    )
/*++
Routine Description:

    EvtDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager. We create and initialize a device object to
    represent a new instance of the device. All the software resources
    should be allocated in this callback.

Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

--*/
{
    WDF_FILEOBJECT_CONFIG     fileConfig;
    WDF_PNPPOWER_EVENT_CALLBACKS        pnpPowerCallbacks;
    WDF_OBJECT_ATTRIBUTES               attributes;
    NTSTATUS                            status;
    WDFDEVICE                           device;
    WDF_DEVICE_PNP_CAPABILITIES         pnpCaps;
    WDF_IO_QUEUE_CONFIG                 ioQueueConfig;
    PDEVICE_CONTEXT                     pDevContext;
    WDFQUEUE                            queue;
    ULONG                               maximumTransferSize;

    UNREFERENCED_PARAMETER(Driver);

    UsbSamp_DbgPrint (3, ("UsbSamp_EvtDeviceAdd routine\n"));

    PAGED_CODE();

    //
    // Initialize the pnpPowerCallbacks structure.  Callback events for PNP
    // and Power are specified here.  If you don't supply any callbacks,
    // the Framework will take appropriate default actions based on whether
    // DeviceInit is initialized to be an FDO, a PDO or a filter device
    // object.
    //

    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

    pnpPowerCallbacks.EvtDevicePrepareHardware = UsbSamp_EvtDevicePrepareHardware;

    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    //
    // Initialize the request attributes to specify the context size and type
    // for every request created by framework for this device.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT);

    WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes);

    //
    // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
    // framework whether you are interested in handle Create, Close and
    // Cleanup requests that gets genereate when an application or another
    // kernel component opens an handle to the device. If you don't register
    // the framework default behaviour would be complete these requests
    // with STATUS_SUCCESS. A driver might be interested in registering these
    // events if it wants to do security validation and also wants to maintain
    // per handle (fileobject) context.
    //

    WDF_FILEOBJECT_CONFIG_INIT(
        &fileConfig,
        UsbSamp_EvtDeviceFileCreate,
        WDF_NO_EVENT_CALLBACK,
        WDF_NO_EVENT_CALLBACK
        );

    //
    // Specify a context for FileObject. If you register FILE_EVENT callbacks,
    // the framework by default creates a framework FILEOBJECT corresponding
    // to the WDM fileobject. If you want to track any per handle context,
    // use the context for FileObject. Driver that typically use FsContext
    // field should instead use Framework FileObject context.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FILE_CONTEXT);

    WdfDeviceInitSetFileObjectConfig(DeviceInit,
                                       &fileConfig,
                                       &attributes);

#if !defined(BUFFERED_READ_WRITE)
    //
    // I/O type is Buffered by default. We want to do direct I/O for Reads
    // and Writes so set it explicitly. Please note that this sample
    // can do isoch transfer only if the io type is directio.
    //
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);

#endif

    //
    // Now specify the size of device extension where we track per device
    // context.DeviceInit is completely initialized. So call the framework
    // to create the device and attach it to the lower stack.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
    attributes.EvtCleanupCallback = UsbSamp_EvtDeviceContextCleanup;

    status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfDeviceCreate failed with Status code 0x%x\n", status));
        return status;
    }

    //
    // Get the DeviceObject context by using accessor function specified in
    // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for DEVICE_CONTEXT.
    //

    pDevContext = GetDeviceContext(device);

    //
    //Get MaximumTransferSize from registry
    //
    maximumTransferSize = 0;

    ReadFdoRegistryKeyValue(Driver,
                              L"MaximumTransferSize",
                              &maximumTransferSize);

    if (maximumTransferSize){
        pDevContext->MaximumTransferSize = maximumTransferSize;
    }
    else {
        pDevContext->MaximumTransferSize = DEFAULT_REGISTRY_TRANSFER_SIZE;
    }

    //
    // Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so
    // that you don't get the popup in usermode (on Win2K) when you surprise
    // remove the device.
    //
    WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
    pnpCaps.SurpriseRemovalOK = WdfTrue;

    WdfDeviceSetPnpCapabilities(device, &pnpCaps);

    //
    // Register I/O callbacks to tell the framework that you are interested
    // in handling WdfRequestTypeRead, WdfRequestTypeWrite, and 
    // IRP_MJ_DEVICE_CONTROL requests.
    // WdfIoQueueDispatchParallel means that we are capable of handling
    // all the I/O request simultaneously and we are responsible for protecting
    // data that could be accessed by these callbacks simultaneously.
    // This queue will be,  by default,  automanaged by the framework with
    // respect to PNP and Power events. That is, framework will take care
    // of queuing, failing, dispatching incoming requests based on the current
    // pnp/power state of the device.
    //

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
                                           WdfIoQueueDispatchParallel);

    ioQueueConfig.EvtIoRead = UsbSamp_EvtIoRead;
    ioQueueConfig.EvtIoWrite = UsbSamp_EvtIoWrite;
    ioQueueConfig.EvtIoDeviceControl = UsbSamp_EvtIoDeviceControl;
    ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop;

    status = WdfIoQueueCreate(device,
                              &ioQueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &queue);// pointer to default queue
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed  for Default Queue 0x%x\n", status));
        return status;
    }

    //
    // Create a synchronized manual queue so we can retrieve one read request at a
    // time and dispatch it to the lower driver with the right StartFrame number.
    //
    WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
                              WdfIoQueueDispatchManual);
    
     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
     attributes.SynchronizationScope=WdfSynchronizationScopeQueue;
     
     ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop;
    
     status = WdfIoQueueCreate(device,
                               &ioQueueConfig,
                               &attributes,
                               &pDevContext->IsochReadQueue);
     if (!NT_SUCCESS(status)) {
         UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed  for isoch 0x%x\n", status));
         return status;
     }

    //
    // Register a ready notification routine so we get notified whenever the queue transitions
    // from empty to non-empty state.
    //
    status = WdfIoQueueReadyNotify(pDevContext->IsochReadQueue,
                                   UsbSamp_EvtIoQueueReadyNotification,
                                   (WDFCONTEXT)pDevContext);
    
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfIoQueueReadyNotify failed  for isoch 0x%x\n", status));
        return status;
    }

    //
    // Create a synchronized manual queue so we can retrieve one write request at a
    // time and dispatch it to the lower driver with the right StartFrame number.
    //
    WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
                              WdfIoQueueDispatchManual);
    
     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
     attributes.SynchronizationScope=WdfSynchronizationScopeQueue;
     
     ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop;
    
     status = WdfIoQueueCreate(device,
                               &ioQueueConfig,
                               &attributes,
                               &pDevContext->IsochWriteQueue);
     if (!NT_SUCCESS(status)) {
         UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed  for isoch 0x%x\n", status));
         return status;
     }

    //
    // Register a ready notification routine so we get notified whenever the queue transitions
    // from empty to non-empty state.
    //
    status = WdfIoQueueReadyNotify(pDevContext->IsochWriteQueue,
                                   UsbSamp_EvtIoQueueReadyNotification,
                                   (WDFCONTEXT)pDevContext);
    
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfIoQueueReadyNotify failed  for isoch 0x%x\n", status));
        return status;
    }     
     
    //
    // Register a device interface so that app can find our device and talk to it.
    //
    status = WdfDeviceCreateDeviceInterface(device,
                        (LPGUID) &GUID_CLASS_USBSAMP_USB,
                        NULL);// Reference String
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfDeviceCreateDeviceInterface failed  0x%x\n", status));
        return status;
    }

    status = USBD_CreateHandle(WdfDeviceWdmGetDeviceObject(device),    
                               WdfDeviceWdmGetAttachedDevice(device),   
                               USBD_CLIENT_CONTRACT_VERSION_602,   
                               POOL_TAG,   
                               &pDevContext->UsbdHandle);   
    if(!NT_SUCCESS(status)){
        UsbSamp_DbgPrint(1, ("USBD_CreateHandle failed 0x%x", status));
        return status;
    }

    UsbSamp_DbgPrint(3, ("EvtDriverDeviceAdd - ends\n"));

    return status;
}

NTSTATUS
UsbSamp_EvtDevicePrepareHardware(
    _In_ WDFDEVICE Device,
    _In_ WDFCMRESLIST ResourceList,
    _In_ WDFCMRESLIST ResourceListTranslated
    )
/*++

Routine Description:

    In this callback, the driver does whatever is necessary to make the
    hardware ready to use.  In the case of a USB device, this involves
    reading and selecting descriptors.

    //TODO:

Arguments:

    Device - handle to a device

Return Value:

    NT status value

--*/
{
    NTSTATUS                    status;
    PDEVICE_CONTEXT             pDeviceContext;

    UNREFERENCED_PARAMETER(ResourceList);
    UNREFERENCED_PARAMETER(ResourceListTranslated);

    UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - begins\n"));

    PAGED_CODE();

    pDeviceContext = GetDeviceContext(Device);

    //
    // Read the device descriptor, configuration descriptor
    // and select the interface descriptors
    //
    status = ReadAndSelectDescriptors(Device);

    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("ReadandSelectDescriptors failed\n"));
        return status;
    }

    //
    // Enable wait-wake and idle timeout if the device supports it
    //
    if (pDeviceContext->WaitWakeEnable){
        status = SetPowerPolicy(Device);
        if (!NT_SUCCESS (status)) {
            UsbSamp_DbgPrint(3, ("UsbSampSetPowerPolicy failed\n"));
            return status;
        }
    }

    UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - ends\n"));

    return status;
}

NTSTATUS
SetPowerPolicy(
    _In_ WDFDEVICE Device
    )
{
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
    NTSTATUS    status = STATUS_SUCCESS;

    PAGED_CODE();

    //
    // Init the idle policy structure.
    //
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IDLE_CAPS_TYPE);
    idleSettings.IdleTimeout = 10000; // 10-sec

    status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
    if ( !NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(3, ("WdfDeviceSetPowerPolicyS0IdlePolicy failed  0x%x\n", status));
        return status;
    }

    //
    // Init wait-wake policy structure.
    //
    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);

    status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(3, ("WdfDeviceAssignSxWakeSettings failed  0x%x\n", status));
        return status;
    }

    return status;
}


NTSTATUS
ReadAndSelectDescriptors(
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:

    This routine configures the USB device.
    In this routines we get the device descriptor,
    the configuration descriptor and select the
    configuration.

Arguments:

    Device - Handle to a framework device

Return Value:

    NTSTATUS - NT status value.

--*/
{
    NTSTATUS               status;
    PDEVICE_CONTEXT        pDeviceContext;

    PAGED_CODE();

    //
    // initialize variables
    //
    pDeviceContext = GetDeviceContext(Device);

    //
    // Create a USB device handle so that we can communicate with the
    // underlying USB stack. The WDFUSBDEVICE handle is used to query,
    // configure, and manage all aspects of the USB device.
    // These aspects include device properties, bus properties,
    // and I/O creation and synchronization. We only create device the first
    // the PrepareHardware is called. If the device is restarted by pnp manager
    // for resource rebalance, we will use the same device handle but then select
    // the interfaces again because the USB stack could reconfigure the device on
    // restart.
    //
    if (pDeviceContext->WdfUsbTargetDevice == NULL) {
        WDF_USB_DEVICE_CREATE_CONFIG config;
        
        WDF_USB_DEVICE_CREATE_CONFIG_INIT(&config,
                                   USBD_CLIENT_CONTRACT_VERSION_602);
        
        status = WdfUsbTargetDeviceCreateWithParameters(Device,
                                              &config,
                                              WDF_NO_OBJECT_ATTRIBUTES,
                                              &pDeviceContext->WdfUsbTargetDevice);
        if (!NT_SUCCESS(status)) {
            UsbSamp_DbgPrint(1, ("WdfUsbTargetDeviceCreateWithParameters failed with Status code %x\n", status));
            return status;
        }
    }
    
    WdfUsbTargetDeviceGetDeviceDescriptor(pDeviceContext->WdfUsbTargetDevice,
                                    &pDeviceContext->UsbDeviceDescriptor);

    NT_ASSERT(pDeviceContext->UsbDeviceDescriptor.bNumConfigurations);

    status = ConfigureDevice(Device);
    
    return status;
}

NTSTATUS
ConfigureDevice(
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:

    This helper routine reads the configuration descriptor
    for the device in couple of steps.

Arguments:

    Device - Handle to a framework device

Return Value:

    NTSTATUS - NT status value

--*/
{
    USHORT                        size = 0;
    NTSTATUS                      status;
    PDEVICE_CONTEXT               pDeviceContext;
    PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
    WDF_OBJECT_ATTRIBUTES attributes;
    WDFMEMORY   memory;
    PUCHAR Offset = NULL;

    PAGED_CODE();

    //
    // initialize the variables
    //
    configurationDescriptor = NULL;
    pDeviceContext = GetDeviceContext(Device);

    //
    // Read the first configuration descriptor
    // This requires two steps:
    // 1. Ask the WDFUSBDEVICE how big it is
    // 2. Allocate it and get it from the WDFUSBDEVICE
    //
    status = WdfUsbTargetDeviceRetrieveConfigDescriptor(pDeviceContext->WdfUsbTargetDevice,
                                               NULL,
                                               &size);

    if (status != STATUS_BUFFER_TOO_SMALL || size == 0) {
        return status;
    }

    //
    // Create a memory object and specify usbdevice as the parent so that
    // it will be freed automatically.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

    attributes.ParentObject = pDeviceContext->WdfUsbTargetDevice;

    status = WdfMemoryCreate(&attributes,
                             NonPagedPool,
                             POOL_TAG,
                             size,
                             &memory,
                             &configurationDescriptor);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    status = WdfUsbTargetDeviceRetrieveConfigDescriptor(pDeviceContext->WdfUsbTargetDevice,
                                               configurationDescriptor,
                                               &size);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Check if the descriptors are valid
    // 
    status = UsbSamp_ValidateConfigurationDescriptor(configurationDescriptor, size , &Offset);
    
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("Descriptor validation failed with Status code %x and at the offset %p\n", status , Offset ));
        return status;
    }

    pDeviceContext->UsbConfigurationDescriptor = configurationDescriptor;

    status = SelectInterfaces(Device);

    return status;
}

NTSTATUS
SelectInterfaces(
    _In_ WDFDEVICE Device
    )
/*++

Routine Description:

    This helper routine selects the configuration, interface and
    creates a context for every pipe (end point) in that interface.

Arguments:

    Device - Handle to a framework device

Return Value:

    NT status value

--*/
{
    WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams;
    NTSTATUS                            status;
    PDEVICE_CONTEXT                     pDeviceContext;
    UCHAR                               i;
    WDF_OBJECT_ATTRIBUTES               pipeAttributes;
    WDF_USB_INTERFACE_SELECT_SETTING_PARAMS selectSettingParams;
    UCHAR                               numberAlternateSettings = 0;
    UCHAR                               numberConfiguredPipes;

    PAGED_CODE();

    pDeviceContext = GetDeviceContext(Device);

    //
    // The device has only one interface and the interface may have multiple
    // alternate settings. It will try to use alternate setting zero if it has
    // non-zero endpoints, otherwise it will try to search an alternate 
    // setting with non-zero endpoints.
    // 

    WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams);

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pipeAttributes, PIPE_CONTEXT);

#if (NTDDI_VERSION >= NTDDI_WIN8)
    pipeAttributes.EvtCleanupCallback = UsbSamp_EvtPipeContextCleanup;
#endif

    status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->WdfUsbTargetDevice,
                                        &pipeAttributes,
                                        &configParams);


    if (NT_SUCCESS(status) &&
        WdfUsbTargetDeviceGetNumInterfaces(pDeviceContext->WdfUsbTargetDevice) > 0) {

        status = RetrieveDeviceInformation(Device);
        if (!NT_SUCCESS(status)) {
            UsbSamp_DbgPrint(1, ("RetrieveDeviceInformation failed %x\n", status));
            return status;
        }

        pDeviceContext->UsbInterface =
            configParams.Types.SingleInterface.ConfiguredUsbInterface;

        //
        // This is written to work with Intel 82930 board, OSRUSBFX2, FX2 MUTT and FX3 MUTT
        // devices. The alternate setting zero of MUTT devices don't have any endpoints. So
        // in the code below, we will walk through the list of alternate settings until we
        // find one that has non-zero endpoints.
        //

        numberAlternateSettings = WdfUsbInterfaceGetNumSettings(pDeviceContext->UsbInterface);

        NT_ASSERT(numberAlternateSettings > 0);

        numberConfiguredPipes = 0;

        for (i = 0; i < numberAlternateSettings && numberConfiguredPipes == 0; i++) {
 
            WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_SETTING(&selectSettingParams, i);

            WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pipeAttributes, PIPE_CONTEXT);

#if (NTDDI_VERSION >= NTDDI_WIN8)
            pipeAttributes.EvtCleanupCallback = UsbSamp_EvtPipeContextCleanup;
#endif

            status = WdfUsbInterfaceSelectSetting(pDeviceContext->UsbInterface,
                                                  &pipeAttributes,
                                                  &selectSettingParams
                                                  );
 
            if (NT_SUCCESS(status)) {
             
                numberConfiguredPipes = WdfUsbInterfaceGetNumConfiguredPipes(pDeviceContext->UsbInterface);

                if (numberConfiguredPipes > 0){
                
                    pDeviceContext->SelectedAlternateSetting = i;
                
                }
            
            }
         
        }

        pDeviceContext->NumberConfiguredPipes = numberConfiguredPipes;

        for (i = 0; i < pDeviceContext->NumberConfiguredPipes; i++) {
            WDFUSBPIPE pipe;

            pipe =  WdfUsbInterfaceGetConfiguredPipe(pDeviceContext->UsbInterface,
                                                    i, //PipeIndex,
                                                    NULL
                                                    );
#if (NTDDI_VERSION >= NTDDI_WIN8)
            if (pDeviceContext->IsDeviceSuperSpeed) {
                status = InitializePipeContextForSuperSpeedDevice(pDeviceContext,
                                                                  pipe);
            }
            else if (pDeviceContext->IsDeviceHighSpeed) {
                status = InitializePipeContextForHighSpeedDevice(pipe);
            } 
            else {
                status = InitializePipeContextForFullSpeedDevice(pipe);
            }
#else
            if (pDeviceContext->IsDeviceHighSpeed) {
                status = InitializePipeContextForHighSpeedDevice(pipe);
            } 
            else {
                status = InitializePipeContextForFullSpeedDevice(pipe);
            }
#endif
            if (!NT_SUCCESS(status)) {
                UsbSamp_DbgPrint(1, ("InitializePipeContext failed %x\n", status));
                break;        
            }
        }

    }

    return status;
}


NTSTATUS
AbortPipes(
    _In_ WDFDEVICE Device
    )
/*++

Routine Description

    sends an abort pipe request on all open pipes.

Arguments:

    Device - Handle to a framework device

Return Value:

    NT status value

--*/
{
    UCHAR              i;
    ULONG              count;
    NTSTATUS           status;
    PDEVICE_CONTEXT    pDevContext;

    PAGED_CODE();

    //
    // initialize variables
    //
    pDevContext = GetDeviceContext(Device);

    UsbSamp_DbgPrint(3, ("AbortPipes - begins\n"));

    count = pDevContext->NumberConfiguredPipes;

    for (i = 0; i < count; i++) {
        WDFUSBPIPE pipe;
        pipe = WdfUsbInterfaceGetConfiguredPipe(pDevContext->UsbInterface,
                                                i, //PipeIndex,
                                                NULL
                                                );

        UsbSamp_DbgPrint(3, ("Aborting open pipe %d\n", i));

        status = WdfUsbTargetPipeAbortSynchronously(pipe,
                                    WDF_NO_HANDLE, // WDFREQUEST
                                    NULL);//PWDF_REQUEST_SEND_OPTIONS

        if (!NT_SUCCESS(status)) {
            UsbSamp_DbgPrint(1, ("WdfUsbTargetPipeAbortSynchronously failed %x\n", status));
            break;
        }
    }

    UsbSamp_DbgPrint(3, ("AbortPipes - ends\n"));

    return STATUS_SUCCESS;
}


#if (NTDDI_VERSION >= NTDDI_WIN8)
NTSTATUS
InitializePipeContextForSuperSpeedDevice(
    _In_ PDEVICE_CONTEXT            DeviceContext,
    _In_ WDFUSBPIPE                 Pipe
    )
/*++

Routine Description

    This function initialize pipe context for super speed isoch and
    bulk endpoints.

Return Value:

    NT status value

--*/
{
    WDF_USB_PIPE_INFORMATION    pipeInfo;
    NTSTATUS                    status = STATUS_SUCCESS;

    PAGED_CODE();

    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo);
    
    //
    // We only use pipe context for super speed isoch and bulk speed bulk endpoints.
    //
    if ((WdfUsbPipeTypeIsochronous == pipeInfo.PipeType)) {

        status = InitializePipeContextForSuperSpeedIsochPipe(DeviceContext,
                    WdfUsbInterfaceGetInterfaceNumber(DeviceContext->UsbInterface),
                    Pipe);

    } else if (WdfUsbPipeTypeBulk == pipeInfo.PipeType) {

        status = InitializePipeContextForSuperSpeedBulkPipe(DeviceContext,
                   WdfUsbInterfaceGetInterfaceNumber(DeviceContext->UsbInterface),
                   Pipe);

    }
    
    return status;
     
}


PUSB_ENDPOINT_DESCRIPTOR
GetEndpointDescriptorForEndpointAddress(
    _In_ PDEVICE_CONTEXT  DeviceContext,
    _In_ UCHAR            InterfaceNumber,
    _In_ UCHAR            EndpointAddress,
    _Out_ PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR *ppEndpointCompanionDescriptor
    )
/*++

Routine Description:

    The helper routine gets the Endpoint Descriptor matched with EndpointAddress and return
    its Endpoint Companion Descriptor if it has.

    UsbSamp_ValidateConfigurationDescriptor already validates that descriptors lie within
    allocated buffer.

Arguments:

    DeviceContext - pointer to the device context which includes configuration descriptor

    InterfaceNumber - InterfaceNumber of selected interface

    EndpointAddress - EndpointAddress of the Pipe

    ppEndpointCompanionDescriptor - pointer to the Endpoint Companioin Descroptor pointer

Return Value:

    Pointer to Endpoint Descriptor

--*/
{

    PUSB_COMMON_DESCRIPTOR                          pCommonDescriptorHeader      = NULL;
    PUSB_CONFIGURATION_DESCRIPTOR                   pConfigurationDescriptor     = NULL;
    PUSB_INTERFACE_DESCRIPTOR                       pInterfaceDescriptor         = NULL;
    PUSB_ENDPOINT_DESCRIPTOR                        pEndpointDescriptor          = NULL;
    PUCHAR                                          startingPosition;
    ULONG                                           index;
    BOOLEAN                                         found                        = FALSE;

    PAGED_CODE();

    pConfigurationDescriptor = DeviceContext->UsbConfigurationDescriptor;

    *ppEndpointCompanionDescriptor = NULL;

    // 
    // Parse the ConfigurationDescriptor (including all Interface and
    // Endpoint Descriptors) and locate a Interface Descriptor which
    // matches the InterfaceNumber, AlternateSetting, InterfaceClass,
    // InterfaceSubClass, and InterfaceProtocol parameters.  
    //
    pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
                                pConfigurationDescriptor,
                                pConfigurationDescriptor,
                                InterfaceNumber,
                                DeviceContext->SelectedAlternateSetting,
                                -1, // InterfaceClass, don't care
                                -1, // InterfaceSubClass, don't care
                                -1  // InterfaceProtocol, don't care
                                );

    if (pInterfaceDescriptor == NULL ) {

        UsbSamp_DbgPrint(1, ("USBD_ParseConfigurationDescriptorEx failed to retrieve Interface Descriptor.\n"));
        goto End;

    }

    startingPosition = (PUCHAR) pInterfaceDescriptor;

    for(index = 0; index < pInterfaceDescriptor->bNumEndpoints; index++) {
    
        pCommonDescriptorHeader = USBD_ParseDescriptors(pConfigurationDescriptor,
                                                        pConfigurationDescriptor->wTotalLength,
                                                        startingPosition,
                                                        USB_ENDPOINT_DESCRIPTOR_TYPE);

        if (pCommonDescriptorHeader == NULL) {

            UsbSamp_DbgPrint(1, ("USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Descriptor unexpectedly\n"));
            goto End;
        
        }

        //
        // UsbSamp_ValidateConfigurationDescriptor validates all descriptors.
        // This means that the descriptor pointed to by pCommonDescriptorHeader( received above ) is completely 
        // contained within the buffer representing ConfigurationDescriptor and
        // it also verifies that pCommonDescriptorHeader->bLength is equal to sizeof(USB_ENDPOINT_DESCRIPTOR).
        //

        pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDescriptorHeader;

        //
        // Search an Endpoint Descriptor that matches the EndpointAddress
        //
        if (pEndpointDescriptor->bEndpointAddress == EndpointAddress){
        
            found = TRUE;

            break;
        
        }

        //
        // Skip the current Endpoint Descriptor and search for the next.
        //
        startingPosition = (PUCHAR)pCommonDescriptorHeader + pCommonDescriptorHeader->bLength;
    
    }

    if (found) {
        //
        // Locate the SuperSpeed Endpoint Companion Descriptor associated with the endpoint descriptor
        //
        pCommonDescriptorHeader = USBD_ParseDescriptors(pConfigurationDescriptor,
                                                        pConfigurationDescriptor->wTotalLength,
                                                        pEndpointDescriptor,
                                                        USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE);

        if (pCommonDescriptorHeader != NULL) {
            
            //
            // UsbSamp_ValidateConfigurationDescriptor validates all descriptors.
            // This means that the descriptor pointed to by pCommonDescriptorHeader( received above ) is completely 
            // contained within the buffer representing ConfigurationDescriptor and
            // it also verifies that pCommonDescriptorHeader->bLength is >= sizeof(USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR)
            //
                    
            *ppEndpointCompanionDescriptor = 
                (PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) pCommonDescriptorHeader;
                
        } else {
        
             UsbSamp_DbgPrint(3, ("USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Companion Descriptor unexpectedly\n"));
        
        }

    } 

 
End:
    return pEndpointDescriptor;
}


NTSTATUS
InitializePipeContextForSuperSpeedIsochPipe(
    _In_ PDEVICE_CONTEXT    DeviceContext,
    _In_ UCHAR              InterfaceNumber,
    _In_ WDFUSBPIPE         Pipe
    )
/*++

Routine Description

    This function validates all the isoch related fields in the endpoint descriptor
    to make sure it's in comformance with the spec and Microsoft core stack
    implementation and intializes the pipe context.

    The TransferSizePerMicroframe and TransferSizePerFrame values will be
    used in the I/O path to do read and write transfers.

Return Value:

    NT status value

-*/
{
    WDF_USB_PIPE_INFORMATION    pipeInfo;
    PPIPE_CONTEXT               pipeContext;    
    UCHAR                       endpointAddress;
    PUSB_ENDPOINT_DESCRIPTOR    pEndpointDescriptor;
    PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR pEndpointCompanionDescriptor;
    USHORT                      wMaxPacketSize;
    UCHAR                       bMaxBurst;
    UCHAR                       bMult;
    USHORT                      wBytesPerInterval;


    PAGED_CODE();

    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo);
    
    //
    // We use the pipe context only for isoch endpoints.
    //
    if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) {

        return STATUS_SUCCESS;

    }

    pipeContext = GetPipeContext(Pipe);

    endpointAddress = pipeInfo.EndpointAddress;

    pEndpointDescriptor = GetEndpointDescriptorForEndpointAddress(
                                DeviceContext,
                                InterfaceNumber,
                                endpointAddress,
                                &pEndpointCompanionDescriptor);

    if (pEndpointDescriptor == NULL || pEndpointCompanionDescriptor == NULL ){
        
        UsbSamp_DbgPrint(1, ("pEndpointDescriptor or pEndpointCompanionDescriptor is invalid (NULL)\n"));
        return STATUS_INVALID_PARAMETER;
    
    }

    //
    // For SuperSpeed isoch endpoint, it uses wBytesPerInterval from its
    // endpoint companion descriptor. If bMaxBurst field in its endpoint
    // companion descriptor is greater than zero, wMaxPacketSize must be
    // 1024. If the value in the bMaxBurst field is set to zero then 
    // wMaxPacketSize can have any value from 0 to 1024.
    //
    wBytesPerInterval = pEndpointCompanionDescriptor->wBytesPerInterval;
    wMaxPacketSize = pEndpointDescriptor->wMaxPacketSize;
    bMaxBurst = pEndpointCompanionDescriptor->bMaxBurst;
    bMult = pEndpointCompanionDescriptor->bmAttributes.Isochronous.Mult;

    if (wBytesPerInterval > (wMaxPacketSize * (bMaxBurst + 1) * (bMult + 1))){
                        
        UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints's wBytesPerInterval value (%d) is greater than wMaxPacketSize * (bMaxBurst+1) * (Mult +1) (%d) \n",
                                wBytesPerInterval, (wMaxPacketSize * (bMaxBurst + 1) * (bMult + 1))));
        return STATUS_INVALID_PARAMETER;

    }

    if (bMaxBurst > 0){

        if (wMaxPacketSize != USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE){              
                    
            UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints must have wMaxPacketSize value of %d bytes when bMaxpBurst is %d \n",
                                    USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE, bMaxBurst));
            return STATUS_INVALID_PARAMETER;

        } 

    } else {

        if (wMaxPacketSize > USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE){

            UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints must have wMaxPacketSize value no more than %d bytes when bMaxpBurst is %d \n",
                                    USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE, bMaxBurst));
            return STATUS_INVALID_PARAMETER;

        } 

    }

 
    //
    // This sample demos how to use wBytesPerInterval from its Endpoint 
    // Companion Descriptor. Actaully, for Superspeed isochronous endpoints,
    // MaximumPacketSize in WDF_USB_PIPE_INFORMATION and USBD_PIPE_INFORMATION
    // is returned with the value of wBytesPerInterval in the endpoint
    // companion descriptor. This is different than the true MaxPacketSize of
    // the endpoint descriptor.
    //
    NT_ASSERT(pipeInfo.MaximumPacketSize == wBytesPerInterval); 
    pipeContext->TransferSizePerMicroframe = wBytesPerInterval;
         
    //
    // Microsoft USB 3.0 stack only supports bInterval value of 1, 2, 3 and 4 
    // (or polling period of 1, 2, 4 and 8).
    // For super-speed isochronous endpoints, the bInterval value is used as
    // the exponent for a 2^(bInterval-1) value expressed in microframes;
    // e.g., a bInterval of 4 means a period of 8 (2^(4-1)) microframes.
    //
    if (pipeInfo.Interval == 0 || pipeInfo.Interval > 4) {

        UsbSamp_DbgPrint(1, ("bInterval value in pipeInfo is invalid (0 or > 4)\n"));
        return STATUS_INVALID_PARAMETER;

    }

    switch (pipeInfo.Interval) {
    case 1:
        //
        // Transfer period is every microframe (8 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 8;
        break;

    case 2:
        //
        // Transfer period is every 2 microframes (4 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 4;
        break;

    case 3:
        //
        // Transfer period is every 4 microframes (2 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 2;
        break;

    case 4:
        //
        // Transfer period is every 8 microframes (1 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe;
        break;
    }

    UsbSamp_DbgPrint(1, ("MaxPacketSize = %d, bInterval = %d\n", 
                       pipeInfo.MaximumPacketSize,
                       pipeInfo.Interval));

    UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d, TransferSizePerMicroframe = %d\n", 
                       pipeContext->TransferSizePerFrame, 
                       pipeContext->TransferSizePerMicroframe));        
    
    return STATUS_SUCCESS;
}

#endif

NTSTATUS
InitializePipeContextForHighSpeedDevice(
    _In_ WDFUSBPIPE Pipe
    )
/*++

Routine Description

    This function validates all the isoch related fields in the endpoint descriptor
    to make sure it's in comformance with the spec and Microsoft core stack
    implementation and intializes the pipe context.

    The TransferSizePerMicroframe and TransferSizePerFrame values will be
    used in the I/O path to do read and write transfers.

Return Value:

    NT status value

--*/
{
    WDF_USB_PIPE_INFORMATION    pipeInfo;
    PPIPE_CONTEXT               pipeContext;

    PAGED_CODE();

    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo);
    
    //
    // We use the pipe context only for isoch endpoints.
    //
    if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) {
        return STATUS_SUCCESS;
    }
    
    pipeContext = GetPipeContext(Pipe);
   
    if (pipeInfo.MaximumPacketSize == 0) {
        UsbSamp_DbgPrint(1, ("MaximumPacketSize in the pipeInfo is invalid (zero)\n"));
        return STATUS_INVALID_PARAMETER;
    }
    
    //
    // Universal Serial Bus Specification Revision 2.0 5.6.3 Isochronous Transfer 
    // Packet Size Constraints: High-speed endpoints are allowed up to 1024-byte data
    // payloads per microframe and allowed up to a maximum of 3 transactions per microframe.
    //
    // For highspeed isoch endpoints, bits 12-11 of wMaxPacketSize in the endpoint descriptor
    // specify the number of additional transactions oppurtunities per microframe.
    // 00 - None (1 transaction per microframe)
    // 01 - 1 additional (2 per microframe)
    // 10 - 2 additional (3 per microframe)
    // 11 - Reserved.
    //
    // Note: MaximumPacketSize of WDF_USB_PIPE_INFORMATION is already adjusted to include 
    // additional transactions if it is a high bandwidth pipe.
    //

    if (pipeInfo.MaximumPacketSize > 1024 * 3) {
        UsbSamp_DbgPrint(1, ("MaximumPacketSize in the endpoint descriptor is invalid (>1024*3)\n"));
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Microsoft USB stack only supports bInterval value of 1, 2, 3 and 4 (or polling period of 1, 2, 4 and 8).
    //    
    if (pipeInfo.Interval == 0 || pipeInfo.Interval > 4) {
        UsbSamp_DbgPrint(1, ("bInterval value in pipeInfo is invalid (0 or > 4)\n"));
        return STATUS_INVALID_PARAMETER;
    }

    pipeContext->TransferSizePerMicroframe = pipeInfo.MaximumPacketSize;

    //
    // For high-speed isochronous endpoints, the bInterval value is used
    // as the exponent for a 2^(bInterval-1) value expressed in
    // microframes; e.g., a bInterval of 4 means a period of 8 (2^(4-1))
    // microframes. The bInterval value must be from 1 to 16.  NOTE: The
    // USBPORT.SYS driver only supports high-speed isochronous bInterval
    // values of {1, 2, 3, 4}.
    //
    switch (pipeInfo.Interval) {
    case 1:
        //
        // Transfer period is every microframe (8 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 8;
        break;

    case 2:
        //
        // Transfer period is every 2 microframes (4 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 4;
        break;

    case 3:
        //
        // Transfer period is every 4 microframes (2 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 2;
        break;

    case 4:
        //
        // Transfer period is every 8 microframes (1 times a frame).
        //
        pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe;
        break;
    }

    UsbSamp_DbgPrint(1, ("MaxPacketSize = %d, bInterval = %d\n", 
                       pipeInfo.MaximumPacketSize,
                       pipeInfo.Interval));

    UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d, TransferSizePerMicroframe = %d\n", 
                       pipeContext->TransferSizePerFrame, 
                       pipeContext->TransferSizePerMicroframe));        
    
    return STATUS_SUCCESS;
}

NTSTATUS
InitializePipeContextForFullSpeedDevice(
    _In_ WDFUSBPIPE Pipe
    )
/*++

Routine Description

    This function validates all the isoch related fields in the endpoint descriptor
    to make sure it's in comformance with the spec and Microsoft core stack
    implementation and intializes the pipe context.

    The TransferSizePerMicroframe and TransferSizePerFrame values will be
    used in the I/O path to do read and write transfers.

Return Value:

    NT status value

--*/
{
    WDF_USB_PIPE_INFORMATION    pipeInfo;
    PPIPE_CONTEXT               pipeContext;

    PAGED_CODE();

    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo);
    
    //
    // We use the pipe context only for isoch endpoints.
    //
    if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) {
        return STATUS_SUCCESS;
    }
    
    pipeContext = GetPipeContext(Pipe);

    if (pipeInfo.MaximumPacketSize == 0) {
        UsbSamp_DbgPrint(1, ("MaximumPacketSize in the endpoint descriptor is invalid\n"));
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Universal Serial Bus Specification Revision 2.0
    // 5.6.3 Isochronous Transfer Packet Size Constraints
    //
    // The USB limits the maximum data payload size to 1,023 bytes
    // for each full-speed isochronous endpoint.
    //
    if (pipeInfo.MaximumPacketSize > 1023) {
        UsbSamp_DbgPrint(1, ("MaximumPacketSize in the endpoint descriptor is invalid\n"));
        return STATUS_INVALID_PARAMETER;
    }

    //
    // Microsoft USB stack only supports bInterval value of 1 for
    // full-speed isochronous endpoints.
    //    
    if (pipeInfo.Interval != 1) {
        UsbSamp_DbgPrint(1, ("bInterval value in endpoint descriptor is invalid\n"));
        return STATUS_INVALID_PARAMETER;
    }

    pipeContext->TransferSizePerFrame = pipeInfo.MaximumPacketSize;
    pipeContext->TransferSizePerMicroframe = 0;

    UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d\n", pipeContext->TransferSizePerFrame));        
    
    return STATUS_SUCCESS;
}

NTSTATUS
RetrieveDeviceInformation(
    _In_ WDFDEVICE Device
    )
{
    PDEVICE_CONTEXT             pDeviceContext;
    WDF_USB_DEVICE_INFORMATION  info;
    NTSTATUS                    status;
    USHORT                      numberOfStreams = 0;
    PAGED_CODE();

    pDeviceContext = GetDeviceContext(Device);

    WDF_USB_DEVICE_INFORMATION_INIT(&info);

    //
    // Retrieve USBD version information, port driver capabilites and device
    // capabilites such as speed, power, etc.
    //
    status = WdfUsbTargetDeviceRetrieveInformation(pDeviceContext->WdfUsbTargetDevice,
                                                   &info);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    pDeviceContext->IsDeviceHighSpeed =
        (info.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? TRUE : FALSE;

    UsbSamp_DbgPrint(3, ("DeviceIsHighSpeed: %s\n",
                 pDeviceContext->IsDeviceHighSpeed ? "TRUE" : "FALSE"));

    UsbSamp_DbgPrint(3, ("IsDeviceSelfPowered: %s\n",
        (info.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE"));

    pDeviceContext->WaitWakeEnable =
                        info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;

    UsbSamp_DbgPrint(3, ("IsDeviceRemoteWakeable: %s\n",
        (info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE) ? "TRUE" : "FALSE"));

    status = GetStackCapability(Device,
                                &GUID_USB_CAPABILITY_DEVICE_CONNECTION_SUPER_SPEED_COMPATIBLE,
                                0,
                                NULL);
    if (NT_SUCCESS(status)) {
        pDeviceContext->IsDeviceSuperSpeed = TRUE;
    }

    UsbSamp_DbgPrint(3, ("DeviceIsSuperSpeed: %s\n",
                 pDeviceContext->IsDeviceSuperSpeed ? "TRUE" : "FALSE"));

    if (pDeviceContext->IsDeviceSuperSpeed == TRUE) {
        status = GetStackCapability(Device,
                                    &GUID_USB_CAPABILITY_STATIC_STREAMS,
                                    sizeof(numberOfStreams),
                                    (PUCHAR)&numberOfStreams);
        if (NT_SUCCESS(status)) {
            pDeviceContext->IsStaticStreamsSupported = TRUE;
            pDeviceContext->NumberOfStreamsSupportedByController = numberOfStreams;
        }

        if (pDeviceContext->IsStaticStreamsSupported) {
             UsbSamp_DbgPrint(3, ("Number of Streams supported by the controller:  %d\n", numberOfStreams));
        } 
    }

    return STATUS_SUCCESS;
}

    
NTSTATUS
ReadFdoRegistryKeyValue(
    _In_  WDFDRIVER   Driver,
    _In_ LPWSTR      Name,
    _Out_ PULONG      Value
    )
/*++

Routine Description:

    Can be used to read any REG_DWORD registry value stored
    under Device Parameter.

Arguments:

    Driver - pointer to the device object
    Name - Name of the registry value
    Value -


Return Value:

    NTSTATUS 

--*/
{
    WDFKEY      hKey = NULL;
    NTSTATUS    status;
    UNICODE_STRING  valueName;

    UNREFERENCED_PARAMETER(Driver);

    PAGED_CODE();

    *Value = 0;

    status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
                                                KEY_READ,
                                                WDF_NO_OBJECT_ATTRIBUTES,
                                                &hKey);

    if (NT_SUCCESS (status)) {

        RtlInitUnicodeString(&valueName,Name);

        status = WdfRegistryQueryULong (hKey,
                                  &valueName,
                                  Value);

        WdfRegistryClose(hKey);
    }

    return status;
}

VOID
UsbSamp_EvtDeviceContextCleanup(
    IN WDFOBJECT WdfDevice
   )
/*++

Routine Description:

    In this callback, it cleans up device context.

Arguments:

    WdfDevice - WDF device object

Return Value:

    NULL

--*/
{
    WDFDEVICE device;
    PDEVICE_CONTEXT	pDevContext;

    PAGED_CODE();

    device = (WDFDEVICE)WdfDevice;

    pDevContext = GetDeviceContext(device);

    if (pDevContext->UsbdHandle != NULL) {
        USBD_CloseHandle(pDevContext->UsbdHandle);
    }

}

USBD_STATUS
UsbSamp_ValidateConfigurationDescriptor(  
    _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc,
    _In_ ULONG BufferLength,
    _Inout_ PUCHAR *Offset
    )
/*++

Routine Description:

    Validates a USB Configuration Descriptor

Parameters:

    ConfigDesc: Pointer to the entire USB Configuration descriptor returned by 
        the device

    BufferLength: Known size of buffer pointed to by ConfigDesc (Not wTotalLength)

    Offset: if the USBD_STATUS returned is not USBD_STATUS_SUCCESS, offet will
        be set to the address within the ConfigDesc buffer where the failure 
        occured.

Return Value:

    USBD_STATUS
    Success implies the configuration descriptor is valid.

--*/
{
    USBD_STATUS status = USBD_STATUS_SUCCESS;
    PUCHAR offset, end;
    PUCHAR index = NULL;
    USHORT ValidationLevel = 3;

    PAGED_CODE();

    //
    // Call USBD_ValidateConfigurationDescriptor to validate the descriptors which are present in this supplied configuration descriptor.
    // USBD_ValidateConfigurationDescriptor validates that all descriptors are completely contained within the configuration descriptor buffer.
    // It also checks for interface numbers, number of endpoints in an interface etc.
    // Please refer to msdn documentation for this function for more information.
    //
    
    status = USBD_ValidateConfigurationDescriptor( ConfigDesc, BufferLength , ValidationLevel , Offset , POOL_TAG );
    if (!(NT_SUCCESS (status)) ){
        return status;
    }

    //
    // TODO: You should validate the correctness of other descriptors which are not taken care by USBD_ValidateConfigurationDescriptor
    // The below code loops through the configuration descriptor and verifies that all instances of 
    // USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR have a size of >= USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR
    // You should put in more validations as per requirement
    // 
  
   
    offset = ((PUCHAR)ConfigDesc) + ConfigDesc->bLength;
    end = ((PUCHAR)ConfigDesc) + ConfigDesc->wTotalLength;

    while(offset < end){
        PUSB_COMMON_DESCRIPTOR commonDesc;
        //
        // Ensure the descriptor header is in bounds.  We always
        // need to ensure we have enough data to look at the descriptor
        // header fields.  Sometimes descriptors are zeroed at the end,         
        // this is OK.
        //
        commonDesc = (PUSB_COMMON_DESCRIPTOR)offset;

        
        switch(commonDesc->bDescriptorType){

            case USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE:

                //
                // basic validation.
                //
                if(commonDesc->bLength < sizeof(USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR)) {
                    status = USBD_STATUS_BAD_DESCRIPTOR;
                    index = (PUCHAR)commonDesc;
                    goto ValidateConfigurationDescriptor_Done;
                }
                break;

            //
            // TODO: Add validation for any other descriptor your device will return.
            //
            default:
                break;
        }
        offset += commonDesc->bLength;

    }

ValidateConfigurationDescriptor_Done:
    
    if(!USBD_SUCCESS(status)){
        NT_ASSERT(index);
        *Offset = index;
    }
    else{
        *Offset = NULL;
    }
    return status;
}

#if (NTDDI_VERSION >= NTDDI_WIN8)
VOID
UsbSamp_EvtPipeContextCleanup(
    IN WDFOBJECT WdfObject
   )
/*++

Routine Description:

    In this callback, it cleans up pipe context.

Arguments:

    WdfObject - WDFUSBPIPE object

Return Value:

    NULL

--*/
{
    WDFUSBPIPE pipe;
    PPIPE_CONTEXT	pPipeContext = NULL;
    PUSBSAMP_STREAM_INFO  pStreamInfo = NULL;

    PAGED_CODE();

    pipe = (WDFUSBPIPE)WdfObject;
    pPipeContext = GetPipeContext(pipe);

    pPipeContext->StreamConfigured = FALSE;
    pStreamInfo = &pPipeContext->StreamInfo;
    pStreamInfo->NumberOfStreams = 0;
    if(pStreamInfo->StreamList != NULL){
        ExFreePool(pStreamInfo->StreamList);
        pStreamInfo->StreamList = NULL;
    }

}
#endif

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS