電源 IRP の受け渡し

デバイス スタック下位の PDO まで電源 IRP を渡して、電力遷移をクリーンに管理できるようにする必要があります。 デバイス スタックの下位へ IRP が移動するとき、ドライバーではデバイスの消費電力を低減する IRP を扱います。 IRP がデバイス スタックの上位へ戻るとき、ドライバーではデバイスに電源を適用する IRP を IoCompletion ルーチンで扱います。

Windows 7 および Windows Vista で、デバイス スタックの下位方向に電源 IRP を渡すためにドライバーで実行する必要がある手順を次の図に示します。

diagram illustrating passing down a power irp in windows vista.

上図に示すように、Windows 7 と Windows Vista ではドライバーで次の操作を実行する必要があります。

  1. IoCompletion ルーチンを設定する場合は IoCopyCurrentIrpStackLocationToNext を呼び出し、IoCompletion ルーチンを設定しない場合は IoSkipCurrentIrpStackLocation を呼び出します。

    これら 2 つのルーチンは、次の下位ドライバーで使用する IRP スタックの場所を設定します。 現在のスタックの場所をコピーすると、IoCompletion ルーチンを実行するときに IRP スタック ポインターが確実に正しい場所に設定されます。

    正しく記述されていないドライバーで、誤って IoSkipCurrentIrpStackLocation を呼び出してから完了ルーチンを設定すると、そのドライバーよりも下位のドライバーによって設定された完了ルーチンが上書きされることがあります。

  2. 完全なルーチンが必要な場合は、IoSetCompletionRoutine を呼び出して IoCompletion ルーチンを設定します。

  3. IoCallDriver を呼び出して、スタックの中で次の下位にあるドライバーに IRP を渡します。

Windows Server 2003、Windows XP、Windows 2000 で、デバイス スタックの下位方向へ電源 IRP を渡すためにドライバーが実行する必要がある手順を次の図に示します。

passing down a power irp (windows server 2003, windows xp, and windows 2000).

上図に示すように、ドライバーは次の操作を実行する必要があります。

  1. ドライバーの種類に応じて PoStartNextPowerIrp の呼び出しが必要になります。 詳細については「PoStartNextPowerIrp の呼び出し」を参照してください。

  2. IoCompletion ルーチンを設定する場合は IoCopyCurrentIrpStackLocationToNext を呼び出し、IoCompletion ルーチンを設定しない場合は IoSkipCurrentIrpStackLocation を呼び出します。

    これら 2 つのルーチンは、次の下位ドライバーで使用する IRP スタックの場所を設定します。 現在のスタックの場所をコピーすると、IoCompletion ルーチンを実行するときに IRP スタック ポインターが確実に正しい場所に設定されます。

  3. IoSetCompletionRoutine を呼び出して IoCompletion ルーチンを設定します。 IoCompletion ルーチンでは、ほとんどのドライバーが PoStartNextPowerIrp を呼び出して、次の電源 IRP を扱う準備ができていることを示します。

  4. PoCallDriver を呼び出して、スタックの中で次の下位にあるドライバーに IRP を渡します。

    ドライバーは、他の IRP の場合と同様に IoCallDriver を使用するのではなく、PoCallDriver を使用して電源 IRP の適切な同期をとる必要があります。 詳細については「IoCallDriver の呼び出しと PoCallDriver の呼び出し」を参照してください。

IoCompletion ルーチンは IRQL = DISPATCH_LEVEL の場合に呼び出すことができる点に注意します。 したがって、あるドライバーが IRP の処理を終了した後、その上位ドライバーが IRQL = PASSIVE_LEVEL で別の処理を必要とする場合、その上位ドライバーの完了ルーチンは、作業項目をキューに置いたうえで STATUS_MORE_PROCESSING_REQUIRED を返す必要があります。 この場合はワーカー スレッドで IRP を完了する必要があります。

