
- •Лекция №9. Многоуровневая модель драйверов. Уровневые драйвера и драйвера-фильтры. [9.1] Понимание многоуровневой модели драйверов
- •[9.2] Реализация уровневых драйверов
- •[9.2.1] Объединение драйверов в стек и освобождение драйверов стека
- •[9.2.2.] Обработка запросов irp стеком драйверов
- •[9.2.2.1] Самостоятельная обработка irp драйвером
- •[9.2.2.2] Передача первоначального пакета irp драйверу нижележащего уровня
- •[9.2.2.3] Создание новых пакетов irp для передачи драйверу нижележащего уровня
- •[9.2.2.4] Создание новых ассоциированных пакетов irp для передачи драйверу нижележащего уровня
- •[9.2.2.5] Получение драйвером вышележащего уровня уведомления о завершении обработки irp драйвером нижележащего уровня (Completion Notification)
- •[9.3] Реализация драйверов-фильтров
- •[9.3.1] Подключение фильтра к устройству
- •[9.3.2] Выгрузка драйвера-фильтра
[9.2.2.4] Создание новых ассоциированных пакетов irp для передачи драйверу нижележащего уровня
Немного другой подход к созданию нового пакета IRP для передачи драйверу нижележащего уровня состоит в создании ассоциированного пакета IRP с помощью функции IoMakeAssociatedIrp().
PIRP IoMakeAssociatedIrp (IN PIRP MasterIrp, IN CCHARStackSize);
StackSize: Число Стеков размешения Ввода - вывода, требуемых в IRP
MasterIrp: Указатель на IRP, с которым должен быть связан создаваемый пакет IRP.
IoMakeAssociatedlrp() позволяет создавать IRP, которые "связаны” с некоторым "главным" IRP. Драйвер, который вызывает IoMakeAssociatedlrp(), должен вручную инициализировать поле Irp.IrpCount главного IRP счетчиком ассоциированных с ним IRP, которые созданы до вызова IoMakeAssociatedIrp. Ассоциированные IRP являются особым видом пакетов IRP, при завершении которых значение поля IrpCount в главном IRP уменьшается. Когда значение поля IrpCount в главном IRP становится равным нулю, Диспетчер в/в автоматически завершает главный IRP.
Ассоциированные IRP могут создаваться только драйвером высшего уровня.
Драйвер высшего уровня, использующий ассоциированные IRP, может вернуть управление диспетчеру в/в после вызова IoCallDriver() для каждого из ассоциированных IRP и вызова IoMarkIrpPending() для главного IRP. Если драйвер такой драйвер устанавливает процедуру завершения для созданного им ассоциированного IRP (с помощью вызова IoSetCompletionRoutine(), см. п. [9.2.2.5]), Диспетчер в/в не завершит автоматически главный IRP. В этом случае процедура завершения должна напрямую завершить главный IRP посредством вызова IoCompleteRequest().
[9.2.2.5] Получение драйвером вышележащего уровня уведомления о завершении обработки irp драйвером нижележащего уровня (Completion Notification)
В некоторых случаях драйвер вышележащего уровня может захотеть получить уведомление о завершении обработки переданного им запроса в/в драйвером нижележащего уровня. Это может быть сделано с помощью вызова функции IoSetCompletionRoutine() перед передачей пакета IRP драйверу нижележащего уровня любым указанным выше способом.
VOID IoSetCompletionRoutine(IN PIRP Irp,
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
INPVOID Context,
IN BOOLEAN InvokeOnSuccess,
IN BOOLEAN InvokeOnError,
IN BOOLEAN InvokeOnCancel),
Irp: Указатель на IRP, при завершении которого вызывается точка входа CompletionRoutine
CompletionRoutine: Указатель на точку входа драйвера, вызываемую при завершении IRP
Context: определенное драйвером значение, которое нужно передать как третий параметр для точки входа CompletionRoutine
InvokeOnSuccess, InvokeOnError, IwokeOnCancel: Параметры, которые, указывают должна ли точка входа CompletionRoutine быть вызвана при завершении IRP с указанным состоянием
В качестве второго параметра в вызове IoSetCompletionRoutine() передается адрес точки входа драйвера, которая должна быть вызвана при завершении указанного в первом параметре пакета IRP. Прототип функции – точки входа драйвера:
NTSTATUS CompletionRoutine(IN PDEVICE_OBJECTDeviceObject,
IN PIRP Irp,
IN PVOID Context),
DeviceObject: Указатель на объект-устройство, которому предназначался закончившийся пакет IRP
IRP: Указатель на закончившийся пакет IRP
Context: Определенное драйвером значение контекста, переданное, когда была вызвана функция IoSetCompletionRoutine().
При вызове IoSetCompletionRoutine() указатель на функцию завершения сохраняется в IRP в следующем после текущего стеке размещения в/в (т.е. в стеке размещения в/в нижележащего драйвера). Из этого следуют два важных вывода:
Если драйвер установит функцию завершения для некоторого IRP и завершит этот IRP, функция завершения не будет вызвана.
Драйвер низшего уровня (либо монолитный драйвер) не может устанавливать функции завершения, так как, во-первых, это бессмысленно (см. предыдущий вывод), а во-вторых, это ошибка, так как следующего за текущим стека размещения в/в для таких драйверов не существует.
Функция завершения вызывается при том же уровне IRQL, при котором нижележащим драйвером была вызвана функция завершения обработки IRP – IoCompleteRequest(). Это может быть любой уровень IRQL меньший либо равный IRQL_DISPATCH_LEVEL.
При завершении IRP, например вызовом IoCompleteRequest(), последовательно вызываются функции завершения для всех драйверов стека начиная с драйвера самого нижнего уровня. Если функция завершения возвращает STATUS_MORE_PROCESSING_REQUIRED, завершение запроса отменяется с сохранением текущей позиции в стеке размещения в/в Позднее запрос должен быть снова завершен (IoCompleteRequest) на уровне того драйвера, чья функция завершения вернула такой статус.
Если драйвер вышележащего уровня создавал новые пакеты IRP для передачи драйверу нижележащего уровня, он обязан использовать функцию завершения для этих IRP, причем параметры InvokeOnSuccess, InvokeOnError, IwokeOnCancel должны быть установлены в TRUE. В этих случаях функция завершения должна освободить созданные драйвером IRP с помощью функции IoFreeIrp() и завершить первоначальный пакет IRP.
Требования к реализации функции завершения достаточно сложные и для их изложения требуется отдельная лекция. Этитребованияможнонайтив(Developing Windows NT Device Drivers, pages 481-485).