Share via


Enumeración de canalizaciones USB

En este artículo se proporciona información general sobre las canalizaciones USB y se describen los pasos que requiere un controlador de cliente USB para obtener identificadores de canalización de la pila de controladores USB.

Un punto de conexión USB es un búfer en el dispositivo del que el controlador cliente envía o recibe datos. Para enviar o recibir datos, el controlador cliente envía una solicitud de transferencia de E/S a la pila de controladores USB, que presenta los datos al controlador host. A continuación, el controlador de host sigue ciertos protocolos (según el tipo de punto de conexión: masivo, interrupción o isócrono) para compilar solicitudes que transfieren datos hacia o desde el dispositivo. Todos los detalles de la transferencia de datos se abstraen del controlador de cliente. Siempre que el controlador cliente envíe una solicitud bien formada, la pila del controlador USB procesa la solicitud y transfiere los datos al dispositivo.

Durante la configuración del dispositivo, la pila del controlador USB crea una canalización USB (en el lado host) para cada uno de los puntos de conexión del dispositivo definidos en la interfaz USB y su configuración alternativa activa. Una canalización USB es un canal de comunicación entre el controlador de host y el punto de conexión. Para el controlador de cliente, una canalización es una abstracción lógica del punto de conexión. Para enviar transferencias de datos, el controlador debe obtener el identificador de canalización asociado al punto de conexión que es el destino de la transferencia. Los identificadores de canalización también son necesarios cuando el controlador quiere anular transferencias o restablecer la canalización, en caso de condiciones de error.

Todos los atributos de una canalización se derivan del descriptor de punto de conexión asociado. Por ejemplo, en función del tipo de punto de conexión, la pila del controlador USB asigna un tipo para la canalización. Para un punto de conexión masivo, la pila del controlador USB crea una canalización masiva; para un punto de conexión isócrono, se crea una canalización isócrónica, etc. Otro atributo importante es la cantidad de datos que el controlador de host puede enviar al punto de conexión en una solicitud. Dependiendo de ese valor, el controlador cliente debe determinar el diseño del búfer de transferencia.

Windows Driver Foundation (WDF) proporciona objetos de destino de E/S especializados en kernel-Mode Driver Framework (KMDF) y User-Mode Driver Framework (UMDF) que simplifican muchas de las tareas de configuración para el controlador cliente. Con esos objetos, el controlador de cliente puede recuperar información sobre la configuración actual, como el número de interfaces, la configuración alternativa dentro de cada interfaz y sus puntos de conexión. Uno de esos objetos, denominado objeto de canalización de destino, realiza tareas relacionadas con el punto de conexión. En este artículo se describe cómo obtener información de canalización mediante el objeto de canalización de destino.

En el caso de los controladores de cliente de Windows Driver Model (WDM), la pila de controladores USB devuelve una matriz de estructuras de USBD_PIPE_INFORMATION . El número de elementos de la matriz depende del número de puntos de conexión definidos para la configuración alternativa activa de una interfaz en la configuración seleccionada. Cada elemento contiene información sobre la canalización creada para un punto de conexión determinado. Para obtener información sobre cómo seleccionar una configuración y obtener la matriz de información de canalización, vea Cómo seleccionar una configuración para un dispositivo USB.

Lo que necesita saber

Antes de que el controlador cliente pueda enumerar canalizaciones, asegúrese de que se cumplen estos requisitos:

Obtención de identificadores de canalización USB en un controlador cliente KMDF

