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.
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.
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.
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.
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.
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.
Tópicos relacionados
Conceitos para todos os desenvolvedores de drivers