Partilhar via


Suporte a interrupções Passive-Level

A partir da versão 1.11 do framework, os drivers Kernel-Mode Driver Framework (KMDF) e UMDF (User-Mode Driver Framework) em execução no Windows 8 ou versões posteriores do sistema operacional podem criar objetos de interrupção que exigem manipulação de nível passivo. Se o driver configurar um objeto de interrupção para tratamento de interrupção de nível passivo, a estrutura chamará a rotina de serviço de interrupção (ISR) do driver e outras funções de retorno de chamada de evento de objeto de interrupção em IRQL = PASSIVE_LEVEL enquanto mantém um bloqueio de interrupção de nível passivo.

Se estiveres a desenvolver um driver baseado numa estrutura para uma plataforma System on a Chip (SoC), podes usar interrupções em modo passivo para comunicar com um dispositivo fora do SoC através de um barramento de baixa velocidade, como I²C, SPI ou UART.

Caso contrário, deve-se usar interrupções que exigem manipulação no nível de IRQL (DIRQL) do dispositivo. Se o seu driver suportar interrupções sinalizadas por mensagem (MSIs), você deve usar o tratamento de interrupção DIRQL. Nas versões 1.9 e anteriores, o framework sempre processa interrupções em IRQL = DIRQL.

Este tópico descreve como criar, atender e sincronizar interrupções de nível passivo.

Criando uma interrupção de nível passivo

Para criar um objeto de interrupção de nível passivo, um driver deve inicializar uma estrutura de WDF_INTERRUPT_CONFIG e passá-la para o método WdfInterruptCreate . Na estrutura de configuração, o driver deve:

  • Defina o membro PassiveHandling como TRUE.
  • Forneça uma função de retorno de chamada EvtInterruptIsr para ser chamada ao nível passivo.
  • Opcionalmente, defina o AutomaticSerialization como TRUE. Se o driver definir AutomaticSerialization como TRUE, a estrutura sincronizará a execução das funções de retorno de chamada EvtInterruptDpc ou EvtInterruptWorkItem do objeto de interrupção com funções de retorno de chamada de outros objetos que estão abaixo do objeto pai da interrupção.
  • Opcionalmente, o driver pode fornecer uma função de retorno de chamada EvtInterruptWorkItem, que será chamada em IRQL = PASSIVE_LEVEL, ou uma função de retorno de chamada EvtInterruptDpc, que será chamada em IRQL = DISPATCH_LEVEL.

Para obter informações adicionais sobre como definir os membros acima da estrutura de configuração, consulte WDF_INTERRUPT_CONFIG. Para obter informações sobre como habilitar e desabilitar interrupções de nível passivo, consulte Habilitando e desabilitando interrupções.

Atendimento a uma interrupção Passive-Level

A função de callback EvtInterruptIsr, que é executada em IRQL = PASSIVE_LEVEL mantendo o bloqueio de interrupção de nível passivo, normalmente agenda uma tarefa de interrupção ou DPC de interrupção para processar informações relacionadas à interrupção num momento posterior. Controladores baseados em estrutura implementam rotinas de item de trabalho ou DPC como funções callback EvtInterruptWorkItem ou EvtInterruptDpc.

Para agendar a execução de uma função de retorno de chamada EvtInterruptWorkItem, um driver chama WdfInterruptQueueWorkItemForIsr a partir de dentro da função de retorno de chamada EvtInterruptIsr.

Para agendar a execução da função de retorno de chamada EvtInterruptDpc, o driver chama WdfInterruptQueueDpcForIsr dentro da função de retorno de chamada EvtInterruptIsr. (Lembre-se de que a função de retorno de chamada EvtInterruptIsr de um driver pode chamar WdfInterruptQueueWorkItemForIsr ou WdfInterruptQueueDpcForIsr, mas não ambas.)

A maioria dos drivers usa uma única função de retorno de chamada EvtInterruptWorkItem ou EvtInterruptDpc para cada tipo de interrupção. Se o seu driver criar vários objetos de interrupção de estrutura para cada dispositivo, considere usar um callback EvtInterruptWorkItem ou EvtInterruptDpc separado para cada interrupção.

Os drivers normalmente concluem as solicitações de E/S nas suas funções de retorno de chamada EvtInterruptWorkItem ou EvtInterruptDpc.

O exemplo de código a seguir demonstra como um driver que utiliza interrupções de nível passivo pode programar um retorno de chamada EvtInterruptWorkItem a partir da sua função 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);
    }
}

Sincronização de uma interrupção de Passive-Level

Para evitar o deadlock, siga estas diretrizes ao escrever um driver que implementa o tratamento de interrupção de nível passivo:

Para obter mais informações sobre como usar bloqueios de interrupção, consulte Sincronizando código de interrupção.