Suspensión selectiva en controladores de función USB KMDF

En este artículo se describe cómo los controladores de función KMDF admiten la suspensión selectiva USB.

Si el controlador USB requiere características o recursos que no están disponibles en modo de usuario, debe proporcionar un controlador de función KMDF. Los controladores de KMDF implementan la suspensión selectiva estableciendo valores relevantes en una estructura de inicialización de KMDF y proporcionando las funciones de devolución de llamada adecuadas. KMDF gestiona los detalles de la comunicación con controladores de nivel inferior para suspender y reanudar el dispositivo.

Directrices para la suspensión selectiva en controladores KMDF

Los controladores KMDF que admiten la suspensión selectiva deben seguir estas directrices:

  • Un controlador de función KMDF debe ser el PPO para su pila de dispositivos. De forma predeterminada, los controladores de función KMDF son el PPO.
  • Un controlador de función KMDF que admita la suspensión selectiva puede utilizar colas con gestión de energía o colas sin gestión de energía. De forma predeterminada, los objetos de cola para PPOs se gestionan energéticamente.

Política de energía y controladores USB de KMDF

De forma predeterminada, el controlador de función KMDF para un dispositivo USB es el PPO para la pila de dispositivos. KMDF administra la suspensión y reanudación selectiva en nombre de este controlador.

Configuración de cola de E/S en controladores KMDF

Un controlador de función KMDF que admita la suspensión selectiva puede usar colas administradas por energía o colas que no están administradas por energía. Normalmente, un controlador configura una cola que no se administra por energía para recibir solicitudes entrantes de control de E/S de dispositivo y configura una o varias colas gestionadas por energía para recibir solicitudes de lectura, escritura y otras solicitudes dependientes de energía. Cuando una solicitud llega a una cola administrada por energía, KMDF garantiza que el dispositivo está en D0 antes de presentar la solicitud al controlador.

Si está escribiendo un controlador de filtro KMDF que está superpuesto por encima del PPO en la pila de dispositivos, no debe usar colas gestionadas por energía. El motivo es el mismo que para los controladores UMDF. El marco no presenta solicitudes de colas administradas por energía mientras el dispositivo está suspendido, por lo que el uso de estas colas podría detener la pila de dispositivos.

Mecanismo de suspensión selectiva para controladores de función KMDF

KMDF controla la mayor parte del trabajo necesario para admitir la suspensión selectiva USB. Realiza un seguimiento de la actividad de E/S, administra el temporizador de inactividad y envía las solicitudes de control de E/S del dispositivo que hacen que el controlador primario (Usbhub.sys o Usbccgp.sys) suspenda y reanude el dispositivo.

Si un controlador de función de KMDF admite la suspensión selectiva, KMDF realiza un seguimiento de la actividad de E/S en todas las colas administradas por energía que posee cada objeto de dispositivo. El marco inicia un temporizador de inactividad cada vez que el recuento de E/S alcanza cero. El valor de tiempo de espera predeterminado es de 5 segundos.

Si una solicitud de E/S llega a una cola administrada por energía que pertenece al objeto de dispositivo antes de que expire el período de tiempo de espera de inactividad, el marco cancela el temporizador de inactividad y no suspende el dispositivo.

Cuando expira el temporizador de inactividad, KMDF emite las solicitudes necesarias para colocar el dispositivo USB en estado suspendido. Si un controlador de función emplea un lector continuo en un punto de conexión USB, el sondeo repetido del lector no se considera como actividad para el temporizador de inactividad de KMDF. Sin embargo, en la función de devolución de llamada EvtDeviceD0Exit, el controlador USB debe detener manualmente el lector continuo y cualquier otro destino de E/S alimentado por colas que no estén gestionados por energía para asegurar que el controlador no envíe solicitudes de E/S mientras el dispositivo no está en estado de funcionamiento. Para detener los destinos, el controlador llama a WdfIoTargetStop y especifica WdfIoTargetWaitForSentIoToComplete como la acción de destino. En respuesta, el framework detiene el objetivo de E/S solo después de que se hayan completado todas las solicitudes de E/S que se encuentran en la cola de E/S del objetivo y se hayan ejecutado las devoluciones de llamada de finalización de E/S asociadas.

