Minidrivers, drivers miniport e pares de motoristas

Um minidriver ou um motorista de miniporta atua como metade de um par de motoristas. Pares de driver como (miniport, porta) podem facilitar o desenvolvimento do driver. Em um par de drivers, um driver lida com tarefas gerais que são comuns a uma coleção inteira 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 passam por 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, você deve entender as ideias apresentadas em Nós de dispositivo e pilhas de dispositivos e pacotes de solicitação de E/S.

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-object com o membro unload.

O membro MajorFunction da estrutura DRIVER_OBJECT é uma matriz de ponteiros para funções que lidam com 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 mostrando a estrutura driver-object 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_WRITE ou 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 implemente uma função de expedição 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 relatório.

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 do driver. GsDriverEntry, que foi gerado automaticamente quando o driver foi criado, executa alguma inicialização e, em seguida, chama DriverEntry, que foi implementado pelo desenvolvedor do driver.

Você também pode ver que o driver de parport (em sua função DriverEntry ) fornece ponteiros para expedir funções para esses códigos de função 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 expedição padrão nt! IopInvalidDeviceRequest.

Na saída do depurador, você pode 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 membro AddDevice de uma extensão para a estrutura DRIVER_OBJECT . O diagrama a seguir ilustra os ponteiros de função fornecidos pelo driver de portabilidade em sua função DriverEntry . Os ponteiros de função fornecidos pelo parport são sombreados.

diagrama de ponteiros de função em uma estrutura driver-object.

Facilitando o uso de pares de driver

Durante um período de tempo, como desenvolvedores de driver dentro e fora da Microsoft ganharam experiência com o Modelo de Driver do Windows (WDM), eles perceberam algumas coisas sobre funções de expedição:

  • As funções de expedição são em grande parte clichês. Por exemplo, grande parte do código na função de expedição para IRP_MJ_PNP é o mesmo para todos os drivers. É apenas uma pequena parte do código de Plug and Play (PnP) que é específica para um driver individual que controla uma parte individual do hardware.
  • As funções de expedição são complicadas e difíceis de acertar. Implementar recursos como sincronização de thread, 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 desenvolvedores de driver, a Microsoft criou vários modelos de driver específicos da tecnologia. À primeira vista, os modelos específicos da tecnologia parecem bem diferentes uns dos outros, mas um olhar mais atento revela que muitos deles se baseiam nesse paradigma:

  • O driver é dividido em duas partes: uma que manipula o processamento geral e outra que manipula 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 criem um robô de toy que exija um driver WDM. Suponha também que a Microsoft forneça um Driver Robô Geral chamado GeneralRobot.sys. A Proseware e a Contoso podem escrever pequenos drivers que lidam com os requisitos de seus robôs específicos. Por exemplo, a 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) poderia ser combinado para formar um único driver WDM. Em sua forma mais geral, a ideia é que você possa criar drivers usando pares (specific.sys, general.sys).

Ponteiros de função em pares de driver

Em um par (specific.sys, general.sys), o Windows carrega specific.sys e chama sua função DriverEntry . 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 expedir funções. Além disso, você esperaria que DriverEntry preenchesse o membro Unload (e possivelmente o membro StartIo ) da estrutura DRIVER_OBJECT e o membro AddDevice da extensão do objeto driver. No entanto, em um modelo de par de driver, DriverEntry não necessariamente faz isso. Em vez disso, a 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 de DRIVER_OBJECT (e sua extensão) e os elementos apropriados da matriz MajorFunction . A ideia é que, quando o gerente de E/S envia um IRP para o par de driver, o IRP vai primeiro para uma função de expedição implementada por GeneralRobot.sys. Se GeneralRobot.sys puder lidar com o IRP por conta própria, o driver específico, ProsewareRobot.sys, não precisará estar envolvido. Se GeneralRobot.sys puder lidar com alguns, mas não todos, do processamento de IRP, ele obterá ajuda de uma das funções de retorno de chamada implementadas por ProsewareRobot.sys. GeneralRobot.sys recebe ponteiros para os retornos de chamada prosewareRobot na chamada GeneralRobotInit.

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

diagrama do nó do dispositivo robô proseware, mostrando três objetos de dispositivo na pilha de dispositivos: afterthought.sys (filtrar), prosewarerobot.sys, generalrobot.sys (fdo) e pci.sys (pdo).

Conforme mostrado no diagrama anterior, a pilha de dispositivos para o Proseware Robot 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 intermediário é um FDO (objeto de dispositivo funcional) associado ao par de driver (ProsewareRobot.sys, GeneralRobot.sys). O par de driver serve como o driver de função para a pilha do dispositivo. O objeto de dispositivo inferior é um PDO (objeto de dispositivo físico) associado a Pci.sys.

Observe que o par de driver ocupa apenas um nível na pilha do dispositivo e está associado a apenas um 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 baixo na pilha do dispositivo. O par de driver forma um único driver WDM que está em um nível na pilha do dispositivo. O par de drivers conclui o IRP ou passa-o para baixo na pilha do dispositivo para o PDO, que está associado a Pci.sys.

Exemplo de um par de drivers

Suponha que você tenha uma rede sem fio cartão em seu computador laptop e examinando Gerenciador de Dispositivos, determine que netwlv64.sys é o driver do cartão 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 do driver. GsDriverEntry, que foi gerado automaticamente quando o driver foi criado, executa alguma inicialização e chama DriverEntry, que foi escrito pelo desenvolvedor do driver.

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

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

diagrama da rede sem fio cartão pilha de dispositivos, mostrando netwlv64.sys, ndis.sys como o par de drivers associado ao fdo e pci.sys associados ao pdo .

Pares de driver disponíveis

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

  • (exibir driver de miniporta, exibir driver de porta)
  • (driver de miniporta de áudio, driver de porta de áudio)
  • (driver de miniporta de armazenamento, driver de porta de armazenamento)
  • (driver de miniclasse de bateria, driver de classe de bateria)
  • (Minidriver HID, driver de classe HID)
  • (driver de miniclasse do alterador, driver de porta do alterador)
  • (Driver de miniporta NDIS, biblioteca NDIS)

Nota Como você pode ver na lista, vários dos modelos usam o termo driver de classe para a parte geral de um par de drivers. Esse 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 driver

Nós de dispositivo e pilhas de dispositivos

Pilhas de driver