ドライバー定義インターフェイスの使用

ドライバーは、他のドライバーがアクセスできるデバイス固有のインターフェイスを定義できます。 これらのドライバー定義インターフェイスは、一連の呼び出し可能なルーチン、データ構造体のセット、またはその両方で構成できます。 ドライバーは通常、ドライバー定義のインターフェイス構造体でこれらのルーチンと構造体へのポインターを提供して、他のドライバーで使用できるようにします。

たとえばバス ドライバーは、1 つ以上のルーチンを提供して、子デバイスに関する情報が子デバイスのリソース一覧で入手できない場合、上位レベルのドライバーが呼び出してその情報を取得できるようにする場合があります。

WDK に記載されているドライバー定義インターフェイスのセットの例については、「USB ルーチン」を参照してください。 また、トースターのサンプルのフレームワークベースのバージョンも参照してください。

インターフェイスの作成

ドライバー定義の各インターフェイスは、次の方法で指定されます。

  • GUID

  • バージョン番号

  • ドライバー定義インターフェイス構造体

  • 参照ルーチンと逆参照ルーチン

インターフェイスを作成して他のドライバーで使用できるようにするには、フレームワークベースのドライバーで次の手順を使用できます。

  1. インターフェイス構造体を定義します。

    このドライバー定義構造体の最初のメンバーは、INTERFACE ヘッダー構造体である必要があります。 その他のメンバーには、インターフェイス データと、別のドライバーが呼び出すことができる追加の構造体またはルーチンへのポインターが含まれる場合があります。

    ドライバーは、定義したインターフェイスを記述する WDF_QUERY_INTERFACE_CONFIG 構造体を提供する必要があります。

    Note

    WDF_QUERY_INTERFACE_CONFIG を使用する場合、WDF は、同じインターフェイス GUID を使用する 1 つのインターフェイスの複数のバージョンをサポートしていません。

    そのため、既存のインターフェイスの新しいバージョンを導入する場合は、INTERFACE 構造体の Size フィールドまたは Version フィールドを変更するのではなく、新しい GUID を作成することをお勧めします。

    同じインターフェイス GUID の Size または Version のフィールドを変更してドライバーで再利用する場合、ドライバーはWDF_QUERY_INTERFACE_CONFIG を提供せず、代わりに IRP_MN_QUERY_INTERFACEEvtDeviceWdmIrpPreprocess コールバック ルーチンを提供する必要があります。

  2. WdfDeviceAddQueryInterface を呼び出します。

    WdfDeviceAddQueryInterface メソッドは、次の処理を実行します:

    • フレームワークがインターフェイスに対する別のドライバーの要求を認識できるように、GUID、バージョン番号、構造体のサイズなど、インターフェイスに関する情報を格納します。
    • オプションの EvtDeviceProcessQueryInterfaceRequest イベント コールバック関数を登録します。これは別のドライバーがインターフェイスを要求したときにフレームワークが呼び出します。

ドライバー定義インターフェイスの各インスタンスは個々のデバイスに関連付けられているので、ドライバーは通常、EvtDriverDeviceAdd または EvtChildListCreateDevice コールバック関数内から WdfDeviceAddQueryInterface を呼び出します。

インターフェイスへのアクセス

ドライバーがインターフェイスを定義している場合、別のフレームワークベースのドライバーは、WdfFdoQueryForInterface を呼び出し、GUID、バージョン番号、構造体へのポインター、および構造体のサイズを渡すことによって、インターフェイスへのアクセスを要求できます。 フレームワークは I/O 要求を作成し、ドライバー スタックの先頭に送信します。

ドライバーは通常、EvtDriverDeviceAdd コールバック関数内から WdfFdoQueryForInterface を呼び出します。 または、デバイスが動作状態でないときにドライバーがインターフェイスを解放する必要がある場合、ドライバーは、EvtDevicePrepareHardware コールバック関数内から WdfFdoQueryForInterface を呼び出し、EvtDeviceReleaseHardware コールバック関数内からインターフェイスの逆参照ルーチンを呼び出すことができます。

ドライバー A がドライバー B に対して、ドライバー B が定義したインターフェイスを要求した場合、フレームワークはドライバー B の要求を処理します。フレームワークは、GUID とバージョンがサポートされているインターフェイスを表していること、およびドライバー A が提供する構造体のサイズがインターフェイスを保持するのに十分な大きさであることを確認します。

