Выборочная приостановка USB

Примечание.

Эта статья предназначена для разработчиков драйверов устройств. Если у вас возникли проблемы с USB-устройством, см. статью "Устранение проблем с USB-C" в Windows

Функция выборочной приостановки USB позволяет драйверу концентратора приостановить отдельный порт, не влияя на работу других портов в концентраторе. Выборочная приостановка USB-устройств особенно полезна на портативных компьютерах, так как она помогает экономии заряда батареи. Во многих устройствах, таких как средства чтения отпечатков пальцев и другие типы биометрических сканеров, требуется только питание периодически. Приостановка таких устройств, когда устройство не используется, уменьшает общее потребление энергии. Более важно, что любое устройство, которое не выборочно приостановлено, может запретить контроллеру USB-узла отключить расписание передачи, которое находится в системной памяти. Прямой доступ к памяти (DMA) контроллер узла планировщику может предотвратить ввод процессоров системы в более глубокие состояния сна, например C3.

Существует два разных механизма выборочной приостановки USB-устройства: простой запрос irps (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) и установка параметров энергопотребления (IRP_MN_SET_POWER). Используемый механизм зависит от операционной системы и типа устройства: составного или не составного.

Выбор механизма выборочной приостановки

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

Сведения об удаленном пробуждении см. в следующем разделе:

Версия операционной системы Windows определяет способ включения выборочной приостановки драйверов для не составных устройств.

  • Windows XP: в Windows XP все драйверы клиента должны использовать неактивные запросы IRPs (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) для выключения устройств. Клиентские драйверы не должны использовать энергопотребление WDM для выборочного приостановки своих устройств. Это предотвращает выборочную приостановку других устройств.
  • В Windows Vista и более поздних версиях Windows: записи драйверов имеют больше возможностей для выключения устройств в Windows Vista и в более поздних версиях Windows. Хотя Windows Vista поддерживает механизм IRP запроса на простой Windows, драйверы не требуются для его использования.

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

Версия Windows Функция на составном устройстве, вооруженная для пробуждения Функция на составном устройстве, не вооруженная для пробуждения USB-устройство с одним интерфейсом
Windows 7 Использование IRP запроса на простой Использование WDM power IRP Использование WDM power IRP
Windows Server 2008 Использование IRP запроса на простой Использование WDM power IRP Использование WDM power IRP
Windows Vista Использование IRP запроса на простой Использование WDM power IRP Использование WDM power IRP
Windows Server 2003 Использование IRP запроса на простой Использование IRP запроса на простой Использование IRP запроса на простой
Windows XP Использование IRP запроса на простой Использование IRP запроса на простой Использование IRP запроса на простой

В этом разделе объясняется механизм выборочной приостановки Windows.

Отправка запроса на простой USB

Когда устройство неактивно, драйвер клиента сообщает водителю шины, отправив запрос на простой IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). После того как водитель шины определяет, что устройство безопасно поместить в низкое состояние питания, он вызывает подпрограмму обратного вызова, которую драйвер клиентского устройства передал стек с запросом на простой запрос IRP.

В подпрограмме обратного вызова драйвер клиента должен отменить все ожидающие операции ввода-вывода и ожидать завершения всех операций ввода-вывода USB/O. Затем он может выдать запрос IRP_MN_SET_POWER для изменения состояния питания устройства WDM на D2. Перед возвратом подпрограмма обратного вызова должна ждать завершения запроса D2 . Дополнительные сведения о подпрограмме обратного вызова бездействия уведомлений см. в разделе "Подпрограмма обратного вызова обратного вызова бездействия USB".

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

  • Получено IRP_MN_SUPRISE_REMOVAL или IRP_MN_REMOVE_DEVICE IRP . Когда один из этих irPs получает неактивный запрос IRP завершается с STATUS_CANCELLED.
  • Водитель шины получает запрос на размещение устройства в рабочее состояние питания (D0). После получения этого драйвера шины запроса завершает ожидающий запрос на простой IRP с STATUS_SUCCESS.

Следующие ограничения применяются к использованию бездействующего irps запроса:

  • Драйверы должны находиться в состоянии питания устройства D0 при отправке IRP запроса на простой.
  • Драйверы должны отправлять только один запрос на простой запрос IRP на стек устройств.

