Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação Este tópico aplica-se apenas ao Kernel-Mode Driver Framework (KMDF) versão 1.13 e anteriores.
Muitos dispositivos têm registros de hardware que controlam a geração de interrupções e o mascaramento. Normalmente, os drivers KMDF e UMDF para esses dispositivos usam o suporte de interrupção interno da estrutura.
No entanto, um dispositivo simples em uma plataforma de hardware System on a Chip (SoC) pode não ter registros de hardware para interrupções. Como resultado, um driver para tal 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, é possível que a interrupção seja acionada antes que a estrutura tenha inicializado totalmente o objeto de interrupção da estrutura. Como resultado, um driver KMDF deve chamar rotinas WDM diretamente para 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 tal dispositivo.
Este tópico descreve como um driver KMDF pode lidar com interrupções para esse dispositivo.
Em plataformas de hardware SoC, os interruptores ativos em ambos os estados são normalmente usados para dispositivos muito simples, como botões de pressão. Quando um usuário pressiona um botão, a linha de sinal de interrupção do dispositivo transita de baixo para alto ou de alto para baixo. Quando o usuário libera o botão, a linha de interrupção transita na direção oposta. Um pino GPIO configurado como uma entrada de interrupção ativa em ambas as direções gera interrupções em transições de nível baixo para nível alto e vice-versa, resultando no sistema chamar a rotina de serviço de interrupção (ISR) do driver de dispositivo periférico em ambos os casos. No entanto, o condutor não recebe uma indicação se a transição é de baixo para alto ou de alto para baixo.
Para distinguir entre transições de baixo para alto e de alto para baixo, o motorista deve rastrear o estado de cada interrupção. Para fazer isso, o driver pode manter um valor de estado de interrupção booleano que é FALSE quando o estado da linha de interrupção é baixo e TRUE quando o estado da linha é alto.
Considere um exemplo em que o estado da linha é, por padrão, baixo quando o sistema é iniciado. O driver inicializa o valor de estado para FALSE em sua função de retorno de chamada EvtDevicePrepareHardware . Em seguida, cada vez que o ISR do controlador é chamado, sinalizando uma mudança de estado, o controlador inverte o valor do estado no seu ISR.
Se o estado da linha for elevado quando o sistema for iniciado, a interrupção é acionada imediatamente após ser ativada. Como o driver chama a rotina IoConnectInterruptEx diretamente, em vez de chamar WdfInterruptCreate, ele tem a garantia de receber uma possível interrupção imediata.
Esta solução requer que o controlador GPIO suporte interrupções de ambos os estados ativos no hardware, ou que o driver para o controlador GPIO emule interrupções de ambos os estados ativos no software. Para obter informações sobre como emular interrupções ativas-ambas, 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 rastrear a polaridade da 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(¶ms, 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(¶ms);
}
}
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(¶ms);
}
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, em seguida, chama IoRequestDpc para enfileirar o DPC.
Na sua função de callback EvtDevicePrepareHardware, o driver inicializa o valor do estado como FALSE e, em seguida, chama IoConnectInterruptEx. Na sua função de retorno de chamada EvtDeviceReleaseHardware, o driver chama IoDisconnectInterruptEx para cancelar o registo do seu ISR.