Suporte a interrupções de Passive-Level

A partir da versão 1.11 da estrutura, os drivers KMDF (Kernel-Mode Driver Framework) e UMDF (User-Mode Driver Framework) em execução em Windows 8 ou versões posteriores do sistema operacional podem criar objetos de interrupção que exigem tratamento 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 ISR (rotina de serviço de interrupção) do driver e outras funções de retorno de chamada de evento de objeto de interrupção em IRQL = PASSIVE_LEVEL mantendo um bloqueio de interrupção de nível passivo.

Se você estiver desenvolvendo um driver baseado em estrutura para uma plataforma SoC (System on a Chip), poderá usar interrupções de modo passivo para se comunicar com um dispositivo fora do SoC em um barramento de baixa velocidade, como I²C, SPI ou UART.

Caso contrário, você deve usar interrupções que exigem tratamento no DIRQL (IRQL) do dispositivo . Se o driver der suporte a MSIs (interrupções sinalizadas por mensagem), você deverá usar o tratamento de interrupção DIRQL. Nas versões 1.9 e anteriores, a estrutura 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 Passive-Level

Para criar um objeto de interrupção de nível passivo, um driver deve inicializar uma estrutura 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 , a ser chamada no nível passivo.
  • Opcionalmente, defina a 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 sob o objeto pai da interrupção.
  • Opcionalmente, o driver pode fornecer uma função de retorno de chamada EvtInterruptWorkItem , a ser chamada em IRQL = PASSIVE_LEVEL ou uma função de retorno de chamada EvtInterruptDpc , a 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.

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

A função de retorno de chamada EvtInterruptIsr , que é executada em IRQL = PASSIVE_LEVEL com o bloqueio de interrupção de nível passivo mantido, normalmente agenda um item de trabalho de interrupção ou interrompe o DPC para processar informações relacionadas à interrupção posteriormente. Os drivers baseados em estrutura implementam rotinas de item de trabalho ou DPC como funções de retorno de chamada EvtInterruptWorkItem ou EvtInterruptDpc .

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

Para agendar a execução de uma função de retorno de chamada EvtInterruptDpc , um driver chama WdfInterruptQueueDpcForIsr de 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 ambos.)

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

Normalmente, os drivers concluem solicitações de E/S em suas funções de retorno de chamada EvtInterruptWorkItem ou EvtInterruptDpc .

O exemplo de código a seguir demonstra como um driver que usa interrupções de nível passivo pode agendar um retorno de chamada EvtInterruptWorkItem de dentro de 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);
    }
}

Sincronizando uma interrupção de Passive-Level

Para evitar deadlock, siga estas diretrizes ao escrever um driver que implementa a manipulação 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.