В следующем примере кода WDM показаны шаги, которые выполняет драйвер устройства для отправки IRP-запроса на простой USB. Проверка ошибок была опущена в следующем примере кода.

  1. Выделение и инициализация IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION IRP

    irp = IoAllocateIrp (DeviceContext->TopOfStackDeviceObject->StackSize, FALSE);
    nextStack = IoGetNextIrpStackLocation (irp);
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
    nextStack->Parameters.DeviceIoControl.InputBufferLength =
    sizeof(struct _USB_IDLE_CALLBACK_INFO);
    
  2. Выделение и инициализация структуры сведений об простое запроса (USB_IDLE_CALLBACK_INFO).

    idleCallbackInfo = ExAllocatePool (NonPagedPool,
    sizeof(struct _USB_IDLE_CALLBACK_INFO));
    idleCallbackInfo->IdleCallback = IdleNotificationCallback;
    // Put a pointer to the device extension in member IdleContext
    idleCallbackInfo->IdleContext = (PVOID) DeviceExtension;  
    nextStack->Parameters.DeviceIoControl.Type3InputBuffer =
    idleCallbackInfo;
    
  3. Задайте подпрограмму завершения.

    Драйвер клиента должен связать подпрограмму завершения с IRP простоя запроса. Дополнительные сведения о подпрограмме завершения простоя уведомлений и примере кода см. в разделе "Подпрограмма завершения запроса на простой USB".

    IoSetCompletionRoutine (irp,
        IdleNotificationRequestComplete,
        DeviceContext,
        TRUE,
        TRUE,
        TRUE);
    
  4. Сохраните запрос простоя в расширении устройства.

    deviceExtension->PendingIdleIrp = irp;
    
    
  5. Отправьте запрос простоя родительскому драйверу.

    ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
    

Отмена запроса на простой USB

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

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

Сценарий Механизм отмены простоя запроса
Драйвер клиента отменил неактивный IRP, а стек USB-драйверов не назвал подпрограмму обратного вызова уведомлений usb idle. Стек USB-драйверов завершает простой IRP. Так как устройство никогда не покидает D0, драйвер не изменяет состояние устройства.
Драйвер клиента отменил неактивный IRP, стек USB-драйверов вызвал подпрограмму обратного вызова уведомлений об простое USB, и он еще не вернулся. Возможно, что подпрограмма обратного вызова бездействия USB вызывается, даже если драйвер клиента вызвал отмену для IRP. В этом случае подпрограмма обратного вызова драйвера клиента должна по-прежнему спустить устройство, отправив устройство в более низкое состояние питания синхронно.

Когда устройство находится в нижнем состоянии питания, драйвер клиента может отправить запрос D0 .

Кроме того, драйвер может ждать завершения простоя IRP в стеке USB-драйверов, а затем отправить IRP D0 .

Если подпрограмма обратного вызова не может поместить устройство в состояние низкой мощности из-за нехватки памяти для выделения power IRP, он должен отменить бездействующий IRP и выйти немедленно. Неактивный IRP не будет завершен до возврата подпрограммы обратного вызова; Таким образом, подпрограмма обратного вызова не должна блокировать ожидание завершения отмененного бездействия IRP.
Устройство уже находится в состоянии низкой мощности. Если устройство уже находится в состоянии низкой мощности, драйвер клиента может отправить IRP D0 . Стек USB-драйверов завершает простой запрос IRP с STATUS_SUCCESS.

Кроме того, драйвер может отменить бездействующий IRP, подождите, пока стек USB-драйверов завершит бездействующий IRP, а затем отправьте IRP D0 .

Подпрограмма завершения IRP запроса на простой USB

Во многих случаях водитель шины может вызвать процедуру завершения бездействия водителя IRP. В этом случае драйвер клиента должен определить, почему водитель шины завершил IRP. Возвращенный код состояния может предоставить эти сведения. Если код состояния не STATUS_POWER_STATE_INVALID, драйвер должен поместить его устройство в D0 , если устройство еще не в D0. Если устройство по-прежнему неактивно, драйвер может отправить другой запрос на простой IRP.

Примечание.

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

В следующем списке показано, как подпрограмма завершения для неактивного запроса должна интерпретировать некоторые распространенные коды состояния:

Код состояния Description
STATUS_SUCCESS Указывает, что устройство больше не должно быть приостановлено. Однако драйверы должны убедиться, что их устройства работают, и поместить их в D0, если они еще не находятся в D0.
STATUS_CANCELLED Водитель автобуса завершает запрос IRP бездействия с STATUS_CANCELLED в любом из следующих обстоятельств:
  • Драйвер устройства отменил IRP.
  • Требуется изменение состояния питания системы.
  • В Windows XP драйвер устройства для одного из подключенных USB-устройств не смог поместить устройство в D2 при выполнении подпрограммы обратного вызова бездействия запроса. В результате водитель шины завершил все ожидающие запросы на простой IRP.
