Utilisation d’interfaces Driver-Defined

Les pilotes peuvent définir des interfaces spécifiques à l’appareil auxquelles d’autres pilotes peuvent accéder. Ces interfaces définies par le pilote peuvent se composer d’un ensemble de routines pouvant être appelées, d’un ensemble de structures de données ou des deux. Le pilote fournit généralement des pointeurs vers ces routines et structures dans une structure d’interface définie par le pilote, que le pilote met à la disposition d’autres pilotes.

Par exemple, un pilote de bus peut fournir une ou plusieurs routines que les pilotes de niveau supérieur peuvent appeler pour obtenir des informations sur un appareil enfant, si ces informations ne sont pas disponibles dans la liste des ressources de l’appareil enfant.

Pour obtenir un exemple d’un ensemble d’interfaces définies par le pilote qui sont documentées dans le WDK, consultez Routines USB. Consultez également la version basée sur l’infrastructure de l’exemple de grille-pain .

Création d’une interface

Chaque interface définie par le pilote est spécifiée par :

  • Identificateur global unique

  • Numéro de version

  • Structure d’interface définie par le pilote

  • Routines de référence et de déréférencement

Pour créer une interface et la mettre à la disposition d’autres pilotes, les pilotes basés sur l’infrastructure peuvent effectuer les étapes suivantes :

  1. Définissez une structure d’interface.

    Le premier membre de cette structure définie par le pilote doit être une structure d’en-tête INTERFACE . D’autres membres peuvent inclure des données d’interface et des pointeurs vers des structures ou des routines supplémentaires qu’un autre pilote peut appeler.

    Votre pilote doit fournir une structure WDF_QUERY_INTERFACE_CONFIG , qui décrit l’interface que vous avez définie.

    Notes

    Lorsque vous utilisez WDF_QUERY_INTERFACE_CONFIG, WDF ne prend pas en charge plusieurs versions d’une seule interface qui utilisent le même GUID d’interface.

    Par conséquent, lors de l’introduction d’une nouvelle version d’une interface existante, nous vous recommandons de créer un GUID au lieu de réviser les champs Taille ou Version de la structure INTERFACE .

    Si votre pilote réutilise le même GUID d’interface avec des champs Taille ou Version modifiés, le pilote ne doit pas fournir WDF_QUERY_INTERFACE_CONFIG et doit à la place fournir une routine de rappel EvtDeviceWdmIrpPreprocess pour IRP_MN_QUERY_INTERFACE.

  2. Appelez WdfDeviceAddQueryInterface.

    La méthode WdfDeviceAddQueryInterface effectue les opérations suivantes :

    • Stocke des informations sur l’interface, telles que son GUID, son numéro de version et sa taille de structure, afin que l’infrastructure puisse reconnaître la demande d’un autre pilote pour l’interface.
    • Inscrit une fonction de rappel d’événement EvtDeviceProcessQueryInterfaceRequest facultative, que l’infrastructure appelle lorsqu’un autre pilote demande l’interface.

Chaque instance d’une interface définie par le pilote est associée à un appareil individuel, de sorte que les pilotes appellent généralement WdfDeviceAddQueryInterface à partir d’une fonction de rappel EvtDriverDeviceAdd ou EvtChildListCreateDevice.

Accès à une interface

Si votre pilote a défini une interface, un autre pilote basé sur l’infrastructure peut demander l’accès à l’interface en appelant WdfFdoQueryForInterface et en transmettant un GUID, un numéro de version, un pointeur vers une structure et la taille de structure. L’infrastructure crée une demande d’E/S et l’envoie en haut de la pile de pilotes.

Un pilote appelle généralement WdfFdoQueryForInterface à partir d’une fonction de rappel EvtDriverDeviceAdd . Sinon, si le pilote doit libérer l’interface lorsque l’appareil n’est pas dans son état de fonctionnement, le pilote peut appeler WdfFdoQueryForInterface à partir d’une fonction de rappel EvtDevicePrepareHardware et appeler la routine de déréférencement de l’interface à partir d’une fonction de rappel EvtDeviceReleaseHardware .

Si le pilote A demande au pilote B une interface que le pilote B a définie, l’infrastructure gère la demande pour le pilote B. L’infrastructure vérifie que le GUID et la version représentent une interface prise en charge, et que la taille de structure fournie par le pilote A est suffisamment grande pour contenir l’interface.