ドライバーが WdfFdoQueryForInterface を呼び出すと、フレームワークによって作成される I/O 要求は、ドライバー スタックの一番下まで移動します。 単純なドライバー スタックが 3 つのドライバー (A、B、C) で構成され、ドライバー A がインターフェイスを要求した場合、ドライバー B とドライバー C の両方でインターフェイスをサポートできます。 たとえば、ドライバー B は、ドライバー C に要求を渡す前に、ドライバー A のインターフェイス構造を埋めることができます。ドライバー C は、インターフェイス構造体の内容を調べて変更する EvtDeviceProcessQueryInterfaceRequest コールバック関数を提供できます。

ドライバー A がドライバー B のインターフェイスにアクセスする必要があり、ドライバー B がリモート I/O ターゲット (つまり、別のドライバー スタック内にあるドライバー) である場合、ドライバー A は WdfFdoQueryForInterface ではなく WdfIoTargetQueryForInterface を呼び出す必要があります。

一方向または双方向の通信の使用

一方向の通信を提供するインターフェイス、または双方向通信を提供するインターフェイスを定義できます。 双方向通信を指定するために、ドライバーは、その WDF_QUERY_INTERFACE_CONFIG 構造体の ImportInterface メンバーを TRUE に設定します。

インターフェイスが一方向の通信を提供し、ドライバー A がドライバー B のインターフェイスを要求した場合、インターフェイス データはドライバー B からドライバー A にのみ流れます。フレームワークが、一方向の通信をサポートするインターフェイスに対するドライバー A の要求を受け取ると、フレームワークはドライバー定義のインターフェイス値をドライバー A のインターフェイス構造体にコピーします。 その後、ドライバー B の EvtDeviceProcessQueryInterfaceRequest コールバック関数が存在すれば呼び出されるため、インターフェイス値を調べて変更できます。

インターフェイスが双方向通信を提供する場合、インターフェイス構造体には、ドライバー B に要求を送信する前に、ドライバー A が入力するいくつかのメンバーが含まれています。ドライバー B は、ドライバー A が指定したパラメーター値を読み取り、それらの値に基づいて、ドライバー A に提供する情報を選択できます。フレームワークが双方向通信をサポートするインターフェイスに対するドライバー A の要求を受け取ると、フレームワークはドライバー B の EvtDeviceProcessQueryInterfaceRequest コールバック関数を呼び出して、受信した値を調べて出力値を指定できるようにします。 双方向通信の場合、フレームワークはインターフェイス値をドライバー A のインターフェイス構造にコピーしないため、コールバック関数が必要です。

参照カウントの維持

各インターフェイスには、参照関数と逆参照関数を含める必要があります。この関数は、インターフェイスの参照カウントをインクリメントおよびデクリメントします。 インターフェイスを定義するドライバーは、INTERFACE 構造体でこれらの関数のアドレスを指定します。

ドライバー A がドライバー B にインターフェイスを要求すると、フレームワークはインターフェイスの参照関数を呼び出してから、ドライバー A でインターフェイスを使用できるようにします。ドライバー A は、インターフェイスの使用を完了したら、インターフェイスの逆参照関数を呼び出す必要があります。

ほとんどのインターフェイスの参照関数と逆参照関数は、何もしない no-op 関数にすることができます。 フレームワークには、ほとんどのドライバーで使用できる no-op 参照カウント関数 WdfDeviceInterfaceReferenceNoOpWdfDeviceInterfaceDereferenceNoOp が用意されています。

ドライバーがインターフェイスの参照カウントを追跡し、実際の参照関数と逆参照関数を提供する必要があるのは、ドライバー A がリモート I/O ターゲット (つまり、別のドライバー スタック内にあるドライバー) からインターフェイスを要求する場合だけです。 この場合、(別のスタック内の) ドライバー B は、ドライバー A がドライバー B のインターフェイスを使用している間にデバイスが削除されないように、参照カウントを実装する必要があります。

インターフェイスを定義するドライバー B を設計する場合は、ドライバーのインターフェイスに別のドライバー スタックからアクセスするかどうかを決定する必要があります。 (ドライバー B は、そのインターフェイスの要求がローカル ドライバー スタックからの要求なのかリモート スタックからの要求なのかを判断できません。) ドライバーがリモート スタックからのインターフェイス要求をサポートする場合、ドライバーは参照カウントを実装する必要があります。

リモート I/O ターゲットのインターフェイスにアクセスするドライバー A を設計する場合、ドライバーは、ドライバー B のデバイスが削除されるときにインターフェイスを解放する EvtIoTargetQueryRemove コールバック関数、ドライバー B のデバイスが突然削除されたときにインターフェイスを解放する EvtIoTargetRemoveComplete コールバック関数、およびデバイスの削除の試行が取り消された場合にインターフェイスを再取得する EvtIoTargetRemoveCanceled コールバック関数を提供する必要があります。