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

在早期,是按照基于包或者基于流的方式来描述DMA的,不过这个描述可能不准确,故在Vista之后修改为使用数据包/使用公共缓冲区的系统DMA。

简单的解释一下基于包和基于流的说法的原因,数据包是指一个个基于一定大小的数据块,例如设定4096大小的内存页面,如果我们使用基于包的方式传输5000个字节,那么就是两个数据包,第一个数据包为4096字节,第二个数据包是904字节,实际传输两个数据包;如果使用基于流的,那么流传输每次回来1个数据包,缓冲区会根据自身的大小从里面拷贝数据,那么系统缓冲区先从第一个数据包中拷贝4096个字节,再从第二个数据包中拷贝906个字节。

在上面的描述中,我们会发现基于流的数据包对于数据利用率和容忍度更高一些。

使用公用缓冲区系统 DMA

使用系统 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 在系统虚拟地址空间中分配请求的内存量,并返回指向该缓冲区的两种不同类型的指针:

  • 上图中缓冲区的 LogicalAddress (BufferLogicalAddress) ,驱动程序必须为此提供存储,但随后应忽略存储
  • 上图中缓冲区虚拟地址 (BufferVirtualAddress) ,驱动程序还必须存储该地址,以便它可以生成描述其缓冲区的 MDL 以执行 DMA 操作

驱动程序应将这些指针存储在设备扩展或其他驱动程序分配的驻留内存中;

3. 驱动程序调用 IoAllocateMdl 为缓冲区分配 MDL。 驱动程序传递 AllocateCommonBuffer 返回的缓冲区的 VirtualAddress 及其缓冲区的 Length 以分配 MDL;

4. 驱动程序使用 IoAllocateMdl 返回的指针调用 MmBuildMdlForNonPagedPool,以将其驻留缓冲区的虚拟地址范围映射到系统物理内存;

分配公共缓冲区并映射其虚拟地址范围后,从属设备的驱动程序可以开始处理请求 DMA 传输的 IRP。 为此,驱动程序调用以下常规支持例程序列:

  • 由驱动程序编写者自行决定, RtlMoveMemory 将数据从锁定的用户缓冲区复制到驱动程序分配的公共缓冲区,以便传输到设备;
  • 当驱动程序准备好针对 DMA 对设备进行编程并需要系统 DMA 控制器时,AllocateAdapterChannel;
  • MapTransfer,使用描述驱动程序分配的公共缓冲区的 MDL,为传输操作设置系统 DMA 控制器,请注意,驱动程序仅调用 MapTransfer 一次,以将系统 DMA 控制器设置为使用其公共缓冲区。 在传输期间,驱动程序可以调用 ReadDmaCounter 来确定剩余要传输的字节数,并在必要时调用 RtlMoveMemory 以向用户缓冲区或从用户缓冲区复制更多数据;
  • 当驱动程序完成从属设备的 DMA 传输时调用FlushAdapterBuffers;
  • 只要传输了所有请求的数据,或者驱动程序必须因设备 I/O 错误而使 IRP 失败,则 FreeAdapterChannel;

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 操作的任何上下文。 上下文可能包括驱动程序必须传递给 MapTransfer 和 FlushAdapterBuffers 的输入 MapRegisterBase 句柄,以及从 IRP 中的 I/O 堆栈位置请求的传输的长度;
  • 设置从属设备以启动传输操作;
  • 返回值 KeepObject;

对于使用系统 DMA 控制器的自动初始化模式的驱动程序, AdapterControl 例程必须返回值 KeepObject, 这允许驱动程序保留系统 DMA 控制器的“所有权”和分配的映射寄存器 ,直到传输所有数据。

由于 AdapterControl 例程不能等待从属设备执行 DMA 操作, 因此 AdapterControl 例程必须至少执行以下操作:

  • 将上下文信息(尤其是 MapRegisterBase 句柄)保存在驱动程序的设备扩展、控制器扩展或其他驱动程序可访问的常驻存储区域中,驱动程序分配的非分页池。
  • 返回 KeepObject。

另一个驱动程序例程 (很可能是 DpcForIsr 例程) 必须在 DMA 传输操作完成时调用 FlushAdapterBuffers 和 FreeAdapterChannel 。

设置系统DMA

