Partilhar via


Minidrivers, drivers Miniport e pares de drivers

Um minidriver ou um miniport driver atua como metade de um par de controladores. Pares de drivers como (miniport, port) podem facilitar o desenvolvimento do driver. Em um par de drivers, um driver lida com tarefas gerais que são comuns a toda uma coleção de dispositivos, enquanto o outro driver lida com tarefas específicas de um dispositivo individual. Os drivers que lidam com tarefas específicas do dispositivo têm uma variedade de nomes, incluindo driver de miniporta, driver de miniclasse e minidriver.

A Microsoft fornece o driver geral e, normalmente, um fornecedor de hardware independente fornece o driver específico. Antes de ler este tópico, deve compreender as ideias apresentadas em nodos de dispositivos e pilhas de dispositivos e pacotes de pedidos de entrada/saída.

Cada driver de modo kernel deve implementar uma função chamada DriverEntry, que é chamada logo após o driver ser carregado. A função DriverEntry preenche determinados membros de uma estrutura DRIVER_OBJECT com ponteiros para várias outras funções que o driver implementa. Por exemplo, a função DriverEntry preenche o membro Unload da estrutura DRIVER_OBJECT com um ponteiro para a função Unload do driver, conforme mostrado no diagrama a seguir.

diagrama mostrando a estrutura driver-objeto com o membro de descarga.

O membro do MajorFunction da estrutura DRIVER_OBJECT é uma matriz de ponteiros para funções que manipulam pacotes de solicitação de E/S (IRPs), conforme mostrado no diagrama a seguir. Normalmente, o driver preenche vários membros da matriz MajorFunction com ponteiros para funções (implementadas pelo driver) que lidam com vários tipos de IRPs.

diagrama que mostra a estrutura controlador-objeto com o membro majorfunction.

Um IRP pode ser categorizado de acordo com seu código de função principal, que é identificado por uma constante, como IRP_MJ_READ, IRP_MJ_WRITEou IRP_MJ_PNP. As constantes que identificam o código de função principal servem como índices na matriz MajorFunction. Por exemplo, suponha que o driver implementa uma função de despacho para lidar com IRPs que têm o código de função principal IRP_MJ_WRITE. Nesse caso, o driver deve preencher o elemento MajorFunction[IRP_MJ_WRITE] da matriz com um ponteiro para a função dispatch.

Normalmente, o driver preenche alguns dos elementos da matriz MajorFunction e deixa os elementos restantes definidos como valores padrão fornecidos pelo gerenciador de E/S. O exemplo a seguir mostra como usar a extensão do depurador !drvobj para inspecionar os ponteiros de função para o driver de parport.

0: kd> !drvobj parport 2
Driver object (fffffa80048d9e70) is for:
 \Driver\Parport
DriverEntry:   fffff880065ea070 parport!GsDriverEntry
DriverStartIo: 00000000 
DriverUnload:  fffff880065e131c parport!PptUnload
AddDevice:     fffff880065d2008 parport!P5AddDevice

Dispatch routines:
[00] IRP_MJ_CREATE                      fffff880065d49d0    parport!PptDispatchCreateOpen
[01] IRP_MJ_CREATE_NAMED_PIPE           fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE                       fffff880065d4a78    parport!PptDispatchClose
[03] IRP_MJ_READ                        fffff880065d4bac    parport!PptDispatchRead
[04] IRP_MJ_WRITE                       fffff880065d4bac    parport!PptDispatchRead
[05] IRP_MJ_QUERY_INFORMATION           fffff880065d4c40    parport!PptDispatchQueryInformation
[06] IRP_MJ_SET_INFORMATION             fffff880065d4ce4    parport!PptDispatchSetInformation
[07] IRP_MJ_QUERY_EA                    fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA                      fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS               fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION      fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL           fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL              fffff880065d4be8    parport!PptDispatchDeviceControl
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     fffff880065d4c24    parport!PptDispatchInternalDeviceControl
[10] IRP_MJ_SHUTDOWN                    fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL                fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP                     fffff880065d4af4    parport!PptDispatchCleanup
[13] IRP_MJ_CREATE_MAILSLOT             fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY              fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY                fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER                       fffff880065d491c    parport!PptDispatchPower
[17] IRP_MJ_SYSTEM_CONTROL              fffff880065d4d4c    parport!PptDispatchSystemControl
[18] IRP_MJ_DEVICE_CHANGE               fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA                 fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA                   fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP                         fffff880065d4840    parport!PptDispatchPnp

