次の方法で共有


ミニドライバー、ミニポート ドライバー、ドライバー ペア

ミニドライバーまたはミニポート ドライバーは、ドライバー ペアの一方として機能します。 (ミニポート、ポート) などのドライバー ペアを使用すると、ドライバーの開発を容易にすることができます。 ドライバー ペアでは、1 つのドライバーがデバイスのコレクション全体に共通する一般的なタスクを処理し、もう 1 つのドライバーは個々のデバイスに固有のタスクを処理します。 デバイス固有のタスクを処理するドライバーは、ミニポート ドライバー、ミニクラス ドライバー、ミニドライバーなど、さまざまな名前で呼ばれます。

Microsoft が汎用ドライバーを提供し、通常は独立系ハードウェア ベンダーが固有のドライバーを提供します。 このトピックを読む前に、「デバイス ノードとデバイス スタック」および「I/O 要求パケット」で提示されている概念を理解しておく必要があります。

すべてのカーネル モード ドライバーは、DriverEntry という名前の関数を実装する必要があります。この関数は、ドライバーが読み込まれた直後に呼び出されます。 DriverEntry 関数により、DRIVER_OBJECT 構造体に含まれる特定のメンバーには、当該のドライバーがこれ以外に実装するいくつかの関数へのポインターが提供されます。 たとえば DriverEntry 関数は、次の図に示すように、DRIVER_OBJECT 構造体の Unload メンバーに、ドライバーの Unload 関数へのポインターを提供します。

diagram showing the driver-object structure with the unload member.

次の図のように、DRIVER_OBJECT 構造体の MajorFunction メンバーは、I/O 要求パケット (IRP) を処理する関数に対するポインターの配列になっています。 通常、ドライバーは MajorFunction 配列のいくつかのメンバーに、さまざまな種類の IRP を処理する関数 (ドライバーによって実装されます) へのポインターを設定します。

diagram showing the driver-object structure with the majorfunction member.

IRP は、IRP_MJ_READIRP_MJ_WRITEIRP_MJ_PNP のような定数で識別されるメジャー関数コードに応じて分類することができます。 主要な関数コードを識別する定数は、MajorFunction 配列のインデックスとして機能します。 たとえば、IRP_MJ_WRITE というメジャー関数コードを含む IRP を処理するディスパッチ関数を実装するドライバーを考えてみます。 この場合、ドライバーは、配列の MajorFunction[IRP_MJ_WRITE] 要素にディスパッチ関数へのポインターを提供する必要があります。

通常、ドライバーは MajorFunction 配列の一部の要素を設定し、残りの要素は I/O マネージャーによって提供される既定値のままにします。 次の例は、!drvobj デバッガー拡張機能を使用して、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

デバッガーの出力では、parport.sys がドライバーのエントリ ポイントである GsDriverEntry を実装していることがわかります。 GsDriverEntry は、ドライバーのビルド時に自動的に生成され、いくつかの初期化を実行してから、ドライバー開発者によって実装された DriverEntry を呼び出します。

また、Parport ドライバーが (DriverEntry 関数内で)、以下の主要な関数コードのディスパッチ関数へのポインターを提供していることもわかります。

  • IRP_MJ_CREATE
  • IRP_MJ_CLOSE
  • IRP_MJ_READ
  • IRP_MJ_WRITE
  • IRP_MJ_QUERY_INFORMATION
  • IRP_MJ_SET_INFORMATION
  • IRP_MJ_DEVICE_CONTROL
  • IRP_MJ_INTERNAL_DEVICE_CONTROL
  • IRP_MJ_CLEANUP
  • IRP_MJ_POWER
  • IRP_MJ_SYSTEM_CONTROL
  • IRP_MJ_PNP

MajorFunction 配列の残りの要素は、既定のディスパッチ関数 nt!IopInvalidDeviceRequest へのポインターを保持します。

デバッガーの出力では、parport ドライバーが UnloadAddDevice の関数ポインターを提供し、StartIo の関数ポインターは提供しなかったことがわかります。 AddDevice は、その関数ポインターが DRIVER_OBJECT 構造体に格納されていないという意味で、特殊な関数です。 関数ポインターは、DRIVER_OBJECT 構造体の拡張機能に含まれる AddDevice メンバーに格納されています。 次の図は、parport ドライバーが DriverEntry 関数で提供した関数ポインターを示しています。 parport によって提供される関数ポインターは、影付きになっています。

