赞
踩
在早期,是按照基于包或者基于流的方式来描述DMA的,不过这个描述可能不准确,故在Vista之后修改为使用数据包/使用公共缓冲区的系统DMA。
简单的解释一下基于包和基于流的说法的原因,数据包是指一个个基于一定大小的数据块,例如设定4096大小的内存页面,如果我们使用基于包的方式传输5000个字节,那么就是两个数据包,第一个数据包为4096字节,第二个数据包是904字节,实际传输两个数据包;如果使用基于流的,那么流传输每次回来1个数据包,缓冲区会根据自身的大小从里面拷贝数据,那么系统缓冲区先从第一个数据包中拷贝4096个字节,再从第二个数据包中拷贝906个字节。
在上面的描述中,我们会发现基于流的数据包对于数据利用率和容忍度更高一些。
使用系统 DMA 控制器的自动初始化模式的驱动程序必须为可以执行 DMA 传输的缓冲区分配内存。驱动程序调用 AllocateCommonBuffer 来获取此缓冲区,通常来自处理IRP_MN_START_DEVICE请求的 DispatchPnP 例程。 下图显示了驱动程序如何分配缓冲区并将其虚拟地址范围映射到系统物理内存。
如上图所示,驱动程序执行以下步骤为系统 DMA 分配缓冲区:
1. 驱动程序调用 AllocateCommonBuffer,将指针传递到 IoGetDmaAdapter 返回的适配器对象,以及为其缓冲区请求的长度(以字节为单位)。 若要以经济方式使用内存,缓冲区的输入 Length 值应小于或等于 PAGE_SIZE或应是PAGE_SIZE的整数倍数;
2. 如果 AllocateCommonBuffer 返回 NULL 指针,驱动程序应释放已声明的任何系统资源,并返回STATUS_INSUFFICIENT_RESOURCES以响应 IRP_MN_START_DEVICE 请求;否则, AllocateCommonBuffer 在系统虚拟地址空间中分配请求的内存量,并返回指向该缓冲区的两种不同类型的指针:
驱动程序应将这些指针存储在设备扩展或其他驱动程序分配的驻留内存中;
3. 驱动程序调用 IoAllocateMdl 为缓冲区分配 MDL。 驱动程序传递 AllocateCommonBuffer 返回的缓冲区的 VirtualAddress 及其缓冲区的 Length 以分配 MDL;
4. 驱动程序使用 IoAllocateMdl 返回的指针调用 MmBuildMdlForNonPagedPool,以将其驻留缓冲区的虚拟地址范围映射到系统物理内存;
分配公共缓冲区并映射其虚拟地址范围后,从属设备的驱动程序可以开始处理请求 DMA 传输的 IRP。 为此,驱动程序调用以下常规支持例程序列:
IoGetDmaAdapter 返回的适配器对象指针是除 RtlMoveMemory 以外的每个支持例程的必需参数。
各个驱动程序在不同点调用此支持例程序列,具体取决于实现每个驱动程序以为其设备提供服务的方式。 例如,一个驱动程序的 StartIo 例程可能会调用 AllocateAdapterChannel,另一个驱动程序可能会从从驱动程序创建的互锁队列中删除 IRP 的例程发出此调用,另一个驱动程序可能在其从属 DMA 设备指示它已准备好传输数据时发出此调用。
驱动程序在其 DispatchRead 或 DispatchWrite 例程 (或任何其他处理 DMA 传输的调度例程之后调用 AllocateAdapterChannel之前) 需要检查 IRP 参数的有效性,可能已将一个或多个 IRP 排队到另一个驱动程序例程进行进一步处理,如果适用可能加载其公共缓冲区以及要传输的数据。
调用 AllocateAdapterChannel 的 驱动程序例程必须在 IRQL=DISPATCH_LEVEL 执行。 AllocateAdapterChannel 例程将驱动程序的 AdapterControl 例程排入队列,该例程在系统 DMA 控制器分配给此驱动程序后运行,并为驱动程序的 DMA 操作分配了一组映射寄存器。
输入时, 向 AdapterControl 例程提供指向设备对象的指针和在 调用 AllocateAdapterChannel 中传递的上下文,以及分配的映射寄存器的句柄。 如果驱动程序具有 StartIo 例程,还会为 AdapterControl 例程提供指向 DeviceObject->CurrentIrp 的指针。 如果驱动程序管理自己的 IRP 队列而不是 StartIo 例程,则驱动程序应包含指向当前 IRP 的指针,作为它在调用 AllocateAdapterChannel 时传递的上下文数据的一部分。
AdapterControl 例程通常执行以下操作:
对于使用系统 DMA 控制器的自动初始化模式的驱动程序, AdapterControl 例程必须返回值 KeepObject, 这允许驱动程序保留系统 DMA 控制器的“所有权”和分配的映射寄存器 ,直到传输所有数据。
由于 AdapterControl 例程不能等待从属设备执行 DMA 操作, 因此 AdapterControl 例程必须至少执行以下操作:
另一个驱动程序例程 (很可能是 DpcForIsr 例程) 必须在 DMA 传输操作完成时调用 FlushAdapterBuffers 和 FreeAdapterChannel 。
当 AllocateAdapterChannel 将控制权转移到驱动程序的 AdapterControl 例程时,驱动程序将“拥有”系统 DMA 控制器和一组映射寄存器。 然后,驱动程序必须调用 MapTransfer ,以将系统 DMA 控制器设置为使用驱动程序分配的公共缓冲区,然后驱动程序为传输操作设置其设备。
驱动程序向 MapTransfer 提供以下参数:
MapTransfer 返回一个逻辑地址,使用系统 DMA 的驱动程序必须忽略该地址。 当 MapTransfer 返回控件时,驱动程序应为 DMA 操作设置其设备。 驱动程序只调用 MapTransfer 一次,但会继续在其公共缓冲区和锁定的用户缓冲区之间复制数据,直到完成请求的传输。
驱动程序可以调用 ReadDmaCounter 来确定当前在公共缓冲区中要传输的字节数,然后驱动程序可以继续使用用户数据填充其公共缓冲区,或将数据从其公共缓冲区复制到用户缓冲区,具体取决于 DMA 操作的方向。
传输完成或驱动程序必须返回 IRP 的错误状态时,驱动程序将调用 FlushAdapterBuffers ,以确保将系统 DMA 控制器中缓存的任何数据读入系统内存或写出到设备。 然后,驱动程序应立即调用 FreeAdapterChannel ,以释放系统 DMA 控制器供任何驱动程序 (包括自身) 使用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。