Cómo implementar la suspensión de la función en un controlador compuesto
En este artículo se proporciona información general sobre las características de suspensión y activación remota de funciones para dispositivos multifunción (dispositivos compuestos) de Bus serie universal (USB) 3.0. En este artículo, obtendrá información sobre cómo implementar esas características en un controlador que controla un dispositivo compuesto. El artículo se aplica a los controladores compuestos que reemplazan Usbccgp.sys.
La especificación Universal Serial Bus (USB) 3.0 define una nueva característica denominada suspensión de función. La característica permite que una función individual de un dispositivo compuesto entre en un estado de baja potencia, independientemente de otras funciones. Considere un dispositivo compuesto que define una función para el teclado y otra función para el mouse. El usuario mantiene la función de teclado en estado de trabajo, pero no mueve el mouse durante un período de tiempo. El controlador cliente del mouse puede detectar el estado inactivo de la función y enviar la función para suspender el estado mientras la función de teclado permanece en estado de trabajo.
Todo el dispositivo puede pasar al estado de suspensión independientemente del estado de alimentación de cualquier función dentro del dispositivo. Si una función determinada y todo el dispositivo entran en estado de suspensión, el estado de suspensión de la función se conserva mientras el dispositivo está en estado de suspensión y a lo largo de los procesos de entrada y salida de suspensión del dispositivo.
De forma similar a la característica de reactivación remota de un dispositivo USB 2.0 (consulte Reactivación remota de dispositivos USB), una función individual en un dispositivo compuesto USB 3.0 puede reactivarse desde un estado de baja potencia sin afectar a los estados de energía de otras funciones. Esta característica se denomina reactivación remota de función. El host habilita explícitamente la característica mediante el envío de una solicitud de protocolo que establece los bits de reactivación remota en el firmware del dispositivo. Este proceso se denomina arming la función para reactivación remota. Para obtener información sobre los bits relacionados con la reactivación remota, consulte la figura 9-6 en la especificación USB oficial.
Si una función está armada para reactivación remota, la función (cuando está en estado de suspensión) conserva suficiente energía para generar una señal de reanudación de reactivación cuando se produce un evento de usuario en el dispositivo físico. Como resultado de esa señal de reanudación, el controlador cliente puede salir del estado de suspensión de la función asociada. En el ejemplo de la función del mouse en el dispositivo compuesto, cuando el usuario alterna el mouse que está en estado inactivo, la función del mouse envía una señal de reanudación al host. En el host, la pila del controlador USB detecta qué función se despertó y propaga la notificación al controlador cliente de la función correspondiente. Después, el controlador cliente puede reactivar la función y especificar el estado de trabajo.
Para el controlador cliente, los pasos para enviar una función para suspender el estado y despertar la función es similar a un controlador de dispositivo de función única que envía todo el dispositivo para suspender el estado. En el procedimiento siguiente se resumen esos pasos.
- Detecte cuándo la función asociada está en estado inactivo.
- Enviar un paquete de solicitud de E/S inactiva (IRP).
- Envíe una solicitud a arm su función para reactivación remota mediante el envío de un paquete de solicitud de E/S de espera-reactivación (IRP).
- Realice la transición de la función a un estado de bajo consumo mediante el envío de IRP de energía Dx (D2 o D3).
Para obtener más información sobre los pasos anteriores, vea "Enviar un IRP de solicitud inactiva USB" en suspensión selectiva usb. Un controlador compuesto crea un objeto de dispositivo físico (PDO) para cada función del dispositivo compuesto y controla las solicitudes de energía enviadas por el controlador cliente (el FDO de la pila del dispositivo de función). Para que un controlador cliente entre y salga correctamente del estado de suspensión para su función, el controlador compuesto debe admitir las características de suspensión y reactivación remota de la función y procesar las solicitudes de energía recibidas.
En Windows 8, la pila de controladores USB para dispositivos USB 3.0 admite esas características. Además, se ha agregado la implementación de reactivación remota de la función y la suspensión de funciones al controlador primario genérico ( Usbccgp.sys) USB proporcionado por Microsoft, que es el controlador compuesto predeterminado de Windows. Si va a escribir un controlador compuesto personalizado, el controlador debe controlar las solicitudes relacionadas con la suspensión de funciones y las solicitudes de reactivación remota, según el procedimiento siguiente.
Paso 1: Determinar si la pila del controlador USB admite la suspensión de la función
En la rutina start-device (IRP_MN_START_DEVICE) del controlador compuesto, realice los pasos siguientes:
- Llame a la rutina USBD_QueryUsbCapability para determinar si la pila de controladores USB subyacente admite la funcionalidad de suspensión de la función. La llamada requiere un identificador USBD válido que obtuvo en la llamada anterior a la rutina USBD_CreateHandle .
Una llamada correcta a USBD_QueryUsbCapability determina si la pila de controladores USB subyacente admite la suspensión de la función. La llamada puede devolver un código de error que indica que la pila del controlador USB no admite la suspensión de la función o que el dispositivo conectado no es un dispositivo de varias funciones USB 3.0.
- Si la llamada USBD_QueryUsbCapability indica que se admite la suspensión de la función, registre el dispositivo compuesto con la pila de controladores USB subyacente. Para registrar el dispositivo compuesto, debe enviar una solicitud de control de E/S IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE . Para obtener más información sobre esta solicitud, consulte Registro de un dispositivo compuesto.
La solicitud de registro usa la estructura REGISTER_COMPOSITE_DEVICE para especificar esa información sobre el controlador compuesto. Asegúrese de establecer CapabilityFunctionSuspend en 1 para indicar que el controlador compuesto admite la suspensión de la función.
Para obtener un ejemplo de código que muestra cómo determinar si la pila del controlador USB admite la suspensión de la función, consulte USBD_QueryUsbCapability.
Paso 2: Control del IRP inactivo
El controlador cliente puede enviar un IRP inactivo (consulte IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). La solicitud se envía después de que el controlador de cliente haya detectado un estado inactivo para la función. IrP contiene un puntero a la rutina de finalización de devolución de llamada (denominada devolución de llamada inactiva) implementada por el controlador de cliente. Dentro de la devolución de llamada inactiva, el cliente realiza tareas, como cancelar transferencias de E/S pendientes, justo antes de enviar la función para suspender el estado.
Nota:
El mecanismo IRP inactivo es opcional para los controladores de cliente de dispositivos USB 3.0. Sin embargo, la mayoría de los controladores de cliente están escritos para admitir dispositivos USB 2.0 y USB 3.0. Para admitir dispositivos USB 2.0, el controlador debe enviar el IRP inactivo, ya que el controlador compuesto se basa en ese IRP para realizar un seguimiento del estado de energía de cada función. Si todas las funciones están inactivas, el controlador compuesto envía todo el dispositivo para suspender el estado.
Al recibir el IRP inactivo del controlador cliente, el controlador compuesto debe invocar inmediatamente la devolución de llamada inactiva para notificar al controlador cliente que el controlador cliente puede enviar la función para suspender el estado.
Paso 3: Enviar una solicitud de notificación de reactivación remota
El controlador cliente puede enviar una solicitud a arm su función para reactivación remota mediante el envío de un IRP de IRP_MJ_POWER con código de función secundario establecido en IRP_MN_WAIT_WAKE (IRP de espera-reactivación). El controlador cliente envía esta solicitud solo si el controlador quiere entrar en estado de trabajo como resultado de un evento de usuario.
Al recibir el IRP de espera-reactivación, el controlador compuesto debe enviar la solicitud de control de E/S de IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION a la pila del controlador USB. La solicitud permite que la pila del controlador USB notifique al controlador compuesto cuando la pila recibe la notificación sobre la señal de reanudación. El IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION usa la estructura REQUEST_REMOTE_WAKE_NOTIFICATION para especificar los parámetros de solicitud. Uno de los valores que el controlador compuesto debe especificar es el controlador de función para la función que está armada para la reactivación remota. El controlador compuesto obtuvo ese identificador en una solicitud anterior para registrar el dispositivo compuesto con la pila del controlador USB. Para obtener más información sobre las solicitudes de registro de controladores compuestos, consulte Registro de un dispositivo compuesto.
En el IRP de la solicitud, el controlador compuesto proporciona un puntero a una rutina de finalización (reactivación remota), que el controlador compuesto implementa.
En el código de ejemplo siguiente se muestra cómo enviar una solicitud de reactivación remota.
/*++
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;
}
La pila del controlador USB completa la solicitud IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION durante el proceso de reactivación cuando recibe una notificación sobre la señal de reanudación. Durante ese tiempo, la pila del controlador USB también invoca la rutina de finalización de reactivación remota.
El controlador compuesto debe mantener el IRP de espera-reactivación pendiente y ponerlo en cola para su procesamiento posterior. El controlador compuesto debe completar ese IRP cuando la rutina de finalización de reactivación remota del controlador se invoca mediante la pila de controladores USB.
Paso 4: Envío de una solicitud a arm la función para reactivación remota
Para enviar la función a un estado de bajo consumo, el controlador cliente envía una IRP_MN_SET_POWER IRP con la solicitud de cambiar el estado de alimentación del dispositivo Modelo de controlador de Windows (WDM) a D2 o D3. Normalmente, el controlador cliente envía D2 IRP si el controlador envió anteriormente un IRP de espera-reactivación para solicitar reactivación remota. De lo contrario, el controlador cliente envía D3 IRP.
Al recibir el IRP D2 , el controlador compuesto debe determinar primero si un IRP de espera-reactivación está pendiente de una solicitud anterior enviada por el controlador cliente. Si ese IRP está pendiente, el controlador compuesto debe arm la función para reactivación remota. Para ello, el controlador compuesto debe enviar una solicitud de control SET_FEATURE a la primera interfaz de la función, para permitir que el dispositivo envíe una señal de reanudación. Para enviar la solicitud de control, asigne una estructura URB llamando a la rutina USBD_UrbAllocate y llame a la macro UsbBuildFeatureRequest para dar formato al URB para una solicitud de SET_FEATURE. En la llamada, especifique URB_FUNCTION_SET_FEATURE_TO_INTERFACE como código de operación y el USB_FEATURE_FUNCTION_SUSPEND como selector de características. En el parámetro Index , establezca bit 1 del byte más significativo. Ese valor se copia en el campo wIndex del paquete de instalación de la transferencia.
En el ejemplo siguiente se muestra cómo enviar una solicitud de control 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;
}
A continuación, el controlador compuesto envía el IRP D2 a la pila del controlador USB. Si todas las demás funciones están en estado de suspensión, la pila del controlador USB suspende el puerto manipulando determinados registros de puerto en el controlador.
Comentarios
En el ejemplo de función del mouse, dado que la característica de reactivación remota está habilitada (consulte el paso 4), la función del mouse genera una señal de reanudación en la conexión ascendente al controlador de host cuando el usuario alterna el mouse. A continuación, el controlador notifica a la pila del controlador USB mediante el envío de un paquete de notificación que contiene información sobre la función que se despertó. Para obtener información sobre la notificación de reactivación de función, vea la figura 8-17 en la especificación USB 3.0.
Al recibir el paquete de notificación, la pila del controlador USB completa la solicitud de IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION pendiente (consulte el paso 3) e invoca la rutina de devolución de llamada de finalización (reactivación remota) especificada en la solicitud e implementada por el controlador compuesto. Cuando la notificación llega al controlador compuesto, notifica al controlador cliente correspondiente que la función ha entrado en estado de trabajo completando el IRP de espera-reactivación que el controlador cliente había enviado anteriormente.
En la rutina de finalización (reactivación remota), el controlador compuesto debe poner en cola un elemento de trabajo para completar el IRP de reactivación de espera pendiente. En el caso de los dispositivos USB 3.0, el controlador compuesto reactiva solo la función que envía la señal de reanudación y deja otras funciones en estado de suspensión. La puesta en cola del elemento de trabajo garantiza la compatibilidad con la implementación existente para los controladores de funciones de los dispositivos USB 2.0. Para obtener información sobre cómo poner en cola un elemento de trabajo, consulte IoQueueWorkItem.
El subproceso de trabajo completa el IRP de reactivación de espera e invoca la rutina de finalización del controlador cliente. A continuación, la rutina de finalización envía un IRP D0 para especificar la función en estado de trabajo. Antes de completar el IRP de espera-reactivación, el controlador compuesto debe llamar a PoSetSystemWake para marcar el IRP de reactivación de espera como el que contribuyó a despertar el sistema desde el estado de suspensión. El administrador de energía registra un evento de seguimiento de eventos para Windows (ETW) (visible en el canal del sistema global) que incluye información sobre los dispositivos que despertó el sistema.