다음을 통해 공유


Active-Both 인터럽트 처리

참고 이 항목은 KMDF(Kernel-Mode Driver Framework) 버전 1.13 이하에만 적용됩니다.

많은 디바이스에는 인터럽트 생성 및 마스킹을 제어하는 하드웨어 레지스터가 있습니다. 일반적으로 이러한 디바이스에 대한 KMDF 및 UMDF 드라이버는 프레임워크의 기본 제공 인터럽트 지원을 사용합니다.

그러나 SoC(System on a Chip) 하드웨어 플랫폼의 간단한 디바이스에는 인터럽트용 하드웨어 레지스터가 없을 수 있습니다. 따라서 이러한 디바이스의 드라이버는 인터럽트 생성 시기를 제어하지 못하거나 하드웨어에서 인터럽트 마스크를 사용할 수 없습니다. 연결 시 디바이스가 즉시 중단되고 드라이버가 프레임워크의 인터럽트 지원을 사용하는 경우 프레임워크가 프레임워크 인터럽트 개체를 완전히 초기화하기 전에 인터럽트에서 실행될 수 있습니다. 따라서 KMDF 드라이버는 WDM 루틴을 직접 호출하여 인터럽트 연결 및 연결을 끊어야 합니다. UMDF 드라이버는 이러한 메서드를 호출할 수 없으므로 이러한 디바이스에 대한 UMDF 드라이버를 작성할 수 없습니다.

이 항목에서는 KMDF 드라이버가 이러한 디바이스에 대한 인터럽트 처리 방법을 설명합니다.

SoC 하드웨어 플랫폼에서 활성-둘 다 인터럽트는 일반적으로 하드웨어 푸시 단추와 같은 매우 간단한 디바이스에 사용됩니다. 사용자가 푸시 단추를 누르면 디바이스의 인터럽트 신호 선이 낮음에서 높음 또는 높음에서 낮음으로 전환됩니다. 사용자가 푸시 단추를 놓으면 인터럽트 줄이 반대 방향으로 전환됩니다. 활성-둘 다 인터럽트 입력으로 구성된 GPIO 핀은 낮은 전환과 높음에서 낮은 전환 모두에서 인터럽트를 생성하므로 두 경우 모두 시스템에서 주변 장치 드라이버의 ISR(인터럽트 서비스 루틴)을 호출합니다. 그러나 드라이버는 전환이 낮음에서 높음 또는 높음에서 낮은지 여부를 표시하지 않습니다.

낮은 전환과 높음에서 낮은 전환 사이를 구분하려면 드라이버가 각 인터럽트 상태를 추적해야 합니다. 이렇게 하려면 드라이버가 줄 상태가 높은 경우 인터럽트 줄 상태가 낮을 때 FALSE 이고 TRUE 인 부울 인터럽트 상태 값을 유지할 수 있습니다.

시스템이 시작될 때 줄 상태가 기본값이 낮음으로 설정되는 예제를 생각해 보세요. 드라이버는 EvtDevicePrepareHardware 콜백 함수에서 상태 값을 FALSE로 초기화합니다. 그런 다음 드라이버의 ISR이 호출되어 상태 변경 신호를 표시할 때마다 드라이버는 ISR의 상태 값을 반전합니다.

시스템이 시작될 때 선 상태가 높으면 인터럽트는 사용하도록 설정된 직후에 실행됩니다. 드라이버는 WdfInterruptCreate를 호출하는 대신 IoConnectInterruptEx 루틴을 직접 호출하므로 가능한 즉각적인 인터럽트를 받을 수 있습니다.

이 솔루션을 사용하려면 GPIO 컨트롤러가 하드웨어에서 활성-둘 다 인터럽트 또는 GPIO 컨트롤러용 드라이버가 소프트웨어의 활성-둘 다 인터럽트 에뮬레이트해야 합니다. active-both 인터럽트 에뮬레이션에 대한 자세한 내용은 CONTROLLER_ATTRIBUTE_FLAGS 구조체의 EmulateActiveBoth 멤버에 대한 설명을 참조하세요.

다음 코드 예제에서는 주변 디바이스에 대한 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의 등록을 취소합니다.