El marco representa cada canalización, abierta por la pila del controlador USB, como un objeto de canalización de destino USB. Un controlador cliente de KMDF puede acceder a los métodos del objeto de canalización de destino para obtener información sobre la canalización. Para realizar transferencias de datos, el controlador cliente debe tener identificadores de canalización WDFUSBPIPE. Para obtener los identificadores de canalización, el controlador debe enumerar las interfaces de la configuración activa y las opciones alternativas y, a continuación, enumerar los puntos de conexión definidos en cada configuración. La realización de operaciones de enumeración, para cada transferencia de datos, puede ser costosa. Por lo tanto, un enfoque consiste en obtener identificadores de canalización después de configurar el dispositivo y almacenarlos en el contexto del dispositivo definido por el controlador. Cuando el controlador recibe solicitudes de transferencia de datos, el controlador puede recuperar los identificadores de canalización necesarios del contexto del dispositivo y usarlos para enviar la solicitud. Si el controlador cliente cambia la configuración del dispositivo, por ejemplo, selecciona una configuración alternativa, el controlador también debe actualizar el contexto del dispositivo con los nuevos identificadores de canalización. De lo contrario, el controlador puede enviar erróneamente solicitudes de transferencia en identificadores de canalización obsoletos.

Los identificadores de canalización no son necesarios para las transferencias de control. Para enviar solicitudes de transferencia de control, un controlador cliente WDF llama a los métodos WdfUsbDevicexxxx expuestos por el objeto de dispositivo de marco. Estos métodos requieren un identificador WDFUSBDEVICE para iniciar transferencias de control destinadas al punto de conexión predeterminado. Para estas transferencias, el destino de E/S para la solicitud es el punto de conexión predeterminado y se representa mediante el identificador WDFIOTARGET, que el identificador WDFUSBPIPE abstrae. En el nivel de dispositivo, el identificador WDFUSBDEVICE es una abstracción del identificador WDFUSBPIPE en el punto de conexión predeterminado.

