Программирование драйверов Windows

       

Работа с нижними слоями драйверного стека


Переходя к модели WDM и многослойной архитектуре, возникает необходимость уяснения практических механизмов сотрудничества драйверов в рамках этой концепции. Что должен делать драйвер, чтобы задействовать нижние драйвера и соответствовать требованиям модели, оправдывая надежды верхних драйверов?

Второе требование удовлетворить легче. Драйвер не знает и не может знать, что за клиент инициировал запрос, какова была его мотивация и полномочия. В этой ситуации драйвер просто получает IRP пакеты и должен добросовестно их обработать, отправляя их нижним слоям драйверов или выполняя всю обработку самостоятельно. Возвращая управление Диспетчеру ввода/вывода, рабочая процедура должна пометить текущий IRP пакет как завершенный (вызовом IoCompleteRequest, таблица 9.10) или как требующий дополнительной обработки (вызовом IoMarkIrpPending, таблица 9.12). Как должна развиваться ситуация в последнем варианте событий, было рассмотрено ранее.

Таблица 9.12. Прототип функции IoMarkIrpPending



VOID IoMarkIrpPending IRQL &#60= DISPATCH_LEVEL
Параметры Помечает пакет IRP как требующий дополнительной обработки
IN PIRP pIrp Указатель на текущий IRP пакет
Возвращаемое значение void

Драйвер обязан возвратить управление и пометить пакет как требующий дополнительной обработки в том случае, если пакет IRP, отправленный нижним слоям драйверов, вернулся со значением TRUE в поле pIRP-&#62PendingReturned (разумеется, если только драйвер сам не является создателем этого пакета).

Ситуация усложняется, если речь заходит о взаимодействии с нижними драйверными слоями. Если драйвер, получая запрос от Диспетчера ввода/вывода, просто отправляет его нижним слоям (устанавливая процедуру CompletionRoutine перехвата пакета "на обратном пути", или не делая этого), то все сводится к тому, чтобы правильно манипулировать вызовами IoSetCompletionRoutine, IoCallDriver, IoGetCurrentIrpStackLocation, IoSkipCurrentIrpStackLocation

и IoCopyCurrentIrpStackLocationToNext.
// Откуда берется указатель pTargetDevice, см. выше в 9 главе pNewIrp = IoAllocateIrp ( pTargetDevice -&#62 StackSize + 1, FALSE );

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

IoSetNextIrpStackLocation( pNewIrp ); pUsefulArea = IoGetCurrentIrpStackLocation ( pNewIrp ); // Теперь можем использовать пространство ячейки стека вывода, // на которую указывает pUsefulArea, по собственному усмотрению.

// Устанавливаем разнообразные необходимые значения в ячейке стека, // соответствующей нижнему драйверу pNextIoStackLocation = IoGetNextIrpStackLocation ( pNewIrp ); pNextIoStackLocation -&#62 MajorFunction = IRP_MJ_XXX; . . . . . . . // Подключаем процедуру завершения IoSetCompletionRoutine( pNewIrp, OurIoCompletionRoutine, NULL, TRUE, TRUE, TRUE );

// Посылаем IRP пакет целевому драйверу: IoCallDriver (pTargetDevice, pNewIrp );

Что можно хранить в такой искусственно созданной ячейке? Например, число попыток отослать данный IRP пакет нижнему драйверу, если он отказывается обработать его немедленно. При сериализации поступившего от клиента запроса на объемную операцию ввода/вывода, можно хранить число переданных байт данных и общий размер операции, используя один и тот же IRP пакет многократно.


Содержание раздела