Compartir a través de


Cómo seleccionar una configuración para un dispositivo USB

Para seleccionar una configuración para un dispositivo USB, el controlador cliente del dispositivo debe elegir al menos una de las configuraciones admitidas y especificar la configuración alternativa de cada interfaz que se va a usar. El controlador cliente empaqueta esas opciones en una solicitud de configuración de selección y envía la solicitud a la pila de controladores USB proporcionada por Microsoft, específicamente el controlador de bus USB (PDO del concentrador USB). El controlador de bus USB selecciona cada interfaz de la configuración especificada y configura un canal de comunicación, o canalización, en cada punto de conexión dentro de la interfaz. Una vez completada la solicitud, el controlador cliente recibe un identificador para la configuración seleccionada y los identificadores de canalización de los puntos de conexión definidos en la configuración alternativa activa para cada interfaz. Después, el controlador cliente puede usar los identificadores recibidos para cambiar las opciones de configuración y enviar solicitudes de lectura y escritura de E/S a un punto de conexión determinado.

Un controlador cliente envía una solicitud de configuración de selección en un bloque de solicitudes USB (URB) del tipo URB_FUNCTION_SELECT_CONFIGURATION. En el procedimiento de este tema se describe cómo usar la rutina de USBD_SelectConfigUrbAllocateAndBuild para compilar ese URB. La rutina asigna memoria para un URB, da formato al URB para una solicitud de configuración de selección y devuelve la dirección del URB al controlador cliente.

Como alternativa, puede asignar una estructura URB y, a continuación, formatear el URB manualmente o llamando a la macro UsbBuildSelectConfigurationRequest .

Requisitos previos

Paso 1: Crear una matriz de estructuras de USBD_INTERFACE_LIST_ENTRY

  1. Obtenga el número de interfaces de la configuración. Esta información se encuentra en el miembro bNumInterfaces de la estructura USB_CONFIGURATION_DESCRIPTOR .

  2. Cree una matriz de estructuras de USBD_INTERFACE_LIST_ENTRY . El número de elementos de la matriz debe ser uno más que el número de interfaces. Inicialice la matriz llamando a RtlZeroMemory.

    El controlador cliente especifica una configuración alternativa en cada interfaz que se va a habilitar, en la matriz de estructuras de USBD_INTERFACE_LIST_ENTRY .

    • El miembro InterfaceDescriptor de cada estructura apunta al descriptor de interfaz que contiene la configuración alternativa.
    • El miembro Interface de cada estructura apunta a una estructura USBD_INTERFACE_INFORMATION que contiene información de canalización en su miembro Pipes . Las canalizaciones almacenan información sobre cada punto de conexión definido en la configuración alternativa.
  3. Obtenga un descriptor de interfaz para cada interfaz (o su configuración alternativa) en la configuración. Puede obtener esos descriptores de interfaz llamando a USBD_ParseConfigurationDescriptorEx.

    Acerca de los controladores de función para un dispositivo compuesto USB:

    Si el dispositivo USB es un dispositivo compuesto, la configuración la selecciona el controlador primario genérico USB proporcionado por Microsoft (Usbccgp.sys). Un controlador cliente, que es uno de los controladores de función del dispositivo compuesto, no puede cambiar la configuración, pero el controlador todavía puede enviar una solicitud de configuración de selección a través de Usbccgp.sys.

    Antes de enviar esa solicitud, el controlador de cliente debe enviar una solicitud de URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. En respuesta, Usbccgp.sys recupera un descriptor de configuración parcial que solo contiene descriptores de interfaz y otros descriptores que pertenecen a la función específica para la que se carga el controlador de cliente. El número de interfaces notificadas en el campo bNumInterfaces de un descriptor de configuración parcial es menor que el número total de interfaces definidas para todo el dispositivo compuesto USB. Además, en un descriptor de configuración parcial, bInterfaceNumber de un descriptor de interfaz indica el número de interfaz real en relación con todo el dispositivo. Por ejemplo, Usbccgp.sys podría notificar un descriptor de configuración parcial con el valor bNumInterfaces de 2 y bInterfaceNumber de 4 para la primera interfaz. Tenga en cuenta que el número de interfaz es mayor que el número de interfaces notificadas.

    Al enumerar interfaces en una configuración parcial, evite buscar interfaces calculando números de interfaz en función del número de interfaces. En el ejemplo anterior, si se llama a USBD_ParseConfigurationDescriptorEx en un bucle que comienza en cero, termina en (bNumInterfaces - 1)e incrementa el índice de interfaz (especificado en el parámetro InterfaceNumber ) en cada iteración, la rutina no obtiene la interfaz correcta. En su lugar, asegúrese de buscar todas las interfaces en el descriptor de configuración pasando -1 en InterfaceNumber. Para obtener más información sobre la implementación, consulte el ejemplo de código de esta sección.

    Para obtener información sobre cómo Usbccgp.sys controla una solicitud select-configuration enviada por un controlador de cliente, consulte Configuring Usbccgp.sys to Select a Non-Default USB Configuration.

  4. Para cada elemento (excepto el último elemento) de la matriz, establezca el miembro InterfaceDescriptor en la dirección de un descriptor de interfaz. Para el primer elemento de la matriz, establezca el miembro InterfaceDescriptor en la dirección del descriptor de interfaz que representa la primera interfaz de la configuración. Del mismo modo, para el elemento de la matriz, establezca el miembro InterfaceDescriptor en la dirección del descriptor de interfaz que representa la n.ªinterfaz de la configuración.

  5. El miembro InterfaceDescriptor del último elemento debe establecerse en NULL.