Na saída do depurador, você pode ver que parport.sys implementa GsDriverEntry, o ponto de entrada para o driver. GsDriverEntry, que foi gerado automaticamente quando o driver foi construído, executa alguma inicialização e, em seguida, chama DriverEntry, que foi implementado pelo desenvolvedor do driver.

Você também pode ver que o driver da parport (na sua função DriverEntry) fornece ponteiros para funções de despacho para esses códigos de funções principais.

  • IRP_MJ_CREATE
  • IRP_MJ_CLOSE
  • IRP_MJ_READ
  • IRP_MJ_WRITE
  • IRP_MJ_QUERY_INFORMATION
  • IRP_MJ_SET_INFORMATION
  • IRP_MJ_DEVICE_CONTROL
  • IRP_MJ_INTERNAL_DEVICE_CONTROL
  • IRP_MJ_CLEANUP
  • IRP_MJ_POWER
  • IRP_MJ_SYSTEM_CONTROL
  • IRP_MJ_PNP

Os elementos restantes da matriz MajorFunction mantêm ponteiros para a função de despacho padrão nt!IopInvalidDeviceRequest.

Na saída do debugger, pode-se ver que o driver de parport forneceu ponteiros de função para Unload e AddDevice, mas não forneceu um ponteiro de função para StartIo. A função AddDevice é incomum porque seu ponteiro de função não é armazenado na estrutura DRIVER_OBJECT. Em vez disso, ele é armazenado no AddDevice membro de uma extensão para a estrutura DRIVER_OBJECT. O diagrama a seguir ilustra os ponteiros de função que o driver de parport forneceu em sua função DriverEntry. Os ponteiros de função fornecidos pelo parport são sombreados.

diagrama de ponteiros de função numa estrutura objeto do driver.

Facilitar a utilização de pares de condutores

Ao longo de um período de tempo, à medida que os desenvolvedores de drivers dentro e fora da Microsoft ganhavam experiência com o Windows Driver Model (WDM), eles perceberam algumas coisas sobre as funções de despacho:

  • As funções de despacho são, em grande parte, clichês. Por exemplo, grande parte do código na função de despacho para IRP_MJ_PNP é o mesmo para todos os drivers. É apenas uma pequena parte do código Plug and Play (PnP) que é específico para um driver individual que controla uma peça individual de hardware.
  • As funções de despacho são complicadas e difíceis de executar corretamente. A implementação de recursos como sincronização de threads, enfileiramento de IRP e cancelamento de IRP é um desafio e requer uma compreensão profunda de como o sistema operacional funciona.

Para facilitar as coisas para os desenvolvedores de drivers, a Microsoft criou vários modelos de driver específicos de tecnologia. À primeira vista, os modelos específicos da tecnologia parecem bastante diferentes entre si, mas um olhar mais atento revela que muitos deles se baseiam neste paradigma:

  • O driver é dividido em duas partes: uma que lida com o processamento geral e outra que lida com o processamento específico de um dispositivo específico.
  • A peça geral é escrita pela Microsoft.
  • A peça específica pode ser escrita pela Microsoft ou por um fornecedor de hardware independente.

Suponha que as empresas Proseware e Contoso façam um robô de brinquedo que exija um driver WDM. Suponha também que a Microsoft fornece um driver de robô geral chamado GeneralRobot.sys. Proseware e Contoso podem escrever pequenos drivers que lidam com os requisitos de seus robôs específicos. Por exemplo, o Proseware poderia escrever ProsewareRobot.sys, e o par de drivers (ProsewareRobot.sys, GeneralRobot.sys) poderia ser combinado para formar um único driver WDM. Da mesma forma, o par de drivers (ContosoRobot.sys, GeneralRobot.sys) pode se combinar para formar um único driver WDM. Na sua forma mais geral, a ideia é que se possam criar drivers usando pares de (specific.sys, general.sys).

Ponteiros de função em pares de drivers

Em um par (specific.sys, general.sys), o Windows carrega specific.sys e chama a função#DriverEntry do. A função DriverEntry de specific.sys recebe um ponteiro para uma estrutura DRIVER_OBJECT. Normalmente, você esperaria que DriverEntry preenchesse vários elementos da matriz MajorFunction com ponteiros para despachar funções. Além disso, você esperaria que DriverEntry preenchesse o Unload membro (e possivelmente o membro StartIo) da estrutura DRIVER_OBJECT e o AddDevice membro da extensão de objeto do driver. No entanto, em um modelo de par de drivers, DriverEntry não faz isso necessariamente. Em vez disso, o função DriverEntry de specific.sys passa a estrutura DRIVER_OBJECT para uma função de inicialização implementada por general.sys. O exemplo de código a seguir mostra como a função de inicialização pode ser chamada no par (ProsewareRobot.sys, GeneralRobot.sys).

