Поделиться через


Обработка прерываний в режиме Active-Both

Заметка Этот раздел относится только к Kernel-Mode Driver Framework (KMDF) версии 1.13 и более ранних версий.

Многие устройства имеют аппаратные регистры, которые управляют созданием прерываний и маскированием. Как правило, драйверы KMDF и UMDF для таких устройств используют встроенную поддержку прерываний платформы.

Однако простое устройство на аппаратной платформе System on a Chip (SoC) может не иметь аппаратных регистров для прерываний. В результате драйвер для такого устройства может не иметь возможности контролировать момент генерации прерывания или не быть способным маскировать прерывание на уровне аппаратного обеспечения. Если устройство генерирует прерывание сразу после подключения, и драйвер использует поддержку прерываний фреймворка, возможно, прерывание сработает до того, как фреймворк полностью инициализировал объект прерывания. В результате драйвер KMDF должен вызывать подпрограммы WDM непосредственно для подключения и отключения прерываний. Так как драйвер UMDF не может вызывать эти методы, вы не можете написать драйвер UMDF для такого устройства.

В этом разделе описывается, как драйвер KMDF может обрабатывать прерывания для такого устройства.

На аппаратных платформах SoC активные с обоих сторон прерывания обычно используются для очень простых устройств, таких как аппаратные кнопки. Когда пользователь нажимает кнопку, линия сигнала прерывания от устройства переходит от низкого к высокому или от высокого до низкого. Когда пользователь освобождает кнопку, линия прерывания переходит в противоположное направление. Контакт GPIO, настроенный как активно реагирующий на оба перехода прерываний, создает прерывания как при переходе с низкого на высокий, так и с высокого на низкий уровень, что приводит к тому, что система вызывает программу обслуживания прерываний (ISR) драйвера периферийных устройств в обоих этих случаях. Однако драйвер не получает сведения о том, является ли переход из низкого в высокий или из высокого в низкий.

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

Рассмотрим пример, в котором состояние линии по умолчанию устанавливается на низкий уровень при запуске системы. Драйвер инициализирует значение состояния false в функции обратного вызова EvtDevicePrepareHardware . Затем каждый раз, когда вызывается ISR драйвера, и это сигнализирует об изменении состояния, драйвер инвертирует значение состояния в своём ISR.

Если состояние линии высокое при запуске системы, прерывание срабатывает сразу после включения. Так как драйвер вызывает подпрограмму IoConnectInterruptEx напрямую, а не вызывает WdfInterruptCreate, он гарантирует получение возможного немедленного прерывания.

Для этого решения требуется, чтобы контроллер GPIO поддерживал активные прерывания в оборудовании или что драйвер контроллера GPIO эмулирует активные прерывания в программном обеспечении. Сведения об эмуляции прерываний active-both см. в описании элемента EmulateActiveBoth структуры CONTROLLER_ATTRIBUTE_FLAGS.

В следующем примере кода показано, как драйвер KMDF для периферийного устройства может отслеживать полярность прерываний.

typedef struct _INTERRUPT_CONTEXT INTERRUPT_CONTEXT, *PINTERRUPT_CONTEXT;
typedef struct _DEVICE_CONTEXT DEVICE_CONTEXT, *PDEVICE_CONTEXT;


struct _INTERRUPT_CONTEXT
{
               BOOLEAN State;
               PDEVICE_CONTEXT DeviceContext;
};

struct _DEVICE_CONTEXT
{
               PKINTERRUPT Interrupt;
               INTERRUPT_CONTEXT InterruptContext;
               PDEVICE_OBJECT PhysicalDeviceObject;
               KSPIN_LOCK SpinLock;
};

...

BOOLEAN
YourInterruptIsr(
               __in PKINTERRUPT Interrupt,
               __in PVOID ServiceContext
               )
{
               PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ServiceContext;
               PDEVICE_CONTEXT DeviceContext = InterruptContext->DeviceContext;

               //
               // Flip the state.
               //
               InterruptContext->State = !InterruptContext->State;

               IoRequestDpc(DeviceContext->PhysicalDeviceObject, DeviceContext->PhysicalDeviceObject->CurrentIrp, InterruptContext);
}

