Freigeben über


Verwenden von Driver-Defined-Schnittstellen

Treiber können gerätespezifische Schnittstellen definieren, auf die andere Treiber zugreifen können. Diese vom Treiber definierten Schnittstellen können aus einer Reihe von aufrufbaren Routinen, einem Satz von Datenstrukturen oder beidem bestehen. Der Treiber stellt in der Regel Zeiger auf diese Routinen und Strukturen in einer vom Treiber definierten Schnittstellenstruktur bereit, die der Treiber anderen Treibern zur Verfügung stellt.

Beispielsweise kann ein Bustreiber eine oder mehrere Routinen bereitstellen, die Treiber auf höherer Ebene aufrufen können, um Informationen zu einem untergeordneten Gerät abzurufen, wenn diese Informationen nicht in der Ressourcenliste des untergeordneten Geräts verfügbar sind.

Ein Beispiel für eine Reihe von treiberdefinierten Schnittstellen, die im WDK dokumentiert sind, finden Sie unter USB-Routinen. Sehen Sie sich auch die frameworkbasierte Version des Toasterbeispiels an.

Erstellen einer Schnittstelle

Jede treiberdefinierte Schnittstelle wird wie folgt angegeben:

  • Eine GUID

  • Eine Versionsnummer

  • Eine vom Treiber definierte Schnittstellenstruktur

  • Referenz- und Dereferenzierungsroutinen

Frameworkbasierte Treiber können die folgenden Schritte ausführen, um eine Schnittstelle zu erstellen und für andere Treiber verfügbar zu machen:

  1. Definieren Sie eine Schnittstellenstruktur.

    Das erste Element dieser treiberdefinierten Struktur muss eine INTERFACE-Headerstruktur sein. Zusätzliche Member können Schnittstellendaten und Zeiger auf zusätzliche Strukturen oder Routinen enthalten, die von einem anderen Treiber aufgerufen werden können.

    Ihr Treiber muss eine WDF_QUERY_INTERFACE_CONFIG-Struktur bereitstellen, die die von Ihnen definierte Schnittstelle beschreibt.

    Hinweis

    Bei Verwendung von WDF_QUERY_INTERFACE_CONFIG unterstützt WDF nicht mehrere Versionen einer einzelnen Schnittstelle, die dieselbe Schnittstellen-GUID verwenden.

    Daher wird beim Einführen einer neuen Version einer vorhandenen Schnittstelle empfohlen, eine neue GUID zu erstellen, anstatt die Felder Größe oder Version der INTERFACE-Struktur zu überarbeiten.

    Wenn Ihr Treiber dieselbe Schnittstellen-GUID mit geänderten Feldern größe oder Version wiederverwendet, sollte der Treiber keine WDF_QUERY_INTERFACE_CONFIG bereitstellen und stattdessen eine EvtDeviceWdmIrpPreprocess-Rückrufroutine für IRP_MN_QUERY_INTERFACE bereitstellen.

  2. Rufen Sie WdfDeviceAddQueryInterface auf.

    Die WdfDeviceAddQueryInterface-Methode führt Folgendes aus:

    • Speichert Informationen zur Schnittstelle, z. B. ihre GUID, Versionsnummer und Strukturgröße, damit das Framework die Anforderung eines anderen Treibers für die Schnittstelle erkennen kann.
    • Registriert eine optionale EvtDeviceProcessQueryInterfaceRequest-Ereignisrückruffunktion , die das Framework aufruft, wenn ein anderer Treiber nach der Schnittstelle fragt.

Jeder instance einer vom Treiber definierten Schnittstelle ist einem einzelnen Gerät zugeordnet, sodass Treiber WdfDeviceAddQueryInterface in einer EvtDriverDeviceAddAdd- oder EvtChildListCreateDevice-Rückruffunktion aufrufen.

Zugreifen auf eine Schnittstelle

Wenn Ihr Treiber eine Schnittstelle definiert hat, kann ein anderer frameworkbasierter Treiber zugriff auf die Schnittstelle anfordern, indem er WdfFdoQueryForInterface aufruft und eine GUID, versionsnummer, zeiger auf eine Struktur und die Strukturgröße übergibt. Das Framework erstellt eine E/A-Anforderung und sendet sie an den Anfang des Treiberstapels.

Ein Treiber ruft in der Regel WdfFdoQueryForInterface aus einer EvtDriverDeviceAdd-Rückruffunktion auf. Wenn der Treiber die Schnittstelle freigeben muss, wenn sich das Gerät nicht im Arbeitszustand befindet, kann der Treiber WdfFdoQueryForInterface aus einer EvtDevicePrepareHardware-Rückruffunktion aufrufen und die Dereferenzierungsroutine der Schnittstelle aus einer EvtDeviceReleaseHardware-Rückruffunktion aufrufen.

Wenn Treiber A Treiber B nach einer Schnittstelle fragt, die Von Treiber B definiert wurde, verarbeitet das Framework die Anforderung für Treiber B. Das Framework überprüft, ob die GUID und die Version eine unterstützte Schnittstelle darstellen und ob die von Treiber A bereitgestellte Strukturgröße groß genug ist, um die Schnittstelle zu halten.

Wenn ein Treiber WdfFdoQueryForInterface aufruft, wird die E/A-Anforderung, die das Framework erstellt, bis zum Ende des Treiberstapels übertragen. Wenn ein einfacher Treiberstapel aus drei Treibern besteht – A, B und C – und wenn Treiber A nach einer Schnittstelle fragt, können sowohl Treiber B als auch Treiber C die Schnittstelle unterstützen. Beispielsweise könnte Treiber B die Schnittstellenstruktur von Treiber A ausfüllen, bevor die Anforderung an Treiber C übergeben wird. Treiber C kann eine EvtDeviceProcessQueryInterfaceRequest-Rückruffunktion bereitstellen, die den Inhalt der Schnittstellenstruktur untersucht und möglicherweise ändert.