De forma predeterminada, KMDF realiza la transición del dispositivo fuera de D0 y al estado de alimentación del dispositivo que el controlador especificó en la configuración de inactividad. Como parte de la transición, KMDF llama a las funciones de retorno de llamada de energía del controlador de la misma manera que lo haría para cualquier otra secuencia de apagado.

Una vez suspendido el dispositivo, el marco reanuda automáticamente el dispositivo cuando se produce cualquiera de los eventos siguientes:

Para reanudar el dispositivo, KMDF envía una solicitud de encendido a la pila de dispositivos y, a continuación, invoca las funciones de devolución de llamada del controlador de la misma manera que lo haría para cualquier otra secuencia de encendido.

Para obtener información detallada sobre las devoluciones de llamada implicadas en las secuencias de apagado y encendido, consulte la conferencia técnica Plug and Play and Power Management in WDF Drivers (Administración de energía y administración de energía en controladores WDF).

Compatibilidad con la suspensión selectiva de USB en un controlador de función KMDF

Para implementar la suspensión selectiva USB en un controlador de función del KMDF:

  • Inicialice la configuración de las políticas de energía relativas al estado inactivo, incluido el tiempo de espera de inactividad.
  • Opcionalmente, incluya lógica para evitar temporalmente la suspensión o la reanudación de la operación cuando el controlador determine que el dispositivo no debe suspenderse debido a un identificador abierto u otro motivo que no esté relacionado con las colas de E/S del dispositivo.
  • En un controlador USB para un dispositivo de interfaz humana (HID), indique en el INF que admite la suspensión selectiva.

Inicialización de la configuración de la directiva de energía en un controlador de función KMDF

Para configurar el soporte para la suspensión selectiva de USB, un controlador KMDF utiliza la estructura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS. El controlador debe inicializar primero la estructura y, a continuación, establecer campos que proporcionen detalles sobre las funcionalidades del controlador y su dispositivo. Normalmente, el controlador rellena esta estructura en su función EvtDriverDeviceAdd o EvtDevicePrepareHardware .

Para inicializar la estructura de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS

Después de que el controlador cree el objeto de dispositivo, el controlador usa la función WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT para inicializar la estructura. Esta función toma dos argumentos:

  • Puntero a la estructura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS que se va a inicializar.
  • Valor de enumeración que indica la compatibilidad con la suspensión selectiva. El controlador debe especificar IdleUsbSelectiveSuspend.

Si el controlador especifica IdleUsbSelectiveSuspend, la función inicializa los miembros de la estructura de la siguiente manera:

  • IdleTimeout se establece en IdleTimeoutDefaultValue (actualmente 5000 milisegundos o 5 segundos).
  • UserControlOfIdleSettings se establece en IdleAllowUserControl .
  • Enabled se establece en WdfUseDefault, lo que indica que la suspensión selectiva está habilitada, pero un usuario puede deshabilitarla si el miembro UserControlOfIdleSettings lo permite.
  • DxState se establece en PowerDeviceMaximum, que usa las funcionalidades de energía notificadas para el dispositivo para determinar el estado al que se va a realizar la transición del dispositivo inactivo.

Para configurar la suspensión selectiva de USB

Una vez que el controlador inicializa la estructura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS , el controlador puede establecer otros campos en la estructura y, a continuación, llamar a WdfDeviceAssignS0IdleSettings para pasar esta configuración al marco. Los siguientes campos se aplican a los controladores de función USB:

  • IdleTimeout: el intervalo, en milisegundos, que debe transcurrir sin recibir una solicitud de E/S antes de que el marco considere que el dispositivo está inactivo. El controlador puede especificar un valor ULONG o puede aceptar el valor predeterminado.

  • UserControlOfIdleSettings: indica si el usuario puede modificar la configuración de inactividad del dispositivo. Los valores posibles son IdleDoNotAllowUserControl e IdleAllowUserControl.

  • DxState: el estado de alimentación del dispositivo al que el marco suspende el dispositivo. Los valores posibles son PowerDeviceD1, PowerDeviceD2 y PowerDeviceD3.

    Los controladores USB no deben cambiar la configuración inicial de este valor. La función WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT establece este valor en PowerDeviceMaximum, lo que garantiza que el marco elija el valor correcto en función de las funcionalidades del dispositivo.

