Active-Both 割り込みの処理

注意 このトピックは、カーネル モード ドライバー フレームワーク (KMDF) バージョン 1.13 およびそれ以前にのみ適用されます。

多くのデバイスには、割り込みの生成とマスキングを制御するハードウェア レジスタがあります。 通常、このようなデバイスの KMDF ドライバーと UMDF ドライバーは、フレームワークの組み込みの割り込みサポートを使用します。

ただし、System on a Chip (SoC) ハードウェア プラットフォーム上の単純なデバイスには、割り込み用のハードウェア レジスタがない可能性があります。 その結果、このようなデバイスのドライバーは、割り込みがいつ生成されるかを制御できないか、ハードウェアで割り込みをマスクできない可能性があります。 デバイスが接続時にすぐに割り込みがかかり、ドライバーがフレームワークの割り込みサポートを使用している場合は、フレームワークがフレームワーク割り込みオブジェクトを完全に初期化する前に割り込みが発生する可能性があります。 その結果、KMDF ドライバーは、割り込みの接続と切断のために WDM ルーチンを直接呼び出す必要があります。 UMDF ドライバーはこれらのメソッドを呼び出すことができないため、このようなデバイスの UMDF ドライバーを書くことはできない。

このトピックでは、KMDF ドライバーがこのようなデバイスの割り込みを処理する方法について説明します。

SoC ハードウェア プラットフォームでは、アクティブ両方の割り込みは通常、ハードウェア プッシュ ボタンなどの非常に単純なデバイスに使用されます。 ユーザーが押しボタンを押すと、デバイスからの割り込み信号ラインが低から高へ、または高から低へ遷移します。 ユーザーが押しボタンを離すと、割り込みラインが逆方向に遷移します。 アクティブ両方の割り込み入力として構成された GPIO ピンは、低から高への遷移と高から低への遷移の両方で割り込みを生成し、その結果、どちらの場合でも、システムは周辺デバイスドライバーの割り込みサービス ルーチン (ISR) を呼び出します。 ただし、ドライバーは、遷移が低から高になるのか、高から低になるのかを示す指示を受け取りません。

低から高への遷移と高から低への遷移を区別するには、ドライバーは各割り込みの状態を追跡する必要があります。 そのために、ドライバーがメイン、割り込みライン状態が低い場合はFALSE、ライン状態が高い場合はTRUEというブール割り込み状態値を維持することがあります。

システムの起動時にライン状態がデフォルトで 低 に設定される例を考えてみましょう。 ドライバーは、EvtDevicePrepareHardware コールバック関数で、状態値をFALSE に初期化します。 その後、ドライバーの ISR が呼び出され、状態の変化が通知されるたびに、ドライバーは ISR 内の状態値を反転します。

システムの起動時にライン状態が高い場合、割り込みは有効になった直後に発生します。 ドライバーは、WdfInterruptCreateを呼び出す代わりに、 IoConnectInterruptExルーチンを直接呼び出すので、可能な即時割り込みを受信することができます。

このソリューションでは、GPIO コントローラーがハードウェアでアクティブ両方の割り込みをサポートするか、GPIO コントローラーのドライバーがソフトウェアでアクティブ両方の割り込みをエミュレートする必要があります。 アクティブ両方の割り込みのエミュレートについては、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 の登録を解除します。