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.
Con l'introduzione della versione 1.11 del framework, i Driver Framework Kernel-Mode (KMDF) e i driver Driver Framework User-Mode (UMDF) in esecuzione su Windows 8 o versioni successive del sistema operativo sono in grado di creare oggetti di interrupt che richiedono la gestione a livello passivo. Se il driver configura un oggetto di interrupt per la gestione degli interrupt a livello passivo, il framework invoca la routine di servizio degli interrupt del driver (ISR) e altre funzioni di callback degli eventi dell'oggetto di interrupt a IRQL = PASSIVE_LEVEL, mantenendo un blocco di interrupt a livello passivo.
Se si sviluppa un driver basato su framework per una piattaforma System on a Chip (SoC), è possibile usare interrupt in modalità passiva per comunicare con un dispositivo off-SoC su un bus a bassa velocità, ad esempio I²C, SPI o UART.
In caso contrario, dovresti usare interrupt che richiedono di essere gestiti all'IRQL (DIRQL) del dispositivo. Se il driver supporta interrupt segnalati tramite messaggio, è necessario usare la gestione degli interrupt DIRQL. Nelle versioni 1.9 e precedenti, il framework elabora sempre le interruzioni a IRQL = DIRQL.
In questo argomento viene descritto come creare, gestire e sincronizzare gli interrupt a livello passivo.
Creazione di un interrupt Passive-Level
Per creare un oggetto interrupt a livello passivo, un driver deve inizializzare una struttura WDF_INTERRUPT_CONFIG e passarla al metodo WdfInterruptCreate . Nella struttura di configurazione il driver deve:
- Impostare il membro PassiveHandling su TRUE.
- Fornire una funzione di callback EvtInterruptIsr da chiamare a livello passivo.
- Facoltativamente, impostare AutomaticSerialization su TRUE. Se il driver imposta AutomaticSerialization su TRUE, il framework sincronizza l'esecuzione delle funzioni di callback EvtInterruptDpc o EvtInterruptWorkItem dell'oggetto interrupt con funzioni di callback di altri oggetti sottostanti all'oggetto padre dell'interrupt.
- Facoltativamente, il driver può fornire una funzione di callback EvtInterruptWorkItem, da chiamare in IRQL = PASSIVE_LEVEL, oppure una funzione di callback EvtInterruptDpc, da chiamare in IRQL = DISPATCH_LEVEL.
Per altre informazioni sull'impostazione dei membri precedenti della struttura di configurazione, vedere WDF_INTERRUPT_CONFIG. Per informazioni sull'abilitazione e la disabilitazione di interrupt a livello passivo, vedere Abilitazione e disabilitazione degli interrupt.
Gestione di un'interruzione Passive-Level
La funzione di callback EvtInterruptIsr, che viene eseguita in IRQL = PASSIVE_LEVEL con il blocco d'interruzione a livello passivo mantenuto, in genere pianifica un compito di lavoro per interrupt o un'interruzione DPC per elaborare le informazioni correlate all'interrupt in un momento successivo. I driver basati su framework implementano routine di elemento di lavoro o DPC come funzioni di callback EvtInterruptWorkItem o EvtInterruptDpc .
Per pianificare l'esecuzione di una funzione di callback EvtInterruptWorkItem , un driver chiama WdfInterruptQueueWorkItemForIsr dall'interno della funzione di callback EvtInterruptIsr .
Per pianificare l'esecuzione di una funzione di callback EvtInterruptDpc , un driver chiama WdfInterruptQueueDpcForIsr dall'interno della funzione di callback EvtInterruptIsr . Tenere presente che la funzione di callback EvtInterruptIsr di un driver può chiamare WdfInterruptQueueWorkItemForIsr o WdfInterruptQueueDpcForIsr, ma non entrambi.
La maggior parte dei driver usa una singola funzione di callback EvtInterruptWorkItem o EvtInterruptDpc per ogni tipo di interrupt. Se il driver crea più oggetti di interrupt del framework per ogni dispositivo, è consigliabile usare un callback EvtInterruptWorkItem separato o EvtInterruptDpc per ogni interrupt.
I driver in genere completano le richieste di I/O nelle funzioni di callback EvtInterruptWorkItem o EvtInterruptDpc .
Nell'esempio di codice seguente, viene illustrato come un driver che utilizza interrupt di livello passivo possa pianificare un callback EvtInterruptWorkItem all'interno della sua funzione EvtInterruptIsr.
BOOLEAN
EvtInterruptIsr(
_In_ WDFINTERRUPT Interrupt,
_In_ ULONG MessageID
)
/*++
Routine Description:
This routine responds to interrupts generated by the hardware.
It stops the interrupt and schedules a work item for
additional processing.
This ISR is called at PASSIVE_LEVEL (passive-level interrupt handling).
Arguments:
Interrupt - a handle to a framework interrupt object
MessageID - message number identifying the device's
hardware interrupt message (if using MSI)
Return Value:
TRUE if interrupt recognized.
--*/
{
UNREFERENCED_PARAMETER(MessageID);
NTSTATUS status;
PDEV_CONTEXT devCtx;
WDFREQUEST request;
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
INT_REPORT intReport = {0};
BOOLEAN intRecognized;
WDFIOTARGET ioTarget;
ULONG_PTR bytes;
WDFMEMORY reqMemory;
intRecognized = FALSE;
//
// Typically the pattern in most ISRs (DIRQL or otherwise) is to:
// a) Check if the interrupt belongs to this device (shared interrupts).
// b) Stop the interrupt if the interrupt belongs to this device.
// c) Acknowledge the interrupt if the interrupt belongs to this device.
//
//
// Retrieve device context so that we can access our queues later.
//
devCtx = GetDevContext(WdfInterruptGetDevice(Interrupt));
//
// Init memory descriptor.
//
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(
&memoryDescriptor,
&intReport,
sizeof(intReport);
//
// Send read registers/data IOCTL.
// This call stops the interrupt and reads the data at the same time.
// The device will reinterrupt when a new read is sent.
//
bytes = 0;
status = WdfIoTargetSendIoctlSynchronously(
ioTarget,
NULL,
IOCTL_READ_REPORT,
&memoryDescriptor,
NULL,
NULL,
&bytes);
//
// Return from ISR if this is not our interrupt.
//
if (intReport->Interrupt == FALSE) {
goto exit;
}
intRecognized = TRUE;
//
// Validate the data received.
//
...
//
// Retrieve the next read request from the ReportQueue which
// stores all the incoming IOCTL_READ_REPORT requests
//
request = NULL;
status = WdfIoQueueRetrieveNextRequest(
devCtx->ReportQueue,
&request);
if (!NT_SUCCESS(status) || (request == NULL)) {
//
// No requests to process.
//
goto exit;
}
//
// Retrieve the request buffer.
//
status = WdfRequestRetrieveOutputMemory(request, &reqMemory);
//
// Copy the data read into the request buffer.
// The request will be completed in the work item.
//
bytes = intReport->Data->Length;
status = WdfMemoryCopyFromBuffer(
reqMemory,
0,
intReport->Data,
bytes);
//
// Report how many bytes were copied.
//
WdfRequestSetInformation(request, bytes);
//
// Forward the request to the completion queue.
//
status = WdfRequestForwardToIoQueue(request, devCtx->CompletionQueue);
//
// Queue a work-item to complete the request.
//
WdfInterruptQueueWorkItemForIsr(FxInterrupt);
exit:
return intRecognized;
}
VOID
EvtInterruptWorkItem(
_In_ WDFINTERRUPT Interrupt,
_In_ WDFOBJECT Device
)
/*++
Routine Description:
This work item handler is triggered by the interrupt ISR.
Arguments:
WorkItem - framework work item object
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(Device);
WDFREQUEST request;
NTSTATUS status;
PDEV_CONTEXT devCtx;
BOOLEAN run, rerun;
devCtx = GetDevContext(WdfInterruptGetDevice(Interrupt));
WdfSpinLockAcquire(devCtx->WorkItemSpinLock);
if (devCtx->WorkItemInProgress) {
devCtx->WorkItemRerun = TRUE;
run = FALSE;
}
else {
devCtx->WorkItemInProgress = TRUE;
devCtx->WorkItemRerun = FALSE;
run = TRUE;
}
WdfSpinLockRelease(devCtx->WorkItemSpinLock);
if (run == FALSE) {
return;
}
do {
for (;;) {
//
// Complete all report requests in the completion queue.
//
request = NULL;
status = WdfIoQueueRetrieveNextRequest(devCtx->CompletionQueue,
&request);
if (!NT_SUCCESS(status) || (request == NULL)) {
break;
}
WdfRequestComplete(request, STATUS_SUCCESS);
}
WdfSpinLockAcquire(devCtx->WorkItemSpinLock);
if (devCtx->WorkItemRerun) {
rerun = TRUE;
devCtx->WorkItemRerun = FALSE;
}
else {
devCtx->WorkItemInProgress = FALSE;
rerun = FALSE;
}
WdfSpinLockRelease(devCtx->WorkItemSpinLock);
}
while (rerun);
}
VOID
EvtIoInternalDeviceControl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
NTSTATUS status;
DEVICE_CONTEXT devCtx;
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
switch (IoControlCode)
{
...
case IOCTL_READ_REPORT:
//
// Forward the request to the manual ReportQueue to be completed
// later by the interrupt work item.
//
status = WdfRequestForwardToIoQueue(Request, devCtx->ReportQueue);
break;
...
}
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, status);
}
}
Sincronizzazione di un interrupt Passive-Level
Per evitare deadlock, seguire queste linee guida durante la scrittura di un driver che implementa la gestione degli interrupt a livello passivo:
Se AutomaticSerialization è impostato su TRUE, non inviare richieste sincrone da una funzione di callback EvtInterruptDpc o EvtInterruptWorkItem .
Rilasciare il blocco di interrupt a livello passivo prima di completare le richieste di I/O.
Fornire EvtInterruptDisable, EvtInterruptEnable e EvtInterruptWorkItem in base alle esigenze.
Se il driver deve eseguire operazioni correlate all'interrupt in un contesto di thread arbitrario, ad esempio in un gestore di richieste, usare WdfInterruptTryToAcquireLock e WdfInterruptReleaseLock. Non chiamare WdfInterruptAcquireLock, WdfInterruptSynchronize, WdfInterruptEnable o WdfInterruptDisable da un contesto di thread arbitrario. Per un esempio di scenario di deadlock che può essere causato dalla chiamata a WdfInterruptAcquireLock da un contesto di thread arbitrario, vedere la sezione Osservazioni di WdfInterruptAcquireLock.
Se la chiamata a WdfInterruptTryToAcquireLock ha esito negativo, il driver può posticipare il lavoro correlato all'interrupt a un elemento di lavoro. In tale elemento di lavoro, il driver può acquisire in modo sicuro il blocco interrupt chiamando WdfInterruptAcquireLock. Per altre informazioni, vedere WdfInterruptTryToAcquireLock.
In un contesto di thread non arbitrario, ad esempio un elemento di lavoro, il driver può chiamare WdfInterruptAcquireLock o WdfInterruptSynchronize.
Per ulteriori informazioni sull'utilizzo dei blocchi di interrupt, vedere Sincronizzazione del codice interrupt.