Posticipare l'elaborazione IRP PnP fino al termine dei driver inferiori
Alcuni PnP e power IRP devono essere elaborati prima dal driver del bus padre per un dispositivo e quindi da ogni driver successivo nello stack di dispositivi. Ad esempio, il driver del bus padre deve essere il primo driver a eseguire le operazioni di avvio per un dispositivo (IRP_MN_START_DEVICE), seguito da ogni driver successivo. Per questo tipo di IRP, i driver di funzione e filtro devono impostare una routine di completamento di I/O, passare l'IRP al driver inferiore successivo e posticipare tutte le attività per elaborare l'IRP fino a quando i driver inferiori non hanno terminato l'IRP.For such an IRP, and delay any activities to process the IRP until the lower drivers have finished with the IRP.
È possibile chiamare una routine IoCompletion in IRQL DISPATCH_LEVEL, ma potrebbe essere necessario elaborare l'IRP in IRQL = PASSIVE_LEVEL. Per tornare a PASSIVE_LEVEL da una routine IoCompletion , un driver può usare un evento kernel. Il driver registra una routine IoCompletion che imposta un evento in modalità kernel e quindi il driver attende l'evento nella routine DispatchPnP . Quando l'evento è impostato, i driver inferiori hanno completato l'IRP e il driver è autorizzato a elaborare l'IRP.
Si noti che un driver non deve usare questa tecnica per attendere il completamento di un IRP di alimentazione (IRP_MJ_POWER). L'attesa di un evento nella routine DispatchPower impostata nella routine IoCompletion può causare un deadlock. Per altre informazioni, vedere Passaggio di power IRP .
Le due figure seguenti mostrano un esempio di come un driver attende il completamento di un IRP PnP da parte di un driver inferiore. L'esempio mostra cosa devono fare le funzioni e i driver del bus, oltre a come interagiscono con il gestore PnP e il gestore di I/O.
Le note seguenti corrispondono ai numeri cerchiati nella figura precedente:
Il gestore PnP chiama il gestore di I/O per inviare un IRP al driver principale nello stack di dispositivi.
Il gestore di I/O chiama la routine DispatchPnP del driver principale. In questo esempio sono presenti solo due driver nello stack di dispositivi (il driver di funzione e il driver del bus padre) e il driver di funzione è il driver principale.
Il driver di funzione dichiara e inizializza un evento in modalità kernel, configura la posizione dello stack per il driver inferiore successivo e imposta una routine IoCompletion per questo IRP.
Il driver di funzione può usare IoCopyCurrentIrpStackLocationToNext per configurare la posizione dello stack.
Nella chiamata a IoSetCompletionRoutine il driver della funzione imposta InvokeOnSuccess, InvokeOnError e InvokeOnCancel su TRUE e passa l'evento in modalità kernel come parte del parametro di contesto.
Il driver di funzione passa l'IRP verso il basso lo stack di dispositivi con IoCallDriver prima di eseguire qualsiasi operazione per gestire IRP.
Il gestore di I/O invia l'IRP al driver inferiore successivo nello stack di dispositivi chiamando la routine DispatchPnP del driver.
Il driver successivo inferiore in questo esempio è il driver più basso nello stack di dispositivi, il driver del bus padre. Il driver dell'autobus esegue le operazioni per avviare il dispositivo. Il driver del bus imposta Irp-IoStatus.Status>, imposta Irp-IoStatus.Information> se pertinente a questo IRP e completa l'IRP chiamando IoCompleteRequest.
Se il driver del bus chiama altre routine del driver o invia operazioni di I/O al dispositivo per avviarlo, il driver del bus non completa il protocollo IRP PnP nella routine DispatchPnP . Deve invece contrassegnare l'IRP in sospeso con IoMarkIrpPending e restituire STATUS_PENDING dalla routine DispatchPnP . Il driver chiama successivamente IoCompleteRequest da un'altra routine del driver, possibilmente una routine DPC.
La figura seguente illustra la seconda parte dell'esempio, in cui i driver più elevati nello stack di dispositivi riprendono l'elaborazione IRP posticipata.
Le note seguenti corrispondono ai numeri cerchiati nella figura precedente:
Quando il driver del bus chiama IoCompleteRequest, il gestore di I/O esamina le posizioni dello stack dei driver superiori e chiama tutte le routine IoCompletion trovate. In questo esempio, il gestore di I/O individua e chiama la routine IoCompletion per il driver successivo superiore, il driver di funzione.
La routine IoCompletion del driver di funzione imposta l'evento in modalità kernel fornito nel parametro di contesto e restituisce STATUS_MORE_PROCESSING_REQUIRED.
La routine IoCompletion deve restituire STATUS_MORE_PROCESSING_REQUIRED per impedire al gestore di I/O di chiamare le routine IoCompletion impostate da driver più elevati in questo momento. La routine IoCompletion usa questo stato per impedire il completamento, in modo che la routine DispatchPnP del driver possa riprendere il controllo. Il gestore di I/O riprenderà a chiamare routine IoCompletion dei driver superiori per questo IRP quando la routine DispatchPnP del driver completa l'IRP.
Il gestore di I/O interrompe il completamento dell'IRP e restituisce il controllo alla routine denominata IoCompleteRequest, che in questo esempio è la routine DispatchPnP del driver del bus.
Il driver del bus restituisce dalla routine DispatchPnP con stato che indica il risultato dell'elaborazione IRP: STATUS_SUCCESS o uno stato di errore.
IoCallDriver restituisce il controllo al chiamante, che in questo esempio è la routine DispatchPnP del driver di funzione.
La routine DispatchPnP del driver di funzione riprende l'elaborazione dell'IRP.
Se IoCallDriver restituisce STATUS_PENDING, la routine DispatchPnP ha ripreso l'esecuzione prima della chiamata della routine IoCompletion . La routine DispatchPnP , pertanto, deve attendere che l'evento kernel venga segnalato dalla routine IoCompletion . In questo modo si garantisce che la routine DispatchPnP non continuerà a elaborare l'IRP fino al completamento di tutti i driver inferiori.
Se Irp-IoStatus.Status> è impostato su un errore, un driver inferiore ha avuto esito negativo su IRP e il driver di funzione non deve continuare a gestire l'IRP (ad eccezione di eventuali operazioni di pulizia necessarie).
Dopo che i driver inferiori hanno completato correttamente l'IRP, il driver di funzione elabora l'IRP.
Per i provider di integrazione gestiti per primi dal driver del bus padre, il driver del bus imposta in genere lo stato di esito positivo in Irp-IoStatus.Status> e, facoltativamente, imposta un valore in Irp-IoStatus.Information>. I driver di funzione e filtro lasciano invariati i valori in IoStatus , a meno che non abbiano esito negativo.
La routine DispatchPnP del driver di funzione chiama IoCompleteRequest per completare l'IRP. Il gestore di I/O riprende l'elaborazione del completamento di I/O. In questo esempio non sono presenti driver di filtro al di sopra del driver di funzione e quindi non sono più routine IoCompletion da chiamare. Quando IoCompleteRequest restituisce il controllo alla routine DispatchPnP del driver della funzione, la routine DispatchPnP restituisce lo stato.
Per alcuni runtime di integrazione, se un driver di funzione o filtro non riesce a eseguire il backup dello stack di dispositivi, il gestore PnP informa i driver inferiori. Ad esempio, se un driver di funzione o filtro non riesce un IRP_MN_START_DEVICE, il gestore PnP invia un IRP_MN_REMOVE_DEVICE allo stack di dispositivi.