Suspensión selectiva de USB
Nota:
Este artículo es para desarrolladores de controladores de dispositivos. Si tiene dificultades con un dispositivo USB, consulte Corregir problemas de USB-C en Windows.
La característica de suspensión selectiva USB permite al controlador del concentrador suspender un puerto individual sin afectar al funcionamiento de los demás puertos del concentrador. La suspensión selectiva de dispositivos USB es especialmente útil en equipos portátiles, ya que ayuda a conservar la energía de la batería. Muchos dispositivos, como lectores de huellas digitales y otros tipos de escáneres biométricos, solo requieren alimentación intermitente. Suspender estos dispositivos, cuando el dispositivo no está en uso, reduce el consumo general de energía. Lo más importante es que cualquier dispositivo que no se suspenda selectivamente puede impedir que el controlador de host USB deshabilite su programación de transferencia, que reside en la memoria del sistema. Las transferencias de acceso directo a memoria (DMA) del controlador host al programador pueden impedir que los procesadores del sistema entren en estados de suspensión más profundos, como C3.
Hay dos mecanismos diferentes para suspender selectivamente un dispositivo USB: IRP de solicitud inactiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) y establecer IRP de energía (IRP_MN_SET_POWER). El mecanismo que se va a usar depende del sistema operativo y del tipo de dispositivo: compuesto o no compuesto.
Selección de un mecanismo de suspensión selectiva
Los controladores de cliente, para una interfaz en un dispositivo compuesto, que habilitan la interfaz para la reactivación remota con un IRP de reactivación de espera (IRP_MN_WAIT_WAKE), deben usar el mecanismo IRP de solicitud inactiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) para suspender selectivamente un dispositivo.
Para obtener información sobre la reactivación remota, consulte:
- Reactivación remota de dispositivos USB
- Información general sobre la operación de espera/reactivación
La versión del sistema operativo Windows determina la forma en que los controladores para dispositivos no compuestos habilitan la suspensión selectiva.
- Windows XP: en Windows XP, todos los controladores cliente deben usar IRP de solicitud inactiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) para apagar sus dispositivos. Los controladores de cliente no deben usar IRP de alimentación WDM para suspender selectivamente sus dispositivos. Al hacerlo, se impide que otros dispositivos se suspendan de forma selectiva.
- Windows Vista y versiones posteriores de Windows: los escritores de controladores tienen más opciones para apagar dispositivos en Windows Vista y en las versiones posteriores de Windows. Aunque Windows Vista admite el mecanismo IRP de solicitud inactiva de Windows, los controladores no son necesarios para usarlo.
En la tabla siguiente se muestran los escenarios que requieren el uso del IRP de solicitud inactiva y los que pueden usar un IRP de energía WDM para suspender un dispositivo USB:
Versión de Windows | Función en un dispositivo compuesto, armado para reactivación | Función en un dispositivo compuesto, no armado para reactivación | Dispositivo USB de interfaz única |
---|---|---|---|
Windows 7 | Uso de IRP de solicitud inactiva | Uso de IRP de energía WDM | Uso de IRP de energía WDM |
Windows Server 2008 | Uso de IRP de solicitud inactiva | Uso de IRP de energía WDM | Uso de IRP de energía WDM |
Windows Vista | Uso de IRP de solicitud inactiva | Uso de IRP de energía WDM | Uso de IRP de energía WDM |
Windows Server 2003 | Uso de IRP de solicitud inactiva | Uso de IRP de solicitud inactiva | Uso de IRP de solicitud inactiva |
Windows XP | Uso de IRP de solicitud inactiva | Uso de IRP de solicitud inactiva | Uso de IRP de solicitud inactiva |
En esta sección se explica el mecanismo de suspensión selectiva de Windows.
Envío de una solicitud de inactividad USB IRP
Cuando un dispositivo deja de estar inactivo, el controlador cliente informa al controlador de bus mediante el envío de un IRP de solicitud inactiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Una vez que el controlador de bus determina que es seguro colocar el dispositivo en un estado de baja potencia, llama a la rutina de devolución de llamada que el controlador de dispositivo cliente ha pasado la pila con el IRP de solicitud inactiva.
En la rutina de devolución de llamada, el controlador cliente debe cancelar todas las operaciones de E/S pendientes y esperar a que se completen todos los IRP de E/S USB. A continuación, puede emitir una solicitud de IRP_MN_SET_POWER para cambiar el estado de alimentación del dispositivo WDM a D2. La rutina de devolución de llamada debe esperar a que se complete la solicitud D2 antes de volver. Para obtener más información sobre la rutina de devolución de llamada de notificación inactiva, vea "Rutina de devolución de llamada de notificación inactiva USB".
El controlador de bus no completa el IRP de solicitud inactiva después de llamar a la rutina de devolución de llamada de notificación inactiva. En su lugar, el controlador de bus contiene la solicitud inactiva IRP pendiente hasta que se cumple una de las condiciones siguientes:
- Se recibe un IRP de IRP_MN_SUPRISE_REMOVAL o IRP_MN_REMOVE_DEVICE. Cuando se recibe uno de estos IRP de solicitud inactiva, IRP se completa con STATUS_CANCELLED.
- El controlador de bus recibe una solicitud para colocar el dispositivo en un estado de energía de trabajo (D0). Al recibir este controlador de bus de solicitud, se completa el IRP de solicitud inactiva pendiente con STATUS_SUCCESS.
Las restricciones siguientes se aplican al uso de IRP de solicitud inactiva:
- Los controladores deben estar en estado de alimentación del dispositivo D0 al enviar un IRP de solicitud inactiva.
- Los controladores deben enviar solo una solicitud inactiva IRP por pila de dispositivos.
En el siguiente código de ejemplo de WDM se muestran los pasos que lleva un controlador de dispositivo para enviar un IRP de solicitud inactiva USB. Se ha omitido la comprobación de errores en el ejemplo de código siguiente.
Asignar e inicializar el IRP de IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION
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);
Asigne e inicialice la estructura de información de solicitud inactiva (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;
Establezca una rutina de finalización.
El controlador cliente debe asociar una rutina de finalización con el IRP de solicitud inactiva. Para obtener más información sobre la rutina de finalización de notificaciones inactivas y el código de ejemplo, vea "Rutina de finalización de IRP de solicitud inactiva USB".
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);
Almacene la solicitud inactiva en la extensión del dispositivo.
deviceExtension->PendingIdleIrp = irp;
Envíe la solicitud De inactividad al controlador primario.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Cancelación de una solicitud de inactividad USB
En determinadas circunstancias, es posible que un controlador de dispositivo tenga que cancelar una solicitud inactiva IRP que se ha enviado al controlador de autobús. Esto puede ocurrir si se quita el dispositivo, se activa después de estar inactivo y enviando la solicitud de inactividad, o si todo el sistema está pasando a un estado de energía del sistema inferior.
El controlador cliente cancela el IRP inactivo llamando a IoCancelIrp. En la tabla siguiente se describen tres escenarios para cancelar un IRP inactivo y se especifica la acción que debe realizar el controlador:
Escenario | Mecanismo de cancelación de solicitudes inactivas |
---|---|
El controlador cliente ha cancelado el IRP inactivo y la pila del controlador USB no ha llamado "Rutina de devolución de llamada de notificación inactiva USB". | La pila del controlador USB completa el IRP inactivo. Dado que el dispositivo nunca abandonó el D0, el controlador no cambia el estado del dispositivo. |
El controlador cliente ha cancelado el IRP inactivo, la pila del controlador USB ha llamado la rutina de devolución de llamada de notificación de inactividad USB y aún no ha devuelto. | Es posible que se invoque la rutina de devolución de llamada de notificación inactiva USB aunque el controlador cliente haya invocado la cancelación en el IRP. En este caso, la rutina de devolución de llamada del controlador cliente debe apagar el dispositivo enviando el dispositivo a un estado de energía inferior sincrónicamente. Cuando el dispositivo está en estado de menor potencia, el controlador cliente puede enviar una solicitud D0 . Como alternativa, el controlador puede esperar a que la pila del controlador USB complete el IRP inactivo y, a continuación, envíe el IRP D0 . Si la rutina de devolución de llamada no puede colocar el dispositivo en un estado de baja potencia debido a una memoria insuficiente para asignar un IRP de energía, debe cancelar el IRP inactivo y salir inmediatamente. El IRP inactivo no se completará hasta que se haya devuelto la rutina de devolución de llamada; Por lo tanto, la rutina de devolución de llamada no debe bloquear la espera de que se complete el IRP inactivo cancelado. |
El dispositivo ya está en estado de baja potencia. | Si el dispositivo ya está en un estado de baja potencia, el controlador cliente puede enviar un IRP D0 . La pila del controlador USB completa el IRP de solicitud inactiva con STATUS_SUCCESS. Como alternativa, el controlador puede cancelar el IRP inactivo, esperar a que la pila del controlador USB complete el IRP inactivo y, a continuación, enviar un IRP D0 . |
Rutina de finalización de IRP de solicitud inactiva USB
En muchos casos, un controlador de autobús podría llamar a la rutina de finalización de IRP de solicitud inactiva de un controlador. Si esto ocurre, un controlador cliente debe detectar por qué el controlador de bus completó el IRP. El código de estado devuelto puede proporcionar esta información. Si el código de estado no es STATUS_POWER_STATE_INVALID, el controlador debe colocar su dispositivo en D0 si el dispositivo aún no está en D0. Si el dispositivo sigue inactivo, el controlador puede enviar otro IRP de solicitud inactiva.
Nota:
La rutina de finalización de IRP de solicitud inactiva no debe bloquear la espera de que se complete una solicitud de energía D0 . El controlador central puede llamar a la rutina de finalización en el contexto de un IRP de energía, y el bloqueo en otro IRP de energía en la rutina de finalización puede provocar un interbloqueo.
En la lista siguiente se indica cómo una rutina de finalización de una solicitud inactiva debe interpretar algunos códigos de estado comunes:
Código de estado | Descripción |
---|---|
STATUS_SUCCESS | Indica que el dispositivo ya no debe suspenderse. Sin embargo, los controladores deben comprobar que sus dispositivos están encendidos y colocarlos en D0 si aún no están en D0. |
STATUS_CANCELLED | El controlador de autobús completa el IRP de solicitud inactiva con STATUS_CANCELLED en cualquiera de las circunstancias siguientes:
|
STATUS_POWER_STATE_INVALID | Indica que el controlador de dispositivo solicitó un estado de alimentación D3 para su dispositivo. Cuando esto ocurre, el controlador de bus completa todos los IRP inactivos pendientes con STATUS_POWER_STATE_INVALID. |
STATUS_DEVICE_BUSY | Indica que el controlador de bus ya contiene una solicitud inactiva IRP pendiente para el dispositivo. Solo un IRP inactivo puede estar pendiente a la vez para un dispositivo determinado. El envío de varios IRP de solicitud inactiva es un error por parte del propietario de la directiva de energía y debe solucionarlo el escritor de controladores. |
En el ejemplo de código siguiente se muestra una implementación de ejemplo para la rutina de finalización de solicitudes inactivas.
/*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;
}
Rutina de devolución de llamada de notificación inactiva USB
El controlador de bus (ya sea una instancia del controlador central o el controlador primario genérico) determina cuándo es seguro suspender los elementos secundarios de su dispositivo. Si es así, llama a la rutina de devolución de llamada de notificación inactiva proporcionada por el controlador cliente de cada elemento secundario.
El prototipo de función para USB_IDLE_CALLBACK es el siguiente:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
Un controlador de dispositivo debe realizar las siguientes acciones en su rutina de devolución de llamada de notificación inactiva:
- Solicite una IRP_MN_WAIT_WAKE IRP para el dispositivo si el dispositivo debe estar armado para la reactivación remota.
- Cancele todas las E/S y prepare el dispositivo para ir a un estado de energía inferior.
- Coloque el dispositivo en un estado de suspensión de WDM llamando a PoRequestPowerIrp con el parámetro PowerState establecido en el valor del enumerador PowerDeviceD2 (definido en wdm.h; ntddk.h). En Windows XP, un controlador no debe colocar su dispositivo en PowerDeviceD3, incluso si el dispositivo no está armado para la reactivación remota.
En Windows XP, un controlador debe depender de una rutina de devolución de llamada de notificación inactiva para suspender selectivamente un dispositivo. Si un controlador que se ejecuta en Windows XP coloca un dispositivo en un estado de energía inferior directamente sin usar una rutina de devolución de llamada de notificación inactiva, esto podría impedir que otros dispositivos del árbol de dispositivos USB se suspendan.
Tanto el controlador del concentrador como el controlador primario genérico USB (Usbccgp.sys) llaman a la rutina de devolución de llamada de notificación inactiva en IRQL = PASSIVE_LEVEL. Esto permite que la rutina de devolución de llamada se bloquee mientras espera a que se complete la solicitud de cambio de estado de energía.
La rutina de devolución de llamada solo se invoca mientras el sistema está en S0 y el dispositivo está en D0.
Las restricciones siguientes se aplican a las rutinas de devolución de llamada de notificación de solicitud inactivas:
- Los controladores de dispositivo pueden iniciar una transición de estado de energía del dispositivo de D0 a D2 en la rutina de devolución de llamada de notificación inactiva, pero no se permite ninguna otra transición de estado de energía. En concreto, un controlador no debe intentar cambiar su dispositivo a D0 mientras ejecuta su rutina de devolución de llamada.
- Los controladores de dispositivos no deben solicitar más de un IRP de energía desde la rutina de devolución de llamada de notificación inactiva.
Arming devices for wakeup in the idle notification callback routine (Arming devices for wakeup in the idle notification callback routine)
La rutina de devolución de llamada de notificación inactiva debe determinar si su dispositivo tiene una solicitud IRP_MN_WAIT_WAKE pendiente. Si no hay ninguna solicitud IRP_MN_WAIT_WAKE pendiente, la rutina de devolución de llamada debe enviar una solicitud de IRP_MN_WAIT_WAKE antes de suspender el dispositivo. Para obtener más información sobre el mecanismo de reactivación de espera, consulte Compatibilidad con dispositivos que tienen funcionalidades de reactivación.
Suspensión global de USB
La especificación USB 2.0 define la suspensión global como la suspensión de todo el bus detrás de un controlador de host USB cediendo todo el tráfico USB en el bus, incluidos los paquetes de inicio de fotograma. Los dispositivos de bajada que aún no están suspendidos detectan el estado Inactivo en su puerto ascendente y entran en el estado de suspensión por su cuenta. Windows no implementa la suspensión global de esta manera. Windows siempre suspende de forma selectiva cada dispositivo USB detrás de un controlador de host USB antes de que deje de funcionar todo el tráfico USB en el bus.
- Condiciones para la suspensión global en Windows 7
- Condiciones para la suspensión global en Windows Vista
- Condiciones para la suspensión global en Windows XP
- Temas relacionados
Condiciones para la suspensión global en Windows 7
Windows 7 es más agresivo sobre la suspensión selectiva de concentradores USB que Windows Vista. El controlador del concentrador USB de Windows 7 suspenderá selectivamente cualquier concentrador en el que todos sus dispositivos conectados estén en estado de alimentación del dispositivo D1, D2 o D3 . Todo el bus entra en suspensión global una vez que se suspenden todos los concentradores USB. La pila de controladores USB de Windows 7 trata un dispositivo como Inactivo siempre que el dispositivo esté en un estado de dispositivo WDM de D1, D2 o D3.
Condiciones para la suspensión global en Windows Vista
Los requisitos para realizar una suspensión global son más flexibles en Windows Vista que en Windows XP.
En concreto, la pila USB trata un dispositivo como Inactivo en Windows Vista cada vez que el dispositivo está en un estado de dispositivo WDM de D1, D2 o D3.
En el diagrama siguiente se muestra un escenario que puede producirse en Windows Vista.
En este diagrama se muestra una situación similar a la que se muestra en la sección "Condiciones de suspensión global en Windows XP". Sin embargo, en este caso el dispositivo 3 se califica como un dispositivo inactivo. Dado que todos los dispositivos están inactivos, el controlador de bus puede llamar a las rutinas de devolución de llamada de notificación inactivas asociadas a los IRP de solicitud inactiva pendientes. Cada controlador suspende su dispositivo y el controlador de bus suspende el controlador host USB tan pronto como sea seguro para hacerlo.
En Windows Vista, todos los dispositivos USB que no son de concentrador deben estar en D1, D2 o D3 antes de que se inicie la suspensión global, en cuyo momento se suspenden todos los concentradores USB, incluido el concentrador raíz. Esto significa que cualquier controlador de cliente USB que no admita la suspensión selectiva, impide que el bus entre en suspensión global.
Condiciones para la suspensión global en Windows XP
Para maximizar el ahorro de energía en Windows XP, es importante que cada controlador de dispositivo use IRP de solicitud inactiva para suspender su dispositivo. Si un controlador suspende su dispositivo con una solicitud de IRP_MN_SET_POWER en lugar de irP de solicitud inactiva, podría impedir que otros dispositivos se suspendan.
En el diagrama siguiente se muestra un escenario que puede producirse en Windows XP.
En esta ilustración, el dispositivo 3 está en estado de energía D3 y no tiene una solicitud inactiva IRP pendiente. El dispositivo 3 no califica como un dispositivo inactivo para fines de una suspensión global en Windows XP, ya que no tiene una solicitud inactiva IRP pendiente con su elemento primario. Esto impide que el controlador de bus llame a las rutinas de devolución de llamada de solicitud inactivas asociadas a los controladores de otros dispositivos del árbol.
Habilitación de la suspensión selectiva
La suspensión selectiva está deshabilitada para las versiones de actualización de Microsoft Windows XP. Está habilitado para instalaciones limpias de Windows XP, Windows Vista y versiones posteriores de Windows.
Para habilitar la compatibilidad selectiva de suspensión para un centro raíz determinado y sus dispositivos secundarios, active la casilla en la pestaña Administración de energía del centro raíz USB en Administrador de dispositivos.
Como alternativa, puede habilitar o deshabilitar la suspensión selectiva estableciendo el valor de HcDisableSelectiveSuspend en la clave de software del controlador de puerto USB. Un valor de 1 deshabilita la suspensión selectiva. Un valor de 0 habilita la suspensión selectiva.
Por ejemplo, las siguientes líneas de Usbport.inf deshabilitan la suspensión selectiva para un controlador Hydra OHCI:
[OHCI_NOSS.AddReg.NT]
HKR,,"HcDisableSelectiveSuspend",0x00010001,1
Los controladores de cliente no deben intentar determinar si la suspensión selectiva está habilitada antes de enviar solicitudes inactivas. Deben enviar solicitudes inactivas siempre que el dispositivo esté inactivo. Si se produce un error en la solicitud inactiva, el controlador cliente debe restablecer el temporizador de inactividad y volver a intentarlo.