Supporto di interruzioni Passive-Level

A partire da framework versione 1.11, Kernel-Mode Driver Framework (KMDF) e driver User-Mode Driver Framework (UMDF) in esecuzione in Windows 8 o versioni successive del sistema operativo possono creare oggetti di interruzione che richiedono la gestione a livello passivo. Se il driver configura un oggetto di interruzione per la gestione degli interruzioni a livello passivo, il framework chiama la routine del servizio di interruzione del driver (ISR) e altre funzioni di callback degli eventi dell'oggetto di interruzione a livello passivo in IRQL = PASSIVE_LEVEL mantenendo un blocco di interruzioni a livello passivo.

Se si sviluppa un driver basato su framework per una piattaforma Chip (SoC), è possibile usare interruzioni 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, è consigliabile usare gli interruzioni che richiedono la gestione in IRQL (DIRQL) del dispositivo . Se il driver supporta le interruzioni segnalate dai messaggi, è necessario usare la gestione degli interruzioni DIRQL. Nelle versioni 1.9 e versioni precedenti il framework elabora sempre gli interruzioni in IRQL = DIRQL.

Questo argomento descrive come creare, servizio e sincronizzare gli interruzioni a livello passivo.

Creazione di un interruzione di 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.
  • Specificare una funzione di callback evtInterruptIsr da chiamare a livello passivo.
  • Facoltativamente, impostare l'opzione AutomaticSerialization su TRUE. Se il driver imposta AutomaticSerialization su TRUE, il framework sincronizza l'esecuzione dell'oggetto interrupt EvtInterruptDpc o EvtInterruptWorkItem 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 o 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 degli interruzioni a livello passivo, vedere Abilitazione e disabilitazione degli interruzioni.

Manutenzione di un interruzione di Passive-Level

La funzione di callback evtInterruptIsr , che viene eseguita in IRQL = PASSIVE_LEVEL con il blocco di interruzione a livello passivo, in genere pianifica un elemento di lavoro di interruzione o interrompe DPC per elaborare le informazioni correlate agli interruzioni in un secondo momento. I driver basati su framework implementano routine di elemento di lavoro o DPC come EvtInterruptWorkItem o EvtInterruptDpc funzioni di callback.

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. Si ricordi 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 interruzione. Se il driver crea più oggetti di interruzione del framework per ogni dispositivo, è consigliabile usare un callback evtInterruptWorkItem o EvtInterruptDpc per ogni interruzione.

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 usa interruzioni a livello passivo potrebbe pianificare un callback EvtInterruptWorkItem dall'interno della 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 Passive-Level di interruzione

Per evitare deadlock, seguire queste linee guida durante la scrittura di un driver che implementa la gestione degli interruzioni a livello passivo:

Per altre informazioni sull'uso di blocchi di interruzione, vedere Sincronizzazione del codice di interruzione.