Minidrivers, pilotes Miniport et paires de pilotes
Un minidriver ou un pilote miniport agit comme la moitié d’une paire de pilotes. Les paires de pilotes telles que (miniport, port) peuvent faciliter le développement de pilotes. Dans une paire de pilotes, un pilote gère les tâches générales qui sont communes à toute une collection d’appareils, tandis que l’autre pilote gère les tâches spécifiques à un appareil individuel. Les pilotes qui gèrent des tâches spécifiques à l’appareil portent différents noms, notamment pilote miniport, pilote miniclasse et minidriver.
Microsoft fournit le pilote général, et généralement un fournisseur de matériel indépendant fournit le pilote spécifique. Avant de lire cette rubrique, vous devez comprendre les idées présentées dans Nœuds d’appareil et piles d’appareils et paquets de demandes d’E/S.
Chaque pilote en mode noyau doit implémenter une fonction nommée DriverEntry, qui est appelée peu après le chargement du pilote. La fonction DriverEntry remplit certains membres d’une structure DRIVER_OBJECT avec des pointeurs vers plusieurs autres fonctions que le pilote implémente. Par exemple, la fonction DriverEntry remplit le membre Décharger de la structure DRIVER_OBJECT avec un pointeur vers la fonction Deload du pilote, comme illustré dans le diagramme suivant.
Le membre MajorFunction de la structure DRIVER_OBJECT est un tableau de pointeurs vers des fonctions qui gèrent les paquets de demandes d’E/S (IRP), comme illustré dans le diagramme suivant. En règle générale, le pilote remplit plusieurs membres du tableau MajorFunction avec des pointeurs vers des fonctions (implémentées par le pilote) qui gèrent différents types d’irps.
Un IRP peut être classé en fonction de son code de fonction principal, qui est identifié par une constante, telle que IRP_MJ_READ, IRP_MJ_WRITE ou IRP_MJ_PNP. Les constantes qui identifient le code de fonction majeure servent d’index dans le tableau MajorFunction . Par exemple, supposons que le pilote implémente une fonction de répartition pour gérer les IRP qui ont le code de fonction principal IRP_MJ_WRITE. Dans ce cas, le pilote doit remplir l’élément MajorFunction[IRP_MJ_WRITE] du tableau avec un pointeur vers la fonction dispatch.
En règle générale, le pilote remplit certains des éléments du tableau MajorFunction et laisse les éléments restants définis sur les valeurs par défaut fournies par le gestionnaire d’E/S. L’exemple suivant montre comment utiliser l’extension de débogueur !drvobj pour inspecter les pointeurs de fonction pour le pilote d’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
Dans la sortie du débogueur, vous pouvez voir que parport.sys implémente GsDriverEntry, le point d’entrée du pilote. GsDriverEntry, qui a été généré automatiquement lors de la création du pilote, effectue une initialisation, puis appelle DriverEntry, qui a été implémenté par le développeur du pilote.
Vous pouvez également voir que le pilote d’parport (dans sa fonction DriverEntry ) fournit des pointeurs vers les fonctions de répartition pour ces codes de fonction principaux :
- 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
Les autres éléments du tableau MajorFunction contiennent des pointeurs vers la fonction de répartition par défaut nt ! IopInvalidDeviceRequest.
Dans la sortie du débogueur, vous pouvez voir que le pilote d’parport a fourni des pointeurs de fonction pour Unload et AddDevice, mais qu’il n’a pas fourni de pointeur de fonction pour StartIo. La fonction AddDevice est inhabituelle, car son pointeur de fonction n’est pas stocké dans la structure DRIVER_OBJECT . Au lieu de cela, il est stocké dans le membre AddDevice d’une extension de la structure DRIVER_OBJECT . Le diagramme suivant illustre les pointeurs de fonction fournis par le pilote d’parport dans sa fonction DriverEntry . Les pointeurs de fonction fournis par parport sont ombrés.
Faciliter la tâche en utilisant des paires de pilotes
Au fil du temps, à mesure que les développeurs de pilotes à l’intérieur et à l’extérieur de Microsoft ont acquis de l’expérience avec le modèle de pilote Windows (WDM), ils ont réalisé quelques choses sur les fonctions de répartition :
- Les fonctions de répartition sont en grande partie réutilisables. Par exemple, une grande partie du code dans la fonction dispatch pour IRP_MJ_PNP est identique pour tous les pilotes. Il ne s’agit que d’une petite partie du code Plug-and-Play (PnP) spécifique à un pilote individuel qui contrôle un élément matériel individuel.
- Les fonctions de répartition sont compliquées et difficiles à obtenir correctement. L’implémentation de fonctionnalités telles que la synchronisation de threads, la mise en file d’attente IRP et l’annulation d’IRP est difficile et nécessite une compréhension approfondie du fonctionnement du système d’exploitation.
Pour faciliter les choses pour les développeurs de pilotes, Microsoft a créé plusieurs modèles de pilotes spécifiques à la technologie. À première vue, les modèles spécifiques à la technologie semblent très différents les uns des autres, mais un examen plus attentif révèle que beaucoup d’entre eux sont basés sur ce paradigme :
- Le pilote est divisé en deux parties : l’une qui gère le traitement général et l’autre qui gère le traitement spécifique à un appareil particulier.
- L’article général est écrit par Microsoft.
- L’article spécifique peut être écrit par Microsoft ou un fournisseur de matériel indépendant.
Supposons que les sociétés Proseware et Contoso créent tous deux un robot-toy qui nécessite un pilote WDM. Supposons également que Microsoft fournit un pilote robot général appelé GeneralRobot.sys. Proseware et Contoso peuvent écrire chacun de petits pilotes qui gèrent les exigences de leurs robots spécifiques. Par exemple, Proseware peut écrire ProsewareRobot.sys, et la paire de pilotes (ProsewareRobot.sys, GeneralRobot.sys) peut être combinée pour former un seul pilote WDM. De même, la paire de pilotes (ContosoRobot.sys, GeneralRobot.sys) peut se combiner pour former un seul pilote WDM. Dans sa forme la plus générale, l’idée est que vous pouvez créer des pilotes à l’aide de paires (specific.sys, general.sys).
Pointeurs de fonction dans les paires de pilotes
Dans une paire (specific.sys, general.sys), Windows charge specific.sys et appelle sa fonction DriverEntry . La fonction DriverEntry de specific.sys reçoit un pointeur vers une structure DRIVER_OBJECT . Normalement, vous vous attendez à ce que DriverEntry remplisse plusieurs éléments du tableau MajorFunction avec des pointeurs vers les fonctions de répartition. Vous vous attendez également à ce que DriverEntry renseigne le membre Unload (et éventuellement le membre StartIo ) de la structure DRIVER_OBJECT et le membre AddDevice de l’extension de l’objet driver. Toutefois, dans un modèle de paire de pilotes, DriverEntry ne le fait pas nécessairement. Au lieu de cela, la fonction DriverEntry de specific.sys transmet la structure DRIVER_OBJECT à une fonction d’initialisation implémentée par general.sys. L’exemple de code suivant montre comment la fonction d’initialisation peut être appelée dans la paire (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);
}
La fonction d’initialisation dans GeneralRobot.sys écrit des pointeurs de fonction vers les membres appropriés de la structure DRIVER_OBJECT (et son extension) et les éléments appropriés du tableau MajorFunction . L’idée est que lorsque le gestionnaire d’E/S envoie un IRP à la paire de pilotes, l’IRP passe d’abord à une fonction de répartition implémentée par GeneralRobot.sys. Si GeneralRobot.sys peut gérer l’IRP seul, le pilote spécifique, ProsewareRobot.sys, n’a pas à être impliqué. Si GeneralRobot.sys peut gérer une partie, mais pas la totalité, du traitement IRP, il obtient de l’aide de l’une des fonctions de rappel implémentées par ProsewareRobot.sys. GeneralRobot.sys reçoit des pointeurs vers les rappels ProsewareRobot dans l’appel GeneralRobotInit.
À un moment donné après le retour de DriverEntry , une pile d’appareils est construite pour le nœud d’appareil Proseware Robot. La pile d’appareils peut ressembler à ceci.
Comme indiqué dans le diagramme précédent, la pile d’appareils pour Proseware Robot a trois objets d’appareil. L’objet d’appareil supérieur est un objet d’appareil de filtre (Filter DO) associé au pilote de filtre AfterThought.sys. L’objet périphérique central est un objet de périphérique fonctionnel (FDO) associé à la paire de pilotes (ProsewareRobot.sys, GeneralRobot.sys). La paire de pilotes sert de pilote de fonction pour la pile de périphériques. L’objet d’appareil inférieur est un objet d’appareil physique (PDO) associé à Pci.sys.
Notez que la paire de pilotes n’occupe qu’un seul niveau dans la pile des appareils et n’est associée qu’à un seul objet d’appareil : le FDO. Quand GeneralRobot.sys traite un IRP, il peut appeler ProsewareRobot.sys pour obtenir de l’aide, mais ce n’est pas la même chose que de passer la demande dans la pile de l’appareil. La paire de pilotes forme un seul pilote WDM qui se trouve à un niveau dans la pile des périphériques. La paire de pilotes termine l’IRP ou la transmet dans la pile des appareils à l’AOP, qui est associé à Pci.sys.
Exemple de paire de pilotes
Supposons que vous disposez d’un réseau sans fil carte dans votre ordinateur portable et qu’en recherchant dans Gestionnaire de périphériques, vous déterminez que netwlv64.sys est le pilote de l’carte réseau. Vous pouvez utiliser l’extension de débogueur !drvobj pour inspecter les pointeurs de fonction pour 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
Dans la sortie du débogueur, vous pouvez voir que netwlv64.sys implémente GsDriverEntry, le point d’entrée du pilote. GsDriverEntry, qui a été généré automatiquement lors de la création du pilote, effectue une initialisation, puis appelle DriverEntry, qui a été écrit par le développeur du pilote.
Dans cet exemple, netwlv64.sys implémente DriverEntry, mais ndis.sys implémente AddDevice, Unload et plusieurs fonctions de répartition. Netwlv64.sys est appelé pilote miniport NDIS et ndis.sys est appelé bibliothèque NDIS. Ensemble, les deux modules forment une paire (miniport NDIS, bibliothèque NDIS).
Ce diagramme montre la pile d’appareils pour le réseau sans fil carte. Notez que la paire de pilotes (netwlv64.sys, ndis.sys) n’occupe qu’un seul niveau dans la pile des appareils et n’est associée qu’à un seul objet d’appareil : le FDO.
Paires de pilotes disponibles
Les différents modèles de pilotes spécifiques à la technologie utilisent une variété de noms pour les éléments spécifiques et généraux d’une paire de pilotes. Dans de nombreux cas, la partie spécifique de la paire a le préfixe « mini ». Voici quelques-unes des paires (spécifiques, générales) disponibles :
- (pilote de miniport d’affichage, pilote de port d’affichage)
- (pilote de miniport audio, pilote de port audio)
- (pilote de miniport de stockage, pilote de port de stockage)
- (pilote de miniclasse de batterie, pilote de classe de batterie)
- (Minidriver HID, pilote de classe HID)
- (pilote de changeur miniclass, pilote de port de changement)
- (Pilote miniport NDIS, bibliothèque NDIS)
Note Comme vous pouvez le voir dans la liste, plusieurs modèles utilisent le terme pilote de classe pour la partie générale d’une paire de pilotes. Ce type de pilote de classe est différent d’un pilote de classe autonome et d’un pilote de filtre de classe.
Rubriques connexes
Concepts pour tous les développeurs de pilotes