当前位置: 首页 > news >正文

Linux DMA Engine 基础

1 DMA基础信息查看 /sys/class/dma

root:~# ls /sys/class/dma/
dma0chan0   dma1chan10  dma1chan27  dma2chan14  dma2chan30  dma2chan47  dma2chan63  dma3chan21  dma3chan38  dma3chan54
dma0chan1   dma1chan11  dma1chan28  dma2chan15  dma2chan31  dma2chan48  dma2chan7   dma3chan22  dma3chan39  dma3chan55
......

还可以查看特定通道信息:

root:~# ls /sys/class/dma/dma2chan1/
bytes_transferred  device  in_use  memcpy_count  power  slave  subsystem  uevent

该命令查看有哪些可用的dma channel, 每个dma有多少个channel. 还可以,
查看特定channel是否在使用,下面这个就是在使用:

cat /sys/class/dma/dma2chan1/in_use
1

2 调试 DMA Engine /sys/kernel/debug/dmaengine

它通过 debugfs 文件系统提供了对 DMA 控制器、通道、传输状态等底层信息的访问,是开发者排查 DMA 相关问题时的重要工具。

2.1 目录结构与核心文件

root:~# ls /sys/kernel/debug/dmaengine/
42000000.dma-controller  42df0000.dma-controller  44000000.dma-controller  4b110000.epxp  summary

虽然网上说的这个目录的调试用法天花乱坠,但是我发现只有summary有用:

root:~# cat /sys/kernel/debug/dmaengine/summary
dma0 (4b110000.epxp): number of channels: 16dma1 (44000000.dma-controller): number of channels: 32dma1chan0    | in-usedma1chan20   | 44380000.serial:txdma1chan21   | 44380000.serial:rxdma2 (42000000.dma-controller): number of channels: 64dma2chan0    | 42530000.i2c:tx (给出了具体分给哪个节点了)dma2chan1    | 42530000.i2c:rxdma3 (42df0000.dma-controller): number of channels: 64dma3chan0    | 42540000.i2c:txdma3chan1    | 42540000.i2c:rxdma3chan2    | 426c0000.i2c:txdma3chan3    | 426c0000.i2c:rx

可以发现,得到的信息包括:控制器名称,如44000000.dma-controller,总通道数 32、64,已经分配的通道分给哪个具体的节点了,还给出了通道对应的外设偏移地址 等。

3 看手册学习

linux-kernel$ ls Documentation/driver-api/dmaengine/
client.rst  dmatest.rst  index.rst  provider.rst  pxa_dma.rst

index.rst 就是个简单的摘要。
dmatest.rst 如何编译和使用dmatest工具来测试指定dma channel。
client.rst 如何使用DMAEngine的Slave-DMA API(仅适用slave DMA)。
provider.rst

3.1 dmatest.rst

文档说明dmatest工具怎么编译,如何使用来测试dma, 该工具只能测试memory to memory。

如何使用edma进行内存到内存测试echo 2000 > /sys/module/dmatest/parameters/timeout  单位是毫秒
echo 1 > /sys/module/dmatest/parameters/iterations
echo dma1chan0 > /sys/module/dmatest/parameters/channel //可以让多个通道同时测试
echo dma1chan1 > /sys/module/dmatest/parameters/channel
echo dma1chan2 > /sys/module/dmatest/parameters/channel
【注】此时通过 cat /sys/kernel/debug/dmaengine/summary 可以看哪些通道正在使用。
echo 1 > /sys/module/dmatest/parameters/run 为1时会启动传输,写0会释放通道
【注】可以通过cat /proc/interrupts | grep dma 查看对应的DMA channel是否发生中断。检查手段
ls /sys/class/dma/ 可以得到哪些cannel可用
echo "" > /sys/module/dmatest/parameters/channel 这样会将所有不忙的通道加入测试,我日了。
cat /sys/module/dmatest/parameters/run 查看run的状态
cat /sys/module/dmatest/parameters/channel 可用查看成功添加的channel
cat /sys/module/dmatest/parameters/test_list 会打印pending的test
echo 0 > /sys/module/dmatest/parameters/run 会释放通道。
dmesg | tail -n 1 查看位于kernel log的测试结果,会显示错误数量