Para obtener información sobre cómo enviar transferencias de control y los métodos KMDF, vea Cómo enviar una transferencia de control USB.

  1. Amplíe la estructura de contexto del dispositivo para almacenar los identificadores de canalización.

    Si conoce los puntos de conexión del dispositivo, amplíe la estructura de contexto del dispositivo agregando miembros WDFUSBPIPE para almacenar los identificadores de canalización USB asociados. Por ejemplo, puede ampliar la estructura de contexto del dispositivo como se muestra aquí:

    typedef struct _DEVICE_CONTEXT {
        WDFUSBDEVICE    UsbDevice;
        WDFUSBINTERFACE UsbInterface;
        WDFUSBPIPE      BulkReadPipe;   // Pipe opened for the bulk IN endpoint.
        WDFUSBPIPE      BulkWritePipe;  // Pipe opened for the bulk IN endpoint.
        WDFUSBPIPE      InterruptPipe;  // Pipe opened for the interrupt IN endpoint.
        WDFUSBPIPE      StreamInPipe;   // Pipe opened for stream IN endpoint.
        WDFUSBPIPE      StreamOutPipe;  // Pipe opened for stream OUT endpoint.
        UCHAR           NumberConfiguredPipes;  // Number of pipes opened.
        ...
        ...                                     // Other members. Not shown.
    
    } DEVICE_CONTEXT, *PDEVICE_CONTEXT;
    
  2. Declare una estructura de contexto de canalización.

    Cada canalización puede almacenar características relacionadas con el punto de conexión en otra estructura denominada contexto de canalización. De forma similar a un contexto de dispositivo, un contexto de canalización es una estructura de datos (definida por el controlador de cliente) para almacenar información sobre las canalizaciones asociadas a los puntos de conexión. Durante la configuración del dispositivo, el controlador cliente pasa un puntero a su contexto de canalización al marco. El marco asigna un bloque de memoria basado en el tamaño de la estructura y almacena un puntero a esa ubicación de memoria con el objeto de canalización de destino USB de la plataforma. El controlador de cliente puede usar el puntero para acceder a la información de canalización y almacenarla en miembros del contexto de canalización.

    typedef struct _PIPE_CONTEXT {
    
        ULONG MaxPacketSize;
        ULONG MaxStreamsSupported;
        PUSBD_STREAM_INFORMATION StreamInfo;
    } PIPE_CONTEXT, *PPIPE_CONTEXT;
    
    WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PIPE_CONTEXT, GetPipeContext)
    
    

    En este ejemplo, el contexto de canalización almacena el número máximo de bytes que se pueden enviar en una transferencia. El controlador de cliente puede usar ese valor para determinar el tamaño del búfer de transferencia. La declaración también incluye la macro WDF_DECLARE_CONTEXT_TYPE_WITH_NAME , que genera una función insertada, GetPipeContext. El controlador cliente puede llamar a esa función para recuperar un puntero al bloque de memoria que almacena el contexto de canalización.

    Para obtener más información sobre los contextos, vea Espacio de contexto de objetos de marco.

    Para pasar un puntero al marco, el controlador de cliente inicializa primero su contexto de canalización llamando a WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE. A continuación, pasa un puntero al contexto de canalización mientras llama a WdfUsbTargetDeviceSelectConfig (para seleccionar una configuración) o WdfUsbInterfaceSelectSetting (para seleccionar una configuración alternativa).

  3. Una vez completada la solicitud de configuración del dispositivo, enumere la interfaz y obtenga los identificadores de canalización de las canalizaciones configuradas. Necesitará este conjunto de información:

    • Controlador WDFUSBINTERFACE a la interfaz que contiene la configuración actual. Puede obtener ese identificador mediante la enumeración de las interfaces en la configuración activa. Como alternativa, si proporcionó un puntero a una estructura de WDF_USB_DEVICE_SELECT_CONFIG_PARAMS en WdfUsbTargetDeviceSelectConfig, puede obtener el identificador del miembro Type.SingleInterface.ConfiguredUsbInterface (para dispositivos de interfaz única) o el miembro Type.MultiInterface.Pairs.UsbInterface (para el dispositivo de varias interfaces).
    • Número de canalizaciones abiertas para los puntos de conexión en la configuración actual. Puede obtener ese número en una interfaz determinada llamando al método WdfUsbInterfaceGetNumConfiguredPipes .
    • Identificadores WDFUSBPIPE para todas las canalizaciones configuradas. Para obtener el identificador, llame al método WdfUsbInterfaceGetConfiguredPipe .

    Después de obtener el identificador de canalización, el controlador cliente puede llamar a métodos para determinar el tipo y la dirección de la canalización. El controlador puede obtener información sobre el punto de conexión, en una estructura de WDF_USB_PIPE_INFORMATION . El controlador puede obtener la estructura rellenada llamando al método WdfUsbTargetPipeGetInformation . Como alternativa, el controlador puede proporcionar un puntero a la estructura en la llamada WdfUsbInterfaceGetConfiguredPipe .

En el ejemplo de código siguiente se enumeran las canalizaciones en la configuración actual. Obtiene identificadores de canalización para los puntos de conexión masivos e interrupciones del dispositivo y los almacena en la estructura de contexto del dispositivo del controlador. Almacena el tamaño máximo de paquete de cada punto de conexión en el contexto de canalización asociado. Si el punto de conexión admite flujos, abre secuencias estáticas mediante una llamada a la rutina OpenStreams. La implementación de OpenStreams se muestra en Cómo abrir y cerrar secuencias estáticas en un punto de conexión masivo USB.

Para determinar si un punto de conexión masivo determinado admite flujos estáticos, el controlador cliente examina el descriptor de punto de conexión. Ese código se implementa en una rutina auxiliar denominada RetrieveStreamInfoFromEndpointDesc, que se muestra en el siguiente bloque de código.

NTSTATUS
    FX3EnumeratePipes(
    _In_ WDFDEVICE Device)

