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


Регистрация составного устройства

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

Устройство универсальной последовательной шины (USB) может предоставлять несколько функций, которые активны одновременно. Такие многофакторные устройства также называются составными устройствами. Например, составное устройство может определить функцию для функций клавиатуры и другой функции для мыши. Составной драйвер перечисляет функции устройства. Составной драйвер может управлять этими функциями в монолитной модели или создавать объекты физического устройства (PDOS) для каждой функции. Usb-драйверы функций, такие как драйвер клавиатуры и драйвер мыши, управляют соответствующими отдельными PDOS.

Спецификация USB 3.0 определяет функцию приостановки и удаленного пробуждения, что позволяет отдельным функциям вводить и выход из состояний низкой мощности, не влияя на состояние питания других функций или всего устройства. Дополнительные сведения о функции см. в статье "Реализация приостановки функции в составном драйвере".

Чтобы использовать эту функцию, составной драйвер должен зарегистрировать устройство в базовом стеке USB-драйверов. Поскольку эта функция применяется к устройствам USB 3.0, составной драйвер должен убедиться, что базовый стек поддерживает версию USBD_INTERFACE_VERSION_602. В запросе на регистрацию составного драйвера, например:

  • Сообщает базовому стеку USB-драйверов, что драйвер отвечает за отправку запроса на активацию функции для удаленного пробуждения. Стек USB-драйверов обрабатывает запрос удаленного пробуждения, который отправляет необходимые запросы протокола на устройство.
  • Получает список идентификаторов функций (по одному на каждую функцию), назначенных стеком USB-драйверов. Затем составной драйвер может использовать дескриптор функции в запросе драйвера на удаленное пробуждение функции, связанной с дескриптором.

Обычно составной драйвер отправляет запрос на регистрацию в AddDevice драйвера или в подпрограмму StartDevice для обработки IRP_MN_START_DEVICE. Таким образом, составной драйвер освобождает ресурсы, выделенные для регистрационных процедур в процедурах выгрузки драйвера, таких как остановка устройства (IRP_MN_STOP_DEVICE) или процедуры удаления устройства (IRP_MN_REMOVE_DEVICE).

Предварительные условия

Перед отправкой запроса на регистрацию убедитесь, что:

  • В вашем устройстве есть несколько функций. Это число может быть выведено из дескрипторов, полученных запросом на получение конфигурации.
  • Вы получили дескриптор USBD в предыдущем вызове USBD_CreateHandle.
  • Базовый стек USB-драйверов поддерживает устройства USB 3.0. Для этого вызовите USBD_IsInterfaceVersionSupported и передайте USBD_INTERFACE_VERSION_602 в качестве проверяемой версии.

Пример кода см. в разделе "Реализация приостановки функции" в составном драйвере.

Регистрация составного устройства

В следующей процедуре описывается, как создавать и отправлять запрос на регистрацию для связывания составного драйвера с стеком USB-драйверов.

  1. Выделите структуру COMPOSITE_DEVICE_CAPABILITIES и инициализируйте ее, вызвав макрос COMPOSITE_DEVICE_CAPABILITIES_INIT.

  2. Установите для элемента CapabilityFunctionSuspend в COMPOSITE_DEVICE_CAPABILITIES значение 1.

  3. Выделите структуру REGISTER_COMPOSITE_DEVICE и инициализируйте её, вызвав процедуру USBD_BuildRegisterCompositeDevice. В вызове укажите дескриптор USBD, инициализированную COMPOSITE_DEVICE_CAPABILITIES структуру и количество функций.

  4. Выделите пакет запроса ввода-вывода (IRP), вызвав IoAllocateIrp и получите указатель на первое расположение стека IRP (IO_STACK_LOCATION), вызвав IoGetNextIrpStackLocation.

  5. Выделите память для буфера, достаточно большого размера для хранения массива дескрипторов функций (USBD_FUNCTION_HANDLE). Число элементов в массиве должно быть числом PDOs.

  6. Создайте запрос, задав следующие члены IO_STACK_LOCATION:

    • Укажите тип запроса, установив для Parameters.DeviceIoControl.IoControlCode значение IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE.
    • Укажите входной параметр, задав Parameters.Others.Argument1 на адрес инициализированной структуры REGISTER_COMPOSITE_DEVICE.
    • Укажите выходной параметр, установив AssociatedIrp.SystemBuffer в качестве буфера, выделенного на шаге 5.
  7. Вызовите IoCallDriver, чтобы отправить запрос, передав IRP в следующее расположение стека.