El siguiente fragmento de código procede del archivo Device.c del controlador de ejemplo de 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;
}

En el ejemplo, el controlador llama a WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT, especificando IdleUsbSelectiveSuspend. El controlador establece IdleTimeout en 10 000 milisegundos (10 segundos) y acepta los valores predeterminados del marco para DxState y UserControlOfIdleSettings. Como resultado, el marco pasa el dispositivo al estado D3 cuando está inactivo y crea una página de propiedades del Administrador de dispositivos que permite a los usuarios con privilegios de administrador habilitar o deshabilitar la compatibilidad de inactividad del dispositivo. A continuación, el controlador llama a WdfDeviceAssignS0IdleSettings para habilitar la compatibilidad inactiva y registrar esta configuración en el marco.

Un controlador puede llamar a WdfDeviceAssignS0IdleSettings en cualquier momento después de crear el objeto de dispositivo. Aunque la mayoría de los controladores llaman inicialmente a este método desde la devolución de llamada EvtDriverDeviceAdd , es posible que esto no siempre sea posible o incluso deseable. Si un controlador admite varios dispositivos o versiones de dispositivo, es posible que el controlador no conozca todas las funcionalidades del dispositivo hasta que consulte el hardware. Estos controladores pueden posponer la invocación de WdfDeviceAssignS0IdleSettings hasta la ejecución de la devolución de llamada EvtDevicePrepareHardware.

En cualquier momento después de su llamada inicial a WdfDeviceAssignS0IdleSettings, el controlador puede cambiar el valor de tiempo de espera de inactividad y el estado del dispositivo en el que el dispositivo está inactivo. Para cambiar una o más configuraciones, el controlador simplemente inicializa otra estructura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS tal como se describió anteriormente y vuelve a llamar a WdfDeviceAssignS0IdleSettings.

Prevención de la suspensión del dispositivo USB

A veces, un dispositivo USB no debe apagarse incluso si no hay solicitudes de E/S presentes dentro del período de tiempo de espera, normalmente cuando un controlador está abierto al dispositivo o el dispositivo se está cargando. Un controlador USB puede impedir que el marco suspenda un dispositivo inactivo en tales situaciones llamando a WdfDeviceStopIdle y llamando a WdfDeviceResumeIdle cuando vuelva a ser aceptable para que el dispositivo se suspenda.

WdfDeviceStopIdle detiene el temporizador de inactividad. Si el período IdleTimeout no ha expirado y el dispositivo aún no se ha suspendido, el marco cancela el temporizador de inactividad y no suspende el dispositivo. Si el dispositivo ya se ha suspendido, el marco devuelve el dispositivo al estado de trabajo. WdfDeviceStopIdleno impide que el marco suspenda el dispositivo cuando el sistema cambia a un estado de suspensión Sx. Su único efecto es evitar la suspensión del dispositivo mientras el sistema está en estado de funcionamiento S0. WdfDeviceResumeIdle reinicia el temporizador de inactividad. Estos dos métodos administran un recuento de referencias en el dispositivo, por lo que si el controlador llama a WdfDeviceStopIdle varias veces, el marco no suspende el dispositivo hasta que el controlador haya llamado a WdfDeviceResumeIdle el mismo número de veces. Un controlador no debe llamar a WdfDeviceResumeIdlesin llamar primero a WdfDeviceStopIdle.

Incluir una clave del Registro (solo controladores HID)

Los controladores de filtro de nivel superior de KMDF para dispositivos USB HID deben especificar en el archivo INF que admiten la suspensión selectiva de modo que el controlador de puerto HIDClass.sys suministrado por Microsoft pueda habilitar la suspensión selectiva para la pila HID. El INF debe incluir una directiva AddReg que agregue la clave SelectiveSuspendEnabled y establezca su valor en 1, como se muestra en la cadena siguiente:

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

Para obtener un ejemplo, vea Hidusbfx2.inx en WDK en %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys.

Compatibilidad con reactivación remota para controladores KMDF

Al igual que con la suspensión selectiva, KMDF incorpora compatibilidad para la reactivación, de modo que un dispositivo USB pueda desencadenar una señal de reactivación mientras el dispositivo está inactivo y el sistema está en estado de trabajo (S0) o en estado de suspensión (S1–S4). En términos de KMDF, estas dos características se denominan "wake from S0" y "wake from Sx", respectivamente.