当 AllocateAdapterChannel 将控制权转移到驱动程序的 AdapterControl 例程时,驱动程序将“拥有”系统 DMA 控制器和一组映射寄存器。 然后,驱动程序必须调用 MapTransfer ,以将系统 DMA 控制器设置为使用驱动程序分配的公共缓冲区,然后驱动程序为传输操作设置其设备。

驱动程序向 MapTransfer 提供以下参数:

  • IoGetDmaAdapter 返回的适配器对象指针;
  • 指向描述驱动程序分配的公共缓冲区的 MDL 的指针;
  • 通过 AllocateAdapterChannel 传递给驱动程序的 AdapterControl 例程的 MapRegisterBase 句柄;
  • 指向变量的指针 (Length) 指示驱动程序分配的公共缓冲区的大小,以字节为单位;
  • 一个布尔值,指示传输操作的方向,请求从系统内存传输到设备;

MapTransfer 返回一个逻辑地址,使用系统 DMA 的驱动程序必须忽略该地址。 当 MapTransfer 返回控件时,驱动程序应为 DMA 操作设置其设备。 驱动程序只调用 MapTransfer 一次,但会继续在其公共缓冲区和锁定的用户缓冲区之间复制数据,直到完成请求的传输。

驱动程序可以调用 ReadDmaCounter 来确定当前在公共缓冲区中要传输的字节数,然后驱动程序可以继续使用用户数据填充其公共缓冲区,或将数据从其公共缓冲区复制到用户缓冲区,具体取决于 DMA 操作的方向。

传输完成或驱动程序必须返回 IRP 的错误状态时,驱动程序将调用 FlushAdapterBuffers ,以确保将系统 DMA 控制器中缓存的任何数据读入系统内存或写出到设备。 然后,驱动程序应立即调用 FreeAdapterChannel ,以释放系统 DMA 控制器供任何驱动程序 (包括自身) 使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/588691.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Tensorflow2.0笔记 - ResNet实践

本笔记记录使用ResNet18网络结构,进行CIFAR100数据集的训练和验证。由于参数较多,训练时间会比较长,因此只跑了10个epoch,准确率还没有提升上去。 import os import time import tensorflow as tf from tensorflow import keras …

数据库和缓存一致性问题

hello,各位小伙伴们大家好,我是颜书凌,下面给大家讲解一下数据库和缓存的一致性问题,话不多说 1、一致性介绍 一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。 强一致性…

2024年【G3锅炉水处理】试题及解析及G3锅炉水处理模拟考试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年G3锅炉水处理试题及解析为正在备考G3锅炉水处理操作证的学员准备的理论考试专题,每个月更新的G3锅炉水处理模拟考试题祝您顺利通过G3锅炉水处理考试。 1、【多选题】在可逆反应中,下面哪…

Node.js -- express 框架

文章目录 1. express 使用2. 路由2.1 路由的使用2.2 获取请求报文参数2.3 获取路由参数2.4 路由参数练习 3. express 响应设置4. 中间件4.1 全局中间件4.2 路由中间件4.3 静态资源中间件 5. 获取请求体数据 body-parser6. 防盗链7. 路由模块化8. 模板引擎8.1 了解EJS8.2 列表渲…

面试二十四、继承多态

一、继承的本质和原理 组合(Composition): 组合是一种"有一个"的关系,表示一个类包含另一个类的对象作为其成员。这意味着一个类的对象包含另一个类的对象作为其一部分。组合关系通常表示强关联,被包含的对象…

【Week-Y7】使用自己的数据集训练YOLO-v8

文章目录 一、官方环境配置与测试1. 配置环境2. 用官方图片测试(图片下载失败)3. 用本地图片测试,检查配置的环境是否可用 二、使用自己的数据集进行训练测试1. 执行split_train_val.py文件2. 执行python .\voc_label.py文件3. 创建fruit.yam…

[Python基础知识]05函数和模块

一、函数的定义 格式:def 函数名(参数列表): 注: 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。即使该函数不需要接收任何参数,也必须保留一对空的圆括号 函数形参不需要声明其类型&#x…

layui中禁用div标签等操作

为了实现点击表格行后触发事件 然后去触发后进行操作 页面流程操作设置规定 不可编辑直接添加属性 class"layui-disabled"如果在最大的 div 设置不可编辑 但是内部有些还是可以触发使用的 所以就重写一下 取到当前 div 下的 所有的子元素 然后在给所有的子元素…

