Поделиться через


Выбор конфигурации для USB-устройства

Чтобы выбрать конфигурацию USB-устройства, драйвер клиента для устройства должен выбрать по крайней мере одну из поддерживаемых конфигураций и указать альтернативные параметры каждого интерфейса для использования. Драйвер клиента упаковает эти варианты в запросе на выборку конфигурации и отправляет запрос в стек USB-драйверов, предоставленный Корпорацией Майкрософт, в частности драйвер USB-шины (PDO USB-концентратора). Драйвер USB-шины выбирает каждый интерфейс в указанной конфигурации и настраивает канал связи или канал для каждой конечной точки в интерфейсе. После завершения запроса драйвер клиента получает дескриптор выбранной конфигурации и дескриптор канала для конечных точек, определенных в активном альтернативном параметре для каждого интерфейса. Затем драйвер клиента может использовать полученные дескрипторы для изменения параметров конфигурации и отправки запросов на чтение и запись операций ввода-вывода в определенную конечную точку.

Драйвер клиента отправляет запрос на выборку конфигурации в блоке ЗАПРОСОВ USB (URB) типа URB_FUNCTION_SELECT_CONFIGURATION. В этой статье описывается, как использовать подпрограмму USBD_SelectConfigUrbAllocateAndBuild для сборки этого URB. Подпрограмма выделяет память для URB, форматирует URB для запроса на выборку конфигурации и возвращает адрес URB драйверу клиента.

Кроме того, можно выделить структуру URB, а затем отформатировать URB вручную или вызвать макрос UsbBuildSelectConfigurationRequest.

Необходимые компоненты

  • Начиная с Windows 8 USBD_SelectConfigUrbAllocateAndBuild заменяет USBD_CreateConfigurationRequestEx.

  • Перед отправкой запроса на выборку конфигурации необходимо иметь USBD-дескриптор для регистрации драйвера клиента в стеке USB-драйверов. Создание вызова usbD-дескриптора USBD_CreateHandle.

  • Убедитесь, что вы получили дескриптор конфигурации (USB_CONFIGURATION_DESCRIPTOR структуру) выбранной конфигурации. Как правило, вы отправляете URB типа URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (см . _URB_CONTROL_DESCRIPTOR_REQUEST) для получения сведений о конфигурации устройства. Дополнительные сведения см. в разделе "Дескрипторы конфигурации USB".

Шаг 1. Создание массива структур USBD_INTERFACE_LIST_ENTRY

  1. Получите количество интерфейсов в конфигурации. Эти сведения содержатся в элементе bNumInterfaces структуры USB_CONFIGURATION_DESCRIPTOR.

  2. Создайте массив структур USBD_INTERFACE_LIST_ENTRY . Число элементов в массиве должно быть больше, чем количество интерфейсов. Инициализировать массив путем вызова RtlZeroMemory.

    Драйвер клиента задает альтернативные параметры в каждом интерфейсе для включения в массиве структур USBD_INTERFACE_LIST_ENTRY .

    • Элемент InterfaceDescriptor каждой структуры указывает на дескриптор интерфейса, содержащий альтернативный параметр.
    • Элемент интерфейса каждой структуры указывает на USBD_INTERFACE_INFORMATION структуру, содержащую сведения о канале в элементе "Каналы". Каналы хранят сведения о каждой конечной точке, определенной в альтернативном параметре.
  3. Получите дескриптор интерфейса для каждого интерфейса (или его альтернативного параметра) в конфигурации. Эти дескрипторы интерфейса можно получить, вызвав USBD_ParseConfigurationDescriptorEx.

    Сведения о драйверах функций для составного USB-устройства:

    Если USB-устройство является составным устройством, конфигурация выбирается предоставленным Корпорацией Майкрософт универсальным родительским драйвером USB (Usbccgp.sys). Драйвер клиента, который является одним из драйверов функций составного устройства, не может изменить конфигурацию, но драйвер по-прежнему может отправить запрос на настройку выбора через Usbccgp.sys.

    Перед отправкой этого запроса драйвер клиента должен отправить запрос URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. В ответ Usbccgp.sys извлекает дескриптор частичной конфигурации, содержащий только дескрипторы интерфейса и другие дескрипторы, относящиеся к конкретной функции, для которой загружается драйвер клиента. Число интерфейсов, сообщаемых в поле bNumInterfaces дескриптора частичной конфигурации, меньше общего числа интерфейсов, определенных для всего составного USB-устройства. Кроме того, в дескрипторе частичной конфигурации дескриптор интерфейса bInterfaceNumber указывает фактический номер интерфейса относительно всего устройства. Например, Usbccgp.sys может сообщать о дескрипторе частичной конфигурации с значением bNumInterfaces 2 и bInterfaceNumber 4 для первого интерфейса. Обратите внимание, что номер интерфейса больше числа сообщаемых интерфейсов.

    При перечислении интерфейсов в частичной конфигурации избежать поиска интерфейсов путем вычисления номеров интерфейсов на основе количества интерфейсов. В предыдущем примере, если USBD_ParseConfigurationDescriptorEx вызывается в цикле, который начинается с нуля, заканчивается (bNumInterfaces - 1)и увеличивает индекс интерфейса (указанный в параметре InterfaceNumber ) в каждой итерации, подпрограмма не получает правильный интерфейс. Вместо этого убедитесь, что поиск всех интерфейсов в дескрипторе конфигурации выполняется путем передачи -1 в InterfaceNumber. Дополнительные сведения о реализации см. в примере кода в этом разделе.

    Сведения о том, как Usbccgp.sys обрабатывает запрос на выборку конфигурации, отправленный драйвером клиента, см. в разделе "Настройка Usbccgp.sys для выбора конфигурации USB, отличной от по умолчанию".

  4. Для каждого элемента (за исключением последнего элемента) в массиве задайте элемент InterfaceDescriptor адрес дескриптора интерфейса. Для первого элемента в массиве задайте элемент InterfaceDescriptor адрес дескриптора интерфейса, представляющего первый интерфейс в конфигурации. Аналогичным образом для n-гоэлемента в массиве задайте элемент InterfaceDescriptor адрес дескриптора интерфейса, представляющего nth интерфейс в конфигурации.

  5. Элемент InterfaceDescriptor последнего элемента должен иметь значение NULL.

