Процедуры StartIo в драйверах Lowest-Level
Вызов диспетчера ввода-вывода к диспетчеру драйвера является первым этапом в удовлетворении запроса на ввод-вывод устройства. Подпрограмма StartIo — это второй этап. Каждый драйвер устройства с подпрограммой StartIo , скорее всего, вызывает IoStartPacket из подпрограмм DispatchRead и DispatchWrite и обычно для подмножества кодов управления вводом-выводом, поддерживаемых в подпрограмме DispatchDeviceControl . Подпрограмма IoStartPacket добавляет IRP в предоставленную системой очередь устройства или, если очередь пуста, немедленно вызывает подпрограмму StartIo драйвера для обработки IRP.
Можно предположить, что при вызове подпрограммы StartIo драйвера целевое устройство не занято. Это связано с тем, что диспетчер ввода-вывода вызывает StartIo в двух случаях. Либо одна из подпрограмм диспетчеризации драйвера только что вызвала IoStartPacket и очередь устройств была пуста, либо подпрограмма DpcForIsr драйвера выполняет другой запрос и только что вызвал IoStartNextPacket , чтобы вывести из очереди следующий IRP.
Перед вызовом подпрограммы StartIo в драйвере устройства самого высокого уровня эта подпрограмма диспетчеризации драйвера должна проверить и при необходимости заблокировать пользовательский буфер, чтобы настроить допустимые сопоставленные адреса буфера в IRP, помещенном в очередь в подпрограмму StartIo . Если драйвер устройства самого высокого уровня настраивает свои объекты устройства для прямого ввода-вывода (или для буферизованного или прямого ввода-вывода), драйвер не может отложить блокировку пользовательского буфера в подпрограмме StartIo . каждая подпрограмма StartIo вызывается в произвольном контексте потока в IRQL = DISPATCH_LEVEL.
Примечание
Любая буферная память, к которую будет обращаться подпрограмма StartIo драйвера, должна быть заблокирована или выделена из постоянной памяти системного пространства и должна быть доступна в произвольном контексте потока.
Как правило, любая подпрограмма StartIo драйвера устройства нижнего уровня отвечает за вызов IoGetCurrentIrpStackLocation с входным IRP, а затем выполнять обработку для конкретного запроса, необходимую для запуска операции ввода-вывода на устройстве. Обработка для конкретного запроса может включать следующее:
Настройка или обновление любых сведений о состоянии текущего запроса, который поддерживает драйвер. Сведения о состоянии могут храниться в расширении устройства целевого объекта устройства или в другом расположении пула, выделенного драйвером.
Например, если драйвер устройства поддерживает логическое значение InterruptExpected для текущей операции передачи, его подпрограмма StartIo может присвоить этой переменной значение TRUE. Если драйвер поддерживает счетчик времени ожидания для текущей операции, его подпрограмма StartIo может настроить это значение или подпрограмма StartIo может поместить в очередь подпрограмму CustomTimerDpc драйвера.
Если подпрограмма StartIo совместно использует доступ к сведениям о состоянии или аппаратным ресурсам с другими подпрограммами драйвера, сведения о состоянии или ресурс должны быть защищены с помощью спин-блокировки. (См . раздел Спин-блокировки.)
Если подпрограмма StartIo использует общий доступ к сведениям о состоянии или ресурсам с подпрограммой Прерывание Службы драйвера, StartIo должен использовать KeSynchronizeExecution для вызова подпрограммы SynchCritSection , которая обращается к сведениям о состоянии или ресурсе. (См . раздел Использование критических разделов.)
Назначение порядкового номера IRP в случае, если драйвер должен регистрировать ошибку ввода-вывода устройства при обработке IRP.
Дополнительные сведения см. в разделе Ведение журнала ошибок .
При необходимости преобразуйте параметры в расположении стека ввода-вывода драйвера в значения для конкретного устройства.
Например, драйверу диска может потребоваться вычислить начальный сектор или смещение байтов к физическому адресу диска для операции передачи, а также будет ли запрошенная длина передачи пересекать границу определенного сектора или превысит пропускную способность физического устройства.
Если драйвер управляет устройством со съемным носителем, проверьте наличие изменений носителя перед программированием устройства для ввода-вывода и уведомите его о том, что носитель изменился.
Дополнительные сведения см. в разделе Поддержка съемных носителей.
Если устройство использует DMA, необходимо ли разделить запрошенную длину (количество байтов для передачи в расположении стека ввода-вывода драйвера IRP) на операции частичной передачи, как описано в разделе Методы ввода-вывода, при условии, что драйвер более высокого уровня не предопределяет большие передачи для драйвера устройства.
Подпрограмма StartIo такого драйвера устройства также может отвечать за вызов KeFlushIoBuffers и, если драйвер использует DMA на основе пакетов, за вызов AllocateAdapterChannel с помощью подпрограммы AdapterControl драйвера.
Дополнительные сведения см. в разделах Объекты адаптера и DMA и Поддержка когерентности кэша.
Если устройство использует pio, сопоставляет базовый виртуальный адрес буфера, описанный в IRP в Irp-MdlAddress>, с адресом системного пространства с помощью MmGetSystemAddressForMdlSafe.
Для запросов на чтение подпрограмма StartIo драйвера устройства может отвечать за вызов KeFlushIoBuffers до начала операций piO. Дополнительные сведения см. в разделе Поддержание когерентности кэша .
Если драйвер, отличный от WDM, использует объект контроллера, вызовите IoAllocateController для регистрации своей подпрограммы ControllerControl .
Если драйвер обрабатывает отменяемые IRP, проверьте, была ли уже отменена входная IRP.
Если входной IRP можно отменить до завершения обработки, подпрограмма StartIo должна вызвать IoSetCancelRoutine с IRP и точкой входа процедуры Отмена драйвера. Подпрограмма StartIo должна получить блокировку отмены спина для вызова IoSetCancelRoutine. Кроме того, драйвер может использовать атрибуты IoSetStartIoAttributes , чтобы задать для атрибута NonCancelable для подпрограммы StartIoзначение TRUE. Это предотвращает попытку системы отменить IRP, переданную в StartIo путем вызова IoStartPacket.
Как правило, драйвер, использующий буферизированные операции ввода-вывода, имеет более простую подпрограмму StartIo , чем тот, который использует прямой ввод-вывод. Драйверы, использующие буферизацию операций ввода-вывода, передают небольшие объемы данных для каждого запроса на передачу, а те, которые используют прямой ввод-вывод (DMA или PIO), передают большие объемы данных в заблокированные буферы, которые могут охватывать физические границы страниц в системной памяти или из них.
Драйверы более высокого уровня, размещенные поверх физических драйверов устройств, обычно настраивают объекты устройств в соответствии с соответствующими драйверами устройств. Однако драйвер самого высокого уровня, особенно драйвер файловой системы, может настроить объекты устройства для прямого или буферизованного ввода-вывода.
Драйверы, которые настраивают объекты устройства для буферизованного ввода-вывода, могут полагаться на диспетчер ввода-вывода для передачи допустимых буферов во всех IRP, отправляемых драйверу. Драйверы более низкого уровня, которые настраивают объекты устройств для прямого ввода-вывода, могут полагаться на драйвер самого высокого уровня в своей цепочке для передачи допустимых буферов во всех IRP, отправляемых через промежуточные драйверы в базовый драйвер устройства нижнего уровня.
Использование буферизованного ввода-вывода в процедурах StartIo
Если подпрограмма DispatchRead, DispatchWrite или DispatchDeviceControl драйвера определяет, что запрос является допустимым и вызывает IoStartPacket, диспетчер операций ввода-вывода вызывает подпрограмму StartIo драйвера для немедленной обработки IRP, если очередь устройств пуста. Если очередь не пуста, IoStartPacket помещает IRP в очередь. В конечном итоге вызов IoStartNextPacket из процедуры DpcForIsr или CustomDpc драйвера приводит к тому, что диспетчер ввода-вывода вывел IRP из очереди и вызвал подпрограмму StartIo драйвера.
Подпрограмма StartIo вызывает IoGetCurrentIrpStackLocation и определяет, какая операция должна быть выполнена для удовлетворения запроса. Он предварительно обрабатывает IRP любым способом, необходимым перед программированием физического устройства для выполнения запроса ввода-вывода.
Если доступ к физическому устройству (или расширению устройства) должен быть синхронизирован с подпрограммой InterruptService , подпрограмма StartIo должна вызвать подпрограмму SynchCritSection для выполнения необходимого программирования устройства. Дополнительные сведения см. в разделе Использование критических разделов.
Драйвер физического устройства, использующий буферизованные операции ввода-вывода, передает данные в буфер системного пространства, выделенный диспетчером ввода-вывода, или из нее, который драйвер находит в каждом IRP в Irp-AssociatedIrp.SystemBuffer>.
Использование прямого ввода-вывода в процедурах StartIo
Если подпрограмма DispatchRead, DispatchWrite или DispatchDeviceControl драйвера определяет, что запрос является допустимым и вызывает IoStartPacket, диспетчер операций ввода-вывода вызывает подпрограмму StartIo драйвера для немедленной обработки IRP, если очередь устройств пуста. Если очередь не пуста, IoStartPacket помещает IRP в очередь. В конечном итоге вызов IoStartNextPacket из процедуры DpcForIsr или CustomDpc драйвера приводит к тому, что диспетчер ввода-вывода вывел IRP из очереди и вызвал подпрограмму StartIo драйвера.
Подпрограмма StartIo вызывает IoGetCurrentIrpStackLocation и определяет, какая операция должна быть выполнена для удовлетворения запроса. Он предварительно обрабатывает IRP любым необходимым способом, например разделение большого запроса на передачу DMA на диапазоны частичной передачи и сохранение состояния длины входящего запроса на передачу, который необходимо разделить. Затем он программировать физическое устройство для выполнения запроса ввода-вывода.
Если доступ к физическому устройству (или расширению устройства) должен быть синхронизирован с ISR драйвера, подпрограмма StartIo должна использовать предоставленную драйвером подпрограмму SynchCritSection для выполнения необходимого программирования. Дополнительные сведения см. в разделе Использование критических разделов.
Любой драйвер, использующий прямой ввод-вывод, считывает или записывает данные из заблокированного буфера, описанного в списке дескрипторов памяти (MDL), который драйвер находит в IRP в Irp-MdlAddress>. Такой драйвер обычно использует буферизированные операции ввода-вывода для запросов на управление устройством. Дополнительные сведения см. в разделе Обработка запросов управления вводом-выводом в подпрограммах StartIo.
Тип MDL — это непрозрачный тип, к которому драйверы не обращаются напрямую. Вместо этого драйверы, использующие PIO, перенаправляют буферы пользовательского пространства, вызывая MmGetSystemAddressForMdlSafe с Irp-MdlAddress> в качестве параметра. Драйверы, использующие DMA, также передают Irp-MdlAddress> для поддержки подпрограмм во время операций передачи, чтобы буферные адреса переназначились в логические диапазоны для своих устройств.
Если тесно взаимосвязанный драйвер более высокого уровня не разделяет большие запросы на передачу DMA для базового драйвера устройства, подпрограмма StartIo драйвера устройства нижнего уровня должна разделить каждый запрос на передачу, размер которых больше, чем его устройство может управлять за одну операцию передачи. Драйверы, использующие системный DMA, должны разделять запросы на передачу, которые слишком велики для системного контроллера DMA или для обработки их устройств в рамках одной операции передачи.
Если устройство является подчиненным устройством DMA, его драйвер должен синхронизировать передачи через системный контроллер DMA с выделенным драйвером объектом адаптера, представляющим канал DMA, и подпрограммой AdapterControl , предоставляемой драйвером. Драйвер устройства DMA master шины также должен использовать объект адаптера, выделенный драйвером, для синхронизации передачи и должен предоставить подпрограмму AdapterControl, если она использует поддержку DMA на основе пакетов в системе, или подпрограмму AdapterListControl, если она использует поддержку системы точечной и сборной.
В зависимости от структуры драйвера он может синхронизировать операции передачи и управления устройством на физическом устройстве с объектом контроллера и предоставить подпрограмму ControllerControl .
Дополнительные сведения см. в разделе Объекты адаптера и объекты DMA и контроллера .
Обработка запросов управления вводом-выводом в процедурах StartIo
Как правило, из подпрограммы DispatchDeviceControl или DispatchInternalDeviceControl драйвера передается только подмножество запросов на управление вводом-выводом устройства для дальнейшей обработки подпрограммой StartIo драйвера. Подпрограмма StartIo драйвера должна обрабатывать только допустимые запросы управления устройствами, которые требуют изменения состояния устройства или возвращают переменную информацию о текущем состоянии устройства.
Каждый новый драйвер должен поддерживать тот же набор кодов управления общедоступным вводом-выводом, что и все остальные драйверы для устройства того же типа. Система определяет общедоступные коды управления вводом-выводом для конкретных типов устройств для IRP_MJ_DEVICE_CONTROL запросов в качестве буферизированных запросов.
Следовательно, физические драйверы устройств передают данные в буфер системного пространства или из нее, который каждый драйвер находит в IRP по адресу Irp-AssociatedIrp.SystemBuffer> для запросов на управление устройством. Даже драйверы, которые настраивают объекты устройства для прямого ввода-вывода, используют буферизируемые операции ввода-вывода для удовлетворения запросов на управление устройствами с помощью кодов управления общедоступным вводом-выводом.
Определение каждого кода элемента управления вводом-выводом определяет, помещается ли в буфер данные, передаваемые для этого запроса. Любые частные коды управления вводом-выводом для конкретных IRP_MJ_INTERNAL_DEVICE_CONTROL запросов между парными драйверами могут определять код с буферизацией метода, прямым методом или ни с методом. Как правило, любой код управления вводом-выводом, определяемый в частном порядке, не должен определяться с помощью метода , если драйвер более высокого уровня должен выделять буфер для этого запроса.
Программирование устройства для операций ввода-вывода
Как правило, подпрограмма StartIo в драйвере устройства самого низкого уровня должна синхронизировать доступ к любой памяти или устройству, регистрируемой им совместно с ISR драйвера, с помощью KeSynchronizeExecution для вызова предоставленной драйвером процедуры SynchCritSection . Подпрограмма StartIo драйвера использует подпрограмму SynchCritSection для фактического программирования физического устройства для ввода-вывода в DIRQL. Дополнительные сведения см. в разделе Использование критических разделов.
Перед вызовом KeSynchronizeExecution подпрограмма StartIo должна выполнить любую предварительную обработку, необходимую для запроса. Предварительная обработка может включать вычисление начального диапазона частичной передачи и сохранение сведений о состоянии исходного запроса для других процедур драйвера.
Если драйвер устройства использует DMA, его подпрограмма StartIo обычно вызывает AllocateAdapterChannel с предоставленной драйвером подпрограммой AdapterControl . В таких случаях подпрограмма StartIo откладывает ответственность за программирование физического устройства на подпрограмму AdapterControl . Он, в свою очередь, может вызвать KeSynchronizeExecution , чтобы предоставленная драйвером подпрограмма SynchCritSection программирует устройство для передачи DMA.