Usando interfaces Driver-Defined

Os drivers podem definir interfaces específicas do dispositivo que outros drivers podem acessar. Essas interfaces definidas pelo driver podem consistir em um conjunto de rotinas chamáveis, um conjunto de estruturas de dados ou ambas. O driver normalmente fornece ponteiros para essas rotinas e estruturas em uma estrutura de interface definida pelo driver, que o driver disponibiliza para outros drivers.

Por exemplo, um motorista de ônibus pode fornecer uma ou mais rotinas que os motoristas de nível superior podem chamar para obter informações sobre um dispositivo filho, se essas informações não estiverem disponíveis na lista de recursos do dispositivo filho.

Para obter um exemplo de um conjunto de interfaces definidas pelo driver que estão documentadas no WDK, consulte Rotinas USB. Além disso, consulte a versão baseada em estrutura do exemplo de torradeira .

Criando uma interface

Cada interface definida pelo driver é especificada por:

  • UMA GUID

  • Um número de versão

  • Uma estrutura de interface definida pelo driver

  • Rotinas de referência e desreferência

Para criar uma interface e disponibilizá-la para outros drivers, os drivers baseados em estrutura podem usar as seguintes etapas:

  1. Defina uma estrutura de interface.

    O primeiro membro dessa estrutura definida pelo driver deve ser uma estrutura de cabeçalho INTERFACE . Membros adicionais podem incluir dados de interface e ponteiros para estruturas ou rotinas adicionais que outro driver pode chamar.

    O driver deve fornecer uma estrutura WDF_QUERY_INTERFACE_CONFIG , que descreve a interface que você definiu.

    Observação

    Ao usar WDF_QUERY_INTERFACE_CONFIG, o WDF não dá suporte a várias versões de uma única interface que usam o mesmo GUID de interface.

    Como resultado, ao introduzir uma nova versão de uma interface existente, recomendamos criar um novo GUID em vez de revisar os campos Tamanho ou Versão da estrutura interface .

    Se o driver reutilizar o mesmo GUID de interface com campos de Tamanho ou Versão modificados, o driver não deverá fornecer WDF_QUERY_INTERFACE_CONFIG e, em vez disso, deve fornecer uma rotina de retorno de chamada EvtDeviceWdmIrpPreprocess para IRP_MN_QUERY_INTERFACE.

  2. Chame WdfDeviceAddQueryInterface.

    O método WdfDeviceAddQueryInterface faz o seguinte:

    • Armazena informações sobre a interface, como seu GUID, número de versão e tamanho da estrutura, para que a estrutura possa reconhecer a solicitação de outro driver para a interface.
    • Registra uma função opcional de retorno de chamada de evento EvtDeviceProcessQueryInterfaceRequest , que a estrutura chama quando outro driver solicita a interface.

Cada instância de uma interface definida pelo driver está associada a um dispositivo individual, portanto, os drivers normalmente chamam WdfDeviceAddQueryInterface de dentro de uma função de retorno de chamada EvtDriverDeviceAdd ou EvtChildListCreateDevice .

Acessando uma interface

Se o driver tiver definido uma interface, outro driver baseado em estrutura poderá solicitar acesso à interface chamando WdfFdoQueryForInterface e passando um GUID, um número de versão, um ponteiro para uma estrutura e o tamanho da estrutura. A estrutura cria uma solicitação de E/S e a envia para a parte superior da pilha de driver.

Um driver normalmente chama WdfFdoQueryForInterface de dentro de uma função de retorno de chamada EvtDriverDeviceAdd . Como alternativa, se o driver precisar liberar a interface quando o dispositivo não estiver em seu estado de trabalho, o driver poderá chamar WdfFdoQueryForInterface de dentro de uma função de retorno de chamada EvtDevicePrepareHardware e chamar a rotina de desreferência da interface de dentro de uma função de retorno de chamada EvtDeviceReleaseHardware .

Se o driver A solicitar ao driver B uma interface que o driver B definiu, a estrutura manipulará a solicitação do driver B. A estrutura verifica se o GUID e a versão representam uma interface com suporte e se o tamanho da estrutura que o driver A forneceu é grande o suficiente para manter a interface.

