WDF驱动开发-DMA(三)

重复使用 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:转储有关给定事务对象的信息;

相关推荐
hhhhhhh_hhhhhh_2 小时前
rk3568制冷项目驱动开发流程汇总(只适用于部分模块CIF DVP等,自用)
驱动开发
icy、泡芙4 小时前
T527-----音频调试
linux·驱动开发·音视频
7yewh15 小时前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux
疯狂飙车的蜗牛1 天前
从零玩转CanMV-K230(4)-小核Linux驱动开发参考
linux·运维·驱动开发
嵌入式进阶行者1 天前
【驱动开发初级】内核模块静态和动态添加功能的步骤
驱动开发
逝灮2 天前
【蓝桥杯——物联网设计与开发】拓展模块3 - 温度传感器模块
驱动开发·stm32·单片机·嵌入式硬件·物联网·蓝桥杯·温度传感器
__NULL__USER3 天前
petalinux-adi ---添加AD9361驱动(二)
linux·驱动开发
7yewh3 天前
嵌入式驱动RK3566 HDMI eDP MIPI 背光 屏幕选型与调试提升篇-eDP屏
linux·arm开发·驱动开发·嵌入式硬件·嵌入式linux·rk·edp
少年、潜行4 天前
树莓派3B+驱动开发(8)- i2c控制PCF8591
驱动开发·树莓派·3b+
千千道5 天前
深入理解 Linux 内核启动流程
linux·arm开发·驱动开发