windows驱动开发-DMA技术(一)

DMA(Direct Memory Access)是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量中断负载,否则CPU 需要从设备缓存中把每一页的数据复制到缓存中,然后把它们再次写入到新的地方,在这个过程中中,CPU 需要一直控制这个过程。 DMA 则是由DMA控制器直接将数据从一个地址空间复制到另外一个地址空间。

由于现在电脑基本上统一了内部总线,所以所有的DMA都是PCI总线来进行的,故了解了PCI总线是如何处理DMA的,那也就了解了DMA是如何进行的。

如果调试过PCI总线,那么会发现,DMA是以一个内存页面为单位进行的,每次DMA传输的总是内存页面的整数倍,PCI会先将windows的内存地址写入到DMA控制器,然后向寄存器写入数据来启动DMA传输,但是,这一切操作已经被windows封装起来了,在windows中,我们会直接分配适配器对象,使用适配器来操作DMA。

适配器对象

任何使用直接 I/O 和 DMA 的驱动程序都必须创建适配器对象。 适配器对象表示 DMA 控制器通道或端口,或总线-主设备。

两种最低级别驱动程序必须使用适配器对象:

  • 使用系统 DMA 控制器的设备驱动程序。 此类设备称为 从属设备 ,称为"使用系统 (或 从属) DMA"。
  • 用作总线-主适配器的设备驱动程序。 此类设备与系统仲裁以使用 I/O 总线,因此使用总线主 DMA。

驱动程序通常位于设备扩展中,为指向适配器对象的指针提供存储。

为了执行 DMA 传输,使用这些 DMA 方法的设备驱动程序通常具有AdapterControl 例程,并调用系统提供的用于操作适配器对象的支持例程。 不需要 AdapterControl 例程的 (驱动程序包括 那些使用Scatter/Gather DMA 的驱动程序,以及那些使用 common-buffer、bus-master DMA.)就是使用总线主DMA的驱动。

作为设备启动操作的一部分,处理 DMA 操作的驱动程序调用 I/O 管理器,后者又调用特定于平台的 HAL 来创建一组适配器对象。 在任何 Windows 平台上,适配器对象集通常包括以下对象的适配器对象:

  • 从属设备连接到的每个系统 DMA 控制器通道或端口。
  • 计算机中的每个总线主 DMA 设备。
获取/释放适配器

在设备启动时,使用系统或总线主 DMA 的驱动程序会调用 IoGetDmaAdapter 来获取指向适配器对象的指针,并确定每个传输操作可用的映射寄存器的最大数目。 当驱动程序调用 IoGetDmaAdapter 时,I/O 管理器反过来会调用 HAL 以获取特定于平台的必要信息。

驱动程序必须在调用 IoGetDmaAdapter 时,在系统定义的DEVICE_DESCRIPTION结构中提供某些信息。 驱动程序必须使用 RtlZeroMemory 初始化 DEVICE_DESCRIPTION 结构,并在其中设置值。

所需数据包括有关驱动程序设备功能的信息,例如设备是否为总线主机、设备是否具有散点/收集功能,以及设备一次可以传输多少字节的数据, (MaximumLength) 。

所需的设备说明数据还包括特定于平台的信息,例如总线主设备驱动程序控制的特定于平台和系统分配的总线编号。 驱动程序可以通过调用 IoGetDeviceProperty 来获取此信息。

DEVICE_DESCRIPTION结构包括一些可能与某些 DMA 设备或驱动程序无关的字段。 例如,WDM 驱动程序中不使用 BusNumber 字段。 每个驱动程序应为相关结构成员提供值,并应将所有其他成员的值设置为零。

当请求必须分解为两个或更多个 DMA 操作时,除非设备能够等待系统 DMA 控制器重新编程,否则从属设备的驱动程序不应在 ScatterGather 字段中传递 TRUE。

IoGetDmaAdapter 返回指向适配器对象的指针和特定于平台或特定于设备的值,该值指示适配器对象可用于每个 DMA 传输操作的映射寄存器数。

返回的适配器对象包含三个可供驱动程序访问的字段:

  • 版本号 (版本)
  • 大小 (大小)
  • 指向 DmaOperations(DMA_OPERATIONS) 结构的指针

DMA_OPERATIONS 结构包含指向驱动程序在其设备上执行 DMA 操作时必须使用的函数的指针表, 函数只能通过此数据结构中的指针访问,驱动程序无法直接按名称调用它们。 (请注意,这些例程替换以前版本的 Windows NT 中支持的 HalXxx 例程。为了确保旧驱动程序的兼容性,Wdm.h 和 Ntddk.h 头文件提供具有过时名称的宏,但新驱动程序应始终通过 data structure.)

映射寄存器的数量可能因设备以及平台而异。 通常,HAL 根据以下条件分配多个映射寄存器:

如果可能,HAL 将返回一个值,该值比传输 MaximumLength 字节所需的映射寄存器数多一个,如驱动程序对 IoGetDmaAdapter 的调用中所述。

否则,HAL 将返回一个较小的值,该值对于特定平台来说尽可能大。

换句话说,HAL 通常为每个驱动程序提供足够的映射寄存器,以最大化其设备的 DMA 吞吐量,但 HAL 在某些 Windows 平台上可以返回较小的值。 无法保证驱动程序将获取它请求的映射寄存器数,因此驱动程序应始终检查返回的值。

