Compartir a través de


Posponer el procesamiento irP de PnP hasta que finalicen los controladores inferiores

Algunos IRP y PnP deben ser procesados primero por el controlador de bus primario para un dispositivo y, a continuación, por cada controlador superior siguiente en la pila de dispositivos. Por ejemplo, el controlador primario del bus debe ser el primer controlador para realizar sus operaciones de inicio para un dispositivo (IRP_MN_START_DEVICE), seguido de cada controlador superior siguiente. Para este tipo de IRP, los controladores de función y filtro deben establecer una rutina de finalización de E/S, pasar el IRP al siguiente controlador inferior y posponer las actividades para procesar el IRP hasta que los controladores inferiores hayan terminado con el IRP.

Se puede llamar a una rutina de IoCompletion en irQL DISPATCH_LEVEL, pero es posible que una función o un controlador de filtro necesite procesar el IRP en IRQL = PASSIVE_LEVEL. Para volver a PASSIVE_LEVEL desde una rutina de IoCompletion , un controlador puede usar un evento de kernel. El controlador registra una rutina de IoCompletion que establece un evento en modo kernel y, a continuación, el controlador espera en el evento en su rutina DispatchPnP . Cuando se establece el evento, los controladores inferiores han completado el IRP y el controlador puede procesar el IRP.

Tenga en cuenta que un controlador no debe usar esta técnica para esperar a que los controladores inferiores finalicen un IRP de energía (IRP_MJ_POWER). Esperar un evento en la rutina DispatchPower establecida en la rutina IoCompletion puede provocar un interbloqueo. Para más información, consulte Paso de IRP de Power .

En las dos figuras siguientes se muestra un ejemplo de cómo un controlador espera a que los controladores inferiores completen un IRP de PnP. En el ejemplo se muestra lo que deben hacer la función y los controladores de autobús, además de cómo interactúan con el administrador de PnP y el administrador de E/S.

diagrama que ilustra el posponer el control irp plug and play, parte 1.

Las notas siguientes corresponden a los números en círculo de la ilustración anterior:

  1. El administrador de PnP llama al administrador de E/S para enviar un IRP al controlador superior de la pila de dispositivos.

  2. El administrador de E/S llama a la rutina DispatchPnP del controlador superior. En este ejemplo, solo hay dos controladores en la pila de dispositivos (el controlador de función y el controlador de bus primario) y el controlador de función es el controlador superior.

  3. El controlador de función declara e inicializa un evento en modo kernel, configura la ubicación de la pila para el controlador inferior siguiente y establece una rutina de IoCompletion para este IRP.

    El controlador de función puede usar IoCopyCurrentIrpStackLocationToNext para configurar la ubicación de la pila.

    En la llamada a IoSetCompletionRoutine, el controlador de función establece InvokeOnSuccess, InvokeOnError e InvokeOnCancel en TRUE y pasa el evento en modo kernel como parte del parámetro de contexto.

  4. El controlador de función pasa el IRP a la pila de dispositivos con IoCallDriver antes de realizar cualquier operación para controlar el IRP.

  5. El administrador de E/S envía el IRP al controlador inferior siguiente de la pila de dispositivos mediante una llamada a la rutina DispatchPnP del controlador.

  6. El siguiente controlador inferior de este ejemplo es el controlador más bajo de la pila de dispositivos, el controlador de bus primario. El controlador de bus realiza sus operaciones para iniciar el dispositivo. El controlador de bus establece Irp-IoStatus.Status>, establece Irp-IoStatus.Information> si es relevante para este IRP y completa el IRP llamando a IoCompleteRequest.

    Si el controlador de autobús llama a otras rutinas de controlador o envía E/S al dispositivo para iniciarlo, el controlador de autobús no completa el IRP de PnP en su rutina DispatchPnP . En su lugar, debe marcar el IRP pendiente con IoMarkIrpPending y devolver STATUS_PENDING de su rutina DispatchPnP . Más adelante, el controlador llama a IoCompleteRequest desde otra rutina de controlador, posiblemente una rutina DPC.

