Регистрация составного устройства
В этой статье описывается, как драйвер устройства с несколькими функциями 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 драйвера или подпрограмме начального устройства для обработки 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-драйверов.
Выделите структуру COMPOSITE_DEVICE_CAPABILITIES и инициализировать ее, вызвав макрос COMPOSITE_DEVICE_CAPABILITIES_INIT.
Задайте для элемента CapabilityFunctionSuspend COMPOSITE_DEVICE_CAPABILITIES значение 1.
Выделите REGISTER_COMPOSITE_DEVICE структуру и инициализируйте структуру, вызвав подпрограмму USBD_BuildRegisterCompositeDevice. В вызове укажите дескриптор USBD, инициализированную COMPOSITE_DEVICE_CAPABILITIES структуру и количество функций.
Выделите пакет запроса ввода-вывода (IRP), вызвав IoAllocateIrp и получите указатель на первое расположение стека IRP (IO_STACK_LOCATION), вызвав IoGetNextIrpStackLocation.
Выделите память для буфера, достаточно большого размера для хранения массива дескрипторов функций (USBD_FUNCTION_HANDLE). Число элементов в массиве должно быть числом PDOS.
Создайте запрос, задав следующие члены IO_STACK_LOCATION:
- Укажите тип запроса, задав Параметр Parameters.DeviceIoControl.IoControlCode значение IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE.
- Укажите входной параметр, задав Parameters.Others.Argument1 адрес инициализированной структуры REGISTER_COMPOSITE_DEVICE.
- Укажите выходной параметр, задав AssociatedIrp.SystemBuffer буфер, выделенный на шаге 5.
Вызовите 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,
®isterInfo);
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 = ®isterInfo;
//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;
}
Отмена регистрации составного устройства
- Выделите IRP путем вызова IoAllocateIrp и получения указателя на первое расположение стека IRP (IO_STACK_LOCATION), вызвав IoGetNextIrpStackLocation.
- Создайте запрос, задав элемент Parameters.DeviceIoControl.IoControlCode IO_STACK_LOCATION на IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE.
- Вызовите 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;
}