STATUS_POWER_STATE_INVALID Указывает, что драйвер устройства запрашивал состояние питания D3 для своего устройства. Когда это происходит, водитель шины завершает все ожидающие неактивные irps с STATUS_POWER_STATE_INVALID.
STATUS_DEVICE_BUSY Указывает, что водитель шины уже содержит неактивный запрос IRP, ожидающий для устройства. Только один бездействующий IRP может находиться в ожидании за раз для данного устройства. Отправка нескольких запросов на простой irPs является ошибкой со стороны владельца политики питания и должна быть устранена средством записи драйвера.

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

/*Routine Description:

  Completion routine for idle notification IRP

Arguments:

    DeviceObject - pointer to device object
    Irp - I/O request packet
    DeviceExtension - pointer to device extension

Return Value:

    NT status value

--*/

NTSTATUS
IdleNotificationRequestComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PDEVICE_EXTENSION DeviceExtension
    )
{
    NTSTATUS                ntStatus;
    POWER_STATE             powerState;
    PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;

    ntStatus = Irp->IoStatus.Status;

    if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED)
    {

        //Idle IRP completes with error.

        switch(ntStatus)
        {

        case STATUS_INVALID_DEVICE_REQUEST:

            //Invalid request.

            break;

        case STATUS_CANCELLED:

            //1. The device driver canceled the IRP.
            //2. A system power state change is required.

            break;

        case STATUS_POWER_STATE_INVALID:

            // Device driver requested a D3 power state for its device
            // Release the allocated resources.

            goto IdleNotificationRequestComplete_Exit;

        case STATUS_DEVICE_BUSY:

            //The bus driver already holds an idle IRP pending for the device.

            break;

        default:
            break;

        }


        // If IRP completes with error, issue a SetD0

        //Increment the I/O count because
        //a new IRP is dispatched for the driver.
        //This call is not shown.

        powerState.DeviceState = PowerDeviceD0;

        // Issue a new IRP
        PoRequestPowerIrp (
            DeviceExtension->PhysicalDeviceObject,
            IRP_MN_SET_POWER,
            powerState,
            (PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc,
            DeviceExtension,
            NULL);
    }

IdleNotificationRequestComplete_Exit:

    idleCallbackInfo = DeviceExtension->IdleCallbackInfo;

    DeviceExtension->IdleCallbackInfo = NULL;

    DeviceExtension->PendingIdleIrp = NULL;

    InterlockedExchange(&DeviceExtension->IdleReqPend, 0);

    if(idleCallbackInfo)
    {
        ExFreePool(idleCallbackInfo);
    }

    DeviceExtension->IdleState = IdleComplete;

    // Because the IRP was created using IoAllocateIrp,
    // the IRP needs to be released by calling IoFreeIrp.
    // Also return STATUS_MORE_PROCESSING_REQUIRED so that
    // the kernel does not reference this.

    IoFreeIrp(Irp);

    KeSetEvent(&DeviceExtension->IdleIrpCompleteEvent, IO_NO_INCREMENT, FALSE);

    return STATUS_MORE_PROCESSING_REQUIRED;
}

Подпрограмма обратного вызова бездействия USB

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

Прототип функции для USB_IDLE_CALLBACK выглядит следующим образом:

typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);

Драйвер устройства должен выполнить следующие действия в подпрограмме обратного вызова бездействия уведомлений:

  • Запросите IRP_MN_WAIT_WAKE IRP для устройства, если устройство должно быть вооружена для удаленного пробуждения.
  • Отмените все операции ввода-вывода и подготовьте устройство для перехода к более низкому состоянию питания.
  • Поместите устройство в состояние сна WDM, вызвав PoRequestPowerIrp с параметром PowerState , заданным для значения PowerDeviceD2 (определено в wdm.h; ntddk.h). В Windows XP драйвер не должен помещать свое устройство в PowerDeviceD3, даже если устройство не вооружена для удаленного пробуждения.

В Windows XP драйвер должен полагаться на подпрограмму обратного вызова бездействия уведомлений, чтобы выборочно приостановить устройство. Если драйвер, работающий в Windows XP, помещает устройство в меньшее состояние питания напрямую без использования подпрограммы обратного вызова уведомления об простое, это может предотвратить приостановку других устройств в дереве USB-устройств.

Драйвер концентратора и USB Generic Parent Driver (Usbccgp.sys) вызывают подпрограмму обратного вызова бездействия уведомлений в IRQL = PASSIVE_LEVEL. Это позволяет подпрограмме обратного вызова блокироваться во время ожидания завершения запроса на изменение состояния питания.

Подпрограмма обратного вызова вызывается только в то время как система находится в S0 , а устройство находится в D0.

