支持被动级别中断

从框架版本 1.11 开始,Kernel-Mode Driver Framework (KMDF) 和 User-Mode Driver Framework (在操作系统的 Windows 8 或更高版本上运行的 UMDF) 驱动程序可以创建需要被动级别处理的中断对象。 如果驱动程序为被动级别中断处理配置了中断对象,则框架在持有被动级别中断锁时调用驱动程序的中断服务例程 (ISR) 和其他 中断对象事件回调函数 (IRQL = PASSIVE_LEVEL)。

如果要为芯片上的系统开发基于框架的驱动程序 (SoC) 平台,则可以使用被动模式中断通过低速总线(如 I²C、SPI 或 UART)与 SoC 外部设备通信。

否则,应使用 需要在设备的 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中断

若要防止死锁,请在编写实现被动级别中断处理的驱动程序时遵循以下准则:

有关使用中断锁的详细信息,请参阅 同步中断代码