次の方法で共有


下位のドライバーが完了するまで PnP IRP 処理を延期する

一部の PnP および電源 IRP は、最初にデバイスの親バス ドライバーによって処理され、その後にデバイス スタック内で次に上位のドライバーによって順次処理される必要があります。 たとえば、親バス ドライバーは、デバイスの開始操作 (IRP_MN_START_DEVICE) を実行する最初のドライバーでなければならず、次に上位のドライバーが順次続く必要があります。 このような IRP の場合、ファンクション ドライバーとフィルター ドライバーは、I/O 完了ルーチンを設定し、次の下位ドライバーに IRP を渡して、下位ドライバーが IRP で終了するまでその IRP を処理するアクティビティを延期する必要があります。

IoCompletion ルーチンは IRQL DISPATCH_LEVEL で呼び出すことができますが、ファンクション ドライバーまたはフィルター ドライバーは IRQL = PASSIVE_LEVEL で IRP の処理が必要になる場合があります。 IoCompletion ルーチンから PASSIVE_LEVEL に戻るために、ドライバーはカーネル イベントを使用できます。 ドライバーは、カーネル モード イベントを設定する IoCompletion ルーチンを登録し、その DispatchPnP ルーチンでイベントを待機します。 イベントが設定されると、下位ドライバーが IRP を完了し、このドライバーが IRP を処理できるようになります。

ドライバーは、下位ドライバーが電源 IRP (IRP_MJ_POWER) を完了するまで待機するために、この手法を使用できないことに注意してください。 IoCompletion ルーチンで設定されている DispatchPower ルーチンでイベントを待機すると、デッドロックが発生する可能性があります。 詳細については、「電源 IRP の受け渡し」を参照してください。

次の 2 つの図は、ドライバーが下位ドライバーによる PnP IRP の完了を待機する方法の例を示しています。 この例では、ファンクション ドライバーとバス ドライバーが実行する必要がある処理に加えて、PnP マネージャーおよび I/O マネージャーとやり取りする方法を示します。

diagram illustrating postponing plug and play irp handling, part 1.

次の注釈は、前の図の丸数字に対応しています。

  1. PnP マネージャーは、I/O マネージャーを呼び出して、デバイス スタックの最上位のドライバーに IRP を送信します。

  2. I/O マネージャーは、最上位のドライバーの DispatchPnP ルーチンを呼び出します。 この例では、デバイス スタックにはドライバーが 2 つだけあり (ファンクション ドライバーと親バス ドライバー)、ファンクション ドライバーが最上位ドライバーです。

  3. ファンクション ドライバーは、カーネル モード イベントを宣言して初期化し、次の下位ドライバーのスタックの場所を設定して、この IRP の IoCompletion ルーチンを設定します。

    ファンクション ドライバーは、IoCopyCurrentIrpStackLocationToNext を使用してスタックの場所を設定できます。

    IoSetCompletionRoutine の呼び出しでは、ファンクション ドライバーは InvokeOnSuccessInvokeOnErrorInvokeOnCancelTRUE に設定し、コンテキスト パラメーターの一部としてカーネル モード イベントを渡します。

  4. ファンクション ドライバーは、IRP を処理する操作を実行する前に、IoCallDriver を使用してデバイス スタックに IRP を渡します。

  5. I/O マネージャーは、次の下位ドライバーの DispatchPnP ルーチンを呼び出すことで、デバイス スタック内のその下位ドライバーに IRP を送信します。

  6. この例で次の下位ドライバーは、デバイス スタック内で最下位のドライバー (親バス ドライバー) です。 バス ドライバーは、デバイスを起動する操作を実行します。 バス ドライバーは Irp->IoStatus.Status を設定し、この IRP に関連する場合は Irp->IoStatus.Information を設定し、IoCompleteRequest を呼び出して IRP を完了します。

    バス ドライバーは、他のドライバー ルーチンを呼び出す場合やデバイスを起動するために I/O をデバイスに送信する場合、DispatchPnP ルーチンで PnP IRP を完了しません。 代わりに、IoMarkIrpPending で保留中の IRP をマークし、DispatchPnP ルーチンから STATUS_PENDING を返す必要があります。 ドライバーは、後で別のドライバー ルーチン、場合によっては DPC ルーチンから IoCompleteRequest を呼び出します。

