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

news2024/10/6 1:44:06

前面描述了DMA技术中适配器相关的部分以及DMA的分类,接下来看一下系统具体在支持两种DMA时候的操作的细微差别。

此处解释一下Scatter/Gather,这个也翻译为散点/收集,是指指示设备能够读取或写入内存中的任何区域,而不仅仅是特定范围。在早期,DMA读写的区域必须是一个整页面,但是这种要求其实对于内核来说,不是每时每刻都可以满足的,故和MDL类似,系统也支持将不在同一个页面上N个数据块映射为一整个页面。

使用基于数据包的系统 DMA

使用基于数据包的 DMA 的从属设备的驱动程序在处理请求 DMA 传输的 IRP 时调用以下常规支持例程序列:

  • 尝试分配系统 DMA 控制器 ;
  • 当驱动程序准备好针对 DMA 对设备进行编程并需要系统 DMA 控制器调用AllocateAdapterChannel,AllocateAdapterChannel 依次调用驱动程序的 AdapterControl 例程。
  • MmGetMdlVirtualAddress 获取 MDL 中的索引,在对 MapTransfer 的初始调用中需要作为参数MapTransfer 为传输操作对系统 DMA 控制器进行编程
  • 驱动程序可能需要多次调用 MapTransfer 来传输所有请求的数据;
  • FlushAdapterBuffers ,就在每个 DMA 传输操作与从属设备之间
  • 如果驱动程序必须多次调用 MapTransfer 以传输所有请求的数据,则它必须调用 FlushAdapterBuffers 的次数与调用 MapTransfer 的次数一样多。
  • FreeAdapterChannel 只要传输了所有请求的数据,或者驱动程序因设备 I/O 错误导致 IRP 失败

IoGetDmaAdapter 返回的适配器对象指针是每个例程的必需参数,KeFlushIoBuffers 和 MmGetMdlVirtualAddress 除外,后者需要指向在 Irp->MdlAddress 传递的 MDL 的指针。

各个驱动程序在不同点调用此支持例程序列,具体取决于实现每个驱动程序以为其设备提供服务的方式。 例如,一个驱动程序的 StartIo 例程可能会调用 AllocateAdapterChannel,另一个驱动程序可能会从从驱动程序创建的互锁队列中删除 IRP 的例程发出此调用,另一个驱动程序可能在其从属 DMA 设备指示它已准备好传输数据时发出此调用。

分配适配器通道

为了准备基于数据包的系统 DMA,驱动程序在收到IRP_MJ_READ或IRP_MJ_WRITE请求后调用 KeFlushIoBuffers 和 AllocateAdapterChannel。

在驱动程序调用这些例程之前,其 DispatchRead 或 DispatchWrite 例程 (或其他处理 DMA 传输) 的调度例程应已检查 IRP 参数的有效性。 调度例程还可能已将 IRP 排到另一个驱动程序例程进行进一步处理。

调用 AllocateAdapterChannel 的 驱动程序例程必须在 IRQL=DISPATCH_LEVEL执行。 除了指向 IoGetDmaAdapter 返回的适配器对象的指针之外,驱动程序还必须在调用 AllocateAdapterChannel 时提供以下内容:

  • 指向目标设备对象的指针
  • 其 AdapterControl 例程的入口点
  • 指向 AdapterControl 例程将使用的任何驱动程序确定的上下文信息的指针

AllocateAdapterChannel 将驱动程序的 AdapterControl 例程排队,该例程在系统 DMA 控制器分配给此驱动程序时运行,并且已为驱动程序的 DMA 操作分配了一组 映射寄存器。

输入时,AdapterControl 例程接收在对 AllocateAdapterChannel 的调用中传递的 DeviceObject 和 Context 指针,以及分配的映射寄存器 (MapRegisterBase) 句柄。

如果驱动程序具有 StartIo 例程,AdapterControl 例程还会收到指向 DeviceObject->CurrentIrp 的指针。 如果驱动程序管理自己的 IRP 队列,而不是使用StartIo 例程,则驱动程序应包含指向当前 IRP 的指针,作为它在调用 AllocateAdapterChannel 时传递的上下文的一部分。

