Partager via


Gestion des interruptions Active-Both

Note Cette rubrique s’applique uniquement à Kernel-Mode Driver Framework (KMDF) version 1.13 et antérieures.

De nombreux appareils ont des registres matériels qui contrôlent la génération et le masquage des interruptions. En règle générale, les pilotes KMDF et UMDF pour ces appareils utilisent la prise en charge intégrée des interruptions de l’infrastructure.

Toutefois, un appareil simple sur une plateforme matérielle Système sur une puce (SoC) peut ne pas avoir de registres matériels pour les interruptions. Par conséquent, un pilote pour un tel appareil peut ne pas être en mesure de contrôler le moment où l’interruption est générée ou de pouvoir masquer l’interruption dans le matériel. Si l’appareil s’interrompt immédiatement après la connexion et que le pilote utilise la prise en charge des interruptions de l’infrastructure, il est possible que l’interruption se déclenche avant que l’infrastructure n’ait entièrement initialisé l’objet d’interruption d’infrastructure. Par conséquent, un pilote KMDF doit appeler des routines WDM directement pour se connecter et déconnecter les interruptions. Étant donné qu’un pilote UMDF ne peut pas appeler ces méthodes, vous ne pouvez pas écrire de pilote UMDF pour un tel appareil.

Cette rubrique décrit comment un pilote KMDF peut gérer les interruptions pour un tel appareil.

Sur les plateformes matérielles SoC, les deux interruptions actives sont généralement utilisées pour les appareils très simples comme les boutons push matériels. Lorsqu’un utilisateur appuie sur un bouton poussoir, la ligne de signal d’interruption de l’appareil passe de faible à élevé, ou de haut à faible. Lorsque l’utilisateur relâche le bouton push-button, la ligne d’interruption passe dans le sens inverse. Une broche GPIO configurée en tant qu’entrée d’interruption active génère des interruptions sur les transitions de bas à haut et de haut en bas, ce qui entraîne l’appel du système à la routine de service d’interruption (ISR) du pilote de périphérique dans les deux cas. Toutefois, le conducteur ne reçoit pas d’indication indiquant si la transition est de faible à haut ou de haut en bas.

Pour faire la distinction entre les transitions de faible à élevé et de haut en bas, le pilote doit suivre l’état de chaque interruption. Pour ce faire, votre pilote peut conserver une valeur d’état d’interruption booléenne qui est FALSE lorsque l’état de ligne d’interruption est faible et TRUE lorsque l’état de la ligne est élevé.

Prenons un exemple dans lequel l’état de ligne par défaut est faible au démarrage du système. Le pilote initialise la valeur d’état sur FALSE dans sa fonction de rappel EvtDevicePrepareHardware . Ensuite, chaque fois que l’ISR du pilote est appelé, signalant un changement d’état, le pilote inverse la valeur d’état dans son ISR.

Si l’état de ligne est élevé au démarrage du système, l’interruption se déclenche immédiatement après son activation. Étant donné que le pilote appelle directement la routine IoConnectInterruptEx , au lieu d’appeler WdfInterruptCreate, il est assuré de recevoir une interruption immédiate possible.

Cette solution nécessite que le contrôleur GPIO prend en charge les interruptions actives dans le matériel, ou que le pilote du contrôleur GPIO émule les interruptions actives dans les deux dans le logiciel. Pour plus d’informations sur l’émulation des interruptions active-both, consultez la description du membre EmulateActiveBoth de la structure CONTROLLER_ATTRIBUTE_FLAGS .

L’exemple de code suivant montre comment un pilote KMDF pour un périphérique peut suivre la polarité des interruptions.

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

Dans l’exemple de code précédent, la fonction de rappel EvtDriverDeviceAdd du pilote configure le contexte de l’appareil, puis appelle IoInitializeDpcRequest pour inscrire une routine DpcForIsr .

La routine InterruptService du pilote inverse la valeur de l’état d’interruption, puis appelle IoRequestDpc pour mettre en file d’attente la DPC.

Dans sa fonction de rappel EvtDevicePrepareHardware , le pilote initialise la valeur d’état sur FALSE , puis appelle IoConnectInterruptEx. Dans sa fonction de rappel EvtDeviceReleaseHardware , le pilote appelle IoDisconnectInterruptEx pour désinscrire son ISR.