Windows 98/Me では、IRQL = PASSIVE_LEVEL でドライバーが電源 IRP を完了する必要があります。

Power IRP では関数コードを変更しない

IRP の処理を管理する通常の規則のほか、IRP_MJ_POWER IRP には、電源マネージャーまたは上位レベルのドライバーによって IRP に指定されている I/O スタックの場所で、電源 IRP を受け取るドライバーがメジャーとマイナーの関数コードを変更してはならないという特別な要件があります。 電源マネージャーは、IRP が完了するまでこれらの関数コードが変更されないことを必要とするからです。 この規則に違反すると、デバッグが困難になる問題が発生することがあります。 たとえば、オペレーティング システムが応答しなくなることや "ハング" することがあります。

電源 IRP の処理中はブロックしない

電源 IRP の処理中にドライバーによって長時間の遅延が発生しないようにする必要があります。

ドライバーは、電源 IRP を下位へ渡すとき、Windows 7 と Windows Vista の場合は IoCallDriver、Windows Server 2003、Windows XP、Windows 2000 の場合は PoCallDriver を呼び出した後、可能な限り早い時点で自身の DispatchPower ルーチンから返る必要があります。 ドライバーは、カーネル イベントを待機しないか、遅延を置いてから返る必要があります。 ドライバーが電源 IRP を短時間で処理できない場合は STATUS_PENDING を返し、電源 IRP が完了するまですべての受信 IRP をキューに置く必要があります (この動作は、ブロックが許可されている PnP IRP と DispatchPnP ルーチンの場合とは異なります)。

デバイス スタックの下位にある別のドライバーによる電源処理を待機する必要があるドライバーは、自身の DispatchPower ルーチンから STATUS_PENDING を返し、電源 IRP の IoCompletion ルーチンを設定する必要があります。 そのドライバーは、自身が必要とするタスクを IoCompletion ルーチンで実行でき、さらに Windows Server 2003、Windows XP、Windows 2000 の場合は PoStartNextPowerIrpIoCompleteRequest を呼び出すことができ、それ以外の場合は IoCompleteRequest を呼び出すことができます。

たとえば、多くの場合、デバイスの電源ポリシー所有者は、要求されたシステム電源状態に適した電源状態にデバイスを設定するために、システム電源 IRP を保持しながらデバイスの電源 IRP を送信します。

このような状況で、その電源ポリシー所有者は、システム電源 IRP に IoCompletion ルーチンを設定し、そのシステム電源 IRP を次の下位ドライバーに渡したうえで、自身の DispatchPower ルーチンから STATUS_PENDING を返す必要があります。

IoCompletion ルーチンで PoRequestPowerIrp を呼び出してデバイス電源 IRP を送信し、その要求でコールバック ルーチンへのポインターを渡します。 その IoCompletion ルーチンは STATUS_MORE_PROCESSING_REQUIRED を返す必要があります。

最後に、ドライバーはコールバック ルーチンからシステム IRP を渡します。 ドライバーは、自身の DispatchPower ルーチンでカーネル イベントを待機してはならず、現在処理している IRP の IoCompletion ルーチンを使用して通知しないようにする必要があります。そのようにすると、システムのデッドロックが発生することがあります。 詳細については「デバイス電源ポリシー所有者のシステム電源設定 IRP の処理」を参照してください。

同様の状況でシステムがスリープ状態になると、電源ポリシー所有者は、デバイスの電源を遮断するデバイス IRP を送信する前に、保留されている何らかの I/O を完了することが必要な場合があります。 ドライバーは、I/O が完了したときにイベントを通知して DispatchPower ルーチンで待機するのではなく、作業項目をキューに置き、DispatchPower ルーチンから STATUS_PENDING を返す必要があります。 ワーカー スレッドでは、ドライバーは I/O が完了するまで待機してからデバイスの電源 IRP を送信します。 詳細については「IoAllocateWorkItem 関数」を参照してください。