Partilhar via


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 recursos ou recursos que não estão disponíveis no modo de usuário, você deverá 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 lida com 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 sua pilha 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.

Propriedade 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 do dispositivo. O KMDF gerencia a suspensão seletiva e a retomada em nome desse 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 gerenciadas por energia ou filas que não são gerenciadas por energia. Normalmente, um driver configura uma fila que não tem energia gerenciada 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 dependentes de energia. Quando uma solicitação chega a uma fila gerenciada por 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 para drivers UMDF. A estrutura não apresenta solicitações de filas gerenciadas por energia enquanto o dispositivo está suspenso, portanto, o uso dessas filas pode parar a pilha do dispositivo.

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

O KMDF lida com 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. A estrutura 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 usar um leitor contínuo em um ponto de extremidade USB, a sondagem repetida do leitor não contará 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 gerenciadas por energia para garantir que o driver não envie solicitações de E/S enquanto o dispositivo não estiver no estado de trabalho. Para interromper os destinos, o driver chama WdfIoTargetStop e especifica WdfIoTargetWaitForSentIoToComplete como a ação de destino. Em resposta, a estrutura interrompe o destino de E/S somente depois que todas as solicitações de E/S que estão na fila de E/S do destino tiverem sido concluídas e quaisquer retornos de chamada de conclusão de E/S associados tiverem sido executados.

Por padrão, o KMDF faz a transição do dispositivo para fora de D0 e para o estado de energia do dispositivo que o driver especificou 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 desligar.

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

Para retomar o dispositivo, o KMDF envia uma solicitação de ativaçã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 ativação.

Para obter informações detalhadas sobre os retornos de chamada envolvidos nas sequências de ligar/desligar e desligar, confira o white paper Plug and Play e Gerenciamento de Energia em Drivers do 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:

  • Inicialize as configurações de política de energia relacionadas ao ocioso, incluindo o tempo limite ocioso.
  • Opcionalmente, inclua a lógica para impedir temporariamente a suspensão ou retomar a operação quando o driver determinar que o dispositivo não deve ser suspenso devido a um identificador aberto ou outro motivo que não esteja relacionado às filas de E/S do dispositivo.
  • Em um driver USB para um dispositivo de interface humana (HID), indique no INF que ele dá suporte à suspensão seletiva.

Inicializando configurações de política de energia em um driver de função KMDF

Para configurar o 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 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS

Depois que o driver cria o objeto de 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 a estrutura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS a ser inicializada.
  • Um valor de enumeração que indica suporte para suspensão seletiva. O driver deve especificar IdleUsbSelectiveSuspend.

Se o driver especificar IdleUsbSelectiveSuspend, a função inicializará os membros da estrutura da seguinte maneira:

  • IdleTimeout é definido como IdleTimeoutDefaultValue (atualmente 5000 milissegundos ou 5 segundos).
  • UserControlOfIdleSettings é definido como IdleAllowUserControl .
  • Enabled é definido como WdfUseDefault, o que indica que a suspensão seletiva está habilitada, mas um usuário pode desabilitá-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, em seguida, chamar WdfDeviceAssignS0IdleSettings para passar essas configurações para a estrutura. Os campos a seguir 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 para o qual a estrutura suspende o dispositivo. 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 snippet de código a seguir é do arquivo Device.c do driver de 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 de 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 propriedades Gerenciador de Dispositivos que permite aos usuários com privilégio de administrador habilitar ou desabilitar o suporte ocioso do dispositivo. Em seguida, o driver chama WdfDeviceAssignS0IdleSettings para habilitar o suporte ocioso e registrar essas configurações com a estrutura.

Um driver pode chamar WdfDeviceAssignS0IdleSettings a qualquer momento depois de criar o objeto do dispositivo. Embora a maioria dos drivers chame esse método inicialmente do retorno de chamada EvtDriverDeviceAdd , isso pode nem sempre ser 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 conhecer todos os recursos do dispositivo até consultar o hardware. Esses drivers podem adiar a chamada de WdfDeviceAssignS0IdleSettings até o retorno de chamada 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 dentro do período de tempo limite , normalmente quando um identificador está aberto no dispositivo ou o dispositivo está carregando. Um driver USB pode impedir que a estrutura 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. WdfDeviceStopIdlenão impede que a estrutura suspenda o dispositivo quando o sistema muda para um estado de suspensão Sx. Seu único efeito é evitar 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)

Os 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 adicione a chave SelectiveSuspendEnabled e defina 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 ativação remota para drivers KMDF

Assim como acontece com a suspensão seletiva, o KMDF incorpora suporte para ativação, para 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 exigem nenhum código para dar suporte à ativação do 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 as funcionalidades do dispositivo

Antes que um driver de função USB KMDF inicialize suas configurações de política de energia para ociosidade e ativação, 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 sinalizadores que indicam se o dispositivo é auto-alimentado, 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 à ativação remota. Depois que essas linhas de código tiverem sido executadas, a variável waitWakeEnable conterá TRUE se o dispositivo der suporte a ativação remota e FALSE se não o fizer:

    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. O KMDF e os drivers de ônibus USB fornecidos pela Microsoft emitem as solicitações de E/S e lidam com a manipulação de hardware necessária para habilitar a ativação remota.

Para inicializar as configurações de ativação

  1. Chame WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT para inicializar uma estrutura de WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS . Essa função define o membro Enabled 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 do estado D3 e o usuário pode habilitar ou desabilitar o sinal de ativação da página de propriedades do dispositivo em 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 ativar. Portanto, os drivers de função USB raramente exigem um retorno de chamada EvtDeviceArmWakeFromS0 . A estrutura envia uma solicitação de suspensão seletiva para o driver de barramento USB quando o tempo limite ocioso expira.

Pelo mesmo motivo, os drivers de função USB raramente exigem um retorno de chamada EvtDeviceWakeFromS0Triggered ou EvtDeviceWakeFromSxTriggered . Em vez disso, a estrutura e o driver de barramento subjacente lidam com todos os requisitos para retornar o dispositivo ao estado de trabalho.