Partager via


Minidrivers, pilotes Miniport et paires de pilotes

Un minidriver ou un miniporteur 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 communes à un ensemble complet 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 aux appareils passent par divers noms, notamment le pilote miniport, le pilote miniclass et le 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 les nœuds de périphériques et les piles de périphériques et les paquets de requêtes 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 Deload de la structure DRIVER_OBJECT avec un pointeur vers la fonction Deload du pilote, comme illustré dans le diagramme suivant.

diagramme montrant la structure de l’objet driver avec la fonction 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 requête d’E/S (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 de l’objet pilote avec le membre principal.

Un IRP peut être classé en fonction de son code de fonction principal, identifié par une constante, telle que IRP_MJ_READ, IRP_MJ_WRITE ou IRP_MJ_PNP. Les constantes qui identifient le code de fonction principal servent d’index dans le tableau MajorFunction . Par exemple, supposons que le pilote implémente une fonction de dispatch pour gérer les IRPs qui ont le code de fonction principal IRP_MJ_WRITE. Dans ce cas, le pilote doit renseigner l’élément MajorFunction[IRP_MJ_WRITE] du tableau avec un pointeur vers la fonction de répartition.

En règle générale, le pilote remplit certains é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 du débogueur !drvobj pour inspecter les pointeurs de fonction pour le pilote 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 génération 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 parport (dans sa fonction DriverEntry) fournit des pointeurs vers des fonctions de distribution 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 (Opération de mise à jour des informations)
  • IRP_MJ_DEVICE_CONTROL
  • IRP_MJ_INTERNAL_DEVICE_CONTROL
  • IRP_MJ_CLEANUP
  • IRP_MJ_POWER
  • IRP_MJ_SYSTEM_CONTROL
  • IRP_MJ_PNP

Les éléments restants 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 de port a fourni des pointeurs de fonction pour Unload et AddDevice, mais 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 à la structure DRIVER_OBJECT . Le diagramme suivant illustre les pointeurs de fonction que le pilote parport a fournis dans sa fonction DriverEntry. Les pointeurs de fonction fournis par le parport sont ombrés.

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

Faciliter l’utilisation des paires de pilotes

Au fil du temps, alors que les développeurs de pilotes, tant à l'intérieur qu'à l'extérieur de Microsoft, ont acquis de l'expérience avec le modèle de pilote Windows (WDM), ils ont réalisé certaines choses à propos des fonctions de distribution :

  • Les fonctions dispatch sont en grande partie du code standard. Par exemple, une grande partie du code dans la fonction dispatch pour IRP_MJ_PNP est la même 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 complexes et difficiles à maîtriser. L’implémentation de fonctionnalités telles que la synchronisation de threads, la mise en file d’attente IRP et l’annulation 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 assez différents les uns des autres, mais un regard plus proche révèle que beaucoup d’entre eux sont basés sur ce paradigme :

  • Le pilote est divisé en deux éléments : un qui gère le traitement général et un qui gère le traitement spécifique à un appareil particulier.
  • La pièce générale est écrite par Microsoft.
  • La pièce spécifique peut être écrite par Microsoft ou par un fournisseur de matériel indépendant.

Supposons que les entreprises Proseware et Contoso fabriquent toutes les deux un robot jouet qui nécessite un pilote WDM. Supposons également que Microsoft fournit un pilote robot général appelé GeneralRobot.sys. Proseware et Contoso peuvent chacun écrire de petits pilotes qui gèrent les exigences de leurs robots respectifs. 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 pour distribuer des fonctions. Vous pouvez également vous attendre à ce que DriverEntry remplisse le membre Unload (et éventuellement le membre StartIo ) de la structure DRIVER_OBJECT et du membre AddDevice de l’extension d’objet du pilote. 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 passe la structure DRIVER_OBJECT le long d’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 les pointeurs de fonction dans les membres appropriés de la structure DRIVER_OBJECT (et de 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 pouvez gérer l’IRP par lui-même, alors le pilote spécifique, ProsewareRobot.sys, n’a pas besoin d’être impliqué. Si GeneralRobot.sys peut gérer une partie, mais pas tout le traitement des 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 de ProsewareRobot dans l’appel GeneralRobotInit.

À un moment donné après que DriverEntry a retourné, une pile de périphériques est construite pour le nœud de périphérique Proseware Robot. La pile d’appareils peut ressembler à ceci.

diagramme du nœud de dispositif du robot Proseware, montrant trois objets de dispositif dans la pile des appareils : afterthought.sys (filtre do), prosewarerobot.sys, generalrobot.sys (fdo) et pci.sys (pdo).

Comme illustré dans le diagramme précédent, la pile d’appareils pour Proseware Robot a trois objets d’appareil. L'objet d'appareil en haut est un objet d'appareil de filtre (Filter DO) lié au pilote de filtre AfterThought.sys. L’objet de périphérique intermédiaire est un objet d’appareil 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 de périphérique inférieur est un objet de périphérique physique (PDO) associé à Pci.sys.

Notez que la paire de pilotes occupe un seul niveau dans la pile d’appareils et est associée à un seul objet d’appareil : le FDO. Lorsque GeneralRobot.sys traite un IRP, il peut appeler ProsewareRobot.sys pour obtenir de l’aide, mais cela ne revient pas à faire descendre la demande dans la pile de périphérique. La paire de pilotes forme un seul pilote WDM qui se trouve à un niveau dans la pile d’appareils. La paire de pilotes termine l’IRP ou la transmet à la pile d’appareils vers le PDO, qui est associé à Pci.sys.

Exemple de paire de pilotes

Supposons que vous disposez d’une carte réseau sans fil sur votre ordinateur portable, et en recherchant Device Manager, vous déterminez que netwlv64.sys est le pilote de la carte réseau. Vous pouvez utiliser l’extension du débogueur !drvobj pour vérifier 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 génération 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ée pilote miniport NDIS et ndis.sys est appelée bibliothèque NDIS. Ensemble, les deux modules forment une paire (miniport NDIS, bibliothèque NDIS).

Ce diagramme montre la pile des appareils pour la carte réseau sans fil. Notez que la paire de pilotes (netwlv64.sys, ndis.sys) occupe un seul niveau dans la pile d’appareils et est associée à un seul objet d’appareil : le FDO.

diagramme de la pile de périphériques de la carte réseau sans fil, montrant netwlv64.sys, ndis.sys en tant que paire de pilotes associée au fdo et pci.sys associé au pdo.

Paires de pilotes disponibles

Les différents modèles de pilotes spécifiques à la technologie utilisent divers 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 paires (spécifiques, générales) disponibles :

  • (pilote de miniport d'affichage, pilote de port d'affichage)
  • (pilote audio miniport, pilote de port audio)
  • (pilote de miniport de stockage, pilote de port de stockage)
  • (pilote de la miniclasse batterie, pilote de la classe batterie)
  • (minipilote HID, pilote de classe HID)
  • (changer le pilote miniclasse, 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 désigner 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 différent d’un pilote de filtre de classe.

Concepts pour tous les développeurs de pilotes

nœuds d’appareil et les piles d’appareils

Piles de pilotes