{
    NTSTATUS                    status;
    PDEVICE_CONTEXT             pDeviceContext;
    UCHAR                       i;
    PPIPE_CONTEXT               pipeContext;
    WDFUSBPIPE                  pipe;
    WDF_USB_PIPE_INFORMATION    pipeInfo;

    PAGED_CODE();

    pDeviceContext = GetDeviceContext(Device);

    // Get the number of pipes in the current alternate setting.
    pDeviceContext->NumberConfiguredPipes = WdfUsbInterfaceGetNumConfiguredPipes(
        pDeviceContext->UsbInterface);

    if (pDeviceContext->NumberConfiguredPipes == 0)
    {
        status = USBD_STATUS_BAD_NUMBER_OF_ENDPOINTS;
        goto Exit;
    }
    else
    {
        status = STATUS_SUCCESS;
    }

    // Enumerate the pipes and get pipe information for each pipe.
    for (i = 0; i < pDeviceContext->NumberConfiguredPipes; i++)
    {
        WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);

        pipe =  WdfUsbInterfaceGetConfiguredPipe(
            pDeviceContext->UsbInterface,
            i,
            &pipeInfo);

        if (pipe == NULL)
        {
            continue;
        }

        pipeContext = GetPipeContext (pipe);

        // If the pipe is a bulk endpoint that supports streams,
        // If the host controller supports streams, open streams.
        // Use the endpoint as an IN bulk endpoint.
        // Store the maximum packet size.

        if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) &&
            WdfUsbTargetPipeIsInEndpoint (pipe))
        {

            // Check if this is a streams IN endpoint. If it is,
            // Get the maximum number of streams and store
            // the value in the pipe context.
            RetrieveStreamInfoFromEndpointDesc (
                Device,
                pipe);

            if ((pipeContext->IsStreamsCapable) &&
                (pipeContext->MaxStreamsSupported > 0))
            {
                status = OpenStreams (
                    Device,
                    pipe);

                if (status != STATUS_SUCCESS)
                {
                    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                        "%!FUNC! Could not open streams.");

                    pDeviceContext->StreamInPipe = NULL;
                }
                else
                {
                    pDeviceContext->StreamInPipe = pipe;
                    pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
                }
            }
            else
            {
                pDeviceContext->BulkReadPipe = pipe;
                pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
            }

            continue;
        }

        if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) &&
            WdfUsbTargetPipeIsOutEndpoint (pipe))
        {
            // Check if this is a streams IN endpoint. If it is,
            // Get the maximum number of streams and store
            // the value in the pipe context.
            RetrieveStreamInfoFromEndpointDesc (
                Device,
                pipe);

            if ((pipeContext->IsStreamsCapable) &&
                (pipeContext->MaxStreamsSupported > 0))
            {
                status = OpenStreams (
                    Device,
                    pipe);

                if (status != STATUS_SUCCESS)
                {
                    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                        "%!FUNC! Could not open streams.");

                    pDeviceContext->StreamOutPipe = NULL;
                }
                else
                {
                    pDeviceContext->StreamOutPipe = pipe;
                    pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
                }
            }
            else
            {
                pDeviceContext->BulkWritePipe = pipe;
                pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
            }

            continue;
        }

        if ((WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) &&
            WdfUsbTargetPipeIsInEndpoint (pipe))
        {
            pDeviceContext->InterruptPipe = pipe;
            pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
            continue;
        }

    }

Exit:
    return status;
}

En el ejemplo de código siguiente se muestra una rutina auxiliar denominada RetrieveStreamInfoFromEndpointDesc, que el controlador cliente llama al enumerar canalizaciones.

En el ejemplo de código siguiente, el controlador cliente llama a la rutina auxiliar anterior, RetrieveStreamInfoFromEndpointDesc, al enumerar canalizaciones. La rutina examina primero el descriptor de configuración y lo analiza para recuperar descriptores de punto de conexión. Si el descriptor de punto de conexión de la canalización contiene un descriptor de USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE, el controlador recupera el número máximo de secuencias admitidas por el punto de conexión.

