Delen via


Active-Both interrupts verwerken

Notitie Dit onderwerp is alleen van toepassing op Kernel-Mode KMDF(Driver Framework) versie 1.13 en eerder.

Veel apparaten hebben hardwareregisters die het genereren en maskeren van interrupts regelen. Normaal gesproken maken KMDF- en UMDF-stuurprogramma's voor dergelijke apparaten gebruik van de ingebouwde interrupt-ondersteuning van het framework.

Een eenvoudig apparaat op een SoC-hardwareplatform (System on a Chip) heeft echter mogelijk geen hardwareregisters voor interrupts. Als gevolg hiervan kan een stuurprogramma voor een dergelijk apparaat mogelijk niet bepalen wanneer de interrupt wordt gegenereerd of kan de interrupt in hardware maskeren. Als het apparaat onmiddellijk wordt onderbroken zodra het is verbonden en het stuurprogramma maakt gebruik van de interrupt-ondersteuning van het framework, is het mogelijk dat de interrupt plaatsvindt voordat het framework het framework-interrupt-object volledig heeft geïnitialiseerd. Als gevolg hiervan moet een KMDF-stuurprogramma WDM-routines rechtstreeks aanroepen om onderbrekingen te verbinden en te verbreken. Omdat een UMDF-stuurprogramma deze methoden niet kan aanroepen, kunt u geen UMDF-stuurprogramma schrijven voor een dergelijk apparaat.

In dit onderwerp wordt beschreven hoe een KMDF-stuurprogramma interrupts voor een dergelijk apparaat kan verwerken.

Op SoC-hardwareplatformen worden "active-both interrupts" typisch gebruikt voor zeer eenvoudige apparaten, zoals hardwarepushknoppen. Wanneer een gebruiker op een drukknop drukt, gaat de interruptsignaallijn van het apparaat over van laag naar hoog of van hoog naar laag. Wanneer de gebruiker de drukknop loslaat, verandert de interruptlijn naar de tegenovergestelde richting. Een GPIO-pin die is geconfigureerd als een actief-op-beide interruptingang genereert interrupts bij zowel laag-naar-hoog als hoog-naar-laag wijzigingen, wat ertoe leidt dat het systeem in beide gevallen de interruptserviceroutine van het apparaatstuurprogramma (ISR) aanroept. Het stuurprogramma ontvangt echter geen indicatie of de overgang laag naar hoog of hoog naar laag is.

Om onderscheid te maken tussen overgangen van laag naar hoog en hoog naar laag, moet het stuurprogramma de status van elke interrupt volgen. Om dit te doen, kan uw stuurprogramma een Booleaanse interruptstatuswaarde behouden die ONWAAR is wanneer de status van de interruptlijn laag is en WAAR wanneer de lijnstatus hoog is.

Bekijk een voorbeeld waarin de lijnstatus standaard laag is wanneer het systeem opstart. Het stuurprogramma initialiseert de statuswaarde naar FALSE in de callback-functie EvtDevicePrepareHardware. Telkens wanneer de ISR van het stuurprogramma wordt aangeroepen, wat een wijziging in de status signaleert, keert het stuurprogramma de statuswaarde in zijn ISR om.

Als de lijnstatus hoog is wanneer het systeem start, wordt de interrupt direct geactiveerd nadat deze is ingeschakeld. Omdat het stuurprogramma de IoConnectInterruptEx-routine rechtstreeks aanroept , in plaats van WdfInterruptCreate aan te roepen, wordt ervoor gezorgd dat er een mogelijke onmiddellijke interrupt wordt ontvangen.

Deze oplossing vereist dat de GPIO-controller actief-beide interrupts in hardware ondersteunt of dat het stuurprogramma voor de GPIO-controller actief-beide interrupts in software emuleren. Zie de beschrijving van het lid EmulateActiveBoth van de CONTROLLER_ATTRIBUTE_FLAGS structuur voor informatie over het emuleren van actief-beide interrupts.

In het volgende codevoorbeeld ziet u hoe een KMDF-stuurprogramma voor een randapparaat de polariteit van interrupts kan bijhouden.

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

In het voorgaande codevoorbeeld configureert de callbackfunctie evtDriverDeviceAdd van het stuurprogramma de apparaatcontext en roept vervolgens IoInitializeDpcRequest aan om een DpcForIsr-routine te registreren.

De interruptserviceroutine van het stuurprogramma zet de interruptstatuswaarde om en roept vervolgens IoRequestDpc aan om de DPC in de wachtrij te plaatsen.

In de functie EvtDevicePrepareHardware callback initialiseert het stuurprogramma de statuswaarde naar FALSE en roept het vervolgens IoConnectInterruptEx aan. In de EvtDeviceReleaseHardware callback functie roept het stuurprogramma IoDisconnectInterruptEx aan om zijn ISR af te melden.