3.2 provider.rst

对dmaengine框架做了介绍,DMAEngine APIs小节讲解内容比较重要。 Device operations 介绍了注册到dma_device structure的操作函数都是干嘛的,什么时候用。

3.3 client.rst

slave DMA 的使用包含的步骤:申请DMA slave channel, 设置参数,得到传输描述符,提交传输,等待回调。这篇文章讲解的很详细。

4 初识 DMA Engine

4.1 DMA controller 了解

DMA Engine是开发DMA控制器驱动程序的通用内核框架。DMA的主要目的是在复制内存的时候减轻CPU的负担。使用通道将事务(I/O数据传输)委托给DMA Engine,DMA Engine通过其驱动程序API提供一组可供其他设备(从设备)使用的通道.

Linux为了方便基于DMA的memcpy、memset等操作,在dma engine之上,封装了一层更为简洁的API,这些API就是Async TX API. 【参考博客】
因为 memory 到 memory 有了简洁的API,没必要直接使用 dma engine 提供的API了,所以把 dma engine 提供的API特指为 Slave-DMA API (slave指的是参与DMA传输的设备,比如IIC, UART等)

这篇博客介绍了如何依据DMA engine框架实现 dma controller driver 。

dma controller driver的核心实现如下:

  1. 定义一个struct dma_device变量,并根据实际的硬件情况,填充其中的关键字段(如device_alloc_chan_resources,device_free_chan_resources接口等)。
  2. 调用dma_async_device_register函数,将dma_device注册到dmaengine框架中。

4.2 如何使用 DMA engine 框架传输

对设备驱动的编写者来说,要基于dma engine提供的Slave-DMA API进行DMA传输的话,需要如下的操作步骤【参考博客】:
1)申请一个DMA channel。
2)根据设备(slave)的特性,配置DMA channel的参数。
3)要进行DMA传输的时候,获取一个用于识别本次传输(transaction)的描述符(descriptor)。
4)将本次传输(transaction)提交给dma engine并启动传输。
5)等待传输(transaction)结束。

4.3 代码示例

关键代码

