Adiando o processamento de PnP IRP até que os drivers inferiores sejam concluídos
Alguns PnP e IRPs de energia devem ser processados primeiro pelo driver de barramento pai de um dispositivo e, em seguida, por cada driver mais próximo na pilha do dispositivo. Por exemplo, o motorista do ônibus pai deve ser o primeiro driver a executar suas operações de início para um dispositivo (IRP_MN_START_DEVICE), seguido por cada driver posterior. Para esse IRP, os drivers de função e filtro devem definir uma rotina de conclusão de E/S, passar o IRP para o driver mais baixo e adiar todas as atividades para processar o IRP até que os drivers inferiores tenham terminado com o IRP.
Uma rotina IoCompletion pode ser chamada em IRQL DISPATCH_LEVEL, mas um driver de função ou filtro pode precisar processar o IRP em IRQL = PASSIVE_LEVEL. Para retornar ao PASSIVE_LEVEL de uma rotina IoCompletion , um driver pode usar um evento de kernel. O driver registra uma rotina IoCompletion que define um evento de modo kernel e, em seguida, o driver aguarda o evento em sua rotina DispatchPnP . Quando o evento é definido, os drivers inferiores concluíram o IRP e o driver tem permissão para processar o IRP.
Observe que um driver não deve usar essa técnica para esperar que os drivers inferiores terminem um IRP de energia (IRP_MJ_POWER). Aguardar um evento na rotina DispatchPower definido na rotina IoCompletion pode causar um deadlock. Consulte Passando POWER IRPs para obter mais informações.
Os dois números a seguir mostram um exemplo de como um driver aguarda que drivers inferiores concluam um PnP IRP. O exemplo mostra o que os motoristas de barramento e função devem fazer, além de como eles interagem com o gerenciador de PnP e o gerente de E/S.
As anotações a seguir correspondem aos números circulados na figura anterior:
O gerenciador de PnP chama o gerente de E/S para enviar um IRP para o driver superior na pilha do dispositivo.
O gerente de E/S chama a rotina DispatchPnP do driver superior. Neste exemplo, há apenas dois drivers na pilha do dispositivo (o driver de função e o driver de barramento pai) e o driver de função é o driver superior.
O driver de função declara e inicializa um evento de modo kernel, configura o local da pilha para o driver inferior seguinte e define uma rotina IoCompletion para esse IRP.
O driver de função pode usar IoCopyCurrentIrpStackLocationToNext para configurar o local da pilha.
Na chamada para IoSetCompletionRoutine, o driver de função define InvokeOnSuccess, InvokeOnError e InvokeOnCancel como TRUE e passa o evento de modo kernel como parte do parâmetro de contexto.
O driver de função passa o IRP para baixo na pilha do dispositivo com IoCallDriver antes de executar qualquer operação para manipular o IRP.
O gerenciador de E/S envia o IRP para o driver mais baixo na pilha do dispositivo chamando a rotina DispatchPnP desse driver.
O driver mais baixo neste exemplo é o driver mais baixo na pilha do dispositivo, o motorista do ônibus pai. O motorista do ônibus executa suas operações para iniciar o dispositivo. O driver de barramento define Irp-IoStatus.Status>, define Irp-IoStatus.Information> se relevante para esse IRP e conclui o IRP chamando IoCompleteRequest.
Se o motorista do ônibus chamar outras rotinas de motorista ou enviar E/S para o dispositivo para iniciá-lo, o motorista do ônibus não concluirá o PnP IRP em sua rotina DispatchPnP . Em vez disso, ele deve marcar o IRP pendente com IoMarkIrpPending e retornar STATUS_PENDING de sua rotina DispatchPnP . Mais tarde, o driver chama IoCompleteRequest de outra rotina de driver, possivelmente uma rotina DPC.
A figura a seguir mostra a segunda parte do exemplo, em que os drivers mais altos na pilha de dispositivos retomam o processamento de IRP adiado.
As anotações a seguir correspondem aos números circulados na figura anterior:
Quando o motorista do ônibus chama IoCompleteRequest, o gerente de E/S examina os locais de pilha dos drivers mais altos e chama qualquer rotina IoCompletion encontrada. Neste exemplo, o gerenciador de E/S localiza e chama a rotina IoCompletion para o driver mais próximo, o driver de função.
A rotina IoCompletion do driver de função define o evento de modo kernel fornecido no parâmetro de contexto e retorna STATUS_MORE_PROCESSING_REQUIRED.
A rotina IoCompletion deve retornar STATUS_MORE_PROCESSING_REQUIRED para impedir que o gerente de E/S chame rotinas de IoCompletion definidas por drivers mais altos no momento. A rotina IoCompletion usa essa status para evitar toda a conclusão para que a rotina DispatchPnP do driver possa recuperar o controle. O gerente de E/S retomará a chamada de rotinas de IoCompletion de drivers mais altos para esse IRP quando a rotina DispatchPnP desse driver concluir o IRP.
O gerente de E/S para de concluir o IRP e retorna o controle para a rotina chamada IoCompleteRequest, que neste exemplo é a rotina DispatchPnP do motorista de ônibus.
O motorista do ônibus retorna de sua rotina DispatchPnP com status indicando o resultado de seu processamento IRP: STATUS_SUCCESS ou um erro status.
IoCallDriver retorna o controle ao chamador, que neste exemplo é a rotina DispatchPnP do driver de função.
A rotina DispatchPnP do driver de função retoma o processamento do IRP.
Se IoCallDriver retornar STATUS_PENDING, a rotina DispatchPnP retomará a execução antes que sua rotina IoCompletion seja chamada. A rotina DispatchPnP , portanto, deve aguardar o evento de kernel ser sinalizado por sua rotina IoCompletion . Isso garante que a rotina DispatchPnP não continue processando o IRP até que todos os drivers inferiores o tenham concluído.
Se Irp-IoStatus.Status> estiver definido como um erro, um driver inferior falhará no IRP e o driver de função não deverá continuar manipulando o IRP (exceto para qualquer limpeza necessária).
Depois que os drivers inferiores tiverem concluído com êxito o IRP, o driver de função processará o IRP.
Para IRPs que estão sendo manipulados primeiro pelo motorista do ônibus pai, o motorista do ônibus normalmente define um status bem-sucedido em Irp-IoStatus.Status> e, opcionalmente, define um valor em Irp-IoStatus.Information>. Os drivers de função e filtro deixam os valores em IoStatus como estão, a menos que eles falhem no IRP.
A rotina DispatchPnP do driver de função chama IoCompleteRequest para concluir o IRP. O gerente de E/S retoma o processamento de conclusão de E/S. Neste exemplo, não há drivers de filtro acima do driver de função e, portanto, não há mais rotinas de IoCompletion para chamar. Quando IoCompleteRequest retorna o controle para a rotina DispatchPnP do driver de função, a rotina DispatchPnP retorna status.
Para alguns IRPs, se um driver de função ou filtro falhar no IRP no caminho de backup da pilha do dispositivo, o gerenciador de PnP informará os drivers inferiores. Por exemplo, se uma função ou driver de filtro falhar em um IRP_MN_START_DEVICE, o gerenciador de PnP enviará um IRP_MN_REMOVE_DEVICE para a pilha do dispositivo.