По завершении работы проверьте массив объектов функций, которые возвращает стек драйверов USB. Массив можно сохранить в контексте устройства драйвера для использования в будущем.

В следующем примере кода показано, как создать и отправить запрос на регистрацию. В примере предполагается, что составной драйвер сохраняет ранее полученное количество функций и дескриптор USBD в контексте устройства драйвера.

VOID  RegisterCompositeDriver(PPARENT_FDO_EXT parentFdoExt)
{
    PIRP                            irp;
    REGISTER_COMPOSITE_DRIVER       registerInfo;
    COMPOSITE_DRIVER_CAPABILITIES   capabilities;
    NTSTATUS                        status;
    PVOID                           buffer;
    ULONG                           bufSize;
    PIO_STACK_LOCATION              nextSp;

    buffer = NULL;

    COMPOSITE_DRIVER_CAPABILITIES_INIT(&capabilities);
    capabilities.CapabilityFunctionSuspend = 1;

    USBD_BuildRegisterCompositeDriver(parentFdoExt->usbdHandle,
        capabilities,
        parentFdoExt->numFunctions,
        &registerInfo);

    irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp == NULL)
    {
        //IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitRegisterCompositeDriver;
    }

    nextSp = IoGetNextIrpStackLocation(irp);

    bufSize = parentFdoExt->numFunctions * sizeof(USBD_FUNCTION_HANDLE);

    buffer = ExAllocatePoolWithTag (NonPagedPool, bufSize, POOL_TAG);

    if (buffer == NULL)
    {
        // Memory alloc for function-handles failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto ExitRegisterCompositeDriver;
    }

    nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DRIVER;

    //Set the input buffer in Argument1
    nextSp->Parameters.Others.Argument1 = &registerInfo;

    //Set the output buffer in SystemBuffer field for USBD_FUNCTION_HANDLE.
    irp->AssociatedIrp.SystemBuffer = buffer;

    // Pass the IRP down to the next device object in the stack. Not shown.
    status = CallNextDriverSync(parentFdoExt, irp, FALSE);

    if (!NT_SUCCESS(status))
    {
        //Failed to register the composite driver.
        goto ExitRegisterCompositeDriver;
    }

    parentFdoExt->compositeDriverRegistered = TRUE;

    parentFdoExt->functionHandleArray = (PUSBD_FUNCTION_HANDLE) buffer;

End:
    if (!NT_SUCCESS(status))
    {
        if (buffer != NULL)
        {
            ExFreePoolWithTag (buffer, POOL_TAG);
            buffer = NULL;
        }
    }

    if (irp != NULL)
    {
        IoFreeIrp(irp);
        irp = NULL;
    }

    return;
}

Отмена регистрации составного устройства

  1. Выделите IRP, вызвав IoAllocateIrp, и получите указатель на следующую позицию в стеке IRP (IO_STACK_LOCATION) путем вызова IoGetNextIrpStackLocation.
  2. Создайте запрос, установив поле Parameters.DeviceIoControl.IoControlCode структуры IO_STACK_LOCATION на значение IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE.
  3. Вызовите IoCallDriver , чтобы отправить запрос, передав IRP в следующую позицию в стеке.

Запрос IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE отправляется один раз составным драйвером в контексте подпрограммы удаления устройства. Цель запроса — удалить связь между стеком USB-драйверов и составным драйвером и перечисленной функцией. Запрос также очищает все ресурсы, созданные для поддержания этой связи и всех дескрипторов функций, возвращенных в предыдущем запросе на регистрацию.

В следующем примере кода показано, как создать и отправить запрос для отмены регистрации составного устройства. В этом примере предполагается, что составной драйвер ранее был зарегистрирован с помощью запроса на регистрацию, как описано ранее в этой статье.

VOID  UnregisterCompositeDriver(
    PPARENT_FDO_EXT parentFdoExt )
{
    PIRP                irp;
    PIO_STACK_LOCATION  nextSp;
    NTSTATUS            status;

    PAGED_CODE();

    irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp == NULL)
    {
        //IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;
        return;
    }

    nextSp = IoGetNextIrpStackLocation(irp);

    nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DRIVER;

    // Pass the IRP down to the next device object in the stack. Not shown.
    status = CallNextDriverSync(parentFdoExt, irp, FALSE);

    if (NT_SUCCESS(status))
    {
        parentFdoExt->compositeDriverRegistered = FALSE;
    }

    IoFreeIrp(irp);

    return;
}