/*++
Routine Description:

This routine parses the configuration descriptor and finds the endpoint
with which the specified pipe is associated.
It then retrieves the maximum number of streams supported by the endpoint.
It stores maximum number of streams in the pipe context.

Arguments:

Device - WDFUSBDEVICE handle to the target device object.
The driver obtained that handle in a previous call to
WdfUsbTargetDeviceCreateWithParameters.

Pipe - WDFUSBPIPE handle to the target pipe object.

Return Value:

NTSTATUS
++*/

VOID RetrieveStreamInfoFromEndpointDesc (
    WDFDEVICE Device,
    WDFUSBPIPE Pipe)
{
    PDEVICE_CONTEXT                                 deviceContext                = NULL;
    PUSB_CONFIGURATION_DESCRIPTOR                   configDescriptor             = NULL;
    WDF_USB_PIPE_INFORMATION                        pipeInfo;
    PUSB_COMMON_DESCRIPTOR                          pCommonDescriptorHeader      = NULL;
    PUSB_INTERFACE_DESCRIPTOR                       pInterfaceDescriptor         = NULL;
    PUSB_ENDPOINT_DESCRIPTOR                        pEndpointDescriptor          = NULL;
    PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR   pEndpointCompanionDescriptor = NULL;
    ULONG                                           maxStreams;
    ULONG                                           index;
    BOOLEAN                                         found                        = FALSE;
    UCHAR                                           interfaceNumber = 0;
    UCHAR                                           alternateSetting = 1;
    PPIPE_CONTEXT                                   pipeContext = NULL;
    NTSTATUS                                        status;

    PAGED_CODE();

    deviceContext = GetDeviceContext (Device);
    pipeContext = GetPipeContext (Pipe);

    // Get the configuration descriptor of the currently selected configuration
    status = FX3RetrieveConfigurationDescriptor (
        deviceContext->UsbDevice,
        &deviceContext->ConfigurationNumber,
        &configDescriptor);

    if (!NT_SUCCESS (status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Could not retrieve the configuration descriptor.");

        status = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;

        goto Exit;
    }

    if (deviceContext->ConfigurationNumber == 1)
    {
        alternateSetting = 1;
    }
    else
    {
        alternateSetting = 0;
    }

    // Get the Endpoint Address of the pipe
    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation (Pipe, &pipeInfo);

    // 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(
        configDescriptor,
        configDescriptor,
        interfaceNumber,  //Interface number is 0.
        alternateSetting,  // Alternate Setting is 1
        -1, // InterfaceClass, ignore
        -1, // InterfaceSubClass, ignore
        -1  // InterfaceProtocol, ignore
        );

    if (pInterfaceDescriptor == NULL )
    {
        // USBD_ParseConfigurationDescriptorEx failed to retrieve Interface Descriptor.
        goto Exit;
    }

    pCommonDescriptorHeader = (PUSB_COMMON_DESCRIPTOR) pInterfaceDescriptor;

    for(index = 0; index < pInterfaceDescriptor->bNumEndpoints; index++)
    {

        pCommonDescriptorHeader = USBD_ParseDescriptors(
            configDescriptor,
            configDescriptor->wTotalLength,
            pCommonDescriptorHeader,
            USB_ENDPOINT_DESCRIPTOR_TYPE);

        if (pCommonDescriptorHeader == NULL)
        {
            // USBD_ParseDescriptors failed to retrieve Endpoint Descriptor unexpectedly.
            goto Exit;
        }

        pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDescriptorHeader;

        // Search an Endpoint Descriptor that matches the EndpointAddress
        if (pEndpointDescriptor->bEndpointAddress == pipeInfo.EndpointAddress)
        {

            found = TRUE;
            break;
        }

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

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

        if (pCommonDescriptorHeader != NULL)
        {
            pEndpointCompanionDescriptor =
                (PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) pCommonDescriptorHeader;

            maxStreams = pEndpointCompanionDescriptor->bmAttributes.Bulk.MaxStreams;

            if (maxStreams == 0)
            {
                pipeContext->MaxStreamsSupported = 0;
                pipeContext->IsStreamsCapable = FALSE;
            }
            else
            {
                pipeContext->IsStreamsCapable = TRUE;
                pipeContext->MaxStreamsSupported = 1 << maxStreams;
            }
        }
        else
        {
            KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
                "USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Companion Descriptor unexpectedly.\n" ));
        }
    }
    else
    {
        pipeContext->MaxStreamsSupported = 0;
        pipeContext->IsStreamsCapable = FALSE;
    }

