Отключение от источника прерываний
В случае, если драйвер должен обладать возможностью быть выгружаемым, то имеется насущная необходимость его отключения от источника прерываний. При этом он удаляется из внутреннего списка кода ядра, где он обозначен как обработчик прерываний. Разумеется, это необходимо выполнить до того, как драйвер будет удален из оперативной памяти. В противном случае код ядра операционной системы в ответ на прерывание, сгенерированной устройством, осуществит вызов процедуры по адресу нестраничном пуле, где раньше "обитала" процедура ISR. Это неминуемо приведет к краху системы.
Отключение от источника прерываний является двухступенчатой процедурой. Во-первых, следует использовать KeSynchronizeExecution и процедуру SynchCritSection для того, чтобы обеспечить такое состояние устройства, когда он не будет производить генерацию сигналов на прерывание. Во-вторых, следует произвести удаление ISR процедуры из системного списка обработчиков прерываний путем осуществления вызова IoDisconnectInterrupt (с передачей этому вызову в качестве аргумента указателя на полученный ранее объект прерывания для данного устройства).
Таблица 8.15. Прототип вызова IoDisconnectInterrupt
VOID IoDisconnectInterrupt | IRQL == PASSIVE_LEVEL |
Параметры | Регистрирует DpcForIsr процедуру для данного объекта устройства |
IN PKINTERRUPT pInterruptObject | Указатель на объект прерывания, полученный ранее в результате вызова IoConnectInterrupt |
Возвращаемое значение | void |
Нерассмотренным остается один весьма деликатный момент. При регистрации ISR процедуры для обслуживания конкретного прерывания используется вызов IoConnectInterrupt, подробно описанный в таблице 8.10. Наиболее важным и трудным в обращении является параметр Vector, представляющий транслированное прерывание, к которому и производится подключение регистрируемой ISR процедуры. Данная процедура имеет свою специфику для каждого типа не-WDM драйверов (в зависимости от того, к шине какого типа подключено устройство, обслуживаемое драйвером, например, ISA или PCI). Однако для WDM драйверов эта процедура универсальна. В данном случае, когда PnP Менеджер делает запрос с кодом IRP_MJ_PNP и субкодом IRP_MN_START_DEVICE, то в каждом таком запросе он передает и список присвоенных драйверу ресурсов. |
Драйвер WDM, например, из пакета разработки устройств PCI шины фирмы PLX Technology, выходит из положения следующим образом.
Прежде всего, данный драйвер, получив IRP пакет с указанными признаками (IRP_MJ_PNP, субкодом IRP_MN_START_DEVICE), дает возможность сначала обработать его нижележащим драйверам, перехватывая его на обратном пути. После этого он производит анализ полученных ресурсов, в частности, подключает собственную функцию обработки прерываний PlxOnInterrupt к прерыванию, как оно было предопределено PnP Менеджером, передавшим выделенные ресурсы в ячейке стека пакета IRP.
int i, count; BOOLEAN interruptPresented = FALSE; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation( pIrp ); PCM_PARTIAL_RESOURCE_LIST pRawResource= &stack->Parameters.StartDevice.AllocatedResources-> List[0].PartialResourceList; count = pRawResource->Count; for (i = 0; i < count; i++, pRawResource++) { //Просмотр всех выделенных драйверу ресурсов switch (pRawResource ->Type) { case CmResourceTypeInterrupt: // Прерывание interruptPresented = TRUE; IrqL = (KIRQL) Resource->u.Interrupt.Level; vector = Resource->u.Interrupt.Vector; affinity = Resource->u.Interrupt.Affinity; if (ResourceRaw->Flags == CM_RESOURCE_INTERRUPT_LATCHED) mode = Latched; else mode = LevelSensitive; break; . . . } } if (interruptPresented) { // Здесь следует запретить прерывания от ведомого устройства . . . // Создание объекта прерывания и подключение к нему status = IoConnectInterrupt( &pDevExtension->pInterruptObject, PlxOnInterrupt, pDevExtension, NULL, vector, IrqL, IrqL, mode, TRUE, affinity, FALSE ); if ( !NT_SUCCESS(status) ) { // обработка ошибки } else { // разрешить прерывания от устройства } }
Здесь pDevExtension указывает на структуру расширения объекта устройства (соответственно, эта структура должна предусмотреть в своем составе место для хранения указателя на создаваемый объект прерывания, здесь pInterruptObject), a PlxOnInterrupt передает адрес предоставляемой данным драйвером ISR процедуры, созданной в соответствии с прототипом, описанным в таблице 8.11.