Откладывание обработки PnP IRP до завершения более низких драйверов

Некоторые PnP и irP питания должны быть обработаны сначала родительским драйвером шины для устройства, а затем каждым следующим драйвером в стеке устройств. Например, водитель родительского автобуса должен быть первым водителем, который выполняет свои операции запуска для устройства (IRP_MN_START_DEVICE), за которым следует каждый водитель следующего уровня. Для такого IRP драйверы функций и фильтров должны задать подпрограмму завершения ввода-вывода, передать IRP следующему ниже драйверу и отложить все действия по обработке IRP до тех пор, пока нижние драйверы не завершат работу с IRP.

Подпрограмму IoCompletion можно вызвать на DISPATCH_LEVEL IRQL, но драйвер функции или фильтра может потребоваться обработать IRP в IRQL = PASSIVE_LEVEL. Чтобы вернуться к PASSIVE_LEVEL из процедуры IoCompletion , драйвер может использовать событие ядра. Драйвер регистрирует подпрограмму IoCompletion , которая задает событие в режиме ядра, а затем драйвер ожидает события в своей подпрограмме DispatchPnP . Когда событие задано, более низкие драйверы завершили IRP, и драйвер может обработать IRP.

Обратите внимание, что драйвер не должен использовать этот метод, чтобы ждать завершения IRP питания более низкими драйверами (IRP_MJ_POWER). Ожидание события в подпрограмме DispatchPower , заданного в процедуре IoCompletion , может привести к взаимоблокировке. Дополнительные сведения см. в разделе Передача power IRP .

На следующих двух рисунках показан пример того, как водитель ожидает более низких драйверов для завершения PnP IRP. В примере показано, что должны делать водители функций и автобусов, а также как они взаимодействуют с диспетчером PnP и диспетчером ввода-вывода.

схема, иллюстрирующая перенос обработки irp plug and play, часть 1.

Следующие примечания соответствуют обведенным числам на предыдущем рисунке:

  1. Диспетчер PnP вызывает диспетчер ввода-вывода для отправки IRP в верхний драйвер в стеке устройств.

  2. Диспетчер ввода-вывода вызывает подпрограмму DispatchPnP верхнего драйвера. В этом примере в стеке устройств есть только два драйвера (драйвер функции и драйвер родительской шины), а драйвер функции является главным драйвером.

  3. Драйвер функции объявляет и инициализирует событие режима ядра, настраивает расположение стека для следующего ниже драйвера и задает подпрограмму IoCompletion для этого IRP.

    Драйвер функции может использовать IoCopyCurrentIrpStackLocationToNext для настройки расположения стека.

    При вызове IoSetCompletionRoutine драйвер функции присваивает Значениям TRUE, InvokeOnSuccess, InvokeOnError и InvokeOnCancel и передает событие режима ядра как часть параметра контекста.

  4. Драйвер функции передает IRP вниз по стеку устройств с помощью IoCallDriver перед выполнением каких-либо операций по обработке IRP.

  5. Диспетчер ввода-вывода отправляет IRP следующему ниже драйверу в стеке устройств, вызывая подпрограмму DispatchPnP этого драйвера.

  6. Следующий ниже драйвер в этом примере является самым низким драйвером в стеке устройств, родительским драйвером шины. Водитель автобуса выполняет свои операции по запуску устройства. Драйвер автобуса задает Irp-IoStatus.Status>, задает Irp-IoStatus.Information>, если это относится к этому IRP, и завершает IRP, вызвав IoCompleteRequest.

    Если водитель автобуса вызывает другие процедуры водителя или отправляет на устройство ввод-вывод, чтобы запустить его, водитель автобуса не завершает PnP IRP в своей подпрограмме DispatchPnP . Вместо этого он должен пометить ожидающий IRP с помощью IoMarkIrpPending и вернуть STATUS_PENDING из своей подпрограммы DispatchPnP . Позже драйвер вызывает IoCompleteRequest из другой процедуры драйвера, возможно, подпрограммы DPC.