Exit:
    if (configDescriptor)
    {
        ExFreePoolWithTag (configDescriptor, USBCLIENT_TAG);
    }

    return;
}

Obtención de identificadores de canalización en un controlador de cliente UMDF

Un controlador de cliente UMDF usa la infraestructura COM e implementa clases de devolución de llamada COM que se emparejan con objetos de dispositivo de marco. De forma similar a un controlador KMDF, un controlador cliente UMDF solo puede obtener información de canalización después de configurar el dispositivo. Para obtener información de canalización, el controlador cliente debe obtener un puntero a la interfaz IWDFUsbTargetPipe del objeto de interfaz de marco que contiene la configuración activa. Mediante el puntero de interfaz, el controlador puede enumerar las canalizaciones de esa configuración para obtener punteros de interfaz IWDFUsbTargetPipe expuestos por los objetos de canalización de destino de la plataforma.

Antes de que el controlador empiece a enumerar las canalizaciones, el controlador debe conocer la configuración del dispositivo y los puntos de conexión admitidos. En función de esa información, el controlador puede almacenar objetos de canalización como variables miembro de clase.

En el ejemplo de código siguiente se amplía la plantilla DE UMDF USB que se proporciona con Visual Studio Professional 2012. Para obtener una explicación del código de inicio, consulte "IPnpCallbackHardware implementación y tareas específicas de USB" en Descripción de la estructura de código del controlador de cliente USB (UMDF).

Amplíe la declaración de clase CDevice como se muestra aquí. En este código de ejemplo se supone que el dispositivo es la placa OSR FX2. Para obtener información sobre su diseño de descriptor, consulte Diseño de dispositivo USB.

class CMyDevice :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IPnpCallbackHardware
{

public:
    DECLARE_NOT_AGGREGATABLE(CMyDevice)

    BEGIN_COM_MAP(CMyDevice)
        COM_INTERFACE_ENTRY(IPnpCallbackHardware)
    END_COM_MAP()

    CMyDevice() :
        m_FxDevice(NULL),
        m_IoQueue(NULL),
        m_FxUsbDevice(NULL)
    {
    }

    ~CMyDevice()
    {
    }

private:
    IWDFDevice *            m_FxDevice;
    CMyIoQueue *            m_IoQueue;
    IWDFUsbTargetDevice *   m_FxUsbDevice;
    IWDFUsbInterface *      m_pIUsbInterface;  //Pointer to the target interface object.
    IWDFUsbTargetPipe *     m_pIUsbInputPipe;  // Pointer to the target pipe object for the bulk IN endpoint.
    IWDFUsbTargetPipe *     m_pIUsbOutputPipe; // Pointer to the target pipe object for the bulk OUT endpoint.
    IWDFUsbTargetPipe *     m_pIUsbInterruptPipe; // Pointer to the target pipe object for the interrupt endpoint.

private:
    HRESULT
    Initialize(
        __in IWDFDriver *FxDriver,
        __in IWDFDeviceInitialize *FxDeviceInit
        );

public:
    static
    HRESULT
    CreateInstanceAndInitialize(
        __in IWDFDriver *FxDriver,
        __in IWDFDeviceInitialize *FxDeviceInit,
        __out CMyDevice **Device
        );

