延後 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常式中等候事件可能會導致死結。 如需詳細資訊 ,請參閱傳遞 Power IRP 。
下圖顯示驅動程式如何等候較低驅動程式完成 PnP IRP 的範例。 此範例示範函式和匯流排驅動程式必須執行的動作,以及它們如何與 PnP 管理員和 I/O 管理員互動。
下列附注對應至上圖中的圓圈數位:
PnP 管理員會呼叫 I/O 管理員,將 IRP 傳送至裝置堆疊中的頂端驅動程式。
I/O 管理員會呼叫頂端驅動程式的 DispatchPnP 常式。 在此範例中,裝置堆疊中只有兩個驅動程式 (函式驅動程式和父匯流排驅動程式) ,而函式驅動程式是頂端驅動程式。
函式驅動程式會宣告並初始化核心模式事件、設定下一個較低驅動程式的堆疊位置,以及為此 IRP 設定 IoCompletion 常式。
函式驅動程式可以使用 IoCopyCurrentIrpStackLocationToNext 來設定堆疊位置。
在 IoSetCompletionRoutine的呼叫中,函式驅動程式會將 InvokeOnSuccess、 InvokeOnError和 InvokeOnCancel 設定為 TRUE ,並將核心模式事件當做內容參數的一部分傳遞。
函式驅動程式會先使用 IoCallDriver 將 IRP 向下傳遞,再執行任何作業來處理 IRP。
I/O 管理員會呼叫該驅動程式的 DispatchPnP 常式,將 IRP 傳送至裝置堆疊中的下一個較低驅動程式。
此範例中的下一個較低驅動程式是裝置堆疊中最低驅動程式,也就是父匯流排驅動程式。 匯流排驅動程式會執行其作業來啟動裝置。 匯流排驅動程式會設定 Irp-IoStatus.Status >、設定 Irp-IoStatus.Information > ,如果與這個 IRP 相關,並藉由呼叫 IoCompleteRequest來完成 IRP。
如果匯流排驅動程式呼叫其他驅動程式常式,或將 I/O 傳送至裝置以啟動它,則匯流排驅動程式不會在其 DispatchPnP 常式中完成 PnP IRP。 相反地,它必須使用 IoMarkIrpPending 標記 IRP 暫止,並從其 DispatchPnP 常式傳回STATUS_PENDING。 驅動程式稍後會從另一個驅動程式常式呼叫 IoCompleteRequest ,可能是 DPC 常式。
下圖顯示範例的第二個部分,其中裝置堆疊中的較高驅動程式會繼續其延後 IRP 處理。
下列附注對應至上圖中的圓圈數位:
當匯流排驅動程式呼叫 IoCompleteRequest時,I/O 管理員會檢查較高驅動程式的堆疊位置,並呼叫找到的任何 IoCompletion 常式。 在此範例中,I/O 管理員會尋找並呼叫下一個較高驅動程式 的 IoCompletion 常式,也就是函式驅動程式。
函式驅動程式的 IoCompletion 常式會設定內容參數中提供的核心模式事件,並傳回STATUS_MORE_PROCESSING_REQUIRED。
IoCompletion 常式必須傳回STATUS_MORE_PROCESSING_REQUIRED,以防止 I/O 管理員呼叫由較高驅動程式設定的 IoCompletion 常式。 IoCompletion常式會使用此狀態進行樹系完成,因此其驅動程式的DispatchPnP常式可以重新取得控制權。 當此驅動程式的DispatchPnP常式完成 IRP 時,I/O 管理員會繼續為此 IRP 呼叫較高驅動程式的IoCompletion常式。
I/O 管理員會停止完成 IRP,並將控制權傳回稱為 IoCompleteRequest的常式,在此範例中為匯流排驅動程式的 DispatchPnP 常式。
匯流排驅動程式會從 其 DispatchPnP 常式傳回狀態,指出其 IRP 處理的結果:STATUS_SUCCESS或錯誤狀態。
IoCallDriver 會將控制權傳回給其呼叫端,在此範例中為函式驅動程式的 DispatchPnP 常式。
函式驅動程式的 DispatchPnP 常式會繼續處理 IRP。
如果 IoCallDriver 傳回STATUS_PENDING, DispatchPnP 常式便已在呼叫 其 IoCompletion 常式之前繼續執行。 因此 ,DispatchPnP 常式必須等候其 IoCompletion 常式發出核心事件的訊號。 這可確保 DispatchPnP 常式在完成所有較低驅動程式之前都不會繼續處理 IRP。
如果 Irp-IoStatus.Status > 設定為錯誤,則較低的驅動程式失敗了 IRP,而且函式驅動程式不得繼續處理 IRP (,除非任何必要的清除) 。
一旦較低的驅動程式成功完成 IRP,函式驅動程式就會處理 IRP。
對於父匯流排驅動程式先處理的 IRP,匯流排驅動程式通常會在 Irp-IoStatus.Status 中設定成功的狀態,並選擇性地在Irp-IoStatus.Information >>中設定值。 函式和篩選驅動程式會讓 IoStatus 中的值保持原樣,除非它們失敗 IRP。
函式驅動程式的 DispatchPnP 常式會呼叫 IoCompleteRequest 來完成 IRP。 I/O 管理員會繼續 I/O 完成處理。 在此範例中,函式驅動程式上方沒有篩選驅動程式,因此不會再呼叫 IoCompletion 常式。 當 IoCompleteRequest 將控制權傳回函式驅動程式 DispatchPnP 常式時, DispatchPnP 常式會傳回狀態。
對於某些 IRP,如果函式或篩選驅動程式在其上備份裝置堆疊時 IRP 失敗,PnP 管理員會通知較低的驅動程式。 例如,如果函式或篩選驅動程式失敗 IRP_MN_START_DEVICE,PnP 管理員會將 IRP_MN_REMOVE_DEVICE 傳送至裝置堆疊。