Report du traitement IRP PnP jusqu’à la fin des pilotes inférieurs
Certains PNP et certains IRP d’alimentation doivent d’abord être traités par le pilote de bus parent pour un appareil, puis par chaque pilote supérieur suivant dans la pile d’appareils. Par exemple, le pilote de bus parent doit être le premier pilote à effectuer ses opérations de démarrage pour un appareil (IRP_MN_START_DEVICE), suivi de chaque pilote supérieur suivant. Pour un tel IRP, les pilotes de fonction et de filtre doivent définir une routine d’achèvement d’E/S, passer l’IRP au pilote inférieur suivant et reporter toutes les activités de traitement de l’IRP jusqu’à ce que les pilotes inférieurs aient terminé l’IRP.
Une routine IoCompletion peut être appelée dans IRQL DISPATCH_LEVEL, mais un pilote de fonction ou de filtre peut avoir besoin de traiter l’IRP à IRQL = PASSIVE_LEVEL. Pour revenir à PASSIVE_LEVEL à partir d’une routine IoCompletion , un pilote peut utiliser un événement de noyau. Le pilote inscrit une routine IoCompletion qui définit un événement en mode noyau, puis le pilote attend l’événement dans sa routine DispatchPnP . Lorsque l’événement est défini, les pilotes inférieurs ont terminé l’IRP et le pilote est autorisé à traiter l’IRP.
Notez qu’un pilote ne doit pas utiliser cette technique pour attendre que les pilotes inférieurs terminent un IRP d’alimentation (IRP_MJ_POWER). L’attente d’un événement dans la routine DispatchPower définie dans la routine IoCompletion peut entraîner un blocage. Pour plus d’informations, consultez Passer des IRPs d’alimentation.
Les deux figures suivantes montrent un exemple de la façon dont un pilote attend que les pilotes inférieurs terminent un IRP PnP. L’exemple montre ce que les pilotes de fonction et de bus doivent faire, ainsi que la façon dont ils interagissent avec le gestionnaire PnP et le gestionnaire d’E/S.
Les notes suivantes correspondent aux nombres cerclé de la figure précédente :
Le gestionnaire PnP appelle le gestionnaire d’E/S pour envoyer un IRP au pilote supérieur de la pile d’appareils.
Le gestionnaire d’E/S appelle la routine DispatchPnP du pilote supérieur. Dans cet exemple, il n’y a que deux pilotes dans la pile de périphériques (le pilote de fonction et le pilote de bus parent) et le pilote de fonction est le pilote supérieur.
Le pilote de fonction déclare et initialise un événement en mode noyau, configure l’emplacement de la pile pour le pilote inférieur suivant et définit une routine IoCompletion pour cette IRP.
Le pilote de fonction peut utiliser IoCopyCurrentIrpStackLocationToNext pour configurer l’emplacement de la pile.
Dans l’appel à IoSetCompletionRoutine, le pilote de fonction définit InvokeOnSuccess, InvokeOnError et InvokeOnCancel sur TRUE et transmet l’événement en mode noyau dans le cadre du paramètre de contexte.
Le pilote de fonction transmet l’IRP dans la pile des appareils avec IoCallDriver avant d’effectuer des opérations pour gérer l’IRP.
Le gestionnaire d’E/S envoie l’IRP au pilote inférieur suivant dans la pile des périphériques en appelant la routine DispatchPnP de ce pilote.
Le pilote inférieur suivant dans cet exemple est le pilote le plus bas de la pile de périphériques, le pilote de bus parent. Le pilote de bus effectue ses opérations pour démarrer l’appareil. Le pilote de bus définit Irp-IoStatus.Status>, définit Irp-IoStatus.Information> si cela est pertinent pour cette IRP et termine l’IRP en appelant IoCompleteRequest.
Si le pilote de bus appelle d’autres routines de pilote ou envoie des E/S à l’appareil pour le démarrer, le pilote de bus ne termine pas l’IRP PnP dans sa routine DispatchPnP . Au lieu de cela, il doit marquer l’IRP en attente avec IoMarkIrpPending et retourner STATUS_PENDING à partir de sa routine DispatchPnP . Le pilote appelle plus tard IoCompleteRequest à partir d’une autre routine de pilote, éventuellement une routine DPC.
La figure suivante montre la deuxième partie de l’exemple, où les pilotes les plus élevés dans la pile des appareils reprennent leur traitement IRP différé.
Les notes suivantes correspondent aux nombres cerclé de la figure précédente :
Lorsque le pilote de bus appelle IoCompleteRequest, le gestionnaire d’E/S examine les emplacements de pile des pilotes supérieurs et appelle toutes les routines IoCompletion qu’il trouve. Dans cet exemple, le gestionnaire d’E/S localise et appelle la routine IoCompletion pour le pilote supérieur suivant, le pilote de fonction.
La routine IoCompletion du pilote de fonction définit l’événement en mode noyau fourni dans le paramètre de contexte et retourne STATUS_MORE_PROCESSING_REQUIRED.
La routine IoCompletion doit retourner STATUS_MORE_PROCESSING_REQUIRED pour empêcher le gestionnaire d’E/S d’appeler les routines IoCompletion définies par les pilotes supérieurs à ce stade. La routine IoCompletion utilise cette status pour prévenir l’achèvement afin que la routine DispatchPnP de son pilote puisse reprendre le contrôle. Le gestionnaire d’E/S va reprendre l’appel des routines IoCompletion des pilotes supérieurs pour cette IRP lorsque la routine DispatchPnP de ce pilote termine l’IRP.
Le gestionnaire d’E/S arrête d’effectuer l’IRP et retourne le contrôle à la routine appelée IoCompleteRequest, qui dans cet exemple est la routine DispatchPnP du pilote de bus.
Le pilote de bus retourne à partir de sa routine DispatchPnP avec status indiquant le résultat de son traitement IRP : STATUS_SUCCESS ou un status d’erreur.
IoCallDriver retourne le contrôle à son appelant, qui dans cet exemple est la routine DispatchPnP du pilote de fonction.
La routine DispatchPnP du pilote de fonction reprend le traitement de l’IRP.
Si IoCallDriver retourne STATUS_PENDING, la routine DispatchPnP a repris l’exécution avant l’appel de sa routine IoCompletion . La routine DispatchPnP doit donc attendre que l’événement du noyau soit signalé par sa routine IoCompletion . Cela garantit que la routine DispatchPnP ne poursuivra pas le traitement de l’IRP tant que tous les pilotes inférieurs ne l’auront pas terminé.
Si Irp-IoStatus.Status> est défini sur une erreur, un pilote inférieur a échoué à l’IRP et le pilote de fonction ne doit pas continuer à gérer l’IRP (sauf pour tout nettoyage nécessaire).
Une fois que les pilotes inférieurs ont réussi l’IRP, le pilote de fonction traite l’IRP.
Pour les IRPs gérés en premier par le pilote de bus parent, le pilote de bus définit généralement une status réussie dans Irp-IoStatus.Status> et définit éventuellement une valeur dans Irp-IoStatus.Information>. Les pilotes de fonction et de filtre laissent les valeurs dans IoStatus telles quelles, sauf s’ils échouent à l’IRP.
La routine DispatchPnP du pilote de fonction appelle IoCompleteRequest pour terminer l’IRP. Le gestionnaire d’E/S reprend le traitement de fin d’E/S. Dans cet exemple, il n’y a aucun pilote de filtre au-dessus du pilote de fonction, et donc plus de routines IoCompletion à appeler. Quand IoCompleteRequest retourne le contrôle à la routine DispatchPnP du pilote de fonction, la routine DispatchPnP retourne status.
Pour certains IRPs, si un pilote de fonction ou de filtre échoue à l’IRP sur son chemin de sauvegarde de la pile d’appareils, le gestionnaire PnP informe les pilotes inférieurs. Par exemple, si un pilote de fonction ou de filtre échoue à une IRP_MN_START_DEVICE, le gestionnaire PnP envoie un IRP_MN_REMOVE_DEVICE à la pile d’appareils.