任何 DMA 设备驱动程序都必须为 IoGetDmaAdapter 返回的适配器对象指针和 NumberOfMapRegisters 值提供存储。 此指针是指向系统提供的用于 DMA 的支持例程的必需参数。 由于其中许多支持例程必须在 IRQL = DISPATCH_LEVEL调用,因此驱动程序分配的存储必须是驻留的。 大多数 DMA 驱动程序在 设备扩展中提供必要的存储。 但是,如果驱动程序也使用控制器对象或驱动程序分配的非分页池,则存储可以位于 控制器 扩展中。

驱动程序完成所有 DMA 操作后,会调用 PutDmaAdapter 以释放适配器对象。

AdaptControl例程要求

AdapterControl 例程必须至少执行以下操作:

  1. 保存输入 MapRegisterBase 值以及驱动程序为当前 IRP 执行一个或多个 DMA 传输操作所需的任何其他上下文信息。 每个 DMA 传输操作完成后,驱动程序必须将 MapRegisterBase 值传递给 FlushAdapterBuffers 。

  2. 返回相应的 IO_ALLOCATION_ACTION 值:

  • 如果设备是从属设备,则为 KeepObject,因此驱动程序使用系统 DMA。
  • 如果设备是总线主机,则 DeallocateObjectKeepRegisters,因此驱动程序使用基于数据包的总线主 DMA。

根据驱动程序的设计,其 AdapterControl 例程也可以在返回控制权之前执行以下操作:

  • 1. 确定传输在其设备上的起始位置;
  • 2. 根据由于传输的开始位置而对其设备的任何限制,计算可能的传输大小; 一般情况下,调用 AllocateAdapterChannel 的例程负责确定是否必须将转移请求拆分为部分传输,因为针对每个 DMA 传输操作可用的 NumberOfMapRegisters 存在任何特定于平台的限制;
  • 3. 设置有关设备 (或控制器) 扩展中的每个传输请求的任何驱动程序维护的状态;例如,AdapterControl 例程可能会使用 CustomTimerDpc 例程的入口点调用 KeSetTimer,该例程超时驱动程序的 DMA 传输操作;
  • 4. 使用在 Irp-> MdlAddress传递的 MDL 指针调用 MmGetMdlVirtualAddress,以获取传输开始的索引,适合传递给 MapTransfer;
  • 5. 调用 MapTransfer 以设置系统 DMA 控制器或获取总线-主设备的物理到逻辑地址映射。
  • 6. 使用通过调用 KeSynchronizeExecution 调用的 SynchCritSection 例程对驱动程序的设备进行编程,以便执行传输操作。

如果传输请求要求驱动程序执行一系列部分传输操作以满足当前 IRP,则驱动程序的 DpcForIsr 或 CustomDpc 例程通常负责对设备进行重新编程,以便执行后续传输操作。 每个传入传输 IRP 仅调用 一次 AdapterControl 例程。

完成当前传输 IRP 的驱动程序例程(通常是 DpcForIsr 或 CustomDpc 例程)也负责通过分别调用 FreeAdapterChannel 或 FreeMapRegisters 来释放系统 DMA 控制器或总线-主适配器。 此驱动程序例程应在完成其最后一次部分传输操作时尽快发出相应的调用,以便从属 DMA 设备的驱动程序可以分配系统 DMA 控制器,或者总线-主驱动程序可以立即开始处理下一个传输 IRP。

IRP_MN_START_DEVICE 请求的驱动程序调度例程必须为 AdapterControl 例程执行以下操作:

  • 通过填写 DEVICE_DESCRIPTION结构并 调用 IoGetDmaAdapter,为设备的 DMA 功能设置适配器对象。
  • 保存 IoGetDmaAdapter 返回的适配器对象指针和 NumberOfMapRegisters。
  • IoGetDmaAdapter 返回的平台特定最大 NumberOfMapRegisters 或驱动程序设备的传输功能(以限制性更强者为准)确定驱动程序是否必须拆分给定的传输请求并在其设备上执行多个 DMA 操作来满足该 IRP。

返回的适配器对象指针、驱动程序的 AdapterControl 例程的入口点、表示当前 IRP 的目标设备的 DeviceObject 指针、指向已为 AdapterControl 例程设置的区域的上下文指针,以及 NumberOfMapRegisters 值(可能小于较小传输请求的最大可能数目)必须在调用中传递给 AllocateAdapterChannel。 通常,驱动程序的 StartIo (或可能是 ControllerControl) 例程在调用 AllocateAdapterChannel 之前在 Context 处设置区域。

在系统中,可能以两种不同的方式使用DMA,基于数据包的系统DMA、基于公用缓冲区的系统DMA。

相关推荐
牧以南歌〆5 小时前
在Ubuntu主机中修改ARM Linux开发板的根文件系统
linux·arm开发·驱动开发·ubuntu
emplace_back6 小时前
C# 集合表达式和展开运算符 (..) 详解
开发语言·windows·c#
Narnat7 小时前
Rk3568驱动开发_中断_14
驱动开发
一禅(OneZen)10 小时前
「Windows/Mac OS」AIGC图片生成视频 ,webui + stable-diffusion环境部署教程
windows·stable diffusion
AirDroid_cn10 小时前
OPPO手机怎样被其他手机远程控制?两台OPPO手机如何相互远程控制?
android·windows·ios·智能手机·iphone·远程工作·远程控制
小龙在山东13 小时前
Python 包管理工具 uv
windows·python·uv
昏睡红猹13 小时前
我在厂里搞wine的日子
windows·wine
love530love16 小时前
Docker 稳定运行与存储优化全攻略(含可视化指南)
运维·人工智能·windows·docker·容器
mmoyula19 小时前
【RK3568 驱动开发:实现一个最基础的网络设备】
android·linux·驱动开发
1024小神21 小时前
tauri项目在windows上的c盘没有权限写入文件
c语言·开发语言·windows