Paso 2: Obtener un puntero a un URB asignado por la pila de controladores USB

A continuación, llame a USBD_SelectConfigUrbAllocateAndBuild especificando la configuración que se va a seleccionar y la matriz rellenada de estructuras de USBD_INTERFACE_LIST_ENTRY . La rutina realiza las siguientes tareas:

  • Crea un URB y lo rellena con información sobre la configuración especificada, sus interfaces y puntos de conexión, y establece el tipo de solicitud en URB_FUNCTION_SELECT_CONFIGURATION.

  • Dentro de ese URB, asigna una estructura de USBD_INTERFACE_INFORMATION para cada descriptor de interfaz que especifica el controlador de cliente.

  • Establece el miembro Interface del elemento del USBD_INTERFACE_LIST_ENTRY proporcionado por el autor de la llamada en la dirección de la estructura de USBD_INTERFACE_INFORMATION correspondiente en el URB.

  • Inicializa interfaceNumber, AlternateSetting, NumberOfPipes, Pipes[i]. MaximumTransferSize y Pipes[i]. Miembros pipeFlags .

    Nota:

    En Windows 7 y ealier, el controlador cliente creó un URB para una solicitud de selección de configuración llamando a USBD_CreateConfigurationRequestEx. En Windows 2000 USBD_CreateConfigurationRequestEx inicializa Canalizaciones[i]. MaximumTransferSize al tamaño de transferencia máximo predeterminado para una única solicitud de lectura y escritura de URB. El controlador cliente puede especificar un tamaño de transferencia máximo diferente en Pipes[i]. MaximumTransferSize. La pila USB omite este valor en Windows XP, Windows Server 2003 y versiones posteriores del sistema operativo. Para obtener más información sobre MaximumTransferSize, vea "Establecer tamaños de paquete y transferencia USB" en Asignación de ancho de banda USB.

Paso 3: Enviar el URB a la pila del controlador USB

Para enviar el URB a la pila de controladores USB, el controlador cliente debe enviar una solicitud de control de E/S de IOCTL_INTERNAL_USB_SUBMIT_URB . Para obtener información sobre el envío de un URB, consulte Envío de un URB.

Después de recibir el URB, la pila del controlador USB rellena el resto de los miembros de cada estructura USBD_INTERFACE_INFORMATION . En concreto, el miembro de matriz Pipes se rellena con información sobre las canalizaciones asociadas a los puntos de conexión de la interfaz.

Paso 4: Al finalizar la solicitud, inspeccione las estructuras de USBD_INTERFACE_INFORMATION y el URB

Una vez completada la pila del controlador USB para la solicitud, la pila devuelve la lista de configuraciones alternativas y las interfaces relacionadas de la matriz USBD_INTERFACE_LIST_ENTRY .

  1. El miembro Pipes de cada estructura de USBD_INTERFACE_INFORMATION apunta a una matriz de estructuras de USBD_PIPE_INFORMATION que contiene información sobre las canalizaciones asociadas a cada extremo de esa interfaz concreta. El controlador cliente puede obtener identificadores de canalización de Pipes[i]. PipeHandle y úselos para enviar solicitudes de E/S a canalizaciones específicas. Las tuberías[i]. El miembro PipeType especifica el tipo de punto de conexión y la transferencia admitidos por esa canalización.

  2. Dentro del miembro UrbSelectConfiguration de la URB, la pila del controlador USB devuelve un identificador que puede usar para seleccionar una configuración de interfaz alternativa mediante el envío de otro URB del tipo URB_FUNCTION_SELECT_INTERFACE (solicitud select-interface). Para asignar y compilar la estructura URB para esa solicitud, llame a USBD_SelectInterfaceUrbAllocateAndBuild.

    Es posible que se produzca un error en la solicitud select-configuration y select-interface si no hay suficiente ancho de banda para admitir los puntos de conexión isócronos, de control e interrupción dentro de las interfaces habilitadas. En ese caso, el controlador de bus USB establece el miembro Status del encabezado URB en USBD_STATUS_NO_BANDWIDTH.

En el código de ejemplo siguiente se muestra cómo crear una matriz de estructuras de USBD_INTERFACE_LIST_ENTRY y llamar a USBD_SelectConfigUrbAllocateAndBuild. En el ejemplo se envía la solicitud de forma sincrónica mediante una llamada a SubmitUrbSync. Para ver el ejemplo de código de SubmitUrbSync, consulte Cómo enviar un URB.