На следующем рисунке показана вторая часть примера, где более высокие драйверы в стеке устройств возобновляют отложенную обработку IRP.

схема, иллюстрирующая перенос обработки irp plug and play, часть 2.

Следующие примечания соответствуют обведенным числам на предыдущем рисунке:

  1. Когда водитель автобуса вызывает IoCompleteRequest, диспетчер ввода-вывода проверяет расположения стека более высоких драйверов и вызывает все процедуры IoCompletion , которые он находит. В этом примере диспетчер ввода-вывода находит и вызывает подпрограмму IoCompletion для следующего драйвера, драйвера функции.

  2. Подпрограмма IoCompletion драйвера функции задает событие режима ядра, указанное в параметре контекста, и возвращает STATUS_MORE_PROCESSING_REQUIRED.

    Подпрограмма IoCompletion должна возвращать STATUS_MORE_PROCESSING_REQUIRED, чтобы не позволить диспетчеру ввода-вывода вызывать подпрограммы IoCompletion , заданные более высокими драйверами в настоящее время. Подпрограмма IoCompletion использует это состояние для того, чтобы предотвратить завершение, поэтому подпрограмма DispatchPnP драйвера может восстановить контроль. Диспетчер операций ввода-вывода возобновит вызов процедур IoCompletion более высоких драйверов для этого IRP, когда подпрограмма DispatchPnP этого драйвера завершит IRP.

  3. Диспетчер ввода-вывода прекращает выполнение IRP и возвращает управление процедуре, которая называется IoCompleteRequest, которая в этом примере является подпрограммой DispatchPnP водителя автобуса.

  4. Драйвер автобуса возвращается из своей подпрограммы DispatchPnP со статусом, указывающим результат обработки IRP: STATUS_SUCCESS или состояние ошибки.

  5. IoCallDriver возвращает управление вызывающей объекту, который в этом примере является подпрограммой DispatchPnP драйвера функции.

  6. Подпрограмма DispatchPnP драйвера функции возобновляет обработку IRP.

    Если IoCallDriver возвращает STATUS_PENDING, подпрограмма DispatchPnP возобновила выполнение до вызова процедуры IoCompletion . Поэтому подпрограмма DispatchPnP должна ожидать передачи сигнала о событии ядра с помощью процедуры IoCompletion . Это гарантирует, что подпрограмма DispatchPnP не будет продолжать обработку IRP до тех пор, пока все более низкие драйверы не будут завершены.

    Если для параметра Irp-IoStatus.Status> задано значение ошибки, драйвер более низкого уровня не выполнил IRP и драйвер функции не должен продолжать обработку IRP (за исключением любой необходимой очистки).

  7. После успешного завершения IRP более низкими драйверами драйвер функции обрабатывает IRP.

    Для irP, обрабатываемых первым драйвером родительского автобуса, драйвер автобуса обычно устанавливает состояние успешно в Irp-IoStatus.Status> и при необходимости задает значение в Irp-IoStatus.Information>. Драйверы функций и фильтров оставляют значения в IoStatus без сбоя IRP.

    Подпрограмма DispatchPnP драйвера функции вызывает IoCompleteRequest для завершения IRP. Диспетчер ввода-вывода возобновляет обработку завершения ввода-вывода. В этом примере нет драйверов фильтров над драйвером функции, и, следовательно, больше нет процедур IoCompletion для вызова. Когда IoCompleteRequest возвращает управление подпрограмме DispatchPnP драйвера функции, подпрограмма DispatchPnP возвращает состояние.

Для некоторых irP, если драйвер функции или фильтра не выполняет IRP на пути резервного копирования стека устройств, диспетчер PnP информирует более низкие драйверы. Например, если функция или драйвер фильтра не IRP_MN_START_DEVICE, диспетчер PnP отправляет IRP_MN_REMOVE_DEVICE в стек устройств.