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

}