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


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

В этой статье представлен обзор функций приостановки и функций удаленного пробуждения для многофункциональных устройств универсальной последовательной шины (USB) 3.0 (составные устройства). Из этой статьи вы узнаете о реализации этих функций в драйвере, который управляет составным устройством. Статья относится к составным драйверам, которые заменяют Usbccgp.sys.

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

Все устройство может перейти в состояние приостановки независимо от состояния питания любой функции на устройстве. Если определенная функция и все устройство переходит в состояние приостановки, состояние приостановки функции сохраняется, пока устройство находится в состоянии приостановки, а также во время всех процессов приостановки входа и выхода устройства.

Как и функция удаленного пробуждения устройства USB 2.0 (см. раздел Удаленное пробуждение USB-устройств), отдельная функция на составном устройстве USB 3.0 может выйти из состояния с низким энергопотреблением, не влияя на состояние питания других функций. Эта функция называется функцией удаленного пробуждения. Эта функция явно включается узлом путем отправки запроса протокола, который задает биты удаленного пробуждения в встроенном ПО устройства. Этот процесс называется развертыванием функции для удаленного пробуждения. Сведения о битах, связанных с удаленным пробуждением, см. на рисунке 9-6 официальной спецификации USB.

Если функция вооружена для удаленного пробуждения, функция (в состоянии приостановки) сохраняет достаточную мощность для создания сигнала возобновления пробуждения при возникновении события пользователя на физическом устройстве. В результате этого сигнала возобновления драйвер клиента может выйти из состояния приостановки связанной функции. В примере для функции мыши на составном устройстве, когда пользователь шевеет мышью, которая находится в состоянии простоя, функция мыши отправляет сигнал возобновления на узел. На узле стек usb-драйвера определяет, какая функция проснулась, и передает уведомление драйверу клиента соответствующей функции. Затем драйвер клиента может разбудить функцию и перейти в рабочее состояние.

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

  1. Определите, когда связанная функция находится в состоянии простоя.
  2. Отправка бездействующего пакета запроса ввода-вывода (IRP).
  3. Отправьте запрос на передачу функции для удаленного пробуждения, отправив пакет запроса ввода-вывода (IRP).
  4. Переведите функцию в низкое энергопотребление, отправив irp питания Dx (D2 или D3).

Дополнительные сведения о предыдущих шагах см. в разделе "Отправка USB-запроса бездействующий запрос IRP" статьи Выборочная приостановка USB. Составной драйвер создает объект физического устройства (PDO) для каждой функции в составном устройстве и обрабатывает запросы на питание, отправленные драйвером клиента (FDO стека устройств функции). Чтобы драйвер клиента успешно вошел и вышел из состояния приостановки для своей функции, составной драйвер должен поддерживать функции приостановки функций и функции удаленного пробуждения, а также обрабатывать полученные запросы на питание.

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

Шаг 1. Определение того, поддерживает ли стек драйвера USB функцию приостановки

В процедуре запуска устройства (IRP_MN_START_DEVICE) составного драйвера выполните следующие действия.

  1. Вызовите подпрограмму USBD_QueryUsbCapability , чтобы определить, поддерживает ли базовый стек драйверов USB возможность приостановки функции. Для вызова требуется действительный дескриптор USBD, полученный при предыдущем вызове подпрограммы USBD_CreateHandle .

Успешный вызов USBD_QueryUsbCapability определяет, поддерживает ли базовый стек драйверов USB приостановку функции. Вызов может возвращать код ошибки, указывающий, что стек USB-драйвера не поддерживает приостановку функции или подключенное устройство не является многофункциональным устройством USB 3.0.

  1. Если вызов USBD_QueryUsbCapability указывает, что функция приостановки поддерживается, зарегистрируйте составное устройство в базовом стеке драйверов USB. Чтобы зарегистрировать составное устройство, необходимо отправить IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE запрос на управление вводом-выводом. Дополнительные сведения об этом запросе см. в разделе Регистрация составного устройства.

Запрос на регистрацию использует структуру REGISTER_COMPOSITE_DEVICE , чтобы указать эти сведения о составной драйвере. Убедитесь, что параметру CapabilityFunctionSuspend присвоено значение 1, чтобы указать, что составной драйвер поддерживает приостановку функции.

Пример кода, в который показано, как определить, поддерживает ли стек драйвера USB функцию приостановки, см . в разделе USBD_QueryUsbCapability.

Шаг 2. Обработка неактивного IRP