Шаг 2. Получение указателя на URB, выделенное стеком USB-драйвера

Затем вызовите USBD_SelectConfigUrbAllocateAndBuild, указав конфигурацию для выбора и заполненного массива структур USBD_INTERFACE_LIST_ENTRY. Подпрограмма выполняет следующие задачи:

  • Создает URB и заполняет его сведениями о указанной конфигурации, его интерфейсах и конечных точках и задает тип запроса для URB_FUNCTION_SELECT_CONFIGURATION.

  • В этом URB выделяет USBD_INTERFACE_INFORMATION структуру для каждого дескриптора интерфейса, указанного драйвером клиента.

  • Задает член интерфейса n-гоэлемента вызываемого USBD_INTERFACE_LIST_ENTRY массива для адреса соответствующей структуры USBD_INTERFACE_INFORMATION в URB.

  • Инициализирует interfaceNumber, AlternateSetting, NumberOfPipes, Каналы[i]. MaximumTransferSize и Pipes[i]. Элементы PipeFlags.

    Примечание.

    В Windows 7 и более ранних версиях драйвер клиента создал URB для запроса на выборку конфигурации, вызвав USBD_CreateConfigurationRequestEx. В Windows 2000 USBD_CreateConfigurationRequestEx инициализирует каналы[i]. MaximumTransferSize до максимального размера передачи по умолчанию для одного запроса на чтение и запись URB. Драйвер клиента может указать другой максимальный размер передачи в канале [i]. MaximumTransferSize. Стек USB игнорирует это значение в Windows XP, Windows Server 2003 и более поздних версиях операционной системы. Дополнительные сведения о MaximumTransferSize см. в разделе "Настройка размера USB-передачи и пакетов" в выделении пропускной способности USB.

Шаг 3. Отправка URB в стек USB-драйверов

Чтобы отправить URB в стек USB-драйверов, драйвер клиента должен отправить запрос на управление IOCTL_INTERNAL_USB_SUBMIT_URB ввода-вывода. Сведения о отправке URB см. в статье "Отправка URB".

После получения URB стек USB-драйверов заполняет остальные элементы каждой USBD_INTERFACE_INFORMATION структуры. В частности, член массива каналов заполняется сведениями о каналах, связанных с конечными точками интерфейса.

Шаг 4. При завершении запроса проверьте структуры USBD_INTERFACE_INFORMATION и URB

После завершения стека USB-драйвера для запроса стек возвращает список альтернативных параметров и связанных интерфейсов в массиве USBD_INTERFACE_LIST_ENTRY.

  1. Член канала каждой USBD_INTERFACE_INFORMATION структуры указывает на массив USBD_PIPE_INFORMATION структур, содержащих сведения о каналах, связанных с каждой конечной точкой этого конкретного интерфейса. Драйвер клиента может получить дескриптор канала из Pipe[i]. PipeHandle и их использование для отправки запросов ввода-вывода в определенные каналы. Каналы [i]. Элемент PipeType указывает тип конечной точки и передачу, поддерживаемую этим каналом.

  2. В элементе URB UrbSelectConfiguration стек USB-драйверов возвращает дескриптор, который можно использовать для выбора альтернативного параметра интерфейса, отправив другой URB типа URB_FUNCTION_SELECT_INTERFACE (запрос на выбор интерфейса). Чтобы выделить и создать структуру URB для этого запроса, вызовите USBD_SelectInterfaceUrbAllocateAndBuild.

    Запрос на выборку конфигурации и запрос на выборку может завершиться ошибкой, если недостаточно пропускной способности для поддержки точек isochronous, управления и прерывания конечных точек в включенных интерфейсах. В этом случае драйвер USB-шины задает элемент состояния заголовка URB для USBD_STATUS_NO_BANDWIDTH.

В следующем примере кода показано, как создать массив структур USBD_INTERFACE_LIST_ENTRY и вызвать USBD_SelectConfigUrbAllocateAndBuild. В этом примере запрос отправляется синхронно путем вызова SubmitUrbSync. Чтобы просмотреть пример кода для SubmitUrbSync, см. статью "Отправка 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;
}

Отключение конфигурации для USB-устройства

Чтобы отключить USB-устройство, создайте и отправьте запрос на выборку конфигурации с дескриптором конфигурации NULL. Для этого типа запроса можно повторно использовать URB, созданный для запроса, который выбрал конфигурацию на устройстве. Кроме того, можно выделить новый URB, вызвав USBD_UrbAllocate. Перед отправкой запроса необходимо отформатировать URB с помощью макроса UsbBuildSelectConfigurationRequest , как показано в следующем примере кода.

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