当前位置: 首页 > 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…...

使用VSCode开发Django指南

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

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; 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相关的已安装包

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

如何在网页里填写 PDF 表格?

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

Linux 中如何提取压缩文件 ?

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

力扣热题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指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...