次の図は、デバイス スタックで上位のドライバーが延期された IRP 処理を再開する例の後半部分を示しています。

diagram illustrating postponing plug and play irp handling, part 2.

次の注釈は、前の図の丸数字に対応しています。

  1. バス ドライバーが IoCompleteRequest を呼び出すと、I/O マネージャーは上位ドライバーのスタックの場所を調べ、見つけた IoCompletion ルーチンを呼び出します。 この例では、I/O マネージャーは、次の上位ドライバーであるファンクション ドライバーの IoCompletion ルーチンを探して呼び出します。

  2. ファンクション ドライバーの IoCompletion ルーチンは、コンテキスト パラメーターで提供されたカーネル モード イベントを設定し、STATUS_MORE_PROCESSING_REQUIRED を返します。

    IoCompletion ルーチンは、I/O マネージャーがこの時点で上位ドライバーによって設定された IoCompletion ルーチンを呼び出さないように、STATUS_MORE_PROCESSING_REQUIRED を返す必要があります。 IoCompletion ルーチンは、ドライバーの DispatchPnP ルーチンが制御を回復できるように、この状態を使用して完了を防ぎます。 I/O マネージャーは、このドライバーの DispatchPnP ルーチンが IRP を完了すると、この IRP に対して上位ドライバーの IoCompletion ルーチンの呼び出しを再開します。

  3. I/O マネージャーは IRP の実行を停止し、IoCompleteRequest を呼び出したルーチン (この例では、バス ドライバーの DispatchPnP ルーチン) に制御を返します。

  4. バス ドライバーは、その IRP 処理の結果を示す状態 (STATUS_SUCCESS またはエラー状態) で DispatchPnP ルーチンから復帰します。

  5. IoCallDriver は呼び出し元に (この例では、ファンクション ドライバーの DispatchPnP ルーチン) に制御を返します。

  6. ファンクション ドライバーの DispatchPnP ルーチンは、IRP の処理を再開します。

    IoCallDriver が STATUS_PENDING を返した場合、DispatchPnP ルーチンは、その IoCompletion ルーチンが呼び出される前に実行を再開しています。 したがって、DispatchPnP ルーチンは、その IoCompletion ルーチンがカーネル イベントのシグナル通知を受けるまで待機する必要があります。 これにより、DispatchPnP ルーチンは、すべての下位ドライバーが IRP を完了するまでその IRP の処理を続行しません。

    Irp->IoStatus.Status がエラーに設定されている場合、下位ドライバーは IRP に失敗し、ファンクション ドライバーは IRP の処理を続行できません (必要なクリーンアップを除く)。

  7. 下位ドライバーが IRP を正常に完了すると、ファンクション ドライバーは IRP を処理します。

    親バス ドライバーによって最初に処理される IRP の場合、バス ドライバーは通常、Irp->IoStatus.Status に正常な状態を設定し、必要に応じて Irp->IoStatus.Information に値を設定します。 ファンクション ドライバーとフィルター ドライバーは、IRP に失敗しない限り、IoStatus の値をそのままにします。

    ファンクション ドライバーの DispatchPnP ルーチンは、IoCompleteRequest を呼び出して IRP を完了します。 I/O マネージャーは、I/O 完了処理を再開します。 この例では、ファンクション ドライバーの上にフィルター ドライバーがないため、呼び出す IoCompletion ルーチンはこれ以上ありません。 IoCompleteRequest が制御をファンクション ドライバーの DispatchPnP ルーチンに返すと、DispatchPnP ルーチンは状態を返します。

一部の IRP では、ファンクション ドライバーまたはフィルター ドライバーが、デバイス スタックに戻る途中で IRP に失敗した場合、PnP マネージャーが下位ドライバーに通知します。 たとえば、ファンクション ドライバーまたはフィルター ドライバーが IRP_MN_START_DEVICE に失敗した場合、PnP マネージャーがデバイス スタックに IRP_MN_REMOVE_DEVICE を送信します。