En la ilustración siguiente se muestra la segunda parte del ejemplo, donde los controladores más altos de la pila de dispositivos reanudan su procesamiento IRP pospuesto.

diagrama que ilustra el posponer el control de irp plug and play, parte 2.

Las notas siguientes corresponden a los números en círculo de la ilustración anterior:

  1. Cuando el controlador de bus llama a IoCompleteRequest, el administrador de E/S examina las ubicaciones de pila de los controladores más altos y llama a las rutinas de IoCompletion que encuentre. En este ejemplo, el administrador de E/S busca y llama a la rutina IoCompletion para el controlador superior siguiente, el controlador de función.

  2. La rutina IoCompletion del controlador de función establece el evento en modo kernel proporcionado en el parámetro de contexto y devuelve STATUS_MORE_PROCESSING_REQUIRED.

    La rutina IoCompletion debe devolver STATUS_MORE_PROCESSING_REQUIRED para evitar que el administrador de E/S llame a las rutinas de IoCompletion establecidas por controladores superiores en este momento. La rutina IoCompletion usa este estado para evitar la finalización, por lo que la rutina DispatchPnP de su controlador puede recuperar el control. El administrador de E/S reanudará la llamada a las rutinas de IoCompletion de los controladores superiores para este IRP cuando la rutina DispatchPnP de este controlador complete el IRP.

  3. El administrador de E/S deja de completar el IRP y devuelve el control a la rutina que llamó a IoCompleteRequest, que en este ejemplo es la rutina DispatchPnP del controlador de bus.

  4. El controlador de bus vuelve de su rutina DispatchPnP con el estado que indica el resultado de su procesamiento IRP: STATUS_SUCCESS o un estado de error.

  5. IoCallDriver devuelve el control a su llamador, que en este ejemplo es la rutina DispatchPnP del controlador de función.

  6. La rutina DispatchPnP del controlador de función reanuda el procesamiento del IRP.

    Si IoCallDriver devuelve STATUS_PENDING, la rutina DispatchPnP ha reanudado la ejecución antes de que se haya llamado a su rutina ioCompletion . La rutina DispatchPnP , por lo tanto, debe esperar a que la rutina ioCompletion indique el evento del kernel. Esto garantiza que la rutina DispatchPnP no continúe procesando el IRP hasta que todos los controladores inferiores lo hayan completado.

    Si Irp-IoStatus.Status> está establecido en un error, un controlador inferior ha producido un error en el IRP y el controlador de función no debe seguir controlando el IRP (excepto para cualquier limpieza necesaria).

  7. Una vez que los controladores inferiores hayan completado correctamente el IRP, el controlador de función procesa el IRP.

    En el caso de irP que controla primero el controlador de bus primario, el controlador de bus establece normalmente un estado correcto en Irp-IoStatus.Status> y, opcionalmente, establece un valor en Irp-IoStatus.Information>. Los controladores de función y filtro dejan los valores de IoStatus tal y como están, a menos que produzcan un error en el IRP.

    La rutina DispatchPnP del controlador de función llama a IoCompleteRequest para completar el IRP. El administrador de E/S reanuda el procesamiento de finalización de E/S. En este ejemplo, no hay controladores de filtro por encima del controlador de función y, por tanto, no hay más rutinas de IoCompletion a las que llamar. Cuando IoCompleteRequest devuelve el control a la rutina DispatchPnP del controlador de función, la rutina DispatchPnP devuelve el estado.

Para algunos IRP, si una función o un controlador de filtro produce un error en el IRP en su camino de copia de seguridad de la pila de dispositivos, el administrador de PnP informa a los controladores inferiores. Por ejemplo, si una función o un controlador de filtro produce un error en un IRP_MN_START_DEVICE, el administrador de PnP envía un IRP_MN_REMOVE_DEVICE a la pila de dispositivos.