Suspensão seletiva em drivers de função KMDF USB

Este artigo descreve como os drivers de função KMDF dão suporte à suspensão seletiva de USB.

Se o driver USB exigir funcionalidades ou recursos que não estão disponíveis no modo de usuário, você precisa fornecer um driver de função KMDF. Os drivers KMDF implementam a suspensão seletiva definindo valores relevantes em uma estrutura de inicialização KMDF e fornecendo as funções de retorno de chamada apropriadas. O KMDF manipula os detalhes da comunicação com drivers inferiores para suspender e retomar o dispositivo.

Diretrizes para suspensão seletiva em drivers KMDF

Os drivers KMDF que dão suporte à suspensão seletiva devem seguir estas diretrizes:

  • Um driver de função KMDF deve ser o PPO para o seu stack de dispositivos. Por padrão, os drivers de função KMDF são o PPO.
  • Um driver de função KMDF que dá suporte à suspensão seletiva pode usar filas gerenciadas por energia ou filas que não são gerenciadas por energia. Por padrão, os objetos de fila para PPOs são gerenciados por energia.

Gerenciamento da política de energia e drivers USB KMDF

Por padrão, o driver de função KMDF para um dispositivo USB é o PPO para a pilha de dispositivos. O KMDF gerencia suspensão seletiva e retomada em nome deste driver.

Configuração da fila de E/S em drivers KMDF

Um driver de função KMDF que dá suporte à suspensão seletiva pode usar filas com gerenciamento de energia ou filas sem gerenciamento de energia. Normalmente, um driver configura uma fila que não é gerenciada por energia para receber solicitações de controle de E/S de dispositivo de entrada e configura uma ou mais filas gerenciadas por energia para receber solicitações de leitura, gravação e outras solicitações dependentes de energia. Quando uma solicitação chega a uma fila com gerenciamento de energia, o KMDF garante que o dispositivo esteja em D0 antes de apresentar a solicitação ao driver.

Se você estiver escrevendo um driver de filtro KMDF que esteja em camadas acima do PPO na pilha do dispositivo, não deverá usar filas gerenciadas por energia. O motivo é o mesmo que para os drivers UMDF. A estrutura de software não processa solicitações de filas gerenciadas por energia enquanto o dispositivo está suspenso. Portanto, o uso dessas filas pode atrasar a pilha do dispositivo.

Mecanismo de suspensão seletiva para drivers de função KMDF

O KMDF gerencia a maior parte do trabalho necessário para dar suporte à suspensão seletiva de USB. Ele controla a atividade de E/S, gerencia o temporizador ocioso e envia as solicitações de controle de E/S do dispositivo que fazem com que o driver pai (Usbhub.sys ou Usbccgp.sys) suspenda e retome o dispositivo.

Se um driver de função KMDF der suporte à suspensão seletiva, o KMDF rastreará a atividade de E/S em todas as filas gerenciadas por energia que cada objeto de dispositivo possui. O framework inicia um temporizador ocioso sempre que a contagem de E/S atinge zero. O valor de tempo limite padrão é 5 segundos.

Se uma solicitação de E/S chegar a uma fila gerenciada por energia que pertence ao objeto do dispositivo antes que o período de tempo limite ocioso expire, a estrutura cancelará o temporizador ocioso e não suspenderá o dispositivo.

Quando o temporizador ocioso expira, o KMDF emite as solicitações necessárias para colocar o dispositivo USB no estado suspenso. Se um driver de função usa um leitor contínuo em um ponto de extremidade USB, a sondagem repetida do leitor não conta como atividade para o temporizador ocioso KMDF. No entanto, na função de retorno de chamada EvtDeviceD0Exit, o driver USB deve parar manualmente o leitor contínuo e quaisquer outros destinos de E/S alimentados por filas que não são geridas por energia para garantir que o driver não envie solicitações de E/S enquanto o dispositivo não está no estado operacional. Para parar os alvos, o driver chama WdfIoTargetStop e especifica WdfIoTargetWaitForSentIoToComplete como a ação de destino. Em resposta, o framework interrompe o alvo de E/S somente depois que todas as solicitações de E/S que estão na fila do alvo de E/S tiverem sido concluídas e quaisquer callbacks de conclusão de E/S associados tiverem sido executados.

