Share via


ドライバーで定義されたインターフェイスの使用

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

たとえば、バス ドライバーは、子デバイスに関する情報が子デバイスのリソース リストで使用できない場合、その情報を取得するために上位レベルのドライバーが呼び出すことができる 1 つ以上のルーチンを提供できます。

WDK に記載されている、ドライバーで定義された一連のインターフェイスの例については、「USB クライアントで使用できるバス ドライバー インターフェイス」(英語の可能性あり) を参照してください。フレームワークベース バージョンの toaster サンプルも参照してください。

インターフェイスの作成

ドライバーで定義された各インターフェイスは、次のものによって指定されます。

  • GUID

  • バージョン番号

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

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

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

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

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

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

  2. WdfDeviceAddQueryInterface を呼び出します。

    WdfDeviceAddQueryInterface メソッドは、次の処理を行います。

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

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

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

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

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

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

ドライバーが WdfFdoQueryForInterface を呼び出すと、フレームワークが作成した I/O 要求がドライバー スタックの最下位ドライバーまで送信されます。単純なドライバー スタックが A、B、および C の 3 つのドライバーで構成されている場合に、ドライバー 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 コールバック関数 (存在する場合) を呼び出すため、インターフェイス値を確認し、場合によってその値を変更できます。

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

参照カウントの保持

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

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

ほとんどのインターフェイスの参照関数と逆参照関数は、何も実行しない非操作関数にすることができます。フレームワークには、ほとんどのドライバーが使用できる WdfDeviceInterfaceReferenceNoOp および WdfDeviceInterfaceDereferenceNoOp という非操作参照カウント関数が用意されています。

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

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

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