Udostępnij przez


Obsługa przerwań typu Passive-Level

Począwszy od struktury w wersji 1.11, sterowniki Kernel-Mode Driver Framework (KMDF) i User-Mode Driver Framework (UMDF) działające w systemie Windows 8 lub nowszym wersjach systemu operacyjnego mogą tworzyć obiekty przerwania, które wymagają obsługi na poziomie pasywnym. Jeśli sterownik konfiguruje obiekt przerwania na potrzeby obsługi przerwań na poziomie pasywnym, struktura wywołuje procedurę usługi przerwania sterownika (ISR) i inne funkcje wywołania zwrotnego zdarzeń przerwania obiektu w IRQL = PASSIVE_LEVEL przy zachowaniu blokady przerwania na poziomie pasywnym.

Jeśli opracowujesz sterownik oparty na frameworku dla platformy System na chipie (SoC), możesz użyć przerwań trybu pasywnego do komunikowania się z urządzeniem spoza SoC za pośrednictwem magistrali o niskiej szybkości, takiej jak I²C, SPI lub UART.

W przeciwnym razie należy użyć przerwań , które wymagają obsługi na poziomie IRQL (DIRQL) urządzenia. Jeśli sterownik obsługuje przerwania sygnalizowane komunikatami (MSI), należy użyć obsługi przerwań DIRQL. W wersjach 1.9 i starszych framework zawsze przetwarza przerwania w IRQL = DIRQL.

W tym temacie opisano sposób tworzenia, usługii synchronizowania przerwań na poziomie pasywnym.

Tworzenie przerwania Passive-Level

Aby utworzyć obiekt przerwania na poziomie pasywnym, sterownik musi zainicjować strukturę WDF_INTERRUPT_CONFIG i przekazać ją do metody WdfInterruptCreate. W strukturze konfiguracji sterownik powinien:

  • Dla elementu członkowskiego PassiveHandling ustaw wartość TRUE.
  • Podaj funkcję EvtInterruptIsr wywołania zwrotnego, która ma być wywoływana na poziomie pasywnym.
  • Opcjonalnie ustaw AutomatycznaSerializacja na TRUE. Jeśli sterownik ustawia AutomaticSerialization na TRUE, struktura synchronizuje wykonywanie funkcji wywołania zwrotnego obiektu przerwania EvtInterruptDpc lub EvtInterruptWorkItem z innymi funkcjami wywołania zwrotnego z obiektów znajdujących się pod obiektem nadrzędnym przerwania.
  • Opcjonalnie sterownik może podać funkcję wywołania zwrotnego EvtInterruptWorkItem, która ma być wywoływana na poziomie IRQL = PASSIVE_LEVEL, lub funkcję wywołania zwrotnego EvtInterruptDpc, która ma być wywoływana na poziomie IRQL = DISPATCH_LEVEL.

Aby uzyskać dodatkowe informacje na temat ustawiania powyższych elementów struktury konfiguracji, zobacz WDF_INTERRUPT_CONFIG. Aby uzyskać informacje na temat włączania i wyłączania przerwań na poziomie pasywnym, zobacz Włączanie i wyłączanie przerwań.

obsługa przerwania Passive-Level

Funkcja wywołania zwrotnego EvtInterruptIsr, która działa przy IRQL = PASSIVE_LEVEL z blokadą przerwań na poziomie pasywnym, zazwyczaj planuje element roboczy przerwania lub przerwanie DPC do przetwarzania informacji związanych z przerwaniem w późniejszym czasie. Sterowniki oparte na strukturze ramowej implementują procedury elementu roboczego lub DPC jako EvtInterruptWorkItem lub EvtInterruptDpc funkcje wywołania zwrotnego.

Aby zaplanować wykonywanie funkcji wywołania zwrotnego EvtInterruptWorkItem, sterownik wywołuje funkcję WdfInterruptQueueWorkItemForIsr z poziomu funkcji EvtInterruptIsr wywołania zwrotnego.

Aby zaplanować wykonywanie funkcji wywołania zwrotnego EvtInterruptDpc, sterownik wywołuje funkcję WdfInterruptQueueDpcForIsr z poziomu funkcji EvtInterruptIsr wywołania zwrotnego. (Pamiętaj, że funkcja wywołania zwrotnego EvtInterruptIsr może wywołać WdfInterruptQueueWorkItemForIsr lub WdfInterruptQueueDpcForIsr, ale nie obie jednocześnie).

Większość kierowców używa jednej funkcji wywołania zwrotnego EvtInterruptWorkItem lub EvtInterruptDpc dla każdego typu przerwania. Jeśli sterownik tworzy wiele obiektów przerwań struktury frameworku dla każdego urządzenia, rozważ użycie oddzielnego wywołania zwrotnego EvtInterruptWorkItem lub EvtInterruptDpc dla każdego przerwania.

Sterowniki zazwyczaj kończą żądania we/wy w swoich funkcjach wywołania EvtInterruptWorkItem lub EvtInterruptDpc.

Poniższy przykład kodu pokazuje, w jaki sposób sterownik korzystający z przerwań na poziomie pasywnym może zaplanować wywołanie zwrotne EvtInterruptWorkItem z poziomu swojej funkcji EvtInterruptIsr.

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

synchronizacja przerwania Passive-Level

Aby zapobiec zakleszczeniom, postępuj zgodnie z tymi wytycznymi podczas pisania sterownika, który implementuje obsługę przerwań na poziomie pasywnym:

Aby uzyskać więcej informacji na temat używania blokad przerwań, zobacz Synchronizowanie kodu przerwania.