Quando um driver chama WdfFdoQueryForInterface, a solicitação de E/S que a estrutura cria percorre todo o caminho até a parte inferior da pilha de driver. Se uma pilha de driver simples consistir em três drivers – A, B e C – e se o driver A solicitar uma interface, o driver B e o driver C poderão dar suporte à interface. Por exemplo, o driver B pode preencher a estrutura de interface do driver A antes de passar a solicitação para o driver C. O driver C pode fornecer uma função de retorno de chamada EvtDeviceProcessQueryInterfaceRequest que examina o conteúdo da estrutura da interface e possivelmente as modifica.

Se o driver A precisar acessar a interface do driver B e o driver B for um destino de E/S remoto (ou seja, um driver que esteja em uma pilha de driver diferente), o driver A deverá chamar WdfIoTargetQueryForInterface em vez de WdfFdoQueryForInterface.

Usando comunicação One-Way ou Two-Way

Você pode definir uma interface que fornece comunicação unidirecional ou uma que fornece comunicação bidirecional. Para especificar a comunicação bidirecional, o driver define o membro ImportInterface de sua estrutura WDF_QUERY_INTERFACE_CONFIG como TRUE.

Se a interface fornecer comunicação unidirecional e se o driver A solicitar a interface do driver B, os dados da interface fluirão apenas do driver B para o driver A. Quando a estrutura recebe a solicitação do driver A para uma interface que dá suporte à comunicação unidirecional, a estrutura copia os valores de interface definidos pelo driver para a estrutura de interface do driver A. Em seguida, ele chama a função de retorno de chamada EvtDeviceProcessQueryInterfaceRequest do driver B, se existir, para que possa examinar e possivelmente modificar os valores da interface.

Se a interface fornecer comunicação bidirecional, a estrutura da interface conterá alguns membros que o driver A preenche antes de enviar a solicitação para o driver B. O driver B pode ler os valores de parâmetro que o driver A forneceu e fazer escolhas, com base nesses valores, sobre quais informações fornecer ao driver A. Quando a estrutura recebe a solicitação do driver A para uma interface que dá suporte à comunicação bidirecional, a estrutura chama a função de retorno de chamada EvtDeviceProcessQueryInterfaceRequest do driver B para que possa examinar os valores recebidos e fornecer valores de saída. Para comunicação bidirecional, a função de retorno de chamada é necessária porque a estrutura não copia nenhum valor de interface para a estrutura de interface do driver A.

Mantendo uma contagem de referência

Cada interface deve incluir uma função de referência e uma função de desreferência, que incrementam e decrementam uma contagem de referência para a interface. O driver que define a interface especifica os endereços dessas funções em sua estrutura interface .

Quando o driver A solicita uma interface ao driver B, a estrutura chama a função de referência da interface antes de disponibilizar a interface para o driver A. Quando o driver A terminar de usar a interface , ele deverá chamar a função de desreferência da interface.

As funções de referência e desreferência para a maioria das interfaces podem ser funções sem operações que não fazem nada. A estrutura fornece funções de contagem de referências sem operações, WdfDeviceInterfaceReferenceNoOp e WdfDeviceInterfaceDereferenceNoOp, que a maioria dos drivers pode usar.

A única vez que os drivers devem acompanhar a contagem de referência de uma interface e fornecer funções reais de referência e desreferência é quando o driver A solicita uma interface de um destino de E/S remoto (ou seja, um driver que está em uma pilha de driver diferente). Nesse caso, o driver B (em uma pilha diferente) deve implementar uma contagem de referência para que possa impedir que seu dispositivo seja removido enquanto o driver A está usando a interface do driver B.

Se você estiver projetando o driver B, que define uma interface, decida se a interface do driver será acessada de uma pilha de driver diferente. (O driver B não pode determinar se uma solicitação para sua interface é da pilha de driver local ou de uma pilha remota.) Se o driver oferecer suporte a solicitações de interface de uma pilha remota, o driver deverá implementar uma contagem de referência.

Se você estiver projetando o driver A, que acessa a interface no destino de E/S remoto, o driver deve fornecer uma função de retorno de chamada EvtIoTargetQueryRemove que libera a interface quando o dispositivo do driver B está prestes a ser removido, uma função de retorno de chamada EvtIoTargetRemoveComplete que libera a interface quando o dispositivo do driver B é removido de surpresa e um EvtIoTargetRemoveCanceled função de retorno de chamada que readquire a interface se uma tentativa de remover o dispositivo foi cancelada.