diagram of function pointers in a driver-object structure.

ドライバー ペアによる作業の効率化

一定期間にわたって、Microsoft の内外のドライバー開発者が Windows ドライバー モデル (WDM) の経験を積んだため、ディスパッチ関数に関して次のようなことがわかってきました。

  • ディスパッチ関数は、主に定型的です。 たとえば、関数コード IRP_MJ_PNP のディスパッチ関数の大部分がすべてのドライバーに共通しています。 これは、個々のハードウェアを制御する個々のドライバーに固有のプラグ アンド プレイ (PnP) コードのごく一部にすぎません。
  • ディスパッチ関数は複雑で、正しく理解するのが困難です。 スレッド同期、IRP のキュー処理、IRP の取り消しなどの機能の実装は困難であり、オペレーティング システムのしくみを深く理解している必要があります。

ドライバー開発者の作業を容易にするために、Microsoft はいくつかのテクノロジ固有のドライバー モデルを作成しました。 一見すると、テクノロジ固有のモデルは互いに大きく異なっているように見えますが、詳しく見ると、それらの多くが次のパラダイムに基づいていることが明らかになります。

  • ドライバーは、2 つの部分に分割されます。1 つは一般的なプロセスを処理し、もう 1 つは特定のデバイスに固有のプロセスを処理します。
  • 汎用の部分は、Microsoft によって作成されます。
  • 固有の部分は、Microsoft によって作成されることも、独立系ハードウェア ベンダーによって作成されることもあります。

Proseware 社と Contoso 社の両方が、WDM ドライバーを必要とするおもちゃのロボットを作っているとします。 また、Microsoft が GeneralRobot.sys という汎用ロボット ドライバーを提供しているとします。 Proseware と Contoso は、それぞれが独自のロボットの要件を処理する小さなドライバーを作成できます。 たとえば、Proseware は ProsewareRobot.sys を作成し、ドライバーのペア (ProsewareRobot.sys、GeneralRobot.sys) を組み合わせて 1 つの WDM ドライバーを形成できます。 同様に、ドライバーのペア (ContosoRobot.sys、GeneralRobot.sys) を組み合わせて、1 つの WDM ドライバーを形成できます。 最も一般的な形式として考えられるのは、(specific.sys、general.sys) ペアを使用してドライバーを作成できるということです。

ドライバー ペア中の関数ポインター

(specific.sys、general.sys) ペアでは、Windows は specific.sys を読み込み、その DriverEntry 関数を呼び出します。 specific.sys の DriverEntry 関数は、DRIVER_OBJECT 構造体に対するポインターを受け取ります。 通常は、DriverEntry によって、MajorFunction 配列のいくつかの要素にディスパッチ関数へのポインターが設定されると想定します。 また、DriverEntry についても DRIVER_OBJECT 構造体に含まれる Unload メンバー (および StartIo メンバー) とドライバー オブジェクトの拡張機能に含まれる AddDevice メンバーにポインターが提供されることを期待します。 ただし、ドライバー ペア モデルでは、DriverEntry は必ずしもこの操作を行いません。 つまり specific.sys の DriverEntry 関数は、general.sys で実装された初期化関数に対し、DRIVER_OBJECT 構造体を受け渡します。 次のコード例は、初期化関数が (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);
}

GeneralRobot.sys の初期化関数により、DRIVER_OBJECT 構造体 (およびその拡張機能) に含まれる該当メンバーと、MajorFunction 配列で該当する要素に対して関数ポインターが書き込まれます。 I/O マネージャーが IRP をドライバー ペアに送信すると、IRP は最初に、GeneralRobot.sys によって実装されているディスパッチ関数に送られるという考え方です。 GeneralRobot.sys が独自に IRP を処理できる場合は、特定のドライバー (ProsewareRobot.sys) を関与させる必要はありません。 GeneralRobot.sys が IRP 処理のすべてではなく一部を処理できる場合は、ProsewareRobot.sys によって実装されているコールバック関数の 1 つから支援を得ます。 GeneralRobot.sys は、GeneralRobotInit 呼び出しで ProsewareRobot コールバックへのポインターを受け取ります。

DriverEntry から制御が戻った後のある時点で、Proseware Robot デバイス ノードのデバイス スタックが構築されます。 デバイス スタックは、次のようになります。