AdapterControl 例程通常执行以下操作:

  • 保存或初始化驱动程序维护的有关 DMA 操作的任何上下文。 上下文可能包括驱动程序必须传递给 MapTransfer 和 FlushAdapterBuffers 的输入 MapRegisterBase 句柄,以及从 IRP 中的 I/O 堆栈位置请求的传输的长度。
  • 调用 MmGetMdlVirtualAddress 和 MapTransfer。 
  • 设置从属设备以启动传输操作;
  • 返回值 KeepObject;

每个 AdapterControl 例程都必须返回 IO_ALLOCATION_ACTION类型的系统定义值。 对于使用系统 DMA 的驱动程序, AdapterControl 例程必须返回值 KeepObject。 这允许驱动程序保留系统 DMA 控制器和分配的映射寄存器的所有权,直到传输了所有请求的数据。

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

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

另一个驱动程序例程 (可能是 DpcForIsr 例程) 必须在每个 DMA 传输操作完成时调用 FlushAdapterBuffers 。 如果需要多次设置 DMA 控制器以满足当前 IRP 的传输请求,此例程还必须再次调用 MapTransfer 和 FlushAdapterBuffers 。

当驱动程序满足当前 IRP 的请求时,它必须调用 FreeAdapterChannel。 此支持例程应在上次调用当前 IRP 的 FlushAdapterBuffers 之后立即调用,以便系统 DMA 控制器可供任何驱动程序 迅速满足其他传输请求。

具有Scatter/Gather功能的从属设备的驱动程序还应从其 AdapterControl 例程返回 KeepObject。 当驱动程序必须拆分给定 DMA 请求时,当系统 DMA 控制器在 DMA 操作之间重新编程时,设备必须能够等待。 在某些 Windows 平台上,此类设备每次 DMA 操作最多可以传输一页数据,因为 HAL 只能将单个映射寄存器分配给该设备的驱动程序。

设置系统DMA

当 AllocateAdapterChannel 将控制权转移到驱动程序的 AdapterControl 例程时,驱动程序将“拥有”系统 DMA 控制器和一组映射寄存器。 然后,驱动程序必须为传输操作设置 DMA 控制器,如下图所示:

如果驱动程序具有 StartIo 例程,则 AllocateAdapterChannel 会将 PIrp 参数中指向 DeviceObject-CurrentIrp> 的指针传递到 AdapterControl 例程。 但是,如果驱动程序管理自己的 IRP 队列,则驱动程序应包含指向当前 IRP 的指针,作为它传递给 AdapterControl 的上下文的一部分。

如上图所示,驱动程序的 AdapterControl 例程设置 DMA 传输,如下所示:

1. AdapterControl 例程获取开始传输的地址。 对于满足 IRP 所需的初始传输, AdapterControl 例程调用 MmGetMdlVirtualAddress,将指针传递到 Irp->MdlAddress 处的 MDL,该指针描述此 DMA 传输的缓冲区;MmGetMdlVirtualAddress 返回一个虚拟地址,驱动程序可以使用该地址作为应开始传输的系统物理地址的索引;如果 IRP 需要多个传输操作,驱动程序将计算更新的起始地址,如后面所述。

2. AdapterControl 例程保存 MmGetMdlVirtualAddress 返回或在步骤 1 中计算的地址。 此地址是 MapTransfer (CurrentVa) 的必需参数。

3. AdapterControl 例程调用 MapTransfer 来设置系统 DMA 控制器,并提供以下参数:

  • IoGetDmaAdapter 返回的适配器对象指针;
  • 指向当前 IRP 的 Irp-> MdlAddress处的 MDL (Mdl 的指针),通过 AllocateAdapterChannel 传递给驱动程序的 AdapterControl 例程的 MapRegisterBase 句柄;
  • 如果这是第一次调用 IRP 的 MapTransfer,则 mmGetMdlVirtualAddress 返回的 Current) Va 值 ;否则,驱动程序会提供更新的 CurrentVa 值,指示下一个传输操作应在缓冲区中的哪个位置启动;
  • 指向变量的指针 (Length) 指示此传输的字节数;
  • 如果驱动程序可以通过一次调用 MapTransfer 来传输请求的所有数据,并且对其 DMA 操作没有特定于设备的约束,则可以在驱动程序的 I/O 堆栈位置的 IRP 中将 Length 设置为 Length 值。 最多可以 (PAGE_SIZE * IoGetDmaAdapter) 返回的 NumberOfMapRegisters,以字节为单位。 否则,驱动程序必须拆分请求(如拆分传输请求中所述),并且必须在对当前 IRP 的 MapTransfer 的后续调用中更新 Length 值;
  • 一个布尔值 (WriteToDevice) ,指示传输操作的方向, TRUE 将数据从系统内存传输到设备;反之亦然;

