[Previous] [Next]

Простейший драйвер для работы с прерываниями

Драйвер, работающий с прерываниями, прежде всего, должен зарегистрировать и подключить к источнику объект прерывания, имея собственную процедуру обработки поступающих прерываний. В нашем случае, при работе с заглушкой CheckIt, драйвер сам выдает в порт комбинацию данных, которая вследствие наличия обратной связи CR.2 -> SR.6 в заглушке CheckIt (она к этому моменту должна быть включена в системный LPT порт) приводит к генерации прерывания и последующему вызову Isr процедуры драйвера.

После загрузки и старта драйвер пребывает в устойчивом состоянии и ожидает запросов от клиентов. В данном случае клиентом выступает консольное приложение, которое Win32 вызовом CreateFile открывает дескриптор для доступа к драйверу, после чего обращается к нему с запросом на запись данных. В тестирующем приложении это осуществляется при помощи Win32 вызова WriteFile. Соответствующий IRP пакет поступает в обработчик DispatchWrite драйвера, который после проверки корректности запроса и перенесения данных во входной внутренний буфер драйвера, записывает в параллельный порт половину первого байта поступивших данных (это делает функция DoNextTransfer), совмещая это с генерацией прерывания (функция ForceInterrupt). Такая запись приводит к переносу первого байта данных и к первому вызову функции обработки прерывания Isr. Для безопасного доступа к данным запуск DoNextTransfer оформлен в драйвере при поддержке системного вызова KeSynchronizeExecution, см. описание прототипа в таблице 10.14.

Для соблюдения целостности передачи данных (а передача данных считается здесь завершенной, когда весь входной буфер данных "перекочует" в другой, выходной, внутренний буфер драйвера, предварительно пройдя через параллельный порт) драйвер отвергает все запросы от клиента, как на чтение (из внутреннего выходного буфера), так и на запись данных. Если по окончании обработки запроса на запись от клиента поступил новый запрос на запись, то оба внутренних буфера драйвера (входной и выходной) заполняются новыми данными заново, без какого-либо сохранения старых данных, хотя бы частично — данный драйвер имеет простейшую организацию и не "церемонится" с не полностью перенесенными данными.

При обработке прерывания Isr функция планирует вызов DpcForIsr процедуры, которая получает управление, читает данные из регистра состояния, помещает их в буфер для хранения и запускает новый перенос (с генерацией прерывания).

Таким образом, начиная с момента первой генерации прерывания до окончания всех данных, поступивших от клиента в одном запросе на запись, перенос половины первого байта продолжается переносом половины второго байта и т.п. По окончании передачи половины последнего байта и прохождении последнего прерывания, процедура DpcForIsr, получив управление, выполняет последнее чтение из порта, но уже не вызывает DoNextTransfer. Перенос данных закончен фактически. Формально же обработка запроса от вызова WriteFile могла бы закончиться раньше — после запуска DoNextTransfer (разумеется, с подачи KeSynchronizeExecution) рабочая функция драйвера DispatchWrite (обработчик запросов от WriteFile) сразу же пытается завершить обработку IRP пакета, поскольку ее основная задача — заполнить внутренний буфер и стартовать первый перенос. Однако в том случае, когда заглушка на месте, в работу поочередно вступают высокоприоритетные фрагменты кода (Isr, DpcForIsr, функции, вызываемые с подачи KeSynchronizeExecution), что более вероятно — перенос данных завершится еще до выхода из DispatchWrite. Лишь при отсутствии заглушки CheckIt прерывания не влияют на работу драйвера. Запуская тестовое приложение в отсутствие заглушки, можем наблюдать, что процедура DispatchWrite завершается, а при входе в DispatchRead драйвер видит, что предыдущий перенос не завершен (нет заглушки — нет способа генерировать прерывания и передавать данные), о чем драйвер и сообщает клиенту кодом ошибки 170, который программой ErrLook расшифровывается как "Требуемый ресурс занят". Такое же сообщение можно увидеть и при повторных запусках тестового приложения (если заглушка CheckIt отсутствует).