PVOID g_ProsewareRobottCallbacks[3] = {DeviceControlCallback, PnpCallback, PowerCallback};

// DriverEntry function in ProsewareRobot.sys
NTSTATUS DriverEntry (DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
   // Call the initialization function implemented by GeneralRobot.sys.
   return GeneralRobotInit(DriverObject, RegistryPath, g_ProsewareRobottCallbacks);
}

A função de inicialização no GeneralRobot.sys grava ponteiros de função para os membros apropriados da estrutura DRIVER_OBJECT (e sua extensão) e os elementos apropriados da matriz MajorFunction. A ideia é que, quando o gestor de E/S envia um IRP para o par de drivers, o IRP vai primeiro para uma função de despacho implementada por GeneralRobot.sys. Se GeneralRobot.sys pode lidar com o IRP por conta própria, então o driver específico, ProsewareRobot.sys, não precisa estar envolvido. Se GeneralRobot.sys consegue lidar com parte, mas não com todo o processamento de IRPs, recebe auxílio de uma das funções de retorno de chamada implementadas por ProsewareRobot.sys. GeneralRobot.sys recebe ponteiros para os callbacks ProsewareRobot durante a chamada GeneralRobotInit.

Em algum momento depois que DriverEntry retorna, uma pilha de dispositivos é construída para o nó do dispositivo Proseware Robot. A pilha de dispositivos pode ter esta aparência.

diagrama do nó do dispositivo do robô Proseware, mostrando três objetos de dispositivo na pilha de dispositivos: afterthought.sys (filtro DO), prosewarerobot.sys, generalrobot.sys (FDO) e pci.sys (DOP).

Como mostrado no diagrama anterior, a pilha de dispositivos para o Robot Proseware tem três objetos de dispositivo. O objeto de dispositivo superior é um objeto de dispositivo de filtro (Filter DO) associado ao driver de filtro AfterThought.sys. O objeto de dispositivo do meio é um objeto de dispositivo funcional (FDO) associado ao par de drivers (ProsewareRobot.sys, GeneralRobot.sys). O conjunto de controladores serve como o controlador funcional para a pilha de dispositivos. O objeto de dispositivo inferior é um objeto de dispositivo físico (PDO) associado a Pci.sys.

Observe que o conjunto de controladores ocupa apenas um nível na hierarquia de dispositivos e está associado a apenas um único objeto de dispositivo: o FDO. Quando GeneralRobot.sys processa um IRP, ele pode chamar ProsewareRobot.sys para obter assistência, mas isso não é o mesmo que passar a solicitação para o nível inferior da pilha de dispositivos. O par de controladores forma um único controlador WDM que está em um nível na pilha de dispositivos. O par de controladores completa o IRP ou o transmite pela pilha de dispositivos até o PDO, que está ligado ao Pci.sys.

Exemplo de um par de condutores

Suponha que você tenha uma placa de rede sem fio em seu laptop e, procurando no Gerenciador de dispositivos, você determina que netwlv64.sys é o driver para a placa de rede. Você pode usar a extensão do depurador !drvobj para inspecionar os ponteiros de função para netwlv64.sys.

1: kd> !drvobj netwlv64 2
Driver object (fffffa8002e5f420) is for:
 \Driver\netwlv64
