Control de interrupciones de Active-Both

Nota Este tema solo se aplica a Kernel-Mode Driver Framework (KMDF) versión 1.13 y anteriores.

Muchos dispositivos tienen registros de hardware que controlan la generación de interrupciones y el enmascaramiento. Normalmente, los controladores KMDF y UMDF para estos dispositivos usan la compatibilidad integrada con interrupciones del marco.

Sin embargo, es posible que un dispositivo simple en un sistema en una plataforma de hardware de chip (SoC) no tenga registros de hardware para interrupciones. Como resultado, es posible que un controlador de este tipo de dispositivo no pueda controlar cuándo se genera la interrupción o puede enmascarar la interrupción en el hardware. Si el dispositivo se interrumpe inmediatamente después de la conexión y el controlador usa la compatibilidad con interrupciones del marco, es posible que la interrupción se active antes de que el marco haya inicializado completamente el objeto de interrupción del marco. Como resultado, un controlador KMDF debe llamar a rutinas WDM directamente para conectarse y desconectar interrupciones. Dado que un controlador UMDF no puede llamar a estos métodos, no puede escribir un controlador UMDF para este tipo de dispositivo.

En este tema se describe cómo un controlador KMDF puede controlar las interrupciones de este tipo de dispositivo.

En las plataformas de hardware de SoC, las interrupciones activas se suelen usar para dispositivos muy sencillos, como botones de inserción de hardware. Cuando un usuario presiona un botón de pulsación, la línea de señal de interrupción del dispositivo pasa de baja a alta o de alta a baja. Cuando el usuario suelta el botón de pulsación, la línea de interrupción pasa en la dirección opuesta. Una patilla GPIO configurada como una entrada de interrupción activa genera interrupciones en transiciones de baja a alta y alta a baja, lo que da lugar a que el sistema llame a la rutina de servicio de interrupción (ISR) del controlador de dispositivo periférico en ambos casos. Sin embargo, el controlador no recibe una indicación de si la transición es baja a alta o alta a baja.

Para distinguir entre transiciones de baja a alta y baja a baja, el controlador debe realizar un seguimiento del estado de cada interrupción. Para ello, el controlador puede mantener un valor de estado de interrupción booleano que es FALSE cuando el estado de la línea de interrupción es bajo y TRUE cuando el estado de la línea es alto.

Considere un ejemplo en el que el estado de línea tiene como valor predeterminado un valor bajo cuando se inicia el sistema. El controlador inicializa el valor de estado en FALSE en su función de devolución de llamada EvtDevicePrepareHardware . A continuación, cada vez que se llama al ISR del controlador, que señala un cambio de estado, el controlador invierte el valor de estado en su ISR.

Si el estado de la línea es alto cuando se inicia el sistema, la interrupción se desencadena inmediatamente después de habilitarla. Dado que el controlador llama directamente a la rutina IoConnectInterruptEx , en lugar de llamar a WdfInterruptCreate, se asegura de recibir una posible interrupción inmediata.

Esta solución requiere que el controlador GPIO admita interrupciones activas en el hardware, o que el controlador para el controlador GPIO emule las interrupciones activas y ambas en el software. Para obtener información sobre cómo simular interrupciones activas y ambas, vea la descripción del miembro EmularActiveBoth de la estructura CONTROLLER_ATTRIBUTE_FLAGS .

En el ejemplo de código siguiente se muestra cómo un controlador KMDF para un dispositivo periférico puede realizar un seguimiento de la polaridad de interrupción.

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

En el ejemplo de código anterior, la función de devolución de llamada EvtDriverDeviceAdd del controlador configura el contexto del dispositivo y, a continuación, llama a IoInitializeDpcRequest para registrar una rutina DpcForIsr .

La rutina InterruptService del controlador invierte el valor de estado de interrupción y, a continuación, llama a IoRequestDpc para poner en cola el DPC.

En su función de devolución de llamada EvtDevicePrepareHardware , el controlador inicializa el valor de estado en FALSE y, a continuación, llama a IoConnectInterruptEx. En su función de devolución de llamada EvtDeviceReleaseHardware , el controlador llama a IoDisconnectInterruptEx para anular el registro de su ISR.