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的核心实现如下:
- 定义一个struct dma_device变量,并根据实际的硬件情况,填充其中的关键字段(如device_alloc_chan_resources,device_free_chan_resources接口等)。
- 调用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注解 后端参数重命名(后端参数映射)六、传递数组七、传递集合,RequestParam八、传递JSON数据8.1 JSON字符串和Java对象互转8.1.1 Test注解8.1.2 Java对象转JSON8.…...

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

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

11、集合框架
一、简介 Java集合框架位于java.util包中 Collection是Set和List的父类,Collections是工具类,提供了对集合进行排序、遍历等多种算法的实现。 ArrayList: 有序(放进去顺序和拿出来顺序一致),可重复 HashSet: 无序(放进去顺序和拿出来顺序不…...
git常用指令详解
文章目录 Git 基本指令的使用Git 远程仓库Git的分支管理 Git 基本指令的使用 git init //初始化一个git仓库,在当前目录下生成.git文件夹,并且会默认生成一个master分支。git clone <url> [directory] //url为git仓库地址,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日,全球首富埃隆马斯克(Elon Musk)携手其人工智能公司xAI,在美国重磅发布了Grok 3.0 Beta版。这款被誉为“迄今为止世界上最智能的语言模型”的AI,不仅集成了先进的“DeepSearch”搜索功能࿰…...

AcWing 3691:有向树形态 ← 卡特兰数 + 复旦大学考研机试题
【题目来源】 https://www.acwing.com/problem/content/3694/ 【题目描述】 求 N 个相同结点能够组成的二叉树的个数。 【输入格式】 一个整数 N。 【输出格式】 输出能组成的二叉树的个数。 【数据范围】 1≤N≤20 【输入样例】 3 【输出样例】 5 【算法分析】 ● 卡特…...
便携式动平衡仪Qt应用层详细设计方案(基于Qt Widgets)
便携式动平衡仪Qt应用层详细设计方案(基于Qt Widgets) 版本:1.0 日期:2023年10月 一、系统概述 1.1 功能需求 开机流程:长按电源键启动,全屏显示商标动画(快闪3~4次)。主界面&…...

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

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

论文笔记(七十二)Reward Centering(四)
Reward Centering(四) 文章概括摘要附录A 伪代码 文章概括 引用: 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格式文件
点击图像的右上角,点击第一个保存按钮键。...

官方文档学习TArray容器
一.TArray中的元素相等 1.重载一下 元素中的 运算符,有时需要重载排序。接下来,我们将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(Tiny Encryption Algorithm)算法是一种分组加密算法,由剑桥大学计算机实验室的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看到我们的环境创建成功了,但是发现他是创建在我们默认的C盘的…...
详解 为什么 tcp 会出现 粘包 拆包 问题
TCP 会出现 粘包 和 拆包 问题,主要是因为 TCP 是 面向字节流 的协议,它不关心应用层发送的数据是否有边界,也不会自动分割或合并数据包。由于 TCP 的流控制和传输机制,数据可能在传输过程中被拆分成多个小的 TCP 包,或…...
Linus的基本命令
以下是一些常见的 Linux 命令: 一、文件和目录操作: - ls:列出目录中的文件和子目录,常用参数有 -a (显示所有文件,包括隐藏文件)、 -l (显示详细信息)、 -h ࿰…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

力扣热题100 k个一组反转链表题解
题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...