Nota
L'accesso a questa pagina richiede l'autorizzazione. Puoi provare ad accedere o a cambiare directory.
L'accesso a questa pagina richiede l'autorizzazione. Puoi provare a cambiare directory.
nota Questo argomento si applica solo a Kernel-Mode Driver Framework (KMDF) versione 1.13 e precedenti.
Molti dispositivi dispongono di registri hardware che controllano la generazione e il mascheramento degli interrupt. In genere, i driver KMDF e UMDF per tali dispositivi usano il supporto di interrupt predefinito del framework.
Tuttavia, un dispositivo semplice su 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 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 routine WDM direttamente per connettersi e disconnettere gli interrupt. Poiché un driver UMDF non può chiamare questi metodi, non è possibile scrivere un driver UMDF per tale dispositivo.
In questo argomento viene descritto come un driver KMDF potrebbe gestire gli interrupt per un dispositivo di questo tipo.
Nelle piattaforme hardware SoC, gli interrupt a doppia attivazione vengono in genere usati per dispositivi molto semplici, come i pulsanti 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 per entrambe le transizioni genera interruzioni sia nel passaggio da basso ad alto che da alto a basso, portando il sistema a chiamare la routine di servizio di interrupt (ISR) del driver del dispositivo periferico in entrambi i casi. Tuttavia, il driver non riceve un'indicazione se la transizione è da bassa ad alta o da alta a bassa.
Per distinguere tra transizioni da basso a alto e da alto a basso, il driver deve tenere traccia dello stato di ogni interrupt. A tale scopo, il driver potrebbe mantenere un valore di stato di interrupt booleano FALSE quando lo stato della linea di interrupt è basso e TRUE quando lo stato della linea è elevato.
Si consideri un esempio in cui lo stato predefinito della riga è basso all'avvio del sistema. Il driver inizializza il valore di stato a FALSE nella funzione di callback relativa EvtDevicePrepareHardware. Quindi ogni volta che viene chiamato l'ISR del driver, segnalando una modifica dello stato, il driver inverte il valore dello stato nel suo 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, si garantisce di ricevere un possibile interrupt immediato.
Questa soluzione richiede che il controller GPIO supporti gli interrupt attivi nell'hardware o che il driver per il controller GPIO emuli gli interrupt attivi nel software. Per informazioni sull'emulazione di interrupt attivi per entrambi, consultare la descrizione del membro EmulateActiveBoth della struttura CONTROLLER_ATTRIBUTE_FLAGS.
L'esempio di codice seguente mostra 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(¶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;
}
Nell'esempio di codice precedente, il driver EvtDriverDeviceAdd funzione di callback 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 sua funzione di richiamata EvtDevicePrepareHardware, il driver inizializza il valore di stato a FALSE e quindi chiama IoConnectInterruptEx. Nella funzione di callback EvtDeviceReleaseHardware, il driver chiama IoDisconnectInterruptEx per annullare la registrazione dell'ISR.