En el caso de los dispositivos USB, la reactivación simplemente indica que el propio dispositivo puede iniciar la transición de un estado de menor potencia al estado de trabajo. Por lo tanto, en términos de USB, despertar desde el estado S0 y desde el estado Sx son lo mismo, y se denomina "despertar remoto".

Los controladores de función USB de KMDF no requieren ningún código para admitir la reactivación desde S0 porque KMDF proporciona esta funcionalidad como parte del mecanismo de suspensión selectiva. Sin embargo, para admitir la reactivación remota cuando el sistema está en el estado Sx, un controlador funcional debe:

Los controladores KMDF suelen configurar la compatibilidad con reactivación al mismo tiempo que configuran la compatibilidad con la suspensión selectiva usb en la función EvtDriverDeviceAdd o EvtDevicePrepareHardware .

Comprobación de las funcionalidades del dispositivo

Antes de que un controlador de función USB KMDF inicialice su configuración de directiva de energía para inactivo y reactivación, debe comprobar que el dispositivo admite la reactivación remota. Para obtener información sobre las características de hardware del dispositivo, el controlador inicializa una estructura de WDF_USB_DEVICE_INFORMATION y llama a WdfUsbTargetDeviceRetrieveInformation, normalmente en su EvtDriverDeviceAdd o EvtDevicePrepareHardware callback.

En la llamada a WdfUsbTargetDeviceRetrieveInformation, el controlador pasa un identificador al objeto de dispositivo y un puntero a la estructura inicializada WDF_USB_DEVICE_INFORMATION. Tras la devolución correcta de la función, el campo Rasgos de la estructura contiene marcas que indican si el dispositivo es autopropulsado, puede funcionar a alta velocidad y admite la reactivación remota.

En el ejemplo siguiente del ejemplo de KMDF de Osrusbfx2 se muestra cómo llamar a este método para determinar si un dispositivo admite la reactivación remota. Una vez ejecutadas estas líneas de código, la variable waitWakeEnable contiene TRUE si el dispositivo admite reactivación remota y FALSE si no:

    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;

Habilitación de la reactivación remota

En la terminología USB, se habilita un dispositivo USB para la reactivación remota cuando se establece su característica de DEVICE_REMOTE_WAKEUP. Según la especificación USB, el software host debe establecer la característica de reactivación remota en un dispositivo "justo antes" de poner el dispositivo en suspensión. El controlador de función KMDF solo es necesario para inicializar la configuración de reactivación. KMDF y los controladores de bus USB proporcionados por Microsoft emiten las solicitudes de E/S y controlan la manipulación de hardware necesaria para habilitar la reactivación remota.

Para inicializar la configuración de reactivación

  1. Llame a WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT para inicializar una estructura WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS. Esta función establece el miembro Enabled de la estructura en WdfUseDefault, establece el miembro DxState en PowerDeviceMaximum y establece el miembro UserControlOfWakeSettings en WakeAllowUserControl.
  2. Llame a WdfDeviceAssignSxWakeSettings con la estructura inicializada. Como resultado, el dispositivo está habilitado para reactivarse desde el estado D3 y el usuario puede habilitar o deshabilitar la señal de reactivación desde la página de propiedades del dispositivo en el Administrador de dispositivos.

El siguiente fragmento de código del ejemplo Osrusbfx2 muestra cómo inicializar la configuración de reactivación a sus valores por defecto:

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;
}

En el caso de los dispositivos USB que admiten la suspensión selectiva, el controlador de bus subyacente prepara el hardware del dispositivo para reactivarse. Por lo tanto, los controladores de función USB rara vez requieren una devolución de llamada EvtDeviceArmWakeFromS0 . El marco envía una solicitud de suspensión selectiva al controlador del bus USB cuando expira el tiempo de espera de inactividad.

Por el mismo motivo, los controladores de funciones USB rara vez requieren un callback EvtDeviceWakeFromS0Triggered o EvtDeviceWakeFromSxTriggered. En su lugar, el marco y el controlador de bus subyacente controlan todos los requisitos para devolver el dispositivo al estado de trabajo.