Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Este artigo é para desenvolvedores de driver de dispositivo. Se estiver a ter dificuldades com um dispositivo USB, consulte Corrigir problemas de USB-C no Windows
O recurso de suspensão seletiva USB permite que o driver do hub suspenda uma porta individual sem afetar a operação das outras portas no hub. Esta funcionalidade é útil em computadores portáteis, uma vez que ajuda a conservar a energia da bateria. Muitos dispositivos, como scanners biométricos, exigem energia apenas intermitentemente. A suspensão desses dispositivos, quando não estão em uso, reduz o consumo geral de energia. Mais importante ainda, qualquer dispositivo que não esteja seletivamente suspenso pode impedir que o controlador host USB desative sua programação de transferência, que reside na memória do sistema. As transferências de acesso direto à memória (DMA) pelo controlador host para o agendador podem impedir que os processadores do sistema entrem em estados de suspensão mais profundos, como C3.
A suspensão seletiva está habilitada por padrão. A Microsoft recomenda vivamente não desativar suspensão seletiva.
Os drivers de cliente não devem tentar determinar se a suspensão seletiva está habilitada antes de enviar solicitações ociosas. Eles devem enviar solicitações ociosas sempre que o dispositivo estiver ocioso. Se a solicitação ociosa falhar, o driver do cliente deve redefinir o temporizador ocioso e tentar novamente.
Para suspender seletivamente um dispositivo USB, existem dois mecanismos diferentes: IRPs de solicitação ociosa (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) e IRPs de energia definidos (IRP_MN_SET_POWER). O mecanismo a utilizar depende do tipo de dispositivo: composto ou não compósito.
Seleção de um mecanismo de suspensão seletiva
Os controladores cliente para uma interface num dispositivo composto, que habilitam a interface para ativação remota com um IRP de espera para ativação (IRP_MN_WAIT_WAKE), devem usar o mecanismo de notificação de inatividade IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) para suspender seletivamente um dispositivo.
Para obter informações sobre a ativação remota, consulte:
Esta seção explica o mecanismo de suspensão seletiva do Windows.
Enviando uma solicitação de inatividade USB IRP
Quando um dispositivo fica inativo, o controlador do cliente informa o controlador do barramento enviando um pedido de inatividade IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Depois que o motorista de autocarro determina que é seguro colocar o dispositivo em um estado de baixa potência, ele chama a rotina de retorno que o driver de dispositivo cliente passou pela pilha com o IRP de pedido ocioso.
Na rotina de retorno de chamada, o driver cliente deve cancelar todas as operações de entrada/saída pendentes e aguardar a conclusão de todos os IRPs de entrada/saída USB. Em seguida, ele pode emitir uma solicitação de IRP_MN_SET_POWER para alterar o estado de energia do dispositivo WDM para D2. A rotina de retorno de chamada deve aguardar que a solicitação D2 seja concluída antes de retornar. Para obter mais informações sobre a rotina de retorno de chamada de notificação ociosa, consulte Implementando uma rotina de retorno de chamada IRP de solicitação ociosa USB.
O motorista do ônibus não conclui o IRP de solicitação ociosa depois de chamar a rotina de retorno de chamada de notificação ociosa. Em vez disso, o motorista de ônibus mantém o IRP de solicitação ocioso pendente até que uma das seguintes condições seja verdadeira:
- É recebida uma IRP_MN_SURPRISE_REMOVAL ou IRP_MN_REMOVE_DEVICE IRP. Quando um desses IRPs é recebido, o pedido IRP em estado ocioso é concluído com STATUS_CANCELADO.
- O motorista do autocarro recebe um pedido para colocar o dispositivo num estado de energia funcional (D0). Ao receber essa solicitação, o motorista do ônibus preenche a solicitação ociosa pendente IRP com STATUS_SUCCESS.
As seguintes restrições se aplicam ao uso de IRPs de solicitação ociosa:
- Os drivers devem estar no estado de energia do dispositivo D0 ao enviar uma solicitação inativa IRP.
- Os drivers devem enviar apenas uma solicitação ociosa IRP por pilha de dispositivos.
O código de exemplo WDM a seguir ilustra as etapas que um driver de dispositivo executa para enviar um IRP de solicitação ociosa USB. A verificação de erros é omitida no exemplo de código a seguir.
Alocar e inicializar o IRP 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);Aloque e inicialize a estrutura de informações de solicitação ociosa (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;Defina uma rotina de conclusão.
O controlador do cliente deve associar uma rotina de conclusão ao IRP de pedido inativo. Para obter mais informações sobre a rotina de conclusão de notificação ociosa e o código de exemplo, consulte Implementando uma rotina de conclusão de IRP de solicitação ociosa USB.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);Armazene a solicitação ociosa na extensão do dispositivo.
deviceExtension->PendingIdleIrp = irp;Envie a solicitação de inatividade para o driver pai.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Cancelar um pedido de inatividade USB
Em determinadas circunstâncias, um driver de dispositivo pode precisar cancelar uma solicitação ociosa IRP enviada ao motorista de ônibus. Essa situação pode ocorrer se o dispositivo for removido, ficar ativo depois de ficar ocioso e enviar a solicitação ociosa, ou se todo o sistema estiver em transição para um estado de energia inferior do sistema.
O driver do cliente cancela o IRP ocioso chamando IoCancelIrp. A tabela a seguir descreve três cenários para cancelar um IRP ocioso e especifica a ação que o driver deve executar:
| Cenário | Mecanismo de cancelamento de pedidos ociosos |
|---|---|
| O driver do cliente cancela o IRP ocioso e a rotina de retorno de chamada de notificação ociosa USB não foi chamada. | A pilha de drivers USB completa o IRP em estado de inatividade. Como o dispositivo nunca saiu do D0, o driver não altera o estado do dispositivo. |
| O driver do cliente cancela o IRP ocioso, a pilha de driver USB chama a rotina de retorno de chamada de notificação ociosa USB, e esta ainda não retornou. | É possível que a rotina de retorno de chamada da notificação de inatividade USB seja invocada mesmo que o driver do cliente tenha feito o cancelamento no IRP. Nesse caso, a rotina de retorno de chamada do driver cliente ainda deve desligar o dispositivo enviando o dispositivo para um estado de energia mais baixo de forma síncrona. Quando o dispositivo está no estado de menor energia, o driver do cliente pode enviar uma solicitação D0 . Como alternativa, o driver pode esperar que a pilha de drivers USB conclua o IRP em espera e depois enviar o IRP D0. Se a rotina de retorno de chamada não conseguir colocar o dispositivo em um estado de baixa energia devido à memória insuficiente para alocar um IRP de energia, deve cancelar o IRP ocioso e finalizar imediatamente. O IRP ocioso não é concluído até que a rotina de retorno de chamada seja concluída. Portanto, a rotina de retorno de chamada não deve bloquear a espera pela conclusão do IRP ocioso cancelado. |
| O dispositivo já está em um estado de baixa energia. | Se o dispositivo já estiver em um estado de baixa energia, o driver do cliente pode enviar um IRP D0 . A pilha de drivers USB conclui a solicitação ociosa IRP com STATUS_SUCCESS. Em alternativa, o driver pode cancelar o IRP ocioso, esperar que a pilha de drivers USB conclua o IRP ocioso e, em seguida, enviar um IRP D0. |
Rotina de conclusão de solicitação de IRP ociosa USB
Em muitos casos, um motorista de ônibus pode chamar a rotina de conclusão de IRP de solicitação ociosa de um motorista. Se essa situação ocorrer, um driver de cliente deve detetar por que o motorista de ônibus completou o IRP. O código de status retornado pode fornecer essas informações. Se o código de estado não for STATUS_POWER_STATE_INVALID, o driver deve colocar o seu dispositivo em D0 se o dispositivo ainda não estiver em D0. Se o dispositivo ainda estiver ocioso, o driver pode enviar outro pedido de inatividade IRP.
Observação
A rotina de conclusão de IRP de solicitação ociosa não deve impedir a espera pela conclusão de uma solicitação de energia D0. A rotina de conclusão pode ser chamada no contexto de um IRP de energia pelo controlador do hub, e bloquear outro IRP de energia durante a rotina de conclusão pode levar a um impasse.
A lista a seguir indica como uma rotina de conclusão para uma solicitação ociosa deve interpretar alguns códigos de status comuns:
| Código de estado | Descrição |
|---|---|
| ESTADO_SUCESSO | Indica que o dispositivo não deve mais ser suspenso. No entanto, os condutores devem verificar se os seus dispositivos estão ligados e colocá-los em D0 se ainda não estiverem em D0. |
| STATUS_CANCELADO | O condutor do autocarro preenche a requisição inativa IRP com STATUS_CANCELLED em quaisquer das seguintes circunstâncias:
|
| STATUS_ESTADO_DE_ENERGIA_INVÁLIDO | Indica que o driver de dispositivo solicitou um estado de energia D3 para seu dispositivo. Quando essa solicitação ocorre, o motorista do ônibus conclui todos os IRPs ociosos pendentes com STATUS_POWER_STATE_INVALID. |
| STATUS_DISPOSITIVO_OCUPADO | Indica que o motorista do ônibus já mantém uma solicitação ociosa IRP pendente para o dispositivo. Apenas um IRP ocioso pode estar pendente numa única ocasião para um dispositivo específico. Enviar vários IRPs de solicitação ociosa é um erro por parte do proprietário da política de energia. O gravador de driver resolve o erro. |
O exemplo de código a seguir mostra uma implementação de exemplo para a rotina de conclusão de solicitação ociosa.
/*
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;
}
Rotina de callback de notificação de inatividade USB
O driver de barramento (uma instância do driver de hub ou o driver pai genérico) determina quando é seguro suspender os filhos do dispositivo. Se estiver, chama a rotina de chamada de notificação inativa fornecida pelo driver cliente de cada filho.
O protótipo de função para USB_IDLE_CALLBACK é o seguinte:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
Um driver de dispositivo deve executar as seguintes ações em sua rotina de retorno de chamada de notificação ociosa:
- Solicite um IRP IRP_MN_WAIT_WAKE para o dispositivo se o dispositivo precisar estar armado para despertar remotamente.
- Cancele todas as E/S e prepare o dispositivo para ir para um estado de energia mais baixo.
- Coloque o dispositivo em um estado de suspensão WDM chamando PoRequestPowerIrp com o parâmetro PowerState definido como o valor do enumerador PowerDeviceD2 (definido em wdm.h; ntddk.h).
Tanto o driver do hub como o driver pai genérico USB (Usbccgp.sys) chamam a rotina de notificação de estado inativo em IRQL = PASSIVE_LEVEL. A rotina de retorno de chamada pode ser bloqueada enquanto aguarda a conclusão da solicitação de alteração do estado de energia.
A rotina de retorno de chamada é invocada apenas enquanto o sistema está em S0 e o dispositivo está em D0.
As seguintes restrições aplicam-se às rotinas de callback de notificação de pedido inativo:
- Os drivers de dispositivo podem iniciar uma transição de estado de energia do dispositivo de D0 para D2 na rotina de retorno de chamada de notificação ociosa, mas nenhuma outra transição de estado de energia é permitida. Em particular, um driver não deve tentar mudar o seu dispositivo para D0 durante a execução da sua rotina de callback.
- Os drivers de dispositivo não devem solicitar mais de um IRP de energia de dentro da rotina de retorno de chamada de notificação ociosa.
Armando dispositivos para despertar na rotina de retorno de chamada de notificação ociosa
A rotina de callback de notificação de inatividade deve determinar se o seu dispositivo tem uma solicitação pendente de IRP_MN_WAIT_WAKE. Se nenhuma solicitação de IRP_MN_WAIT_WAKE estiver pendente, a rotina de retorno de chamada deve enviar uma solicitação de IRP_MN_WAIT_WAKE antes de suspender o dispositivo. Para obter mais informações sobre o mecanismo de despertar de espera, consulte Suporte a dispositivos com recursos de ativação.
Suspensão global USB
A especificação USB 2.0 define suspensão global como a suspensão de todo o barramento controlado por um controlador host USB, cessando todo o tráfego USB no barramento, incluindo pacotes de início de quadros. Os dispositivos downstream que ainda não estão suspensos detetam o estado inativo na sua porta upstream e entram no estado de suspensão de forma autónoma. O Windows não implementa a suspensão global dessa maneira. O Windows sempre suspende seletivamente cada dispositivo USB por meio de um controlador host USB antes de interromper todo o tráfego USB no barramento.
Condições para a suspensão global
O driver do hub USB suspende seletivamente qualquer hub onde todos os seus dispositivos conectados estejam no estado de energia do dispositivo D1, D2 ou D3 . O barramento inteiro entra em suspensão global depois que todos os hubs USB são suspensos seletivamente. A pilha de drivers USB trata um dispositivo como ocioso sempre que o dispositivo está em um estado de dispositivo WDM de D1, D2 ou D3.