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

Este artigo descreve de que forma 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 utilizador, você deve 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, em seguida, fornecendo as funções de callback apropriadas. O KMDF trata dos detalhes da comunicação com drivers inferiores para suspender e retomar o funcionamento do dispositivo.

Diretrizes para suspensão seletiva em drivers KMDF

Os drivers KMDF que oferecem suporte para 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 definidos como PPO.
  • Um driver de função KMDF que suporta suspensão seletiva pode usar filas que são 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 pela 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 de dispositivos. 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 suporta suspensão seletiva pode usar filas que são gerenciadas por energia ou filas que não são gerenciadas por 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 gestão 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 de dispositivos, não deverá usar filas gerenciadas por energia. O motivo é o mesmo que para os 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 paralisar a pilha de dispositivos.

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

O KMDF lida com a maior parte do trabalho necessário para suportar a suspensão seletiva 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 suportar 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 gerida por energia pertencente ao objeto de dispositivo antes que o período de tempo limite ocioso expire, o arcabouço 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 endereço de endpoint USB, a interrogação contínua do leitor não contará como atividade para o temporizador de inatividade do 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 pela 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 alvos, o driver chama WdfIoTargetStop e especifica WdfIoTargetWaitForSentIoToComplete como a ação alvo. Em resposta, a estrutura interrompe o alvo de E/S somente depois que todas as solicitações de E/S que estão na fila de E/S do alvo 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 do estado D0 para o estado de energia do dispositivo especificado pelo driver nas configurações de inatividade. Como parte da transição, o KMDF chama as funções de callback de energia do driver da mesma maneira que faria para qualquer outra sequência de desligamento.

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

Para retomar o dispositivo, o KMDF envia uma solicitação de ativação para baixo da pilha de dispositivos e, em seguida, invoca as funções de retorno de chamada do driver da mesma forma que faria para qualquer outra sequência de inicialização.

Para obter informações detalhadas sobre os callbacks envolvidos nas sequências de desligamento e inicialização, consulte o white paper Plug and Play and Power Management in WDF Drivers.

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

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

  • Inicialize as configurações de política de energia relacionadas à ociosidade, incluindo o tempo limite de inatividade.
  • Opcionalmente, inclua 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 uma alça aberta 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 suporta suspensão seletiva.

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

Para configurar o suporte para 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 as capacidades 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. Esta função usa dois argumentos:

  • Um ponteiro para a estrutura WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS para inicializar.
  • 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 inicializa os membros da estrutura da seguinte maneira:

  • IdleTimeout é definido como IdleTimeoutDefaultValue (atualmente 5000 milissegundos ou 5 segundos).
  • UserControlOfIdleSettings está definido como IdleAllowUserControl .
  • Enabled é definido como WdfUseDefault, o que indica que a suspensão seletiva está habilitada, 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 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 seguintes campos aplicam-se aos controladores 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 trecho 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 para 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 propriedades do Gerenciador de Dispositivos que permite que os usuários com privilégio de administrador habilitem ou desabilitem o suporte ao dispositivo ocioso. 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 de dispositivo. Embora a maioria dos drivers chame este método inicialmente a partir da função de retorno EvtDriverDeviceAdd, isto nem sempre é possível ou sequer desejável. Se um driver suportar vários dispositivos ou versões de dispositivos, o driver pode não conhecer todos os recursos do dispositivo até consultar o hardware. Esses drivers podem adiar a chamada 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 fica 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.

Impedir 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 uma alça está aberta para o dispositivo ou o dispositivo está carregando. Um driver USB pode impedir que a estrutura suspenda um dispositivo ocioso em tais situações, chamando WdfDeviceStopIdle e chamando WdfDeviceResumeIdle quando for novamente aceitável que o dispositivo seja suspenso.

WdfDeviceStopIdle pára 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 ao 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 está no estado de funcionamento 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 de registo (apenas controladores HID)

Os drivers de filtro superior KMDF para dispositivos HID USB devem indicar no INF que eles suportam 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 para despertar remoto para controladores KMDF

Tal como acontece com a suspensão seletiva, o KMDF incorpora suporte para despertar, de modo que um dispositivo USB pode acionar um sinal de despertar enquanto o dispositivo está ocioso e o sistema está no estado de trabalho (S0) ou em estado de suspensão (S1–S4). Em termos KMDF, esses dois recursos são chamados de "despertar de S0" e "despertar de 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 consumo de energia para o estado de trabalho. Assim, em termos USB, wake from S0 e wake from Sx são os mesmos, e são chamados de "wake remoto".

Os drivers de função KMDF para USB não exigem nenhum código para suportar acordar a partir do estado S0, porque o KMDF fornece esse recurso como parte do mecanismo de suspensão seletiva. No entanto, para suportar a ativação remota quando o sistema está em Sx, o driver da função deve:

Os drivers KMDF geralmente configuram o suporte para despertar simultaneamente com a configuração do suporte para suspensão seletiva USB na função EvtDriverDeviceAdd ou EvtDevicePrepareHardware.

Verificação das capacidades do dispositivo

Antes de um driver de função USB KMDF inicializar suas configurações de política de energia para inatividade e reativação, ele deve verificar se o dispositivo suporta reativaçã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 no seu retorno de chamada EvtDriverDeviceAdd ou EvtDevicePrepareHardware.

Na chamada para WdfUsbTargetDeviceRetrieveInformation, o driver passa um identificador para o objeto de dispositivo USB e um ponteiro para a estrutura inicializada WDF_USB_DEVICE_INFORMATION. Após o retorno bem-sucedido da função, o campo Traits da estrutura contém sinalizadores que indicam se o dispositivo é autoalimentado, pode operar em alta velocidade e suporta vigília remota.

O exemplo a seguir do exemplo KMDF Osrusbfx2 mostra como chamar esse método para determinar se um dispositivo oferece suporte à ativação remota. Depois que essas linhas de código forem executadas, a variável waitWakeEnable conterá TRUE se o dispositivo suportar despertar remoto e FALSE se não suportar:

    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;

Ativando o despertar remoto

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

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

  1. Chame WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT para inicializar uma estrutura WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS. Esta 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 é habilitado para despertar a partir do estado D3 e o usuário pode ativar ou desativar o sinal de despertar da página de propriedades do dispositivo no Gerenciador de dispositivos.

O seguinte trecho de código do exemplo Osrusbfx2 mostra como inicializar as configurações de despertar 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 suportam suspensão seletiva, o controlador do barramento subjacente prepara o hardware do dispositivo para acordar. Assim, os drivers de função USB raramente exigem um callback EvtDeviceArmWakeFromS0. A estrutura envia uma solicitação de suspensão seletiva para o driver do barramento USB quando o tempo limite ocioso expira.

Pela mesma razão, os drivers de função USB raramente exigem callback 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 operacional.