Behandeln von Active-Both Interrupts

Hinweis Dieses Thema gilt nur für Kernel-Mode Driver Framework (KMDF) Version 1.13 und früher.

Viele Geräte verfügen über Hardwareregister, die die Erzeugung und Maskierung von Unterbrechungen steuern. In der Regel verwenden KMDF- und UMDF-Treiber für solche Geräte die integrierte Interruptunterstützung des Frameworks.

Ein einfaches Gerät auf einer SoC-Hardwareplattform (System on a Chip) verfügt jedoch möglicherweise nicht über Hardwareregister für Interrupts. Infolgedessen kann ein Treiber für ein solches Gerät möglicherweise nicht steuern, wann der Interrupt generiert wird, oder kann den Interrupt in der Hardware maskieren. Wenn das Gerät sofort nach der Verbindung unterbricht und der Treiber die Interruptunterstützung des Frameworks verwendet, kann der Interrupt ausgelöst werden, bevor das Framework das Framework-Interruptobjekt vollständig initialisiert hat. Daher muss ein KMDF-Treiber WDM-Routinen direkt aufrufen, um Interrupts zu verbinden und zu trennen. Da ein UMDF-Treiber diese Methoden nicht aufrufen kann, können Sie keinen UMDF-Treiber für ein solches Gerät schreiben.

In diesem Thema wird beschrieben, wie ein KMDF-Treiber Unterbrechungen für ein solches Gerät verarbeiten kann.

Auf SoC-Hardwareplattformen werden aktiv-beide Interrupts in der Regel für sehr einfache Geräte wie Hardware-Taster verwendet. Wenn ein Benutzer einen Drucktasten drückt, wechselt die Unterbrechungssignalleitung des Geräts von niedrig zu hoch oder von hoch zu niedrig. Wenn der Benutzer die Taste loslässt, wechselt die Interruptlinie in die entgegengesetzte Richtung. Ein GPIO-Pin, der als Aktiv-Beide-Interrupt-Eingabe konfiguriert ist, generiert Interrupts sowohl bei Übergängen mit niedrigen zu hohen als auch bei hohen zu niedrigen Übergängen, was dazu führt, dass das System in beiden Fällen die Interruptdienstroutine (Interrupt Service Routine, ISR) des Treibers des Peripheriegeräts aufruft. Der Treiber erhält jedoch keine Angabe, ob der Übergang niedrig zu hoch oder hoch zu niedrig ist.

Um zwischen Übergängen mit niedrigen zu hohen und hohen Zugängen zu unterscheiden, muss der Fahrer den Zustand der einzelnen Unterbrechungen nachverfolgen. Dazu kann Ihr Treiber einen booleschen Interruptzustandswert beibehalten, der FALSE ist, wenn der Unterbrechungszeilenzustand niedrig ist, und true , wenn der Zeilenzustand hoch ist.

Betrachten Sie ein Beispiel, in dem der Zeilenzustand standardmäßig niedrig ist, wenn das System gestartet wird. Der Treiber initialisiert den Zustandswert in seiner Rückruffunktion EvtDevicePrepareHardware auf FALSE. Jedes Mal, wenn der ISR des Treibers aufgerufen wird und eine Änderung des Zustands signalisiert, invertiert der Treiber den Zustandswert in seiner ISR.

Wenn der Zeilenstatus beim Starten des Systems hoch ist, wird der Interrupt sofort nach der Aktivierung ausgelöst. Da der Treiber die IoConnectInterruptEx-Routine direkt aufruft , wird anstelle von WdfInterruptCreate sichergestellt, dass ein möglicher sofortiger Interrupt empfangen wird.

Diese Lösung erfordert, dass der GPIO-Controller aktiv-beide Interrupts in der Hardware unterstützt oder dass der Treiber für den GPIO-Controller aktiv-beide Interrupts in der Software emuliert. Informationen zum Emulieren von Interrupts mit aktiv-beidem finden Sie in der Beschreibung des EmulateActiveBoth-Elements der CONTROLLER_ATTRIBUTE_FLAGS-Struktur .

Das folgende Codebeispiel zeigt, wie ein KMDF-Treiber für ein Peripheriegerät die Unterbrechungspolarität nachverfolgen kann.

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;
}

Im vorherigen Codebeispiel konfiguriert die EvtDriverDeviceAdd-Rückruffunktion des Treibers den Gerätekontext und ruft dann IoInitializeDpcRequest auf, um eine DpcForIsr-Routine zu registrieren.

Die InterruptService-Routine des Treibers invertiert den Interruptstatuswert und ruft dann IoRequestDpc auf, um den DPC in die Warteschlange zu stellen.

In seiner Rückruffunktion EvtDevicePrepareHardware initialisiert der Treiber den Zustandswert auf FALSE und ruft dann IoConnectInterruptEx auf. In seiner Rückruffunktion EvtDeviceReleaseHardware ruft der Treiber IoDisconnectInterruptEx auf, um die Registrierung seiner ISR aufzuheben.