Por padrão, o KMDF faz a transição do dispositivo para fora do D0 e para o estado de energia do dispositivo especificado pelo driver nas configurações ociosas. Como parte da transição, o KMDF chama as funções de retorno de chamada de energia do driver da mesma forma que faria para qualquer outra sequência de desligamento.

Depois que o dispositivo for suspenso, a estrutura retomará automaticamente o dispositivo quando qualquer um dos seguintes eventos ocorrer:

Para reativar o dispositivo, o KMDF envia uma solicitação de energização para baixo na pilha do dispositivo e invoca as funções de retorno de chamada do driver da mesma maneira que faria para qualquer outra sequência de energização.

Para obter informações detalhadas sobre os callbacks envolvidos nas sequências de desligamento e de ativação, consulte o white paper Plug and Play e Gerenciamento de Energia em Drivers WDF.

Suporte à suspensão seletiva de USB em um driver de função KMDF

Para implementar a suspensão seletiva de USB em um driver de função KMDF:

  • Inicializar as configurações de política de energia relacionadas ao estado de inatividade, incluindo o tempo limite de inatividade.
  • Inclua opcionalmente a lógica para impedir temporariamente a suspensão ou para retomar a operação, caso o driver determine que o dispositivo não deva ser suspenso devido a um identificador aberto ou outro motivo não relacionado às filas de E/S do dispositivo.
  • Em um driver USB para um dispositivo de interface humana (HID), indique no INF que ele suporta a suspensão seletiva.

Inicializando configurações de política de energia (Power Policy Settings) em um driver de função KMDF

Para configurar suporte à suspensão seletiva de USB, um driver KMDF usa a estrutura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS. O driver deve primeiro inicializar a estrutura e, em seguida, pode definir campos que fornecem detalhes sobre os recursos do driver e seu dispositivo. Normalmente, o driver preenche essa estrutura em sua função EvtDriverDeviceAdd ou EvtDevicePrepareHardware .

Para inicializar a estrutura de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS

Depois que o driver cria o objeto do dispositivo, o driver usa a função WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT para inicializar a estrutura. Essa função usa dois argumentos:

  • Um ponteiro para inicializar a estrutura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS.
  • Um valor de enumeração que indica suporte para suspensão seletiva. O driver deve especificar IdleUsbSelectiveSuspend.

Se o driver especifica IdleUsbSelectiveSuspend, a função inicializa os membros da estrutura da seguinte maneira:

  • IdleTimeout é definido como IdleTimeoutDefaultValue (atualmente 5000 milissegundos ou 5 segundos).
  • UserControlOfIdleSettings está definido como IdleAllowUserControl .
  • Habilitado está definido como WdfUseDefault, o que indica que a suspensão seletiva está ativada, mas um usuário pode desativá-la se o membro UserControlOfIdleSettings permitir.
  • DxState é definido como PowerDeviceMaximum, que usa os recursos de energia relatados para o dispositivo para determinar o estado para o qual fazer a transição do dispositivo ocioso.

Para configurar a suspensão seletiva de USB

Depois que o driver inicializa a estrutura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS , o driver pode definir outros campos na estrutura e chamar WdfDeviceAssignS0IdleSettings para passar essas configurações para a estrutura. Os seguintes campos se aplicam aos drivers de função USB:

  • IdleTimeout — O intervalo, em milissegundos, que deve decorrer sem receber uma solicitação de E/S antes que a estrutura considere o dispositivo ocioso. O driver pode especificar um valor ULONG ou pode aceitar o padrão.

  • UserControlOfIdleSettings – se o usuário pode modificar as configurações ociosas do dispositivo. Os valores possíveis são IdleDoNotAllowUserControl e IdleAllowUserControl.

  • DxState — o estado de energia do dispositivo que a estrutura usa para suspendê-lo. Os valores possíveis são PowerDeviceD1, PowerDeviceD2 e PowerDeviceD3.

    Os drivers USB não devem alterar a configuração inicial desse valor. A função WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT define esse valor como PowerDeviceMaximum, o que garante que a estrutura escolha o valor correto com base nos recursos do dispositivo.

