Aracılığıyla paylaş


Passive-Level Kesme Desteği

Çerçeve sürüm 1.11'den başlayarak, işletim sisteminin Windows 8 veya sonraki sürümlerinde çalışan Kernel-Mode Driver Framework (KMDF) ve User-Mode Driver Framework (UMDF) sürücüleri pasif düzeyde işleme gerektiren kesme nesneleri oluşturabilir. Eğer sürücü, pasif düzeyde kesme işleme için bir kesme nesnesi yapılandırırsa, framework, pasif düzeyde bir kesme kilidi tutarken sürücünün kesme hizmeti rutini (ISR) ve diğer kesme nesnesi olay geri çağırma işlevlerini IRQL = PASSIVE_LEVEL seviyesinde çağırır.

Yonga Üzerinde Sistem (SoC) platformu için çerçeve tabanlı bir sürücü geliştiriyorsanız, I²C, SPI veya UART gibi düşük hızlı bir veri yolu üzerinden SoC dışı bir cihazla iletişim kurmak için pasif mod kesmelerini kullanabilirsiniz.

Aksi takdirde, cihazın IRQL'inde (DIRQL) işleme gerektiren kesmeleri kullanmanız gerekir. Sürücünüz ileti sinyalli kesmeleri (MSI) destekliyorsa, DIRQL kesme işlemeyi kullanmanız gerekir. 1.9 ve önceki sürümlerde framework her zaman IRQL = DIRQL'de kesmeleri işleme alır.

Bu konu, pasif düzey kesmelerin nasıl oluşturulacağını, servis edileceğini ve eşitleneceğini açıklar.

Pasif Düzey Kesmesi Oluşturma

Pasif düzeyde kesme nesnesi oluşturmak için, bir sürücünün bir WDF_INTERRUPT_CONFIG yapısını başlatması ve onu WdfInterruptCreate yöntemine geçirmesi gerekir. Yapılandırma yapısında sürücü şunları yapmalıdır:

  • PassiveHandling üyesini TRUE olarak ayarlayın.
  • Pasif düzeyde çağrılmak üzere bir EvtInterruptIsr geri çağırma işlevi sağlayın.
  • İsteğe bağlı olarak AutomaticSerialization değerini TRUE olarak ayarlayın. Sürücü AutomaticSerialization değerini TRUE olarak ayarlarsa, çerçeve kesme nesnesinin EvtInterruptDpc veya EvtInterruptWorkItem geri çağırma işlevlerinin yürütülmesini kesmenin üst nesnesinin altındaki diğer nesnelerden gelen geri çağırma işlevleriyle eşitler.
  • İsteğe bağlı olarak, sürücü IRQL = PASSIVE_LEVEL'de çağrılmak üzere bir EvtInterruptWorkItem geri çağırma işlevi veya IRQL = DISPATCH_LEVEL'de çağrılmak üzere bir EvtInterruptDpc geri çağırma işlevi sağlayabilir.

Yapılandırma yapısının yukarıdaki üyelerini ayarlama hakkında ek bilgi için bkz. WDF_INTERRUPT_CONFIG. Pasif düzeyde kesmeleri etkinleştirme ve devre dışı bırakma hakkında bilgi için bkz. Kesmeleri Etkinleştirme ve Devre Dışı Bırakma.

Passive-Level Kesmesini Giderme

IRQL = PASSIVE_LEVEL seviyesinde, pasif düzeyde kesme kilidi tutularak çalışan EvtInterruptIsr geri çağırma fonksiyonu, genellikle kesmeyle ilgili bilgileri daha sonra işlemek için bir kesme iş öğesi veya kesme DPC'si planlar. Çerçeve tabanlı sürücüler, evtInterruptWorkItem veya EvtInterruptDpc geri çağırma işlevleri olarak iş öğesi veya DPC yordamlarını uygular.

Bir EvtInterruptWorkItem geri çağırma işlevinin yürütülmesini zamanlamak için, bir sürücü EvtInterruptIsr geri çağırma işlevi üzerinden WdfInterruptQueueWorkItemForIsr'ı çağırır.

Bir EvtInterruptDpc geri çağırma işlevinin yürütülmesini zamanlamak için, bir sürücü EvtInterruptIsr geri çağırma işlevinin içinden WdfInterruptQueueDpcForIsr'ı çağırır. (Bir sürücünün EvtInterruptIsr geri çağırma işlevinin WdfInterruptQueueWorkItemForIsr veya WdfInterruptQueueDpcForIsr'ı çağırabildiğini ancak ikisini birden çağıramadığını hatırlayın.)

Sürücülerin çoğu her kesme türü için tek bir EvtInterruptWorkItem veya EvtInterruptDpc geri çağırma işlevi kullanır. Sürücünüz her cihaz için birden çok framework kesme nesnesi oluşturuyorsa, her kesme için ayrı bir EvtInterruptWorkItem veya EvtInterruptDpc geri çağırması kullanmayı düşünebilirsiniz.

Sürücüler genellikle EvtInterruptWorkItem veya EvtInterruptDpc geri çağırma işlevlerinde G/Ç isteklerini tamamlar.

Aşağıdaki kod örneği, pasif düzey kesmeleri kullanan bir sürücünün EvtInterruptIsr işlevinin içinden bir EvtInterruptWorkItem geri çağırma işlevini nasıl zamanlayabileceğini gösterir.

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 Kesmesini Senkronize Etme

Kilitlenmeyi önlemek için pasif düzeyde kesme işleme uygulayan bir sürücü yazarken şu yönergeleri izleyin:

Kesme kilitlerini kullanma hakkında daha fazla bilgi için bkz. Kesme Kodunu Eşitleme.