Compartilhar via


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.

diagrama ilustrando o adiamento da manipulação de irp plug and play, parte 1.

As anotações a seguir correspondem aos números circulados na figura anterior:

  1. O gerenciador de PnP chama o gerente de E/S para enviar um IRP para o driver superior na pilha do dispositivo.

  2. 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.

  3. 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.

  4. 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.

  5. O gerenciador de E/S envia o IRP para o driver mais baixo na pilha do dispositivo chamando a rotina DispatchPnP desse driver.

  6. 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.

diagrama ilustrando o adiamento da manipulação de irp plug and play, parte 2.

As anotações a seguir correspondem aos números circulados na figura anterior:

  1. 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.

  2. 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.

  3. 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.

  4. O motorista do ônibus retorna de sua rotina DispatchPnP com status indicando o resultado de seu processamento IRP: STATUS_SUCCESS ou um erro status.

  5. IoCallDriver retorna o controle ao chamador, que neste exemplo é a rotina DispatchPnP do driver de função.

  6. 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).

  7. 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.