编程 DMA 硬件

[仅适用于 KMDF]

本主题介绍总线主 DMA 设备的 KMDF 驱动程序在其 EvtProgramDma 事件回调函数中通常提供的功能。 如果驱动程序使用框架的 DMA 支持,则必须提供此回调。 此信息也适用于适用于系统模式 DMA 设备且具有硬件中断的 KMDF 驱动程序。

在 IRQL = DISPATCH_LEVEL 调用的 EvtProgramDma 回调函数将设备编程为启动 DMA 传输。 此回调函数的输入参数提供传输的方向(输入或输出)和散点/收集列表。 如果传输由单个数据包组成,则散/聚列表包含单个元素。

EvtProgramDma 回调函数使用驱动程序的 EvtDevicePrepareHardware 回调函数收到的硬件资源来编程设备。 如果 EvtProgramDma 回调函数成功编程硬件,则返回 TRUE

硬件完成 DMA 传输后,通常会发出中断,系统调用驱动程序的 EvtInterruptIsr 回调函数。 驱动程序的 EvtInterruptIsr 回调函数通常:

  • 清除硬件中断。

  • 如果需要,保存中断的上下文信息。 回调函数返回后,由于系统降低了 IRQL,因此该信息可能会丢失,因为降低 IRQL 允许其他中断发生。

  • 调用 WdfInterruptQueueDpcForIsr 来调度 EvtInterruptDpc 回调函数。

EvtInterruptDpc 回调函数通过使用 EvtInterruptIsr 回调函数保存的上下文信息完成 DMA 传输

如果 EvtProgramDma 回调函数检测到错误,驱动程序可以停止事务。

若要在驱动程序检测到错误时停止事务, EvtProgramDma 回调函数必须:

  1. 调用 WdfDmaTransactionDmaCompletedFinal

  2. 调用 WdfObjectDelete 以删除 DMA 事务对象,或调用 WdfDmaTransactionRelease 释放并重复使用 DMA 事务对象。

  3. 如果事务与框架请求对象关联,请重新排队 I/O 请求完成 I/O 请求。 若要检索请求的句柄,驱动程序可以调用 WdfDmaTransactionGetRequest

  4. 返回 FALSE

下面的代码示例演示了步骤 1 和 4,取自 PLX9x5x 示例的 EvtProgramDma 回调函数,用于 Read.c 文件中的读取请求。

    // If errors occur in the EvtProgramDma callback,
    // release the DMA transaction object and complete the request.

    if (errors) {
        NTSTATUS status;

        //
        // Must abort the transaction before deleting.
        //
        (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status);
        ASSERT(NT_SUCCESS(status));

        PLxReadRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE );
        TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,
                    "<-- PLxEvtProgramReadDma: errors ****");
        return FALSE;
    }

此示例调用 PLxReadRequestComplete 函数来执行步骤 2 和 3:

VOID
PLxReadRequestComplete(
    IN WDFDMATRANSACTION  DmaTransaction,
    IN NTSTATUS           Status
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    WDFREQUEST         request;
    size_t             bytesTransferred;

    //
    // Get the associated request from the transaction.
    //
    request = WdfDmaTransactionGetRequest(DmaTransaction);

    ASSERT(request);

    //
    // Get the final bytes transferred count.
    //
    bytesTransferred =  WdfDmaTransactionGetBytesTransferred( DmaTransaction );

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
                "PLxReadRequestComplete:  Request %p, Status %!STATUS!, "
                "bytes transferred %d\n",
                 request, Status, (int) bytesTransferred );

    WdfDmaTransactionRelease(DmaTransaction);

    //
    // Complete this Request.
    //
    WdfRequestCompleteWithInformation( request, Status, bytesTransferred);

}