Gestione degli interrupt Active-Both

Nota Questo argomento si applica solo a Kernel-Mode Driver Framework (KMDF) versione 1.13 e precedenti.

Molti dispositivi hanno registri hardware che controllano la generazione e la maschera degli interrupt. In genere, i driver KMDF e UMDF per tali dispositivi usano il supporto di interrupt predefinito del framework.

Tuttavia, un dispositivo semplice in una piattaforma hardware SoC (System on a Chip) potrebbe non avere registri hardware per gli interrupt. Di conseguenza, un driver per tale dispositivo potrebbe non essere in grado di controllare quando viene generato l'interrupt o essere in grado di mascherare l'interruzione nell'hardware. Se il dispositivo interrompe immediatamente la connessione e il driver usa il supporto di interrupt del framework, è possibile che l'interrupt venga attivato prima che il framework abbia inizializzato completamente l'oggetto interrupt del framework. Di conseguenza, un driver KMDF deve chiamare direttamente routine WDM per connettersi e disconnettersi. Poiché un driver UMDF non può chiamare questi metodi, non è possibile scrivere un driver UMDF per un dispositivo di questo tipo.

Questo argomento descrive come un driver KMDF può gestire gli interrupt per un dispositivo di questo tipo.

Nelle piattaforme hardware SoC, gli interrupt attivi-entrambi vengono in genere usati per dispositivi molto semplici come i pulsanti di scelta hardware. Quando un utente preme un pulsante di pressione, la linea del segnale di interruzione dal dispositivo passa da bassa a alta o da alta a bassa. Quando l'utente rilascia il pulsante di pressione, la linea di interruzione passa nella direzione opposta. Un pin GPIO configurato come input di interrupt attivo-both genera interrupt sia nelle transizioni low-to-high che high-to-low, con conseguente chiamata del sistema alla routine del servizio di interrupt del driver di dispositivo periferico (ISR) in entrambi i casi. Tuttavia, il driver non riceve un'indicazione se la transizione è bassa o alta a bassa.

Per distinguere tra transizioni da bassa a bassa e bassa, il driver deve tenere traccia dello stato di ogni interruzione. A tale scopo, il driver potrebbe mantenere un valore di stato di interrupt booleano FALSE quando lo stato della riga di interruzione è basso e TRUE quando lo stato della riga è elevato.

Si consideri un esempio in cui per impostazione predefinita lo stato della riga è basso all'avvio del sistema. Il driver inizializza il valore dello stato su FALSE nella relativa funzione di callback EvtDevicePrepareHardware . Ogni volta che viene chiamato l'ISR del driver, segnalando una modifica dello stato, il driver inverte il valore di stato nel relativo ISR.

Se lo stato della linea è elevato all'avvio del sistema, l'interrupt viene attivato immediatamente dopo l'abilitazione. Poiché il driver chiama direttamente la routine IoConnectInterruptEx , anziché chiamare WdfInterruptCreate, viene garantita la ricezione di un possibile interrupt immediato.

Questa soluzione richiede che il controller GPIO supporti interruzioni attive-entrambe nell'hardware o che il driver per il controller GPIO emula gli interrupt attivi-entrambi nel software. Per informazioni sulla simulazione di interrupt attivi-entrambi, vedere la descrizione del membro EmulateActiveBoth della struttura CONTROLLER_ATTRIBUTE_FLAGS .

L'esempio di codice seguente illustra come un driver KMDF per un dispositivo periferico può tenere traccia della polarità di interruzione.

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

Nell'esempio di codice precedente, la funzione di callback EvtDriverDeviceAdd del driver configura il contesto di dispositivo e quindi chiama IoInitializeDpcRequest per registrare una routine DpcForIsr .

La routine InterruptService del driver inverte il valore dello stato di interrupt e quindi chiama IoRequestDpc per accodare il DPC.

Nella funzione di callback EvtDevicePrepareHardware il driver inizializza il valore di stato su FALSE e quindi chiama IoConnectInterruptEx. Nella funzione di callback EvtDeviceReleaseHardware , il driver chiama IoDisconnectInterruptEx per annullare la registrazione dell'ISR.