Выборочная приостановка в драйверах функций USB KMDF

В этой статье описывается, как драйверы функций KMDF поддерживают выборочную приостановку USB.

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

Рекомендации по выборочной приостановке работы в драйверах KMDF

Драйверы KMDF, поддерживающие выборочную приостановку, должны соответствовать следующим рекомендациям:

  • Драйвер функции KMDF должен быть основным драйвером (PPO) для стека устройств. По умолчанию драйверы функций KMDF — это PPO.
  • Функциональный драйвер KMDF, поддерживающий выборочную приостановку, может использовать очереди, управляемые питанием, или не управляемые питанием. По умолчанию объекты очередей для PPO находятся под управлением энергопотребления.

Владение политикой питания и USB-драйверы KMDF

По умолчанию драйвер функции KMDF для USB-устройства — это PPO для стека устройств. KMDF управляет выборочной приостановкой и возобновлением от имени этого драйвера.

Настройка очереди ввода-вывода в драйверах KMDF

Драйвер функции KMDF, поддерживающий выборочную приостановку, может использовать очереди, управляемые питанием или очереди, которые не управляются питанием. Как правило, драйвер настраивает очередь без управления питанием для получения входящих запросов управления устройствами ввода-вывода и настраивает одну или несколько управляемых питанием очередей для получения запросов на чтение, запись и другие запросы, зависящие от питания. Когда запрос поступает в очередь, управляемой питанием, KMDF гарантирует, что устройство находится в D0 перед отправкой запроса драйверу.

Если вы пишете драйвер фильтра KMDF, расположенный поверх PPO в стеке устройств, не следует использовать очереди с управлением питанием. Причина аналогична драйверам UMDF. Платформа не предоставляет запросы из управляемых питанием очередей во время приостановки устройства, поэтому использование таких очередей может застопорить стек устройств.

Механизм выборочной приостановки для драйверов функций KMDF

KMDF обрабатывает основную часть работы, необходимой для обеспечения поддержки выборочной приостановки USB. Он отслеживает действия ввода-вывода, управляет таймером простоя и отправляет запросы управления устройствами, которые вызывают родительский драйвер (Usbhub.sys или Usbccgp.sys) для приостановки и возобновления устройства.

Если драйвер функции KMDF поддерживает выборочную приостановку, KMDF отслеживает действие ввода-вывода во всех управляемых питанием очередях, принадлежащих каждому объекту устройства. Платформа запускает таймер простоя всякий раз, когда число операций ввода-вывода достигает нуля. Значение времени ожидания по умолчанию — 5 секунд.

Если запрос ввода-вывода поступает в очередь, управляемую питанием, которая принадлежит объекту устройства до истечения срока ожидания простоя, платформа отменяет таймер простоя и не приостанавливает устройство.

После истечения срока действия таймера KMDF выдает запросы, необходимые для хранения USB-устройства в приостановленном состоянии. Если драйвер функции использует непрерывный считыватель в конечной точке USB, повторные опросы считывателя не учитываются как активность в направлении таймера простоя KMDF. Однако в функции обратного вызова EvtDeviceD0Exit USB-драйвер должен вручную остановить непрерывный считыватель и любые другие целевые объекты ввода-вывода, обслуживаемые очередями без управления питанием, чтобы гарантировать, что драйвер не отправляет запросы ввода-вывода, пока устройство не находится в рабочем состоянии. Чтобы остановить целевые объекты, драйвер вызывает WdfIoTargetStop и указывает WdfIoTargetWaitForSentIoToComplete в качестве целевого действия. В ответ фреймворк останавливает целевой объект ввода-вывода только после завершения всех запросов ввода-вывода, находящихся в его очереди, и выполнения всех связанных с ними обратных вызовов завершения ввода-вывода.

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

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

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

Подробные сведения об обратных вызовах, участвующих в последовательностях выключения и включения питания, см. в техническом документе по Plug and Play и управлению питанием в драйверах WDF.

Поддержка выборочного приостановления USB в функциональном драйвере KMDF

Чтобы реализовать выборочную приостановку USB в драйвере функции KMDF:

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

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

Чтобы настроить поддержку выборочного приостановления USB, драйвер KMDF использует структуру WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS. Драйвер должен сначала инициализировать структуру и затем задать поля, предоставляющие сведения о возможностях драйвера и его устройства. Как правило, драйвер заполняет эту структуру в функции EvtDriverDeviceAdd или EvtDevicePrepareHardware .

Инициализировать структуру WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS

После создания объекта устройства драйвер использует функцию WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT для инициализации структуры. Эта функция принимает два аргумента:

  • Указатель на структуру WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS для инициализации.
  • Значение перечисления, указывающее на поддержку выборочной приостановки. Драйвер должен указать IdleUsbSelectiveSuspend.

