Passive-Level 인터럽트 지원

프레임워크 버전 1.11부터 운영 체제의 Windows 8 이상 버전에서 실행되는 KMDF(드라이버 프레임워크) 및 User-Mode 드라이버 프레임워크(UMDF) 드라이버를 Kernel-Mode 수동 수준 처리가 필요한 인터럽트 개체를 만들 수 있습니다. 드라이버가 수동 수준 인터럽트 처리를 위해 인터럽트 개체를 구성하는 경우 프레임워크는 수동 수준 인터럽트 잠금을 유지하면서 IRQL = PASSIVE_LEVEL 드라이버의 ISR(인터럽트 서비스 루틴) 및 기타 인터럽트 개체 이벤트 콜백 함수 를 호출합니다.

SoC(System on a Chip) 플랫폼에 대한 프레임워크 기반 드라이버를 개발하는 경우 수동 모드 인터럽트를 사용하여 I²C, SPI 또는 UART와 같은 저속 버스를 통해 Off-SoC 디바이스와 통신할 수 있습니다.

그렇지 않으면 디바이스의 IRQL(DIRQL)에서 처리해야 하는 인터럽트 를 사용해야 합니다. 드라이버가 MSI(메시지 신호 인터럽트)를 지원하는 경우 DIRQL 인터럽트 처리를 사용해야 합니다. 버전 1.9 이하에서 프레임워크는 항상 IRQL = DIRQL에서 인터럽트 처리합니다.

이 항목에서는 수동 수준 인터럽트 만들기, 서비스동기화 방법에 대해 설명합니다.

Passive-Level 인터럽트 만들기

수동 수준 인터럽트 개체를 만들려면 드라이버는 WDF_INTERRUPT_CONFIG 구조를 초기화하고 WdfInterruptCreate 메서드에 전달해야 합니다. 구성 구조에서 드라이버는 다음을 수행해야 합니다.

  • PassiveHandling 멤버를 TRUE로 설정합니다.
  • 수동 수준에서 호출할 EvtInterruptIsr 콜백 함수를 제공합니다.
  • 필요에 따라 AutomaticSerialization 을 TRUE로 설정합니다. 드라이버가 AutomaticSerialization 을 TRUE로 설정하면 프레임워크는 인터럽트 개체의 EvtInterruptDpc 또는 EvtInterruptWorkItem 콜백 함수의 실행을 인터럽트의 부모 개체 아래에 있는 다른 개체의 콜백 함수와 동기화합니다.
  • 필요에 따라 드라이버는 IRQL = PASSIVE_LEVEL 호출할 EvtInterruptWorkItem 콜백 함수 또는 IRQL = DISPATCH_LEVEL 호출할 EvtInterruptDpc 콜백 함수를 제공할 수 있습니다.

구성 구조의 위의 멤버를 설정하는 방법에 대한 자세한 내용은 WDF_INTERRUPT_CONFIG 참조하세요. 수동 수준 인터럽트 사용 및 비활성화에 대한 자세한 내용은 인터 럽트 사용 및 비활성화를 참조하세요.

Passive-Level 인터럽트 서비스

IRQL = PASSIVE_LEVEL 수동 수준 인터럽트 잠금이 유지된 상태에서 실행되는 EvtInterruptIsr 콜백 함수는 일반적으로 인터럽트 작업 항목 또는 인터럽트 DPC를 예약하여 나중에 인터럽트 관련 정보를 처리합니다. 프레임워크 기반 드라이버는 작업 항목 또는 DPC 루틴을 EvtInterruptWorkItem 또는 EvtInterruptDpc 콜백 함수로 구현합니다.

EvtInterruptWorkItem 콜백 함수의 실행을 예약하기 위해 드라이버는 EvtInterruptIsr 콜백 함수 내에서 WdfInterruptQueueWorkItemForIsr를 호출합니다.

EvtInterruptDpc 콜백 함수의 실행을 예약하기 위해 드라이버는 EvtInterruptIsr 콜백 함수 내에서 WdfInterruptQueueDpcForIsr를 호출합니다. (드라이버의 EvtInterruptIsr 콜백 함수는 WdfInterruptQueueWorkItemForIsr 또는 WdfInterruptQueueDpcForIsr를 호출할 수 있지만 둘 다 호출할 수는 없습니다.)

대부분의 드라이버는 각 인터럽트 유형에 대해 단일 EvtInterruptWorkItem 또는 EvtInterruptDpc 콜백 함수를 사용합니다. 드라이버가 각 디바이스에 대해 여러 프레임워크 인터럽트 개체를 만드는 경우 각 인터럽트에 대해 별도의 EvtInterruptWorkItem 또는 EvtInterruptDpc 콜백을 사용하는 것이 좋습니다.

드라이버는 일반적으로 EvtInterruptWorkItem 또는 EvtInterruptDpc 콜백 함수에서 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 인터럽트 동기화

교착 상태를 방지하려면 수동 수준 인터럽트 처리를 구현하는 드라이버를 작성할 때 다음 지침을 따릅니다.

인터럽트 잠금 사용에 대한 자세한 내용은 인터 럽트 코드 동기화를 참조하세요.