支援Passive-Level中斷

從架構 1.11 版開始,Kernel-Mode Driver Framework (KMDF) 和 User-Mode Driver Framework (UMDF) 作業系統 Windows 8上執行的驅動程式,可以建立需要被動層級處理的中斷物件。 如果驅動程式為被動層級中斷處理設定中斷物件,架構會在 IRQL = PASSIVE_LEVEL同時保留被動層級中斷鎖定時,呼叫驅動程式的插斷服務常式 (ISR) 和其他 中斷物件事件回 呼函式。

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

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

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

建立Passive-Level中斷

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

  • PassiveHandling 成員設定為 TRUE。
  • 提供 EvtInterruptIsr 回呼函式,以在被動層級呼叫。
  • 選擇性地將 AutomaticSerialization 設定為 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中斷

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

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