Если драйвер задает IdleUsbSelectiveSuspend, функция инициализирует элементы структуры следующим образом:

  • IdleTimeout имеет значение IdleTimeoutDefaultValue (в настоящее время 5000 миллисекунд или 5 секунд).
  • UserControlOfIdleSettings имеет значение IdleAllowUserControl.
  • Включено значение WdfUseDefault, указывающее, что выборочная приостановка включена, но пользователь может отключить его, если член UserControlOfIdleSettings разрешает его.
  • DxState устанавливается в PowerDeviceMaximum, что использует сообщаемые возможности питания устройства для определения состояния, в которое должно перейти бездействующее устройство.

Настроить выборочную приостановку USB

После инициализации WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS структуры драйвер может задать другие поля в структуре, а затем вызвать WdfDeviceAssignS0IdleSettings для передачи этих параметров в платформу. Следующие поля применяются к драйверам функций USB:

  • IdleTimeout — интервал в миллисекундах, который должен пройти без получения запроса ввода-вывода, прежде чем платформа считает устройство бездействующим. Драйвер может указать значение ULONG или принять значение по умолчанию.

  • UserControlOfIdleSettings— может ли пользователь изменять параметры простоя устройства. Возможные значения: IdleDoNotAllowUserControl и IdleAllowUserControl.

  • DxState — состояние питания устройства, в котором платформа приостанавливает устройство. Возможные значения: PowerDeviceD1, PowerDeviceD2 и PowerDeviceD3.

    USB-драйверы не должны изменять начальный параметр этого значения. Функция WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT задает это значение в PowerDeviceMaximum, что гарантирует, что платформа выбирает правильное значение на основе возможностей устройства.

Следующий фрагмент кода находится в файле Device.c примера драйвера Osrusbfx2:

WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
NTSTATUS    status = STATUS_SUCCESS;
//
// Initialize the idle policy structure.
//
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, 
     IdleUsbSelectiveSuspend);
idleSettings.IdleTimeout = 10000; // 10 sec

status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
if ( !NT_SUCCESS(status)) {
     TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                 "WdfDeviceSetPowerPolicyS0IdlePolicy failed %x\n", 
                 status);
    return status;
}

В примере драйвер вызывает WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT, указав IdleUsbSelectiveSuspend. Драйвер устанавливает значение IdleTimeout в 10 000 миллисекунд (10 секунд) и принимает платформу по умолчанию для DxState и UserControlOfIdleSettings. В результате платформа перемещает устройство в состояние D3 при простое и создает страницу свойств Диспетчера устройств, которая позволяет пользователям с правами администратора включить или отключить поддержку простоя устройства. Затем драйвер вызывает WdfDeviceAssignS0IdleSettings , чтобы включить поддержку простоя и зарегистрировать эти параметры в платформе.

Драйвер может вызывать WdfDeviceAssignS0IdleSettings в любое время после создания объекта устройства. Хотя большинство драйверов изначально вызывают этот метод из обратного вызова EvtDriverDeviceAdd , это может не всегда быть возможным или даже желательным. Если драйвер поддерживает несколько устройств или версий устройств, драйвер может не знать все возможности устройств, пока он не запрашивает оборудование. Такие драйверы могут отложить вызов WdfDeviceAssignS0IdleSettings до того момента, как будет вызван обратный вызов EvtDevicePrepareHardware.

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

Предотвращение приостановки USB-устройства

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

WdfDeviceStopIdle останавливает таймер простоя. Если срок действия IdleTimeout не истек и устройство еще не приостановлено, платформа отменяет таймер простоя и не приостанавливает устройство. Если устройство уже приостановлено, платформа возвращает устройство в рабочее состояние. WdfDeviceStopIdleне препятствует тому, чтобы фреймворк приостанавливал устройство, когда система переходит в спящий режим Sx. Его единственным эффектом является предотвращение приостановки устройства, пока система находится в рабочем состоянии S0. WdfDeviceResumeIdle перезапускает таймер простоя. Эти два метода управляют счетчиком ссылок на устройстве, поэтому если драйвер вызывает WdfDeviceStopIdle несколько раз, платформа не приостанавливает устройство, пока драйвер не вызовет WdfDeviceResumeIdle одинаковое количество раз. Драйвер не должен вызывать WdfDeviceResumeIdleбез первого вызова WdfDeviceStopIdle.

Включение ключа реестра (только драйверы HID)