struct dmatest_dev {struct cdev cdev;		/* 字符设备结构 */
};
static struct dmatest_dev *dmatest_dev_all;
static dev_t devt;           /* 用于存储设备号 */static int dmatest_major = DMATEST_MAJOR;
static int dmatest_minor = DMATEST_MINOR;
static int dmatest_nr_devs = 1; /* 设备数量 *///bus address 给DMA用
dma_addr_t dma_src;
dma_addr_t dma_dst;//virtual address 给驱动程序用
char *src = NULL;
char *dst = NULL ;struct dma_device *dev; //dma设备结构体
struct dma_chan *chan; //channel 结构体/*
* 申请主设备号,但需要自己用mknod的方式依据主设备号注册设备节点。
*/
static int dmatest_register_chrdev(void)
{int result;	if (dmatest_major) {devt = MKDEV(dmatest_major, dmatest_minor);result = register_chrdev_region(devt, dmatest_nr_devs, DEVICE_NAME); //向内核注册设备号, 设备名称示在 /proc/devices} else {result = alloc_chrdev_region(&devt, dmatest_minor, dmatest_nr_devs, DEVICE_NAME);//向内核注册设备号, 设备名称示在 /proc/devicesdmatest_major = MAJOR(devt);}if (result < 0) {printk(KERN_WARNING "dmatest: can't get major %d\n", dmatest_major);}return result;
}/* 初始化module */
static int dmatest_init(void)
{int i, result;dma_cap_mask_t mask;result = dmatest_register_chrdev();//向内核申请主设备号printk(KERN_NOTICE "%s : get device major number %d\n", __func__, dmatest_major);dmatest_dev_all = kmalloc(dmatest_nr_devs * sizeof(struct dmatest_dev), GFP_KERNEL);for (i = 0; i < dmatest_nr_devs; i++)dmatest_setup_cdev(&dmatest_dev_all[i], i); //调用cdev_init,cdev_add实现字符设备注册dma_cap_zero(mask);dma_cap_set(DMA_MEMCPY, mask); //direction:memory to memorychan = dma_request_channel(mask, 0, NULL); //request a dma channelflags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;dev = chan->device;//alloc 512B src memory and dst memorysrc = dma_alloc_coherent(dev->dev, 512, &dma_src, GFP_KERNEL); //一致性dma映射,参数3为总线地址会给DMA用,返回值为内核虚拟地址驱动程序可用pr_info("src = %px, dma_src = %#llx\n",src, dma_src);dst = dma_alloc_coherent(dev->dev, 512, &dma_dst, GFP_KERNEL); //一致性dma映射,参数3为总线地址,给DMA用,返回值为内核虚拟地址驱动程序可用pr_info("dst = %px, dma_dst = %#llx\n",dst, dma_dst);for (i = 0; i < 512; i++){*(src + i) = (unsigned char)(i % 256); //启动传输前将源地址初始化}
...
}/* 卸载退出module */
static void dmatest_exit(void)
{int i;if (dmatest_dev_all) {for (i = 0; i < dmatest_nr_devs; i++) {cdev_del(&dmatest_dev_all[i].cdev);}kfree(dmatest_dev_all);}unregister_chrdev_region(devt, dmatest_nr_devs);//free memory and dma channeldma_free_coherent(dev->dev, 512, src, dma_src);dma_free_coherent(dev->dev, 512, dst, dma_dst);dma_release_channel(chan);
}

注册字符设备

//When dma transfer finished,this function will be called.
static void dma_callback_func(void * arg)
{int i;printk("Debug in %s() \n", __func__);for (i = 0; i < 10; i++){pr_info("%c",dst[i]); //打印EDMA传输结束后目的地址内容}
}static int dmatest_open(struct inode *inode, struct file *filp)
{return 0;
}
static int dmatest_release(struct inode *inode, struct file *filp)
{return 0;
}
static ssize_t dmatest_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{int ret = 0;//alloc a desc,and set dst_addr,src_addr,data_size.tx = dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, 512, flags);if (!tx){pr_info("Failed to prepare DMA memcpy \n");}/* 设置回调函数 */tx->callback = dma_callback_func;tx->callback_param = NULL;cookie = tx->tx_submit(tx); //submit the descif (dma_submit_error(cookie)){pr_info("Failed to do DMA tx_submit \n");}/* begin dma transfer */dma_async_issue_pending(chan);return ret;
}static ssize_t dmatest_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{int ret = 0;return ret;
}static const struct file_operations dmatest_fops = {.owner = THIS_MODULE,.read = dmatest_read,.write = dmatest_write,.open = dmatest_open,.release = dmatest_release,
};/** dmatest charter device registration*/
static void dmatest_setup_cdev(struct dmatest_dev *devt, int index)
{int err, devno = MKDEV(dmatest_major, dmatest_minor + index);cdev_init(&devt->cdev, &dmatest_fops);devt->cdev.owner = THIS_MODULE;devt->cdev.ops = &dmatest_fops;err = cdev_add(&devt->cdev, devno, 1);if (err)printk(KERN_NOTICE "Error %d adding dmatest%d", err, index);
}

编译成module后运行 mknod_edma.sh 依据设备号注册设备节点, 文件内容如下。

#!/bin/sh
module='edma_test_lyrix'
device='edma_test_lyrix'
mode='664'# 使用传入该脚本的i所有参数调用insmod,同时使用路径名来指定模块位置
/sbin/insmod ./$module.ko $* || exit 1# 删除原有节点
rm -f /dev/${device}[0-4]major=$(awk "\$2==\"$module\"{print \$1}" /proc/devices)mknod /dev/${device}0 c $major 0

最后在文件系统中使用 cat /dev/edma_test_lyrix0 触发DMA拷贝操作,然后可以看到模块打印,验证其目的地址内容与源地址内容相同了。

相关文章:

Linux DMA Engine 基础

1 DMA基础信息查看 /sys/class/dma root:~# ls /sys/class/dma/ dma0chan0 dma1chan10 dma1chan27 dma2chan14 dma2chan30 dma2chan47 dma2chan63 dma3chan21 dma3chan38 dma3chan54 dma0chan1 dma1chan11 dma1chan28 dma2chan15 dma2chan31 dma2chan48 dma2…...

【JavaEE】SpringMVC 请求传参

目录 一、请求二、传递单个参数三、传递多个参数四、传递对象五、RequestParam注解 后端参数重命名&#xff08;后端参数映射&#xff09;六、传递数组七、传递集合&#xff0c;RequestParam八、传递JSON数据8.1 JSON字符串和Java对象互转8.1.1 Test注解8.1.2 Java对象转JSON8.…...

观察者模式说明(C语言版本)

观察者模式主要是为了实现一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时&#xff0c;会通知所有观察者对象&#xff0c;使它们能够自动更新自己。下面使用C语言实现了一个具体的应用示例&#xff0c;有需要的可以参考…...

LeetCode 230.二叉搜索树中第K小的元素

题目&#xff1a;给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 小的元素&#xff08;从 1 开始计数&#xff09;。 思路&#xff1a; 代码&#xff1a; /*** Definition for a binary tree node.* public class Tre…...

11、集合框架

一、简介 Java集合框架位于java.util包中 Collection是Set和List的父类&#xff0c;Collections是工具类&#xff0c;提供了对集合进行排序、遍历等多种算法的实现。 ArrayList: 有序(放进去顺序和拿出来顺序一致)&#xff0c;可重复 HashSet: 无序(放进去顺序和拿出来顺序不…...

git常用指令详解

文章目录 Git 基本指令的使用Git 远程仓库Git的分支管理 Git 基本指令的使用 git init //初始化一个git仓库,在当前目录下生成.git文件夹&#xff0c;并且会默认生成一个master分支。git clone <url> [directory] //url为git仓库地址&#xff0c;directory为本地目录 gi…...

Debezium 报错:“The db history topic is missing” 的处理方法

Debezium 报错:“The db history topic is missing” 的处理方法 一、引言 在使用 Debezium 进行数据同步时,可能会遇到一个常见的错误:“The db history topic is missing”。这个错误表明 Debezium 无法找到或访问其数据库历史记录主题(db history topic),这通常是由…...

Grok 3.0 Beta 版大语言模型评测

2025年2月17日至18日&#xff0c;全球首富埃隆马斯克&#xff08;Elon Musk&#xff09;携手其人工智能公司xAI&#xff0c;在美国重磅发布了Grok 3.0 Beta版。这款被誉为“迄今为止世界上最智能的语言模型”的AI&#xff0c;不仅集成了先进的“DeepSearch”搜索功能&#xff0…...

AcWing 3691:有向树形态 ← 卡特兰数 + 复旦大学考研机试题

【题目来源】 https://www.acwing.com/problem/content/3694/ 【题目描述】 求 N 个相同结点能够组成的二叉树的个数。 【输入格式】 一个整数 N。 【输出格式】 输出能组成的二叉树的个数。 【数据范围】 1≤N≤20 【输入样例】 3 【输出样例】 5 【算法分析】 ● 卡特…...

便携式动平衡仪Qt应用层详细设计方案(基于Qt Widgets)

便携式动平衡仪Qt应用层详细设计方案&#xff08;基于Qt Widgets&#xff09; 版本&#xff1a;1.0 日期&#xff1a;2023年10月 一、系统概述 1.1 功能需求 开机流程&#xff1a;长按电源键启动&#xff0c;全屏显示商标动画&#xff08;快闪3~4次&#xff09;。主界面&…...

SpringBoot源码解析(十一):准备应用上下文

SpringBoot源码系列文章 SpringBoot源码解析(一)&#xff1a;SpringApplication构造方法 SpringBoot源码解析(二)&#xff1a;引导上下文DefaultBootstrapContext SpringBoot源码解析(三)&#xff1a;启动开始阶段 SpringBoot源码解析(四)&#xff1a;解析应用参数args Sp…...

CSS 使用white-space属性换行

一、white-space属性的常见值 * 原本格式&#xff1a; 1、white-space:normal 默认值&#xff0c;空格和换行符会被忽略过滤掉&#xff1b;宽度不够时文本会自动换行 * 宽度足够时&#xff0c;normal 处理后的格式 * 宽度不够时&#xff0c; normal 处理后的格式 2、white-spa…...

论文笔记(七十二)Reward Centering(四)

Reward Centering&#xff08;四&#xff09; 文章概括摘要附录A 伪代码 文章概括 引用&#xff1a; article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan, Yi and Tomar, Manan and Sutton, Richard S},journal{arXiv preprint arXiv:2405.09999…...

Matlab——图像保存导出成好看的.pdf格式文件

点击图像的右上角&#xff0c;点击第一个保存按钮键。...

官方文档学习TArray容器

一.TArray中的元素相等 1.重载一下 元素中的 运算符&#xff0c;有时需要重载排序。接下来&#xff0c;我们将id 作为判断结构体的标识。 定义结构体 USTRUCT() struct FXGEqualStructInfo {GENERATED_USTRUCT_BODY() public:FXGEqualStructInfo(){};FXGEqualStructInfo(in…...

unxi-进程间通信

1.进程间通信实现方式 【1】同一主机 linux下通信方式: a.传统的进程间通信方式 管道 --- 进行数据传输的"管道" 无名管道 有名管道 信号 --- b.system v 进程间通信 (posix 进程间通信) 共享内存 (进程间…...

微型分组加密算法TEA、XTEA、XXTEA

微型分组加密算法TEA、XTEA、XXTEA TEA&#xff08;Tiny Encryption Algorithm&#xff09;算法是一种分组加密算法&#xff0c;由剑桥大学计算机实验室的‌David Wheeler和‌Roger Needham于1994年发明。TEA、XTEA、XXTEA算法采用64位的明文分组和128位的密钥。它使用Feistel…...

conda 基本命令

1、查询当前所有的环境 conda env list 2、创建虚拟环境 conda create -n 环境名 [pythonpython版本号] 其中[pythonpython版本号]可以不写 conda create -n test python3.12 我们输入conda env list看到我们的环境创建成功了&#xff0c;但是发现他是创建在我们默认的C盘的…...

详解 为什么 tcp 会出现 粘包 拆包 问题

TCP 会出现 粘包 和 拆包 问题&#xff0c;主要是因为 TCP 是 面向字节流 的协议&#xff0c;它不关心应用层发送的数据是否有边界&#xff0c;也不会自动分割或合并数据包。由于 TCP 的流控制和传输机制&#xff0c;数据可能在传输过程中被拆分成多个小的 TCP 包&#xff0c;或…...

Linus的基本命令

以下是一些常见的 Linux 命令&#xff1a; 一、文件和目录操作&#xff1a; - ls&#xff1a;列出目录中的文件和子目录&#xff0c;常用参数有 -a &#xff08;显示所有文件&#xff0c;包括隐藏文件&#xff09;、 -l &#xff08;显示详细信息&#xff09;、 -h &#xff0…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...