Wenn Treiber A auf die Schnittstelle von Treiber B zugreifen muss und Treiber B ein Remote-E/A-Ziel ist (d. h. ein Treiber, der sich in einem anderen Treiberstapel befindet), muss Treiber A WdfIoTargetQueryForInterface anstelle von WdfFdoQueryForInterface aufrufen.

Verwenden von One-Way oder Two-Way Kommunikation

Sie können eine Schnittstelle definieren, die eine unidirektionale Kommunikation ermöglicht, oder eine Schnittstelle, die bidirektionale Kommunikation bereitstellt. Um die bidirektionale Kommunikation anzugeben, legt Ihr Treiber den ImportInterface-Member seiner WDF_QUERY_INTERFACE_CONFIG-Struktur auf TRUE fest.

Wenn die Schnittstelle eine unidirektionale Kommunikation ermöglicht und Treiber A nach der Schnittstelle von Treiber B fragt, fließen schnittstellendaten nur von Treiber B zu Treiber A. Wenn das Framework die Anforderung von Treiber A für eine Schnittstelle empfängt, die unidirektionale Kommunikation unterstützt, kopiert das Framework die vom Treiber definierten Schnittstellenwerte in die Schnittstellenstruktur des Treibers A. Anschließend wird die EvtDeviceProcessQueryInterfaceRequest-Rückruffunktion von Treiber B aufgerufen, sofern vorhanden, damit die Schnittstellenwerte untersucht und möglicherweise geändert werden können.

Wenn die Schnittstelle bidirektionale Kommunikation ermöglicht, enthält die Schnittstellenstruktur einige Member, die Treiber A vor dem Senden der Anforderung an Treiber B ausfüllt. Treiber B kann die Parameterwerte lesen, die Treiber A bereitgestellt hat, und basierend auf diesen Werten auswählen, welche Informationen an Treiber A bereitgestellt werden sollen. Wenn das Framework die Anforderung von Treiber A für eine Schnittstelle empfängt, die bidirektionale Kommunikation unterstützt, ruft das Framework die EvtDeviceProcessQueryInterfaceRequest-Rückruffunktion des Treibers B auf, damit es empfangene Werte untersuchen und Ausgabewerte angeben kann. Für die bidirektionale Kommunikation ist die Rückruffunktion erforderlich, da das Framework keine Schnittstellenwerte in die Schnittstellenstruktur von Treiber A kopiert.

Verwalten einer Verweisanzahl

Jede Schnittstelle muss eine Verweisfunktion und eine Dereferenzierungsfunktion enthalten, die eine Verweisanzahl für die Schnittstelle erhöhen und verringern. Der Treiber, der die Schnittstelle definiert, gibt die Adressen dieser Funktionen in seiner INTERFACE-Struktur an.

Wenn Treiber A Treiber B nach einer Schnittstelle fragt, ruft das Framework die Referenzfunktion der Schnittstelle auf, bevor die Schnittstelle für Treiber A verfügbar gemacht wird. Wenn Treiber A die Verwendung der Schnittstelle abgeschlossen hat, muss er die Dereferenzierungsfunktion der Schnittstelle aufrufen.

Die Referenz- und Dereferenzierungsfunktionen für die meisten Schnittstellen können No-Op-Funktionen sein, die nichts tun. Das Framework stellt die No-Op-Verweisanzahlfunktionen WdfDeviceInterfaceReferenceNoOp und WdfDeviceInterfaceDereferenceNoOp bereit, die die meisten Treiber verwenden können.

Die einzige Zeit, in der Treiber die Referenzanzahl einer Schnittstelle nachverfolgen und echte Referenz- und Dereferenzierungsfunktionen bereitstellen müssen, ist, wenn Treiber A eine Schnittstelle von einem Remote-E/A-Ziel anfordert (d. h. einen Treiber, der sich in einem anderen Treiberstapel befindet). In diesem Fall muss Treiber B (in einem anderen Stapel) eine Verweisanzahl implementieren, damit verhindert werden kann, dass sein Gerät entfernt wird, während Treiber A die Schnittstelle von Treiber B verwendet.

Wenn Sie Treiber B entwerfen, der eine Schnittstelle definiert, müssen Sie entscheiden, ob von einem anderen Treiberstapel auf die Schnittstelle Ihres Treibers zugegriffen wird. (Treiber B kann nicht ermitteln, ob eine Anforderung für seine Schnittstelle vom lokalen Treiberstapel oder von einem Remotestapel stammt.) Wenn Ihr Treiber Schnittstellenanforderungen von einem Remotestapel unterstützt, muss der Treiber eine Verweisanzahl implementieren.

Wenn Sie Treiber A entwerfen, der auf die Schnittstelle auf dem Remote-E/A-Ziel zugreift, muss der Treiber eine EvtIoTargetQueryRemove-Rückruffunktion bereitstellen, die die Schnittstelle freigibt, wenn das Gerät von Treiber B entfernt werden soll, eine EvtIoTargetRemoveComplete-Rückruffunktion , die die Schnittstelle freigibt, wenn das Gerät von Treiber B überraschend entfernt wird, und ein EvtIoTargetRemoveCanceled Rückruffunktion, die die Schnittstelle erneut abruft, wenn ein Versuch, das Gerät zu entfernen, abgebrochen wurde.