共用方式為


支援 Passive-Level 中斷

從框架版本 1.11 開始,在 Windows 8 或更高版本的操作系統上運行的 Kernel-Mode 驅動程序框架(KMDF)和 User-Mode 驅動程序框架(UMDF)允許驅動程序建立需要被動層級處理的中斷物件。 如果驅動程式設定了被動層級中斷處理的中斷對象,架構會呼叫驅動程式的插斷服務例程 (ISR) 和其他 插斷物件事件回呼函式, 在 IRQL = PASSIVE_LEVEL,同時保留被動層級中斷鎖定。

如果您要在系統單晶片(SoC)平台上開發基於框架的驅動程式,您可以使用被動模式中斷,透過低速匯流排與晶片外裝置通訊,例如 I²C、SPI 或 UART。

否則,您應該使用需要在裝置 IRQL (DIRQL)處理的 中斷。 如果您的驅動程式支援訊息訊號中斷 (MSIs),您必須使用 DIRQL 中斷處理。 在 1.9 版和更早版本中,架構一律會在 IRQL = DIRQL 處理中斷。

本主題描述如何 建立服務,以及 同步處理 被動層級中斷。

建立 Passive-Level 中斷

若要建立被動層級中斷對象,驅動程式必須初始化 WDF_INTERRUPT_CONFIG 結構,並將它傳遞給 WdfInterruptCreate 方法。 在組態結構中,驅動程序應該:

  • PassiveHandling 成員設定為 TRUE。
  • 提供 EvtInterruptIsr 回呼函式,在被動層級上呼叫。
  • 選擇性地將 自動串行化 設定為 TRUE。 如果驅動程式將 AutomaticSerialization 設為 TRUE,則架構會同步處理中斷物件的 EvtInterruptDpcEvtInterruptWorkItem 回呼函式,以及中斷父物件下其他物件的回呼函式。
  • 驅動程式可以選擇提供 EvtInterruptWorkItem 回呼函式,以便在 IRQL = PASSIVE_LEVEL 時呼叫,或者提供 EvtInterruptDpc 回呼函式,以便在 IRQL = DISPATCH_LEVEL 時呼叫。

如需設定上述組態結構成員的詳細資訊,請參閱 WDF_INTERRUPT_CONFIG。 如需啟用和停用被動層級中斷的相關資訊,請參閱 啟用和停用中斷

處理 Passive-Level 中斷

EvtInterruptIsr 回呼函式,會在 IRQL = PASSIVE_LEVEL 且保持被動層級中斷鎖定時執行,通常會排程中斷工作項目或中斷 DPC,以便稍後處理中斷相關資訊。 架構型驅動程式會將工作專案或 DPC 例程實作為 EvtInterruptWorkItemEvtInterruptDpc 回呼函式。

若要排程執行 EvtInterruptWorkItem 回呼函式,驅動程式會從 EvtInterruptIsr 回呼函式內呼叫 WdfInterruptQueueWorkItemForIsr

若要排程執行 EvtInterruptDpc 回呼函式,驅動程式會從 EvtInterruptIsr 回呼函式內呼叫 WdfInterruptQueueDpcForIsr。 (回想一下,驅動程式的 EvtInterruptIsr 回呼函式可以呼叫 WdfInterruptQueueWorkItemForIsrWdfInterruptQueueDpcForIsr,但不能同時呼叫兩者。

大部分的驅動程式都會針對每種中斷類型使用單一 EvtInterruptWorkItemEvtInterruptDpc 回呼函式。 如果您的驅動程式為每個裝置構建多個框架中斷物件,請考慮針對每個中斷使用不同的 EvtInterruptWorkItemEvtInterruptDpc 回呼。

驅動程式通常會在其 EvtInterruptWorkItemEvtInterruptDpc 回呼函式中完成 I/O 要求。

下列程式碼範例說明一個使用被動層級中斷的驅動程式如何在其 EvtInterruptIsr 函式中排程 EvtInterruptWorkItem 回呼。

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);
    }
}

同步處理 Passive-Level 中斷

若要防止死結,請在撰寫實作被動層級中斷處理的驅動程式時,遵循下列指導方針:

如需使用中斷鎖定的詳細資訊,請參閱 同步中斷代碼