    HRESULT
    Configure(
        VOID
        );

    HRESULT                     // Declare a helper function to enumerate pipes.
    ConfigureUsbPipes(
        );

public:
    // IPnpCallbackHardware methods
    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnPrepareHardware(
            __in IWDFDevice *FxDevice
            );

    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnReleaseHardware(
        __in IWDFDevice *FxDevice
        );

};

En la definición de la clase CDevice, implemente un método auxiliar denominado CreateUsbIoTargets. Se llama a este método desde la implementación IPnpCallbackHardware::OnPrepareHardware después de que el controlador haya obtenido un puntero al objeto de dispositivo de destino.

HRESULT  CMyDevice::CreateUsbIoTargets()
{
    HRESULT                 hr;
    UCHAR                   NumEndPoints = 0;
    IWDFUsbInterface *      pIUsbInterface = NULL;
    IWDFUsbTargetPipe *     pIUsbPipe = NULL;

    if (SUCCEEDED(hr))
    {
        UCHAR NumInterfaces = pIUsbTargetDevice->GetNumInterfaces();

        WUDF_TEST_DRIVER_ASSERT(1 == NumInterfaces);

        hr = pIUsbTargetDevice->RetrieveUsbInterface(0, &pIUsbInterface);
        if (FAILED(hr))
        {
            TraceEvents(TRACE_LEVEL_ERROR,
                        TEST_TRACE_DEVICE,
                        "%!FUNC! Unable to retrieve USB interface from USB Device I/O Target %!HRESULT!",
                        hr
                        );
        }
        else
        {
            m_pIUsbInterface = pIUsbInterface;

            DriverSafeRelease (pIUsbInterface); //release creation reference
        }
     }

    if (SUCCEEDED(hr))
    {
        NumEndPoints = pIUsbInterface->GetNumEndPoints();

        if (NumEndPoints != NUM_OSRUSB_ENDPOINTS)
        {
            hr = E_UNEXPECTED;
            TraceEvents(TRACE_LEVEL_ERROR,
                        TEST_TRACE_DEVICE,
                        "%!FUNC! Has %d endpoints, expected %d, returning %!HRESULT! ",
                        NumEndPoints,
                        NUM_OSRUSB_ENDPOINTS,
                        hr
                        );
        }
    }

    if (SUCCEEDED(hr))
    {
        for (UCHAR PipeIndex = 0; PipeIndex < NumEndPoints; PipeIndex++)
        {
            hr = pIUsbInterface->RetrieveUsbPipeObject(PipeIndex,
                                                  &pIUsbPipe);

            if (FAILED(hr))
            {
                TraceEvents(TRACE_LEVEL_ERROR,
                            TEST_TRACE_DEVICE,
                            "%!FUNC! Unable to retrieve USB Pipe for PipeIndex %d, %!HRESULT!",
                            PipeIndex,
                            hr
                            );
            }
            else
            {
                if ( pIUsbPipe->IsInEndPoint() )
                {
                    if ( UsbdPipeTypeInterrupt == pIUsbPipe->GetType() )
                    {
                        m_pIUsbInterruptPipe = pIUsbPipe;
                    }
                    else if ( UsbdPipeTypeBulk == pIUsbPipe->GetType() )
                    {
                        m_pIUsbInputPipe = pIUsbPipe;
                    }
                    else
                    {
                        pIUsbPipe->DeleteWdfObject();
                    }
                }
                else if ( pIUsbPipe->IsOutEndPoint() && (UsbdPipeTypeBulk == pIUsbPipe->GetType()) )
                {
                    m_pIUsbOutputPipe = pIUsbPipe;
                }
                else
                {
                    pIUsbPipe->DeleteWdfObject();
                }

                DriverSafeRelease(pIUsbPipe);  //release creation reference
            }
        }

        if (NULL == m_pIUsbInputPipe || NULL == m_pIUsbOutputPipe)
        {
            hr = E_UNEXPECTED;
            TraceEvents(TRACE_LEVEL_ERROR,
                        TEST_TRACE_DEVICE,
                        "%!FUNC! Input or output pipe not found, returning %!HRESULT!",
                        hr
                        );
        }
    }

    return hr;
}