闲话 ASP.NET Core 数据校验(二):FluentValidation 基本用法

前言 除了使用 ASP.NET Core 内置框架来校验数据,事实上,通过很多第三方框架校验数据,更具优势。 比如 FluentValidation,FluentValidation 是第三方的数据校验框架,具有许多优势,是开发人员首选的数据校验…

抢先体验:MacOS成功安装PHP8.4教程

根据官方消息,PHP 8.4将于2024年11月21日发布。它将通过三个 alpha 版本、三个 beta 版本和六个候选版本进行测试。 这次的重大更新将为PHP带来许多优化和强大的功能。我们很高兴能够引导您完成最有趣的更新升级,这些更改将使我们能够编写更好的代码并构…

解决React报错Encountered two children with the same key

当我们从map()方法返回的两个或两个以上的元素具有相同的key属性时,会产生"Encountered two children with the same key"错误。为了解决该错误,为每个元素的key属性提供独一无二的值,或者使用索引参数。 这里有个例子来展示错误是…

YOLOv8主要命令讲解

YOLOv8主要有三个常用命令,分别是:train(训练)、predict(预测)、export(转化模型格式),下面我将展开讲讲三个常用命令的常用参数与具体使用方法。 一、训练 通过自己标…

STM32单片机通过串口控制DDSM210 直驱伺服电机

1 电机介绍 官方资料:https://www.waveshare.net/wiki/DDSM210 DDSM210 直驱伺服电机是基于一体化开发理念,集外转子无刷电机、编码器、伺服驱动于一体的高可靠性永磁同步电动机,其结构紧凑,安装方便,运行稳定&#x…

react核心知识

1. 对 React 的理解、特性 React 是靠数据驱动视图改变的一种框架,它的核心驱动方法就是用其提供的 setState 方法设置 state 中的数据从而驱动存放在内存中的虚拟 DOM 树的更新 更新方法就是通过 React 的 Diff 算法比较旧虚拟 DOM 树和新虚拟 DOM 树之间的 Chan…

【PCL】教程 supervoxel_clustering执行超体聚类并可视化点云数据及其聚类结果

[done, 417.125 ms : 307200 points] Available dimensions: x y z rgba 源点云milk_cartoon_all_small_clorox.pcd > Loading point cloud... > Extracting supervoxels! Found 423 supervoxels > Getting supervoxel adjacency 这段代码主要是使用PCL(Po…

Linux进程——进程的创建(fork的原理)

前言:在上一篇文章中,我们已经会使用getpid/getppid函数来查看pid和ppid,本篇文章会介绍第二种查看进程的方法,以及如何创建子进程! 本篇主要内容: 查看进程的第二种方法创建子进程系统调用函数fork 在开始前&#xff…

【华为】路由综合实验(基础)

【华为】路由综合实验 实验需求拓扑配置AR1AR2AR3AR4AR5PC1PC2 查看通信OSPF邻居OSPF路由表 BGPBGP邻居BGP 路由表 配置文档 实验需求 ① 自行规划IP地址 ② 在区域1里面 启用OSPF ③ 在区域1和区域2 启用BGP,使AR4和AR3成为eBGP,AR4和AR5成为iBGP对等体…

buuctf-misc-22.神秘龙卷风1

22.神秘龙卷风1 题目:暴力破解-翻译Brainfuck计算机语言 根据提示是4位密码,直接破解密码即可 解压后发现是这样一个文档 我们尝试使用网站翻译这个 内容由“”、“.”、“>”三种符号组成,我刚开始认为这是一种密文,经过搜索…

thinkpad电脑文件隐藏了怎么恢复?教你几招

在使用ThinkPad电脑时,有时我们可能会发现一些文件或文件夹突然“消失”了,这通常是因为它们被隐藏了。本文将为您介绍几招恢复ThinkPad电脑上隐藏文件的方法,帮助您轻松找回丢失的文件。 图片来源于网络,如有侵权请告知 一、了解…

【实时数仓架构】方法论

笔者不是专业的实时数仓架构,这是笔者从其他人经验和网上资料整理而来,仅供参考。写此文章意义,加深对实时数仓理解。 一、实时数仓架构技术演进 1.1 四种架构演进 1)离线大数据架构 一种批处理离线数据分析架构,…