diagram of the proseware robot device node, showing three device objects in the device stack: afterthought.sys (filter do), prosewarerobot.sys, generalrobot.sys (fdo), and pci.sys (pdo).

前の図に示されているように、Proseware Robot のデバイス スタックには 3 つのデバイス オブジェクトがあります。 一番上のデバイス オブジェクトは、フィルター ドライバー AfterThought.sys に関連付けられているフィルター デバイス オブジェクト (Filter DO) です。 中央のデバイス オブジェクトは、ドライバー ペア (ProsewareRobot.sys、GeneralRobot.sys) に関連付けられている機能デバイス オブジェクト (FDO) です。 ドライバー ペアは、デバイス スタックのファンクション ドライバーとして機能します。 一番下のデバイス オブジェクトは、Pci.sys に関連付けられている物理デバイス オブジェクト (PDO) です。

ドライバー ペアは、デバイス スタック内の 1 つのレベルのみを占有し、1 つのデバイス オブジェクト (FDO) にのみ関連付けられていることに注意してください。 GeneralRobot.sys が IRP を処理する場合、ProsewareRobot.sys を呼び出して支援を受ける可能性がありますが、これはデバイス スタックで要求を下に渡すことと同じではありません。 ドライバー ペアは、デバイス スタック内の 1 つのレベルで、単一の WDM ドライバーを形成します。 ドライバー ペアは、IRP を完了するか、デバイス スタックで下の PDO に渡します。PDO は、Pci.sys に関連付けられています。

ドライバー ペアの例

ノート PC にワイヤレス ネットワーク カードがあり、デバイス マネージャーを調べて、netwlv64.sys がネットワーク カードのドライバーであると判断したとします。 !drvobj デバッガー拡張機能を使用して、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

デバッガーの出力では、netwlv64.sys がドライバーのエントリ ポイントである GsDriverEntry を実装していることがわかります。 GsDriverEntry は、ドライバーのビルド時に自動的に生成され、いくつかの初期化を実行してから、ドライバー開発者によって作成された DriverEntry を呼び出します。

この例では、netwlv64.sys が DriverEntry を実装しますが、ndis.sys は AddDeviceUnload、およびいくつかのディスパッチ関数を実装します。 Netwlv64.sys は NDIS ミニポート ドライバーと呼ばれ、ndis.sys は NDIS ライブラリと呼ばれます。 2 つのモジュールが一緒になって、(NDIS ミニポート、NDIS ライブラリ) ペアを形成します。

次の図は、ワイヤレス ネットワーク カードのデバイス スタックを示しています。 ドライバー ペア (netwlv64.sys, ndis.sys) は、デバイス スタック内の 1 つのレベルのみを占有し、1 つのデバイス オブジェクト (FDO) にのみ関連付けられていることに注意してください。

diagram of the wireless network card device stack, showing netwlv64.sys, ndis.sys as the driver pair associated with the fdo and pci.sys associated with the pdo .

利用可能なドライバー ペア

さまざまなテクノロジ固有のドライバー モデルが、ドライバー ペアの固有の部分と汎用の部分にさまざまな名前を使用しています。 多くの場合、ペアの固有部分には "ミニ" というプレフィックスが付きます。以下に、利用可能なペア (固有ドライバー、汎用ドライバー) の一部を示します。

  • (ディスプレイ ミニポート ドライバー、ディスプレイ ポート ドライバー)
  • (オーディオ ミニポート ドライバー、オーディオ ポート ドライバー)
  • (ストレージ ミニポート ドライバー、ストレージ ポート ドライバー)
  • (バッテリ ミニクラス ドライバー、バッテリ クラス ドライバー)
  • (HID ミニドライバー、HID クラス ドライバー)
  • (チェンジャー ミニクラス ドライバー、チェンジャー ポート ドライバー)
  • (NDIS ミニポート ドライバー、NDIS ライブラリ)

一覧からわかるように、モデルの一部ではドライバー ペアの汎用部分について "クラス ドライバー" という用語が使われています。 この種のクラス ドライバーは、スタンドアロン クラス ドライバーとは異なり、クラス フィルター ドライバーとも異なります。

すべてのドライバー開発者のための概念

デバイス ノードとデバイス スタック

ドライバー スタック