En UMDF, el controlador de cliente usa un índice de canalización para enviar solicitudes de transferencia de datos. Un índice de canalización es un número asignado por la pila del controlador USB cuando abre canalizaciones para los puntos de conexión en una configuración. Para obtener el índice de canalización, llame al método**IWDFUsbTargetPipe::GetInformation**. El método rellena una estructura de WINUSB_PIPE_INFORMATION . El valor PipeId indica el índice de canalización.

Una manera de realizar operaciones de lectura y escritura en la canalización de destino es llamar a IWDFUsbInterface::GetWinUsbHandle para obtener un identificador de WinUSB y, a continuación, llamar a funciones de WinUSB. Por ejemplo, el controlador puede llamar a la función WinUsb_ReadPipe o WinUsb_WritePipe . En esas llamadas de función, el controlador debe especificar el índice de canalización. Para obtener más información, vea Cómo acceder a un dispositivo USB mediante funciones de WinUSB.

Identificadores de canalización para controladores de cliente basados en WDM

Una vez seleccionada una configuración, la pila del controlador USB configura una canalización a cada uno de los puntos de conexión del dispositivo. La pila del controlador USB devuelve una matriz de estructuras de USBD_PIPE_INFORMATION . El número de elementos de la matriz depende del número de puntos de conexión definidos para la configuración alternativa activa de una interfaz en la configuración seleccionada. Cada elemento contiene información sobre la canalización creada para un punto de conexión determinado. Para obtener más información sobre cómo obtener identificadores de canalización, vea Cómo seleccionar una configuración para un dispositivo USB.

Para compilar una solicitud de transferencia de E/S, el controlador cliente debe tener un identificador para la canalización asociada a ese punto de conexión. El controlador cliente puede obtener el identificador de canalización del miembro PipeHandle de USBD_PIPE_INFORMATION de la matriz.

Además del identificador de canalización, el controlador cliente también requiere el tipo de canalización. El controlador de cliente puede determinar el tipo de canalización examinando el miembro PipeType .

En función del tipo de punto de conexión, la pila del controlador USB admite distintos tipos de canalizaciones. El controlador de cliente puede determinar el tipo de canalización examinando el miembro PipeType de USBD_PIPE_INFORMATION. Los diferentes tipos de canalización requieren diferentes tipos de bloques de solicitud USB (URB) para realizar transacciones de E/S.

A continuación, el controlador cliente envía el URB a la pila del controlador USB. La pila del controlador USB procesa la solicitud y envía los datos especificados a la canalización de destino solicitada.

El URB contiene información sobre la solicitud, como el identificador de canalización de destino, el búfer de transferencia y su longitud. Cada estructura dentro de la unión URB comparte determinados miembros: TransferFlags, TransferBuffer, TransferBufferLength y TransferBufferMDL. Hay marcas específicas del tipo en el miembro TransferFlags que corresponden a cada tipo URB. Para todas las direcciones URL de transferencia de datos, la marca USBD_TRANSFER_DIRECTION_IN en TransferFlags especifica la dirección de la transferencia. Los controladores de cliente establecen la marca de USBD_TRANSFER_DIRECTION_IN para leer datos del dispositivo. Los controladores borran esta marca para enviar datos al dispositivo. Los datos se pueden leer o escribir en un búfer residente en la memoria o en una MDL. En cualquier caso, el controlador especifica el tamaño del búfer en el miembro TransferBufferLength . El controlador proporciona un búfer residente en el miembro TransferBuffer y un MDL en el miembro TransferBufferMDL . Cualquiera que proporcione el controlador, el otro debe ser NULL.