4. MapTransfer 返回逻辑地址,使用系统 DMA 的驱动程序必须忽略此值;

5. AdapterControl 例程为 DMA 操作设置设备;

6. AdapterControl 例程返回 KeepObject;

当设备指示其当前 DMA 操作已完成时,驱动程序应调用 FlushAdapterBuffers,通常是从驱动程序的 DpcForIsr 例程调用。

完成 DMA 操作的 DpcForIsr 例程或其他驱动程序例程调用 FlushAdapterBuffers,以确保将系统 DMA 控制器中缓存的任何数据读入系统内存或写出到设备。 如果需要重新编程系统 DMA 控制器以便为当前 IRP 传输更多数据,则同一例程还必须再次调用 MapTransfer ;同样它必须在每次传输操作后再次调用 FlushAdapterBuffers 。

如果驱动程序必须多次为当前 IRP 调用 MapTransfer ,它将在每次调用中提供相同的适配器对象指针、 Mdl 指针、 MapRegisterBase 句柄和传输方向。 但是,驱动程序必须先更新 CurrentVa 和 Length 参数,然后才能对 MapTransfer 进行第二次和任何后续调用。 若要计算其中每个参数的更新值,请使用以下公式:

  • CurrentVa = 在对 MapTransfer的前面调用中请求的 CurrentVa + Length;
  • Length = 最小剩余要传输的长度, (PAGE_SIZE * IoGetDmaAdapter) ) 返回的 NumberOfMapRegisters;

每个驱动程序应维护的有关其 DMA 传输的上下文信息取决于其特定设备的需求。 典型上下文可能包括 MDL (CurrentVa) 中的当前虚拟地址、到目前为止传输的字节数、要传输的剩余字节数、可能指向当前 IRP 的指针,以及驱动程序编写者认为有用的任何其他信息。

当请求的传输完成时,或者如果驱动程序必须返回 IRP 的错误状态,则驱动程序应立即调用 FreeAdapterChannel ,以释放系统 DMA 控制器以供其他驱动程序和此驱动程序使用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLO系列改进,自研模块助力涨点

目录 一、原理 二、代码 三、添加到YOLOv5中 一、原理 论文地址:

LiveCD镜像文件的定制化

最近想要定制化一款属于自己的LiveCD镜像文件,并且里边封装好所需要的软件程序,本文将会记录具体的操作步骤,供有需要的人参考借鉴。 环境说明: 环境配置说明配置参数编码环境Centos7.9LiveCD文件CentOS-7-livecd-x86_64.iso 附…

袁庭新ES系列18节|Spring Data Elasticsearch高级

前言 这一章节袁老师将带领同学们来学习Spring Data Elasticsearch高级操作相关的内容。我们继续来探索SDE是如何将原始操作Elasticsearch的客户端API进行封装的,以及通过Spring Data Elasticsearch如何来操作ES。准备好了吗?我们继续来探索ES的内容。 …

探索AI工具的巅峰:个人体验与深度剖析

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

【Go语言快速上手(六)】管道, 网络编程,反射,用法讲解

💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:Go语言专栏⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多Go语言知识   🔝🔝 GO快速上手 1. 前言2. 初识管道3. 管…

自定义SpringBoot的starter

案例需求:自定义redis-stater。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。 实现步骤: 1、创建redis-spring-boot-autoconfigure模块 2、创建redis-spring-boot-starter模块,依赖redis-spring-boot-autoconfigure的…

Codeforces Round 938 (Div. 3)H-The Most Reckless Defense

来源 题目 You are playing a very popular Tower Defense game called "Runnerfield 2". In this game, the player sets up defensive towers that attack enemies moving from a certain starting point to the players base. You are given a grid of size nm&a…