Драйверы верхнего фильтра KMDF для USB HID-устройств должны указывать в INF, что они поддерживают выборочное приостановление, чтобы предоставленный Microsoft драйвер порта HIDClass.sys мог включить выборочное приостановление для стека HID. INF должен включать директиву AddReg, которая добавляет ключ SelectiveSuspendEnabled и задает его значение равным 1, как показано в следующей строке:

HKR,,"SelectiveSuspendEnabled",0x00000001,0x1

Пример см. в разделе Hidusbfx2.inx в WDK по адресу %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys.

Поддержка удаленного пробуждения для драйверов KMDF

Как и при выборочной приостановке, KMDF включает поддержку пробуждения так, чтобы USB-устройство могло активировать сигнал пробуждения, когда устройство находится в простое, а система находится в рабочем состоянии (S0) или в состоянии сна (S1–S4). В терминах KMDF эти две функции называются "пробуждение от S0" и "пробуждение от Sx" соответственно.

Для USB-устройств пробуждение означает, что само устройство может инициировать переход от состояния нижней мощности к рабочему состоянию. Таким образом, в терминологии USB, пробуждение от S0 и пробуждение от Sx являются одинаковыми и называются "удалённым пробуждением".

Драйверы USB-функций KMDF не требуют никакого кода для поддержки пробуждения от S0, так как KMDF предоставляет эту возможность в рамках выборочного механизма приостановки. Однако для поддержки удаленного пробуждения, если система находится в Sx, драйвер функции должен:

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

Драйверы KMDF обычно настраивают поддержку пробуждения в то же время как они настраивают поддержку выборочной приостановки USB в функциях EvtDriverDeviceAdd или EvtDevicePrepareHardware.

Проверка возможностей устройства

Прежде чем драйвер USB-функции KMDF инициализирует параметры политики питания для простоя и пробуждения, он должен убедиться, что устройство поддерживает удаленное пробуждение. Чтобы получить сведения об аппаратных функциях устройства, драйвер инициализирует структуру WDF_USB_DEVICE_INFORMATION и вызывает WdfUsbTargetDeviceRetrieveInformation, как правило, в его обратном вызове EvtDriverDeviceAdd или EvtDevicePrepareHardware .

В вызове WdfUsbTargetDeviceRetrieveInformation драйвер передает дескриптор объекта устройства и указатель на инициализированную структуру WDF_USB_DEVICE_INFORMATION. При успешном возвращении из функции поле Traits структуры содержит флаги, указывающие, работает ли устройство самостоятельно, может работать с высокой скоростью и поддерживает удаленное пробуждение.

В следующем примере из примера KMDF Osrusbfx2 показано, как вызвать этот метод, чтобы определить, поддерживает ли устройство удаленное пробуждение. После выполнения этих строк кода переменная waitWakeEnable содержит ЗНАЧЕНИЕ TRUE, если устройство поддерживает удаленное пробуждение и FALSE, если это не так:

    WDF_USB_DEVICE_INFORMATION          deviceInfo;
// Retrieve USBD version information, port driver capabilities and device
// capabilities such as speed, power, etc.
//

WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo);

status = WdfUsbTargetDeviceRetrieveInformation(
                            pDeviceContext->UsbDevice,
                            &deviceInfo);
waitWakeEnable = deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;

Включение удаленного пробуждения

В терминологии USB устройство USB поддерживает удаленное пробуждение, если активирована его функция DEVICE_REMOTE_WAKEUP. Согласно спецификации USB, программное обеспечение узла должно установить функцию удаленного пробуждения на устройстве "только перед тем, как поместить устройство в спящий режим". Драйвер функции KMDF необходим только для инициализации параметров пробуждения. KMDF и драйверы USB-шины, предоставляемые Корпорацией Майкрософт, выдают запросы ввода-вывода и обрабатывают аппаратные манипуляции, необходимые для включения удаленного пробуждения.

Инициализация параметров пробуждения

  1. Вызовите WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT, чтобы инициализировать структуру WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS. Эта функция задает член структуры Enabled на WdfUseDefault, устанавливает член DxState на PowerDeviceMaximum и устанавливает член UserControlOfWakeSettings на WakeAllowUserControl.
  2. Вызывайте функцию WdfDeviceAssignSxWakeSettings с инициализированной структурой. В результате устройство включено для пробуждения от состояния D3, и пользователь может включить или отключить сигнал пробуждения со страницы свойств устройства в Диспетчере устройств.

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

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
if (!NT_SUCCESS(status)) {
    return status;
}

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

По той же причине драйверы функций USB редко требуют обратного вызова EvtDeviceWakeFromS0Triggered или EvtDeviceWakeFromSxTriggered . Вместо этого платформа и базовый драйвер шины обрабатывают все требования для возврата устройства в рабочее состояние.