Condividi tramite


Supporto di interrupt Passive-Level

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:

Per ulteriori informazioni sull'utilizzo dei blocchi di interrupt, vedere Sincronizzazione del codice interrupt.