重复使用 DMA 事务对象
在驱动程序处理与 DMA 事务关联的所有 DMA 传输后,驱动程序可以删除或重用事务对象。 通常,驱动程序的 EvtInterruptDpc 回调函数通过调用 WdfObjectDelete 删除 事务对象。 随后,当驱动程序创建新的 DMA 事务时,它会调用 WdfDmaTransactionCreate 来创建新的事务对象。
但是,有时,驱动程序重用事务对象是有益的。 在这种情况下,驱动程序调用 WdfDmaTransactionRelease 而不是 WdfObjectDelete。
例如,假设驱动程序和设备必须在计算机内存资源不足时运行。 若要处理此内存问题,驱动程序可以使用以下过程:
驱动程序的 EvtDriverDeviceAdd 回调函数可以调用 WdfDmaTransactionCreate 来创建一个或多个事务对象。 驱动程序将句柄保存到这些事务对象。
每次驱动程序准备好创建和初始化新事务时,它都会调用 WdfDmaTransactionCreate。 如果此方法返回STATUS_INSUFFICIENT_RESOURCES,驱动程序可以使用其中一个存储的事务对象。
如果驱动程序使用其中一个存储的事务对象,则应在事务完成时重用事务对象,而不是删除它。 驱动程序通过调用 WdfDmaTransactionRelease 而不是 WdfObjectDelete 来设置事务对象以供重复使用。
支持适用于 DMA 设备的电源管理
DMA 启用程序对象定义一组可选的事件回调函数,DMA 设备的驱动程序可以使用这些函数来管理进入和传出设备工作 (D0) 状态的转换。
每次 DMA 设备进入其工作状态,并在框架调用驱动程序的 EvtDeviceD0Entry 回调函数后,框架将按照其列出的顺序调用以下 DMA 回调函数:
- EvtDmaEnablerFil: 分配设备的 DMA 缓冲区;
- EvtDmaEnablerEnabl: 在设备进入其工作 (D0) 状态后启用设备的 DMA 功能;
- EvtDmaEnablerSelfManagedIoStar: 启动 DMA 设备的自我管理 I/O 操作;
每次 DMA 设备离开其工作状态时,在框架调用驱动程序的 EvtDeviceD0Exit 回调函数之前,框架会按照列出的顺序调用以下 DMA 回调函数:
- EvtDmaEnablerSelfManagedIoSto: 停止 DMA 设备的自托管 I/O 操作;
- EvtDmaEnablerDisabl: 禁用设备的 DMA 功能,然后设备离开其工作 (D0) 状态;
- EvtDmaEnablerFlus: 解除分配设备的 DMA 缓冲区;
使用常用缓冲区
DMA 设备的驱动程序有时必须分配设备和驱动程序都可以访问的缓冲区空间。 例如,设备可能会将传输信息(如字节计数)写入此缓冲区空间,驱动程序可以读取它以确定传输的字节数。 这种类型的缓冲区空间称为 通用缓冲区。
若要分配公共缓冲区,驱动程序的 EvtDriverDeviceAdd 回调函数:
- 调用 WdfDmaEnablerCreate 来创建 DMA 启用程序对象;
- 调用 WdfCommonBufferCreate 或 WdfCommonBufferCreateWithConfig 来创建缓冲区;
- 调用 WdfCommonBufferGetAlignedLogicalAddress 以获取设备可以访问的缓冲区的逻辑地址;
- 调用 WdfCommonBufferGetAlignedVirtualAddress 以获取缓冲区的虚拟地址,驱动程序可以访问该地址;
以下代码示例演示 KMDF 驱动程序如何分配公共缓冲区空间:
// Allocate common buffer for building writes
DevExt->WriteCommonBufferSize =
sizeof( DMA_TRANSFER_ELEMENT) * DevExt->WriteTransferElements;
status = WdfCommonBufferCreate( DevExt->DmaEnabler,
DevExt->WriteCommonBufferSize,
WDF_NO_OBJECT_ATTRIBUTES,
&DevExt->WriteCommonBuffer );
if (!NT_SUCCESS(status)) {
. . . //Error-handling code omitted
}
DevExt->WriteCommonBufferBase =
WdfCommonBufferGetAlignedVirtualAddress(
DevExt->WriteCommonBuffer);
DevExt->WriteCommonBufferBaseLA =
WdfCommonBufferGetAlignedLogicalAddress(
DevExt->WriteCommonBuffer);
RtlZeroMemory( DevExt->WriteCommonBufferBase, DevExt->WriteCommonBufferSize);
如果驱动程序在调用 WdfDmaEnablerCreate 之前调用 WdfDeviceSetAlignmentRequirement,则 WdfDmaEnablerCreate 创建的缓冲区与驱动程序指定给 WdfDeviceSetAlignmentRequirement 的内存地址边界对齐。 否则,通用缓冲区与字地址边界对齐。 或者,驱动程序可以调用 WdfCommonBufferCreateWithConfig 来指定单个缓冲区的对齐方式。
若要获取驱动程序已分配的公共缓冲区的长度,驱动程序可以调用 WdfCommonBufferGetLength。
当驱动程序使用完公共缓冲区时,驱动程序将调用 WdfObjectDelete。
在 KMDF 驱动程序中调试DMA
以下命令在windbg工具可帮助调试支持 DMA 的基于框架的驱动程序:
驱动程序验证程序:包括特定的验证测试,用于检测各种 DMA 操作的不当使用;
!dma 内核调试器扩展显示有关由驱动程序验证程序验证的 DMA 子系统和 DMA 设备驱动程序的信息。;
内核模式驱动程序框架扩展包含以下特定于 DMA 的命令:
!wdfcommonbuffer:转储有关给定公共缓冲区对象的信息;
!wdfdmaenabler:转储有关特定 DMA 启用程序对象及其事务和常见缓冲区对象的信息;
!wdfdmaenablers:列出所有 DMA 启用程序及其事务和常见缓冲区对象;
!wdfdmatransaction:转储有关给定事务对象的信息;