Драйвер клиента может отправлять бездействующий IRP (см . IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Запрос отправляется после того, как драйвер клиента обнаружит состояние простоя функции. IRP содержит указатель на подпрограмму завершения обратного вызова (называемую неактивным обратным вызовом), которая реализуется драйвером клиента. В рамках обратного вызова бездействия клиент выполняет такие задачи, как отмена ожидающих передачи ввода-вывода, непосредственно перед отправкой функции в состояние приостановки.

Примечание

Механизм простоя IRP необязателен для клиентских драйверов устройств USB 3.0. Однако большинство клиентских драйверов написаны для поддержки устройств USB 2.0 и USB 3.0. Для поддержки устройств USB 2.0 драйвер должен отправить бездействующее IRP, так как составной драйвер использует его для отслеживания состояния питания каждой функции. Если все функции простаивают, составной драйвер отправляет все устройство в состояние приостановки.

После получения неактивного IRP от клиентского драйвера составной драйвер должен немедленно вызвать обратный вызов бездействия, чтобы уведомить драйвер клиента о том, что драйвер клиента может отправить функцию в состояние приостановки.

Шаг 3. Отправка запроса на уведомление удаленного пробуждения

Драйвер клиента может отправить запрос на управление функцией для удаленного пробуждения, отправив IRP_MJ_POWER IRP с дополнительным кодом функции, для IRP_MN_WAIT_WAKE ( ожидание пробуждения). Драйвер клиента отправляет этот запрос, только если драйвер хочет войти в рабочее состояние в результате события пользователя.

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

В IRP для запроса составной драйвер предоставляет указатель на подпрограмму завершения (удаленного пробуждения), которая реализуется составным драйвером.

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

/*++

Description:
    This routine sends a IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION request
    to the USB driver stack. The IOCTL is completed by the USB driver stack
    when the function wakes up from sleep.

    Parameters:
    parentFdoExt: The device context associated with the FDO for the
    composite driver.

    functionPdoExt: The device context associated with the PDO (created by
    the composite driver) for the client driver.
--*/

VOID
SendRequestForRemoteWakeNotification(
    __inout PPARENT_FDO_EXT parentFdoExt,
    __inout PFUNCTION_PDO_EXT functionPdoExt
)

{
    PIRP                                irp;
    REQUEST_REMOTE_WAKE_NOTIFICATION    remoteWake;
    PIO_STACK_LOCATION                  nextStack;
    NTSTATUS                            status;

    // Allocate an IRP
    irp =  IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp)
    {

        //Initialize the USBDEVICE_REMOTE_WAKE_NOTIFICATION structure
        remoteWake.Version = 0;
        remoteWake.Size = sizeof(REQUEST_REMOTE_WAKE_NOTIFICATION);
        remoteWake.UsbdFunctionHandle = functionPdoExt->functionHandle;
        remoteWake.Interface = functionPdoExt->baseInterfaceNumber;

        nextStack = IoGetNextIrpStackLocation(irp);

        nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION;

        nextStack->Parameters.Others.Argument1 = &remoteWake;

        // Caller's completion routine will free the IRP when it completes.

        SetCompletionRoutine(functionPdoExt->debugLog,
                             parentFdoExt->fdo,
                             irp,
                             CompletionRemoteWakeNotication,
                             (PVOID)functionPdoExt,
                             TRUE, TRUE, TRUE);

        // Pass the IRP
        IoCallDriver(parentFdoExt->topDevObj, irp);

    }

    return;
}

Запрос IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION выполняется стеком USB-драйвера во время пробуждения при получении уведомления о сигнале возобновления. В это время стек USB-драйвера также вызывает процедуру завершения удаленного пробуждения.

Составной драйвер должен оставить IRP в ожидании пробуждения и ставить его в очередь для последующей обработки. Составной драйвер должен завершить этот IRP, когда стек usb-драйверов вызывает подпрограмму удаленного завершения пробуждения драйвера.

Шаг 4. Отправка запроса на включение функции для удаленного пробуждения

Чтобы отправить функцию в состояние с низким энергопотреблением, драйвер клиента отправляет IRP_MN_SET_POWER IRP с запросом на изменение состояния питания устройства модели драйвера Windows (WDM) на D2 или D3. Как правило, клиентский драйвер отправляет D2 IRP, если драйвер ранее отправил IRP для ожидания пробуждения, чтобы запросить удаленное пробуждение. В противном случае драйвер клиента отправляет D3 IRP.