DriverEntry:   fffff8800482f064 netwlv64!GsDriverEntry
DriverStartIo: 00000000 
DriverUnload:  fffff8800195c5f4 ndis!ndisMUnloadEx
AddDevice:     fffff88001940d30 ndis!ndisPnPAddDevice
Dispatch routines:
[00] IRP_MJ_CREATE                      fffff880018b5530 ndis!ndisCreateIrpHandler
[01] IRP_MJ_CREATE_NAMED_PIPE           fffff88001936f00 ndis!ndisDummyIrpHandler
[02] IRP_MJ_CLOSE                       fffff880018b5870 ndis!ndisCloseIrpHandler
[03] IRP_MJ_READ                        fffff88001936f00 ndis!ndisDummyIrpHandler
[04] IRP_MJ_WRITE                       fffff88001936f00 ndis!ndisDummyIrpHandler
[05] IRP_MJ_QUERY_INFORMATION           fffff88001936f00 ndis!ndisDummyIrpHandler
[06] IRP_MJ_SET_INFORMATION             fffff88001936f00 ndis!ndisDummyIrpHandler
[07] IRP_MJ_QUERY_EA                    fffff88001936f00 ndis!ndisDummyIrpHandler
[08] IRP_MJ_SET_EA                      fffff88001936f00 ndis!ndisDummyIrpHandler
[09] IRP_MJ_FLUSH_BUFFERS               fffff88001936f00 ndis!ndisDummyIrpHandler
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    fffff88001936f00 ndis!ndisDummyIrpHandler
[0b] IRP_MJ_SET_VOLUME_INFORMATION      fffff88001936f00 ndis!ndisDummyIrpHandler
[0c] IRP_MJ_DIRECTORY_CONTROL           fffff88001936f00 ndis!ndisDummyIrpHandler
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         fffff88001936f00 ndis!ndisDummyIrpHandler
[0e] IRP_MJ_DEVICE_CONTROL              fffff8800193696c ndis!ndisDeviceControlIrpHandler
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     fffff880018f9114 ndis!ndisDeviceInternalIrpDispatch
[10] IRP_MJ_SHUTDOWN                    fffff88001936f00 ndis!ndisDummyIrpHandler
[11] IRP_MJ_LOCK_CONTROL                fffff88001936f00 ndis!ndisDummyIrpHandler
[12] IRP_MJ_CLEANUP                     fffff88001936f00 ndis!ndisDummyIrpHandler
[13] IRP_MJ_CREATE_MAILSLOT             fffff88001936f00 ndis!ndisDummyIrpHandler
[14] IRP_MJ_QUERY_SECURITY              fffff88001936f00 ndis!ndisDummyIrpHandler
[15] IRP_MJ_SET_SECURITY                fffff88001936f00 ndis!ndisDummyIrpHandler
[16] IRP_MJ_POWER                       fffff880018c35e8 ndis!ndisPowerDispatch
[17] IRP_MJ_SYSTEM_CONTROL              fffff880019392c8 ndis!ndisWMIDispatch
[18] IRP_MJ_DEVICE_CHANGE               fffff88001936f00 ndis!ndisDummyIrpHandler
[19] IRP_MJ_QUERY_QUOTA                 fffff88001936f00 ndis!ndisDummyIrpHandler
[1a] IRP_MJ_SET_QUOTA                   fffff88001936f00 ndis!ndisDummyIrpHandler
[1b] IRP_MJ_PNP                         fffff8800193e518 ndis!ndisPnPDispatch

Na saída do depurador, você pode ver que netwlv64.sys implementa GsDriverEntry, o ponto de entrada para o driver. GsDriverEntry, que foi gerado automaticamente quando o driver foi criado, executa alguma inicialização e, em seguida, chama DriverEntry, que foi escrito pelo desenvolvedor do driver.

Neste exemplo, netwlv64.sys implementa DriverEntry, mas ndis.sys implementa AddDevice, Unloade várias funções de despacho. Netwlv64.sys é chamado de driver de miniporta NDIS e ndis.sys é chamado de Biblioteca NDIS. Juntos, os dois módulos formam um par (miniporta NDIS, Biblioteca NDIS).

Este diagrama mostra o stack de dispositivos para a placa de rede sem fios. Observe que o par de drivers (netwlv64.sys, ndis.sys) ocupa apenas um nível na pilha de dispositivos e está associado a apenas um objeto de dispositivo: o FDO.

diagrama da pilha de dispositivos da placa de rede sem fio, mostrando netwlv64.sys, ndis.sys como o par de drivers associado ao FDO e pci.sys associado ao PDO.

Pares de controladores disponíveis

Os diferentes modelos de driver específicos da tecnologia usam uma variedade de nomes para as peças específicas e gerais de um par de motoristas. Em muitos casos, a porção específica do par tem o prefixo "mini". Aqui estão alguns dos pares (específicos, gerais) que estão disponíveis:

  • (controlador de miniporta de exibição, controlador de porta de exibição)
  • (controlador miniporta de áudio, controlador de porta de áudio)
  • (driver de miniporta de armazenamento, driver de porta de armazenamento)
  • (driver de minicategoria de bateria, driver de classe de bateria)
  • (minidriver HID, driver de classe HID)
  • (driver de miniclasse do trocador, driver da porta do trocador)
  • (Driver de miniporta NDIS, biblioteca NDIS)

Nota Como você pode ver na lista, vários dos modelos usam o termo classe driver para a parte geral de um par de motoristas. Este tipo de driver de classe é diferente de um driver de classe autônomo e diferente de um driver de filtro de classe.

Conceitos para todos os desenvolvedores de drivers

Nós de dispositivo e pilhas de dispositivos

Pilhas de drivers