Manipulando interrupções de Active-Both

Nota Este tópico se aplica somente à versão 1.13 do KMDF (Kernel-Mode Driver Framework) e anteriores.

Muitos dispositivos têm registros de hardware que controlam a geração e o mascaramento de interrupção. Normalmente, os drivers KMDF e UMDF para esses dispositivos usam o suporte interno de interrupção da estrutura.

No entanto, um dispositivo simples em uma plataforma de hardware SoC (Sistema em um Chip) pode não ter registros de hardware para interrupções. Como resultado, um driver para esse dispositivo pode não ser capaz de controlar quando a interrupção é gerada ou ser capaz de mascarar a interrupção no hardware. Se o dispositivo interromper imediatamente após a conexão e o driver estiver usando o suporte de interrupção da estrutura, será possível que a interrupção seja acionada antes que a estrutura inicialize totalmente o objeto de interrupção da estrutura. Como resultado, um driver KMDF deve chamar rotinas WDM diretamente para se conectar e desconectar interrupções. Como um driver UMDF não pode chamar esses métodos, você não pode escrever um driver UMDF para esse dispositivo.

Este tópico descreve como um driver KMDF pode lidar com interrupções para esse dispositivo.

Em plataformas de hardware SoC, as interrupções ativas-ambas normalmente são usadas para dispositivos muito simples, como botões de push de hardware. Quando um usuário pressiona um botão de pressão, a linha de sinal de interrupção do dispositivo faz a transição de baixo para alto ou de alto para baixo. Quando o usuário libera o botão de pressão, a linha de interrupção faz a transição na direção oposta. Um pino gpio configurado como uma entrada de interrupção ativa-ambos gera interrupções em transições de baixo para alto e alto para baixo, resultando no sistema chamando a ISR (rotina de serviço de interrupção) do driver de dispositivo periférico em ambos os casos. No entanto, o driver não recebe uma indicação se a transição é baixa para alta ou alta para baixa.

Para distinguir entre transições de baixo para alto e alto para baixo, o driver deve acompanhar o estado de cada interrupção. Para fazer isso, o driver pode manter um valor de estado de interrupção booliano que é FALSE quando o estado da linha de interrupção é baixo e TRUE quando o estado da linha é alto.

Considere um exemplo no qual o estado da linha usa como padrão baixo quando o sistema é iniciado. O driver inicializa o valor de estado como FALSE em sua função de retorno de chamada EvtDevicePrepareHardware . Em seguida, sempre que o ISR do driver é chamado, sinalizando uma alteração no estado, o driver inverte o valor de estado em seu ISR.

Se o estado da linha for alto quando o sistema for iniciado, a interrupção será disparada imediatamente depois de habilitada. Como o driver chama a rotina IoConnectInterruptEx diretamente, em vez de chamar WdfInterruptCreate, é preciso receber uma possível interrupção imediata.

Essa solução exige que o controlador GPIO dê suporte a interrupções ativas em hardware ou que o driver para o controlador GPIO emule interrupções ativas-ambas no software. Para obter informações sobre como emular interrupções active-both, consulte a descrição do membro EmulateActiveBoth da estrutura CONTROLLER_ATTRIBUTE_FLAGS .

O exemplo de código a seguir mostra como um driver KMDF para um dispositivo periférico pode controlar a polaridade de interrupção.

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

No exemplo de código anterior, a função de retorno de chamada EvtDriverDeviceAdd do driver configura o contexto do dispositivo e, em seguida, chama IoInitializeDpcRequest para registrar uma rotina DpcForIsr .

A rotina InterruptService do driver inverte o valor do estado de interrupção e chama IoRequestDpc para enfileirar o DPC.

Em sua função de retorno de chamada EvtDevicePrepareHardware , o driver inicializa o valor de estado como FALSE e, em seguida, chama IoConnectInterruptEx. Em sua função de retorno de chamada EvtDeviceReleaseHardware , o driver chama IoDisconnectInterruptEx para cancelar o registro de seu ISR.