/*++

Routine Description:
This helper routine selects the specified configuration.

Arguments:
USBDHandle - USBD handle that is retrieved by the 
client driver in a previous call to the USBD_CreateHandle routine.

ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.

Return Value: NT status value
--*/

NTSTATUS SelectConfiguration (PDEVICE_OBJECT DeviceObject,
                              PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PDEVICE_EXTENSION deviceExtension;
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    PURB urb = NULL;

    KEVENT    kEvent;
    NTSTATUS ntStatus;    

    PUSBD_INTERFACE_LIST_ENTRY   interfaceList = NULL; 
    PUSB_INTERFACE_DESCRIPTOR    interfaceDescriptor = NULL;
    PUSBD_INTERFACE_INFORMATION  Interface = NULL;
    USBD_PIPE_HANDLE             pipeHandle;

    ULONG                        interfaceIndex;

    PUCHAR StartPosition = (PUCHAR)ConfigurationDescriptor;

    deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

    // Allocate an array for the list of interfaces
    // The number of elements must be one more than number of interfaces.
    interfaceList = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
        NonPagedPool, 
        sizeof(USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    if(!interfaceList)
    {
        //Failed to allocate memory
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    // Initialize the array by setting all members to NULL.
    RtlZeroMemory (interfaceList, sizeof (
        USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    // Enumerate interfaces in the configuration.
    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
            ConfigurationDescriptor, 
            StartPosition, // StartPosition 
            -1,            // InterfaceNumber
            0,             // AlternateSetting
            -1,            // InterfaceClass
            -1,            // InterfaceSubClass
            -1);           // InterfaceProtocol

        if (!interfaceDescriptor) 
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto Exit;
        }

        // Set the interface entry
        interfaceList[interfaceIndex].InterfaceDescriptor = interfaceDescriptor;
        interfaceList[interfaceIndex].Interface = NULL;

        // Move the position to the next interface descriptor
        StartPosition = (PUCHAR)interfaceDescriptor + interfaceDescriptor->bLength;

    }

    // Make sure that the InterfaceDescriptor member of the last element to NULL.
    interfaceList[deviceExtension->NumInterfaces].InterfaceDescriptor = NULL;

    // Allocate and build an URB for the select-configuration request.
    ntStatus = USBD_SelectConfigUrbAllocateAndBuild(
        deviceExtension->UsbdHandle, 
        ConfigurationDescriptor, 
        interfaceList,
        &urb);

    if(!NT_SUCCESS(ntStatus)) 
    {
        goto Exit;
    }

    // Allocate the IRP to send the buffer down the USB stack.
    // The IRP will be freed by IO manager.
    irp = IoAllocateIrp((deviceExtension->NextDeviceObject->StackSize)+1, TRUE);  

    if (!irp)
    {
        //Irp could not be allocated.
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    ntStatus = SubmitUrbSync( 
        deviceExtension->NextDeviceObject, 
        irp, 
        urb, 
        CompletionRoutine);

    // Enumerate the pipes in the interface information array, which is now filled with pipe
    // information.

    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        ULONG i;

        Interface = interfaceList[interfaceIndex].Interface;

        for(i=0; i < Interface->NumberOfPipes; i++) 
        {
            pipeHandle = Interface->Pipes[i].PipeHandle;

            if (Interface->Pipes[i].PipeType == UsbdPipeTypeInterrupt)
            {
                deviceExtension->InterruptPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkInPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkOutPipe = pipeHandle;
            }
        }
    }

Exit:

    if(interfaceList) 
    {
        ExFreePool(interfaceList);
        interfaceList = NULL;
    }

    if (urb)
    {
        USBD_UrbFree( deviceExtension->UsbdHandle, urb); 
    }

    return ntStatus;
}

NTSTATUS CompletionRoutine ( PDEVICE_OBJECT DeviceObject,
                            PIRP           Irp,
                            PVOID          Context)
{
    PKEVENT kevent;

    kevent = (PKEVENT) Context;

    if (Irp->PendingReturned == TRUE)
    {
        KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
    }

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Select-configuration request completed. \n" ));

    return STATUS_MORE_PROCESSING_REQUIRED;
}

Deshabilitación de una configuración para un dispositivo USB

Para deshabilitar un dispositivo USB, cree y envíe una solicitud de configuración de selección con un descriptor de configuración NULL. Para ese tipo de solicitud, puede volver a usar el URB que creó para la solicitud que seleccionó una configuración en el dispositivo. Como alternativa, puede asignar un nuevo URB llamando a USBD_UrbAllocate. Antes de enviar la solicitud, debe dar formato al URB mediante la macro UsbBuildSelectConfigurationRequest , como se muestra en el código de ejemplo siguiente.

URB Urb;
UsbBuildSelectConfigurationRequest(
  &Urb,
  sizeof(_URB_SELECT_CONFIGURATION),
  NULL
);