デバイスドライバをはじめ、デバイスにかかわるお困りごとの際はお気軽にお問い合わせください。
DriverEntryルーチンはドライバのロード後必ず呼び出されるルーチンです。デバイスの種類によって多少の違いはありますが、次のような処理が行われます。
ドライバに使うディスパッチルーチンや他のルーチンのポインタが準備されます。
DriverObject->DriverUnload =「アンロードルーチン」(必須)
DriverObject->MajorFunction[IRP_MJ_XXX] =「ディスパッチルーチン」 (必要に応じて複数存在)
DriverObject->DriverExtension->AddDevice = 「AddDeviceルーチン」(必須)
DriverObject->MajorFunction[IRP_MJ_PNP] = 「PnPディスパッチルーチン」(必須)
DriverObject->MajorFunction[IRP_MJ_POWER] = 「Powerマネジメントディスパッチルーチン」(必須)
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = 「WMIのサポートディスバッチルーチン」(必要に応じて)
ドライバエントリではまず、ドライバが制御するデバイスの物理デバイスを確認します。PnPマネージャが行います。
デバイスが見つかると、そのデバイスをソフト的に表現する必要があります。そのためにデバイスオブジェクトがドライバによって非ページプールメモリに作成されます。デバイスオブジェクトはデバイスのすべての状態を保持しています。I/Oマネージャはデバイスにアクセスする際、デバイスオブジェクトを使用します。ドライバは物理デバイスの存在の有無に関わらずデバイスオブジェクトを作成する必要があります。物理デバイスが存在しない場合、デバイスオブジェクトの作成は失敗します。デバイスオブジェクトの構造体を次に示します。
NTSTATUS IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT PDEVICE_OBJECT * DeviceObject );
この関数が成功した時、作成されたデバイスオブジェクへのポインタがセットされます。デバイスオブジェクト作成終了後に次のコードを追加し、OSにデバイスオブジェクトの作成完了を通知する必要があります。
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
バスドライバがパワーマネジメントのディスパッチルーチンをサポートするためには必要に応じて、DO_POWER_PAGABLE、DO_POWER_INRUSH,、DO_POWER_NOOPのフラグを設定する必要があります。ドライバによってデバイス名を指定する必要がない場合があります。デバイスオブジェクトを作成時にデバイス名を指定しない場合、ドライバインタフェースを使用します。ドライバインタフェースはIoRegisterDeviceInterface関数を使って設定します。この機能は比較的複雑なので説明は省略します。
アプリケーションからデバイスにアクセスするには、そのデバイスのシンボリックリンク名が必要となります。デバイスオブジェクトで定義されたデバイス名はUnicode型であり、アプリケーションから認識できません。ユーザーモードから認識されるには、ASCII型でなければなりません。ユーザーモードからアプリケーションが識別できる名前にリンクする必要があり、これをデバイス名のシンボリックリンクと言います。シンボリックを作成するためには次の二つの関数を使用します。
RtlInitUnicodeString関数はDestinationStringの示す領域を初期化し、SourceStringの内容を指すように設定されています。
VOID RtlInitUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
PCWSTR SourceString
);
IoCreateSymbolicLink関数はユーザーから参照可能な名前のシンボリックリンクを作成します。
NTSTATUS IoCreateSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName,
IN PUNICODE_STRING DeviceName
);
実際にこれらの二つの関数がどのようにシンボリックリンクを作成するか次に示します。
RtlInitUnicodeString(&linkName, L"\\??\\MyDevice");
code = IoCreateSymbolicLink(&linkName, &devName);
Handle = CreateFile ("\\\\.\\MyDevice",
GENERIC_READ,
0,
0,
OPEN_EXISTING,
0,
0);
CreateFile関数を呼ぶ際Win32のデバイス名は上のような形で使われています。デバイス名が指定されていないドライバはシンボリックを作成する必要はありません。シンボリックリンクを作成しない場合は特殊ですので、ここでは説明を省略します。シンボリック名とデバイスのネイティブNT名に関連性は必要ありません。また、1つのネイティブNTデバイスに複数のシンボリックリンクを張ることも可能です。
ドライバのハードウェアリソースが識別されると、次にドライバはI/Oマネージャからそのリソースを割り当てようとします。この作業はPnPディスパッチルーチンのマイナー関数[IRP_MN_START_DEVICE]で処理されます。デバイスリソースの割り当てには次の3つの関数を使用します。
現在ほとんどのドライバはIoAssignResources関数を用いてリソースの割り当てを行っています。HalAssignSlotResources関数はPCIバス専用の関数です。新しくドライバを書く場合はIoAssignResources関数を使うべきです。
初期化終了前にDMAドライバは、デバイスが必要とするマップレジスタの最大数を示さなければなりません。DMA転送可能なデバイスのドライバはここでアダプタオブジェクトを取得し、処理します。詳細の説明は省略します。この処理はディスパッチルーチンのマイナー関数[IRP_MN_START_DEVICE]で処理します。
デバイスの初期化はPnPディスパッチルーチンのマイナー関数[IRP_MN_START_DEVICE]で処理します。初期化に2秒以上かかるデバイスはここで再初期化ルーチンを登録します。
DriverEntryルーチンで実行される処理を図1にまとめます。処理が行われる順番は上の①~⑦ではなく任意です。