VOID
YourInterruptDpc(
               __in PKDPC Dpc,
               __in PDEVICE_OBJECT DeviceObject,
               __inout PIRP Irp,
               __in_opt PVOID ContextPointer
               )
{
               PINTERRUPT_CONTEXT InterruptContext = (PINTERRUPT_CONTEXT)ContextPointer;

               ...
}

NTSTATUS
EvtDriverDeviceAdd(
               __in  WDFDRIVER Driver,
               __in  PWDFDEVICE_INIT DeviceInit
               )
{
               WDFDEVICE Device;
               PDEVICE_CONTEXT DeviceContext;

               ...

               DeviceContext->Interrupt = NULL;
               DeviceContext->PhysicalDeviceObject = WdfDeviceWdmGetPhysicalDevice(Device);
               KeInitializeSpinLock(&DeviceContext->SpinLock);

               IoInitializeDpcRequest(DeviceContext->PhysicalDeviceObject, YourInterruptDpc);
}

NTSTATUS
EvtDevicePrepareHardware(
               __in  WDFDEVICE Device,
               __in  WDFCMRESLIST ResourcesRaw,
               __in  WDFCMRESLIST ResourcesTranslated
               )
{
               PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);

               for (ULONG i = 0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++)
               {
                              PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);

                              if (descriptor->Type == CmResourceTypeInterrupt)
                              {
                                             IO_CONNECT_INTERRUPT_PARAMETERS params;
                                             RtlZeroMemory(&params, sizeof(params));

                                             params.Version = CONNECT_FULLY_SPECIFIED;
                                             params.FullySpecified.PhysicalDeviceObject = DeviceContext->PhysicalDeviceObject;
                                             params.FullySpecified.InterruptObject = &DeviceContext->Interrupt;
                                             params.FullySpecified.ServiceRoutine = YourInterruptIsr;
                                             params.FullySpecified.ServiceContext = (PVOID)&DeviceContext->InterruptContext;
                                             params.FullySpecified.SpinLock = &DeviceContext->SpinLock;
                                             params.FullySpecified.Vector = descriptor->u.Interrupt.Vector;
                                             params.FullySpecified.Irql = (KIRQL)descriptor->u.Interrupt.Level;
                                             params.FullySpecified.SynchronizeIrql = (KIRQL)descriptor->u.Interrupt.Level;
                                             params.FullySpecified.InterruptMode = (descriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
                                             params.FullySpecified.ProcessorEnableMask = descriptor->u.Interrupt.Affinity;
                                             params.FullySpecified.ShareVector = descriptor->ShareDisposition;

                                             //
                                             // Default state is low.
                                             //
                                             DeviceContext->InterruptContext.State = 0;
                                             DeviceContext->InterruptContext.DeviceContext = DeviceContext;

                                             return IoConnectInterruptEx(&params);
                              }
               }

               return STATUS_SUCCESS;
}

NTSTATUS
EvtDeviceReleaseHardware(
               __in  WDFDEVICE Device,
               __in  WDFCMRESLIST ResourcesTranslated
)
{
               PDEVICE_CONTEXT DeviceContext = YourGetDeviceContext(Device);

               if (NULL != DeviceContext->Interrupt)
               {
                              IO_DISCONNECT_INTERRUPT_PARAMETERS params;

                              params.Version = CONNECT_FULLY_SPECIFIED;
                              params.ConnectionContext.InterruptObject = DeviceContext->Interrupt;

                              IoDisconnectInterruptEx(&params);
               }

               return STATUS_SUCCESS;
}

В предыдущем примере кода функция обратного вызова EvtDriverDeviceAdd настраивает контекст устройства, а затем вызывает IoInitializeDpcRequest для регистрации подпрограммы DpcForIsr .

Рутина InterruptService драйвера инвертирует значение состояния прерывания, а затем вызывает IoRequestDpc для постановки DPC в очередь.

В функции обратного вызова EvtDevicePrepareHardware драйвер инициализирует значение состояния в FALSE, а затем вызывает IoConnectInterruptEx. В функции обратного вызова EvtDeviceReleaseHardware драйвер вызывает IoDisconnectInterruptEx, чтобы отменить регистрацию своего ISR.