デバイスドライバをはじめ、デバイスにかかわるお困りごとの際はお気軽にお問い合わせください。
【 デバドラ講座16 : ドライバの構成 】
C言語は図1のように必ずmain()関数から始まります。必要に応じて他の機能を持つ関数を使用して、C言語は構成されています。
ドライバはC言語をベースとして書かれていますが、main()関数は存在しません。ドライバはルーチン(関数)によって構成されています。これらのルーチンはI/Oマネージャから呼び出されます。ドライバのルーチンは以下のように分別することができます。
- ドライバエントリルーチン
- アンロードルーチン
- ディスパッチルーチン
- 割り込みサービスルーチン
- DPCルーチン
- 特別処理、特別種類ドライバのみ適応ルーチン
1. ドライバエントリルーチン
1-1.ドライバ初期化ルーチン(DriverEntryルーチン)
すべてのドライバにこのルーチンが必要となります。ドライバのロード後、最初にこのルーチンが呼ばれます。ここで、ドライバに使うディスパッチルーチンや他のポインタが準備されます。Windows NTシステムではここでデバイスの初期化、ドライバに対応するハードウェアリソースの割り当て、デバイスオブジェクトの作成が実行されます。このルーチンはPASSIVE_LEVELで実行されます。
1-2.AddDeviceルーチン
PnPに対応するOSは、このルーチンが必要になります。Windowsでは、このルーチンでデバイスの初期化、ドライバに対応するハードウェアリソースの割り当て、デバイスオブジェクトの作成が実行されます。このルーチンはPASSIVE_LEVELで実行されます。
Windows はWindows 2000からPnPに対応しています。ここで、Windows NTとWindows 2000/XPではドライバ初期化エントリルーチンで行われる処理が異なるということを理解する必要があります。NTでは、存在しないデバイスに対しても初期化やデバイスオブジェクトの作成が行われます。デバイスが存在しない場合、デバイスオブジェクトの作成は失敗し、削除されます。Windows 2000/XPでは存在するデバイス、及び新たに挿入されたデバイスに対して初期化、ハードウェアリソースの割り当て、デバイスオブジェクトの作成が行われます。
2.アンロードルーチン
ドライバのアンロード時にアンロードルーチンを呼び出します。ハードウェアリソース割り当ての解除、デバイスオブジェクト及びドライバが作成したオブジェクトの削除、DriverEntryルーチンで確保されたメモリの解放を行います。このルーチンはPASSIVE_LEVELで実行されます。
3.ディスパッチルーチン
ディスパッチルーチンはアプリケーションからデバイスへの要求を処理します。詳しくはデバドラ講座24で説明します。
3-1.シャットダウンディスパッチルーチン
シャットダウンディスパッチルーチンはPCのシャットダウン時に呼ばれるルーチンです。このルーチンの目的はハードウェアを元の状態に戻すことです。通常このルーチンは必要ありません。このルーチンはPASSIVE_LEVELで実行されます。このルーチンのI/O機能コードはIRP_MJ_SHUTDOWNです。
3-2.オープンとクローズディスパッチルーチン
オープンとクローズディスパッチルーチンはAPI関数のCreateFileとCloseHandleに対応するルーチンです。このルーチンはI/Oマネージャより呼び出され、すべてのドライバに必要なルーチンです。このルーチンはPASSIVE_LEVELで実行されます。このルーチンのI/O機能コードはIRP_MJ_CREATEとIRP_MJ_CLOSEです。
3-3.デバイス操作ディスパッチルーチン
デバイス操作ディスパッチルーチンはAPI関数のReadFile, WriteFile, DeviceIoControlに対応するルーチンで、I/Oマネージャから必要なときに呼び出されます。このルーチンの多くはPASSIVE_LEVELで実行されます。このルーチンのI/O機能コードはIRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_DEVICEIOCONTROLです。
3-4.PnPディスパッチルーチン
PnPディスパッチルーチンはOSに対応するPnP処理を行います。デバイスが接続されると、まずAddDeviceルーチンが呼び出され、その後PnPディスパッチルーチンが呼び出されます。また、デバイスが取り外された際にもこのルーチンが呼び出されます。このルーチンはPASSIVE_LEVELで実行されます。このルーチンのI/O機能コードはIRP_MJ_PNPです。
3-5.パワーマネジメントルーチン
パワーマネジメントルーチンは電源管理処理を行います。このルーチンはPASSIVE_LEVELで実行されます。このルーチンのI/O機能コードはIRP_MJ_POWERです。
4. 割り込みサービスルーチン
割り込みサービスルーチン(ISR: Interrupt Service Routine)はドライバが割り込み処理を行う際に呼び出されます。中間ドライバにはこのルーチンは存在しません。このルーチンはDIRQ_LEVELで実行されます。
5. DPCルーチン
DPCルーチンは遅延プロシージャコールを実行する際に呼び出されます。DPCルーチンはDpcForIsrとCustomDpcの2種類があります。DpcForIsrは割り込みサービスルーチンと深く関わりがあり、時間がかかる割り込み処理はこのルーチンで実行されます。CustomDpcは任意に設定できるDPCルーチンです。このルーチンは開発者が設定することができます。例えば、タイマーを使ってデバイスをポーリングする際にはこのルーチンを使用します。このルーチンはDISPATCH_LEVELで実行されます。
6. 特別処理、特別種類ドライバのみ適応ルーチン
6-1.再初期化ルーチン
DriverEntryルーチンは…\system32\driversに存在するすべてのドライバに対して行うため、処理を簡潔にして早く終了する必要があります。そのため、初期化処理に2秒以上かかる場合、初期化処理は再初期化ルーチンに任せるべきでしょう。再初期化ルーチンは、時間があるときに処理を実行します。通常このルーチンは必要ありません。このルーチンはPASSIVE_LEVELで実行されます。
6-2.StartI/Oルーチン
複数のI/O要求が同時に1つのドライバにアクセスした際、キューイングを行うドライバがあります。キューイングされた要求を取り出して処理する際にStartI/Oルーチンは呼び出され、キューイングを行うドライバにのみこのルーチンは存在します。このルーチンはDISPATCH_LEVELで実行されます。デバイスが複数のアプリケーションからI/O要求があったとします(図2)。するとPASSIVE_LEVELでキューが貯められます。デバイスが自由になるとキューに貯められたI/O要求がDISPATCH_LEVELで実行されます。
6-3.キャンセルルーチン
キャンセルルーチンはドライバ内部キューに存在するIRPがキャンセルされるときに呼び出されます。このルーチンはDISPATCH_LEVELで実行されます。
6-4.I/O完了ルーチン
上位のドライバが下位のドライバに要求を出した後、下位のドライバの結果を受け取って次の処理を行う場合があります。このとき、上位のドライバは下位のドライバの結果を何もせずに待つのは効率が悪いです。そこで、結果を受け取った時にどのような処理を行うかをI/O完了ルーチンに収めます。上位のドライバは下位ドライバに要求を出した後、その結果を待つことなく他の処理を行います。そして下位ドライバから結果が戻ってくるとI/O完了ルーチンを呼び出し、処理を行います。このルーチンの多くはDISPATCH_LEVELで実行されます。
参考資料
- 「NTドライバプログラミング」
- Peter G. Viscarola、W. Anthony Mason著 久保雅俊、三浦秀朗訳 サイエンスパーク株式会社 監修
- 「Windows NTデバイスドライバプログラミング」
- アート・ベーカー著 WinProgDDK ML和訳プロジェクト訳