O trecho de código a seguir foi extraído do arquivo Device.c do driver exemplo 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;
}

No exemplo, o driver chama WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT, especificando IdleUsbSelectiveSuspend. O driver define IdleTimeout como 10.000 milissegundos (10 segundos) e aceita os padrões da estrutura para DxState e UserControlOfIdleSettings. Como resultado, a estrutura faz a transição do dispositivo para o estado D3 quando ele está ocioso e cria uma página de propriedade do Gerenciador de Dispositivos que permite aos usuários com privilégio de administrador habilitar ou desabilitar o suporte de dispositivo ocioso. Em seguida, o driver chama WdfDeviceAssignS0IdleSettings para ativar o suporte ao modo de inatividade e registrar essas configurações na estrutura de trabalho.

Um driver pode chamar WdfDeviceAssignS0IdleSettings a qualquer momento depois de criar o objeto do dispositivo. Ainda que a maioria dos drivers chame este método inicialmente a partir do retorno de chamada EvtDriverDeviceAdd, isso pode não ser sempre possível ou até mesmo desejável. Se um driver der suporte a vários dispositivos ou versões de dispositivo, o driver poderá não saber todos os recursos do dispositivo até consultar o hardware. Esses drivers podem adiar a chamada de WdfDeviceAssignS0IdleSettings até o callback EvtDevicePrepareHardware.

A qualquer momento após sua chamada inicial para WdfDeviceAssignS0IdleSettings, o driver pode alterar o valor de tempo limite ocioso e o estado do dispositivo no qual o dispositivo está ocioso. Para alterar uma ou mais configurações, o driver simplesmente inicializa outra estrutura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS conforme descrito anteriormente e chama WdfDeviceAssignS0IdleSettings novamente.

Impedindo a suspensão do dispositivo USB

Às vezes, um dispositivo USB não deve ser desligado mesmo que nenhuma solicitação de E/S esteja presente durante o tempo limite, normalmente quando uma conexão está aberta com o dispositivo ou o dispositivo está sendo carregado. Um driver USB pode impedir que o framework suspenda um dispositivo ocioso nessas situações chamando WdfDeviceStopIdle e chamando WdfDeviceResumeIdle quando for novamente aceitável que o dispositivo seja suspenso.

WdfDeviceStopIdle interrompe o temporizador ocioso. Se o período IdleTimeout não tiver expirado e o dispositivo ainda não tiver sido suspenso, a estrutura cancelará o temporizador ocioso e não suspenderá o dispositivo. Se o dispositivo já tiver sido suspenso, a estrutura retornará o dispositivo para o estado de trabalho. WdfDeviceStopIdle não impede que o framework suspenda o dispositivo quando o sistema muda para um estado de suspensão Sx. Seu único efeito é impedir a suspensão do dispositivo enquanto o sistema estiver no estado de trabalho S0. WdfDeviceResumeIdle reinicia o temporizador ocioso. Esses dois métodos gerenciam uma contagem de referência no dispositivo, portanto, se o driver chamar WdfDeviceStopIdle várias vezes, a estrutura não suspenderá o dispositivo até que o driver tenha chamado WdfDeviceResumeIdle o mesmo número de vezes. Um driver não deve chamar WdfDeviceResumeIdlesem primeiro chamar WdfDeviceStopIdle.

Incluindo uma chave do Registro (somente drivers HID)

Drivers de filtro superior KMDF para dispositivos USB HID devem indicar no INF que dão suporte à suspensão seletiva para que o driver de porta HIDClass.sys fornecido pela Microsoft possa habilitar a suspensão seletiva para a pilha HID. O INF deve incluir uma diretiva AddReg que adiciona a chave SelectiveSuspendEnabled e define seu valor como 1, como mostra a seguinte cadeia de caracteres:

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

Para obter um exemplo, consulte Hidusbfx2.inx no WDK em %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys.

Suporte de despertar remoto para drivers KMDF

Assim como acontece com a suspensão seletiva, o KMDF incorpora suporte para ativação, de modo que um dispositivo USB possa disparar um sinal de ativação enquanto o dispositivo estiver ocioso e o sistema estiver no estado de trabalho (S0) ou em um estado de suspensão (S1–S4). Em termos KMDF, esses dois recursos são chamados de "wake from S0" e "wake from Sx", respectivamente.