Следующие ограничения применяются к подпрограммам обратного вызова уведомлений об простои запросах:

  • Драйверы устройств могут инициировать переход состояния питания устройства с D0 на D2 в подпрограмме обратного вызова уведомлений об простое, но другие переходы состояния питания не разрешены. В частности, драйвер не должен пытаться изменить устройство на D0 при выполнении процедуры обратного вызова.
  • Драйверы устройств не должны запрашивать несколько power IRP из подпрограммы обратного вызова бездействия уведомлений.

Вооружение устройств для пробуждения в подпрограмме обратного вызова бездействия уведомлений

Подпрограмма обратного вызова бездействия уведомлений должна определить, имеет ли устройство IRP_MN_WAIT_WAKE запрос на ожидание. Если запрос IRP_MN_WAIT_WAKE не ожидается, подпрограмма обратного вызова должна отправить запрос IRP_MN_WAIT_WAKE перед приостановкой устройства. Дополнительные сведения о механизме пробуждения ожидания см. в разделе "Поддержка устройств с возможностями пробуждения".

Глобальная приостановка USB

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

Условия глобальной приостановки в Windows 7

Windows 7 более агрессивно относится к выборочному приостановке USB-концентраторов, чем Windows Vista. Драйвер USB-концентратора Windows 7 выборочно приостанавливает любой концентратор, где все подключенные устройства находятся в состоянии питания устройства D1, D2 или D3 . Вся шина входит в глобальную приостановку после выборочной приостановки всех USB-концентраторов. Стек USB-драйверов Windows 7 обрабатывает устройство как простой, когда устройство находится в состоянии устройства WDM D1, D2 или D3.

Условия глобальной приостановки в Windows Vista

Требования к выполнению глобальной приостановки более гибки в Windows Vista, чем в Windows XP.

В частности, стек USB обрабатывает устройство как простой в Windows Vista всякий раз, когда устройство находится в состоянии устройства WDM D1, D2 или D3.

На следующей схеме показан сценарий, который может произойти в Windows Vista.

Схема, демонстрирующая глобальную приостановку в Windows Vista.

На этой схеме показана ситуация, аналогичная описанной в разделе "Условия глобальной приостановки в Windows XP". Однако в этом случае устройство 3 является неактивным. Так как все устройства неактивны, драйвер шины может вызывать подпрограммы обратного вызова бездействия, связанные с ожидающих запросов на простой. Каждый водитель приостанавливает устройство, а драйвер шины приостанавливает контроллер USB-узла, как только это безопасно.

В Windows Vista все USB-устройства, отличные от концентратора, должны находиться в D1, D2 или D3 перед началом глобальной приостановки, в то время как все USB-концентраторы, включая корневой концентратор, приостановлены. Это означает, что любой ДРАЙВЕР USB-клиента, который не поддерживает выборочную приостановку, предотвращает переход шины на глобальную приостановку.

Условия глобальной приостановки в Windows XP

Чтобы максимально повысить экономию энергии в Windows XP, важно, чтобы каждый драйвер устройства использовал неактивные запросы IRPs для приостановки устройства. Если один драйвер приостанавливает свое устройство с помощью запроса IRP_MN_SET_POWER вместо неактивного запроса IRP, он может предотвратить приостановку других устройств.

На следующей схеме показан сценарий, который может произойти в Windows XP.

Схема, иллюстрирующая глобальную приостановку в Windows XP.

На этом рисунке устройство 3 находится в состоянии питания D3 и не имеет простой запрос IRP в ожидании. Устройство 3 не относится к неактивным устройствам для целей глобальной приостановки в Windows XP, так как у него нет запроса на простой запрос IRP, ожидающий с родительским элементом. Это позволяет водителю шины вызывать подпрограммы обратного вызова бездействия, связанные с драйверами других устройств в дереве.

Включение выборочной приостановки

Выборочная приостановка отключена для обновления версий Microsoft Windows XP. Она включена для чистых установок Windows XP, Windows Vista и более поздних версий Windows.

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

Кроме того, можно включить или отключить выборочную приостановку, задав значение HcDisableSelectiveSelectiveSuspend в программном ключе драйвера USB-порта. Значение 1 отключает выборочную приостановку. Значение 0 включает выборочную приостановку.

Например, следующие строки в Usbport.inf отключают выборочную приостановку для контроллера Hydra OHCI:

[OHCI_NOSS.AddReg.NT]
HKR,,"HcDisableSelectiveSuspend",0x00010001,1

Драйверы клиентов не должны пытаться определить, включена ли выборочная приостановка перед отправкой запросов на простой. Они должны отправлять запросы бездействия всякий раз, когда устройство неактивно. Если запрос на простой завершается сбоем, драйвер клиента должен сбросить таймер простоя и повторить попытку.