Delta lake with Java--利用spark sql操作数据2

上一篇文章尝试了建库,建表,插入数据,还差删除和更新,所以在这篇文章补充一下,代码很简单,具体如下: import org.apache.spark.sql.SaveMode; import org.apache.spark.sql.SparkSession;publi…

DRF中的请求入口分析及request对象分析

DRF中的请求入口分析及request对象分析 django restframework框架是在django的基础上又给我们提供了很多方便的功能,让我们可以更便捷基于django开发restful API 1 drf项目 pip install django pip install djangorestframework1.1 核心配置 INSTALLED_APPS [d…

【源码阅读】Golang中的go-sql-driver库源码探究

文章目录 前言一、go-sql-driver/mysql1、驱动注册:sql.Register2、驱动实现:MysqlDriver3、RegisterDialContext 二、总结 前言 在上篇文章中我们知道,database/sql只是提供了驱动相关的接口,并没有相关的具体实现,具…

PG数据库结构与oracle比较

1.数据库集簇逻辑结构 数据库集簇概念:一个大的数据库是由若干个小的数据库组成,实现数据的隔离存放,在概念上应该是与mysql一样的 在mysql中可以用show database列出数据库 PG中用\l 数据库对象存放在数据库中: PG中的所有数据…

Mac 上安装多版本的 JDK 且实现 自由切换

背景 当前电脑上已经安装了 jdk8; 现在再安装 jdk17。 期望 完成 jdk17 的安装,并且完成 环境变量 的配置,实现自由切换。 前置补充知识 jdk 的安装路径 可以通过查看以下目录中的内容,确认当前已经安装的 jdk 版本。 cd /Library/Java/Java…

Maven3.9.6下载安装教程

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ 👀👀👀 个人博客:小奥的博客 👍👍👍:个人CSDN ⭐️⭐️⭐️:Github传送门 🍹 本人24应届生一枚,技术和水平有…

Typescript精进:前端必备的5大技巧(AI写作)

首先,这篇文章是基于笔尖AI写作进行文章创作的,喜欢的宝子,也可以去体验下,解放双手,上班直接摸鱼~ 按照惯例,先介绍下这款笔尖AI写作,宝子也可以直接下滑跳过看正文~ 笔尖Ai写作:…

瑞_23种设计模式_解释器模式

文章目录 1 解释器模式(Interpreter Pattern)1.1 介绍1.2 概述1.2.1 文法(语法)规则1.2.2 抽象语法树 1.3 解释器模式的结构1.4 解释器模式的优缺点1.5 解释器模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代…

【右一的开发日记】全导航,持续更新...

文章目录 📚前端【跟课笔记】🐇核心技术🐇高级技术 📚捣鼓捣鼓🐇小小案例🐇喵喵大王立大功🐇TED自用学习辅助网站🐇世界top2000计算机科学家可视化大屏🐇基于CBDB的唐代历…

【Java EE】MyBatis使用注解操作数据库

文章目录 🍀参数传递🌴增(Insert)🌸返回主键 🍃删(Delete)🌳改(Update)🌲查(Select)🌸起别名🌸结果映射🌸开启驼峰命名(推荐) ⭕总结 🍀参数传递 需求: 查找…

【JavaEE】进程的概念

文章目录 1、什么是进程(Process)2、PCB1.pid进程的id/标识符2.内存指针3.文件描述符表4、进程调度4.1状态4.2优先级4.3上下文4.4记账信息 1、什么是进程(Process) 一个程序,运行起来/跑起来,在操作系统中…

Delta lake with Java--利用spark sql操作数据1

今天要解决的问题是如何使用spark sql 建表,插入数据以及查询数据 1、建立一个类叫 DeltaLakeWithSparkSql1,具体代码如下,例子参考Delta Lake Up & Running第3章内容 import org.apache.spark.sql.SaveMode; import org.apache.spark.…

Ollamallama

Olllama 直接下载ollama程序,安装后可在cmd里直接运行大模型; llama 3 meta 开源的最新llama大模型; 下载运行 1 ollama ollama run llama3 2 github 下载仓库,需要linux环境,windows可使用wsl; 接…