Para dispositivos USB, a ativação apenas indica que o próprio dispositivo pode iniciar a transição de um estado de menor potência para o estado de trabalho. Assim, em termos USB, a ativação de S0 e a ativação do Sx são as mesmas e são chamadas de "ativação remota".

Os drivers de função USB KMDF não requerem código para suportar o acordar do estado S0 porque o KMDF fornece essa funcionalidade como parte do mecanismo de suspensão seletiva. No entanto, para dar suporte à ativação remota quando o sistema estiver no Sx, um driver de função deve:

Os drivers KMDF normalmente configuram o suporte de ativação ao mesmo tempo em que configuram o suporte para suspensão seletiva USB na função EvtDriverDeviceAdd ou EvtDevicePrepareHardware .

Verificando os recursos do dispositivo

Antes que um driver de função USB KMDF inicialize suas configurações de política de energia para inatividade e acordar, ele deve verificar se o dispositivo dá suporte à ativação remota. Para obter informações sobre os recursos de hardware do dispositivo, o driver inicializa uma estrutura WDF_USB_DEVICE_INFORMATION e chama WdfUsbTargetDeviceRetrieveInformation, normalmente em seu retorno de chamada EvtDriverDeviceAdd ou EvtDevicePrepareHardware .

Na chamada para WdfUsbTargetDeviceRetrieveInformation, o driver passa um identificador para o objeto do dispositivo e um ponteiro para a estrutura de WDF_USB_DEVICE_INFORMATION inicializada. Após o retorno bem-sucedido da função, o campo Características da estrutura contém indicadores que indicam se o dispositivo é autoalimentado, pode operar em alta velocidade e dá suporte à ativação remota.

O exemplo a seguir do exemplo KMDF Osrusbfx2 mostra como chamar esse método para determinar se um dispositivo dá suporte ao wake remoto. Depois que essas linhas de código tiverem sido executadas, a variável waitWakeEnable conterá TRUE se o dispositivo oferecer suporte a ativação remota e FALSE, caso contrário:

    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;

Habilitando a ativação remota

Na terminologia USB, um dispositivo USB é habilitado para ativação remota quando seu recurso de DEVICE_REMOTE_WAKEUP é definido. De acordo com a especificação USB, o software host deve definir o recurso de ativação remota em um dispositivo "apenas antes" de colocar o dispositivo em suspensão. O driver de função KMDF é necessário apenas para inicializar as configurações de ativação. KMDF e os controladores de barramento USB fornecidos pela Microsoft emitem as requisições de E/S e lidam com a manipulação de hardware necessária para habilitar o despertar remoto.

Para inicializar as configurações de despertar

  1. Chame WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT para inicializar uma estrutura WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS. Essa função define o membro Habilitado da estrutura como WdfUseDefault, define o membro DxState como PowerDeviceMaximum e define o membro UserControlOfWakeSettings como WakeAllowUserControl.
  2. Chame WdfDeviceAssignSxWakeSettings com a estrutura inicializada. Como resultado, o dispositivo está habilitado para ativar-se do estado D3 e o usuário pode habilitar ou desabilitar o sinal de ativação na página de propriedades do dispositivo no Gerenciador de Dispositivos.

O snippet de código a seguir do exemplo Osrusbfx2 mostra como inicializar as configurações de ativação para seus valores padrão:

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

Para dispositivos USB que dão suporte à suspensão seletiva, o driver de barramento subjacente prepara o hardware do dispositivo para acordar. Portanto, os drivers de função USB raramente necessitam de um callback EvtDeviceArmWakeFromS0. O framework envia uma solicitação de suspensão seletiva para o driver de barramento USB quando o tempo limite de inatividade expira.

Pelo mesmo motivo, é pouco frequente que os drivers de função USB exijam um retorno de chamada EvtDeviceWakeFromS0Triggered ou EvtDeviceWakeFromSxTriggered. Em vez disso, a estrutura e o motorista do ônibus subjacente lidam com todos os requisitos para retornar o dispositivo ao estado de trabalho.