Partager via


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.

diagramme montrant la structure pilote-objet avec le membre de déchargement.

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.

diagramme montrant la structure pilote-objet avec le membre majorfunction.

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.

diagramme de pointeurs de fonction dans une structure d’objet de pilote.

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.

diagramme du nœud d’appareil de robot proseware, montrant trois objets d’appareil dans la pile d’appareils : afterthought.sys (filtre à faire), prosewarerobot.sys, generalrobot.sys (fdo) et pci.sys (pdo).

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.

diagramme de la pile d’appareils carte réseau sans fil, montrant netwlv64.sys, ndis.sys comme la paire de pilotes associée au fdo et pci.sys associée à l’objet pdo .

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.

Concepts pour tous les développeurs de pilotes

Nœuds d’appareil et piles d’appareils

Piles de pilotes