После получения D2 IRP составной драйвер должен сначала определить, ожидается ли ожидание IRP ожидания от предыдущего запроса, отправленного драйвером клиента. Если этот IRP находится в состоянии ожидания, составной драйвер должен вооружить функцию для удаленного пробуждения. Для этого составной драйвер должен отправить запрос на управление SET_FEATURE первому интерфейсу функции, чтобы устройство отправляло сигнал возобновления. Чтобы отправить запрос на управление, выделите структуру URB , вызвав подпрограмму USBD_UrbAllocate и вызовите макрос UsbBuildFeatureRequest для форматирования URB для запроса SET_FEATURE. В вызове укажите URB_FUNCTION_SET_FEATURE_TO_INTERFACE в качестве кода операции, а USB_FEATURE_FUNCTION_SUSPEND в качестве селектора функций. В параметре Index задайте бит 1 наиболее значительного байта. Это значение копируется в поле wIndex в пакете установки передачи.

В следующем примере показано, как отправить запрос SET_FEATURE элемента управления.

/*++

Routine Description:

Sends a SET_FEATURE for REMOTE_WAKEUP to the device using a standard control request.

Parameters:
parentFdoExt: The device context associated with the FDO for the
composite driver.

functionPdoExt: The device context associated with the PDO (created by
the composite driver) for the client driver.

Returns:

NTSTATUS code.

--*/
VOID
    NTSTATUS SendSetFeatureControlRequestToSuspend(
    __inout PPARENT_FDO_EXT parentFdoExt,
    __inout PFUNCTION_PDO_EXT functionPdoExt,
    )

{
    PURB                            urb
    PIRP                            irp;
    PIO_STACK_LOCATION              nextStack;
    NTSTATUS                        status;

    status = USBD_UrbAllocate(parentFdoExt->usbdHandle, &urb);

    if (!NT_SUCCESS(status))
    {
        //USBD_UrbAllocate failed.
        goto Exit;
    }

    //Format the URB structure.
    UsbBuildFeatureRequest (
        urb,
        URB_FUNCTION_SET_FEATURE_TO_INTERFACE, // Operation code
        USB_FEATURE_FUNCTION_SUSPEND,          // feature selector
        functionPdoExt->firstInterface,           // first interface of the function
        NULL);

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

    if (!irp)
    {
        // IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;

        goto Exit;
    }

    nextStack = IoGetNextIrpStackLocation(irp);

    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;

    //  Attach the URB to the IRP.
    USBD_AssignUrbToIoStackLocation(nextStack, (PURB)urb);

    // Caller's completion routine will free the IRP when it completes.
    SetCompletionRoutine(functionPdoExt->debugLog,
        parentFdoExt->fdo,
        irp,
        CompletionForSuspendControlRequest,
        (PVOID)functionPdoExt,
        TRUE, TRUE, TRUE);


    // Pass the IRP
    IoCallDriver(parentFdoExt->topDevObj, irp);


Exit:
    if (urb)
    {
        USBD_UrbFree( parentFdoExt->usbdHandle, urb);
    }

    return status;

}

Затем составной драйвер отправляет D2 IRP в стек драйверов USB. Если все остальные функции находятся в состоянии приостановки, стек драйвера USB приостанавливает порт, управляя определенными регистрами портов на контроллере.

Комментарии

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

Получив пакет уведомлений, стек USB-драйвера завершает ожидающий запрос IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION (см. шаг 3) и вызывает подпрограмму обратного вызова завершения (удаленного пробуждения), которая была указана в запросе и реализована составным драйвером. Когда уведомление достигает составного драйвера, оно уведомляет соответствующий клиентский драйвер о том, что функция перешла в рабочее состояние, завершив ранее отправленное драйвером клиента IRP для ожидания пробуждения.

В процедуре завершения (удаленного пробуждения) составной драйвер должен ставить рабочий элемент в очередь для завершения ожидающего ожидания IRP. Для устройств USB 3.0 составной драйвер активирует только функцию, которая отправляет сигнал возобновления и оставляет другие функции в состоянии приостановки. Постановка в очередь рабочего элемента обеспечивает совместимость с существующей реализацией для драйверов функций устройств USB 2.0. Сведения о постановке в очередь рабочего элемента см. в разделе IoQueueWorkItem.

Рабочий поток завершает IRP ожидания и вызывает подпрограмму завершения драйвера клиента. Затем подпрограмма завершения отправляет D0 IRP для ввода функции в рабочее состояние. Перед завершением IRP ожидания и пробуждения составной драйвер должен вызвать PoSetSystemWake , чтобы пометить IRP для ожидания пробуждения как того, который способствовал пробуждению системы из состояния приостановки. Диспетчер питания регистрирует событие трассировки событий Windows (ETW), которое можно просмотреть в глобальном системном канале, включающее сведения об устройствах, которые разбудили систему.