Lorsqu’un pilote appelle WdfFdoQueryForInterface, la demande d’E/S créée par le framework se déplace jusqu’au bas de la pile de pilotes. Si une pile de pilotes simple se compose de trois pilotes (A, B et C), et si le pilote A demande une interface, les pilotes B et C peuvent prendre en charge l’interface. Par exemple, le pilote B peut remplir la structure d’interface du pilote A avant de transmettre la demande au pilote C. Le pilote C peut fournir une fonction de rappel EvtDeviceProcessQueryInterfaceRequest qui examine le contenu de la structure d’interface et peut les modifier.

Si le pilote A doit accéder à l’interface du pilote B et si le pilote B est une cible d’E/S distante (c’est-à-dire un pilote qui se trouve dans une autre pile de pilotes), le pilote A doit appeler WdfIoTargetQueryForInterface au lieu de WdfFdoQueryForInterface.

Utilisation de One-Way ou Two-Way Communication

Vous pouvez définir une interface qui fournit une communication unidirectionnelle ou une autre qui fournit une communication bidirectionnelle. Pour spécifier la communication bidirectionnelle, votre pilote définit le membre ImportInterface de sa structure WDF_QUERY_INTERFACE_CONFIG sur TRUE.

Si l’interface fournit une communication unidirectionnelle et si le pilote A demande l’interface du pilote B, les données d’interface circulent uniquement du pilote B vers le pilote A. Lorsque l’infrastructure reçoit la demande du pilote A pour une interface qui prend en charge la communication unidirectionnelle, l’infrastructure copie les valeurs d’interface définies par le pilote dans la structure d’interface du pilote A. Il appelle ensuite la fonction de rappel EvtDeviceProcessQueryInterfaceRequest du pilote B, si elle existe, afin qu’elle puisse examiner et éventuellement modifier les valeurs d’interface.

Si l’interface fournit une communication bidirectionnelle, la structure de l’interface contient certains membres que le pilote A remplit avant d’envoyer la demande au pilote B. Le pilote B peut lire les valeurs de paramètres fournies par le pilote A et faire des choix, en fonction de ces valeurs, sur les informations à fournir au pilote A. Lorsque l’infrastructure reçoit la demande du pilote A pour une interface qui prend en charge la communication bidirectionnelle, l’infrastructure appelle la fonction de rappel EvtDeviceProcessQueryInterfaceRequest du pilote B afin qu’elle puisse examiner les valeurs reçues et fournir des valeurs de sortie. Pour la communication bidirectionnelle, la fonction de rappel est requise, car l’infrastructure ne copie aucune valeur d’interface dans la structure d’interface du pilote A.

Gestion d’un nombre de références

Chaque interface doit inclure une fonction de référence et une fonction de déréférence, qui incrémentent et décrémentent un nombre de références pour l’interface. Le pilote qui définit l’interface spécifie les adresses de ces fonctions dans sa structure INTERFACE .

Lorsque le pilote A demande une interface au pilote B, l’infrastructure appelle la fonction de référence de l’interface avant de rendre l’interface disponible pour le pilote A. Lorsque le pilote A a terminé d’utiliser l’interface, il doit appeler la fonction de déréférencement de l’interface.

Les fonctions de référence et de déréférencement pour la plupart des interfaces peuvent être des fonctions sans opération qui ne font rien. L’infrastructure fournit des fonctions de nombre de références sans opération, WdfDeviceInterfaceReferenceNoOp et WdfDeviceInterfaceDereferenceNoOp, que la plupart des pilotes peuvent utiliser.

La seule fois où les pilotes doivent suivre le nombre de références d’une interface et fournir des fonctions de référence et de déréférence réelles, c’est lorsque le pilote A demande une interface à partir d’une cible d’E/S distante (c’est-à-dire un pilote qui se trouve dans une autre pile de pilotes). Dans ce cas, le pilote B (dans une autre pile) doit implémenter un nombre de références afin qu’il puisse empêcher la suppression de son appareil pendant que le pilote A utilise l’interface du pilote B.

Si vous concevez le pilote B, qui définit une interface, vous devez décider si l’interface de votre pilote sera accessible à partir d’une autre pile de pilotes. (Le pilote B ne peut pas déterminer si une requête pour son interface provient de la pile de pilotes locale ou d’une pile distante.) Si votre pilote prend en charge les demandes d’interface à partir d’une pile distante, le pilote doit implémenter un nombre de références.

Si vous concevez un pilote A qui accède à l’interface sur la cible d’E/S distante, le pilote doit fournir une fonction de rappel EvtIoTargetQueryRemoveRemove qui libère l’interface lorsque l’appareil du pilote B est sur le point d’être supprimé, une fonction de rappel EvtIoTargetRemoveComplete qui libère l’interface lorsque l’appareil du pilote B est supprimé par surprise et une EvtIoTargetRemoveCanceled fonction de rappel qui réacquièrent l’interface si une tentative de suppression de l’appareil a été annulée.