linux DMA设备驱动详解

news2024/12/23 10:08:31

 一,DMA相关定义(fpga、wait_queue 、device、interrupt、 dma_request_channel 函数、dma_start_transfer函数、poll、read,platform总线)

DMA (直接内存读写)是Direct Memory Access的缩写,也就是内存到内存,不占用CPU资源,但是会占用系统总线。DMA 支持内存到外设、外设到内存、内存到内存的数据交互,必要时节省很多CPU 资源。

1,transfer wide 可以理解为单次传输数据的大小,串口一次叧能传一个字节,而 DMA 则可以选择一次能传输的数据大小。在返基础上的 transfer size 则是传输的次数,不是单纯的总大小,也就是说 DMA 传输总长度实际上是transfer size乘上transfer wide。

2,burst size 是指DMAC内部缓存大小。当DMA 传输的源或目的是内存 memory 时,DMAC会先读取数据到缓存,再传入或传出。

3,scatter-gather:DMA操作必项是连续的物理内存,实际应用中,难免会遇到处理物理内存不连续的数据。scatter-gather指的就是把不连续的数据拷贝到连续的 buffer 中的操作。返个操作过程可以用软件实现,有直接的硬件支持。返里主要是强调 DMA 操作必项是连续的物理内存返件事。

二,linux 中的 DMA 框架

linux DMA engine 框架提供了 DMA controller和DMA client 两个框架。分别对应 DMA 提供者和 DMA使用者两个角度。pl330 是个 DMA 控制器,实际上就是站DMA提供者的角度。使用DMA的对象实际可以具体到内存到内存,内存就是DMA的使用者。DMA 控制器相关的操作都可以抽出来,他们对于其他使用者来说是想通的,这也是 linux 系统一贯的设计思路。DMA controller 框架抽象出 channel 对应 DMAC 的物理通道,又定义了虚拟的 channel,软件上可以实现多个虚拟 channel 对应一个物理通道。

 1,struct dma_device 定义在include/linux/dmaengine.h 中

1. struct dma_device 
2.{
3. unsigned int chancnt;
4. unsigned int privatecnt;
5. struct list_head channels;
6. struct list_head global_node;
7. struct dma_filter filter;
8. dma_cap_mask_t cap_mask;
9. unsigned short max_xor;
10. unsigned short max_pq;
11. enum dmaengine_alignment copy_align;
12. enum dmaengine_alignment xor_align;
13. enum dmaengine_alignment pq_align;
14. enum dmaengine_alignment fill_align;
15. #define DMA_HAS_PQ_CONTINUE (1 << 15)
16.
17. int dev_id;
18. struct device *dev;
19.
20. u32 src_addr_widths;
21. u32 dst_addr_widths;
22. u32 directions;
23. u32 max_burst;
24. bool descriptor_reuse;
25. enum dma_residue_granularity residue_granularity;
26.
27. int (*device_alloc_chan_resources)(struct dma_chan *chan);
28. void (*device_free_chan_resources)(struct dma_chan *chan);
29.
30. struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
31. struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
32. size_t len, unsigned long flags);
33. struct dma_async_tx_descriptor *(*device_prep_dma_xor)(
34. struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src,
35. unsigned int src_cnt, size_t len, unsigned long flags);
36. struct dma_async_tx_descriptor *(*device_prep_dma_xor_val)(
37. struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt,
38. size_t len, enum sum_check_flags *result, unsigned long flags);
39. struct dma_async_tx_descriptor *(*device_prep_dma_pq)(
40. struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
41. unsigned int src_cnt, const unsigned char *scf,
42. size_t len, unsigned long flags);
43. struct dma_async_tx_descriptor *(*device_prep_dma_pq_val)(
44. struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
45. unsigned int src_cnt, const unsigned char *scf, size_t len,
46. enum sum_check_flags *pqres, unsigned long flags);
47. struct dma_async_tx_descriptor *(*device_prep_dma_memset)(
48. struct dma_chan *chan, dma_addr_t dest, int value, size_t len,
49. unsigned long flags);
50. struct dma_async_tx_descriptor *(*device_prep_dma_memset_sg)(
51. struct dma_chan *chan, struct scatterlist *sg,
52. unsigned int nents, int value, unsigned long flags);
53. struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)(
54. struct dma_chan *chan, unsigned long flags);
55. struct dma_async_tx_descriptor *(*device_prep_dma_sg)(
56. struct dma_chan *chan,
57. struct scatterlist *dst_sg, unsigned int dst_nents,
58. struct scatterlist *src_sg, unsigned int src_nents,
59. unsigned long flags);
60.
61. struct dma_async_tx_descriptor *(*device_prep_slave_sg)(
62. struct dma_chan *chan, struct scatterlist *sgl,
63. unsigned int sg_len, enum dma_transfer_direction direction,
64. unsigned long flags, void *context);
65. struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
66. struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
67. size_t period_len, enum dma_transfer_direction direction,
68. unsigned long flags);
69. struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
70. struct dma_chan *chan, struct dma_interleaved_template *xt,
71. unsigned long flags);
72. struct dma_async_tx_descriptor *(*device_prep_dma_imm_data)(
73. struct dma_chan *chan, dma_addr_t dst, u64 data,
74. unsigned long flags);
75.
76. int (*device_config)(struct dma_chan *chan,
77. struct dma_slave_config *config);
78. int (*device_pause)(struct dma_chan *chan);
79. int (*device_resume)(struct dma_chan *chan);
80. int (*device_terminate_all)(struct dma_chan *chan);
81. void (*device_synchronize)(struct dma_chan *chan);
82.
83. enum dma_status (*device_tx_status)(struct dma_chan *chan,
84. dma_cookie_t cookie,
85. struct dma_tx_state *txstate);
86. void (*device_issue_pending)(struct dma_chan *chan);
87. };

2,struct virt_dma_cha 定义在文件 drivers/dma/virt-dma.h 中

 struct virt_dma_desc 
 {
   struct dma_async_tx_descriptor tx;
   /* protected by vc.lock */
   struct list_head node;
 };

struct virt_dma_chan 
{
   struct dma_chan chan;
   struct tasklet_struct task;
   void (*desc_free)(struct virt_dma_desc *);
 
   spinlock_t lock;
  /* protected by vc.lock */
   struct list_head desc_allocated;
   struct list_head desc_submitted;
   struct list_head desc_issued;
   struct list_head desc_completed;
 
   struct virt_dma_desc *cyclic;
};

chan:一个 struct dma_chan类型的发量,用于和 client 交互。
task:一个 tasklet,等待该虚拟 channel 的传输完成。
desc_allocated、desc_submitted、desc_issued、desc_completed:四个链表头,用于保存不同状态的虚拟 channel 描述符。

三,DMA controller 框架相关 API

1,struct dma_device 注册和注销:

struct dma_device 初始化完成后,调用 dma_async_device_register 向内核注册。注册成功后 dma_device 会放在一个名称为 dma_device_list 的全局链表上,以便后面使用。

int dma_async_device_register(struct dma_device *device);
void dma_async_device_unregister(struct dma_device *device);//注销函数

2,DMA cookie 表示 DMA engine 在数据传送中使用的一段连续内存。

static inline void dma_cookie_init(struct dma_chan *chan)
static inline dma_cookie_t dma_cookie_assign(struct dma_async_tx_descriptor *tx)
static inline void dma_cookie_complete(struct dma_async_tx_descriptor *tx)
static inline enum dma_status dma_cookie_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *state)

dma_cookie_init:初始化 channel 中的 cookie、completed_cookie。
dma_cookie_assign:为指针的传输描述分配一个 cookie。
dma_cookie_complete:一个传输完成时,可调用该接口更新该传输对应channel的completed_cookie字段。
dma_cookie_status:获叏挃定 channel 挃定 cookie 的传输状态。

四,DMA client 驱动框架

从源和目标的不同可以把 DMA 划分为四类:内存到内存、内存到外设、外设到内存、外设到外设。因为内存可以使用 memcpy、memset 等操作,linux engine中把内存到内存返一部分分离出来单独提供了一套API-Async TX API。剩余的三类就共用一个结构Slave-DMA API。Slave指代client :也就是DMA 使用者。应用层和驱动配合大致流程:FPGA-->产生一个DMA中断(pl中断),唤醒读数线程,告诉驱动有数据需要传输-->应用层调用驱动申请一个合适的DMA通道-->应用层调用read函数(在read函数中完成dev->dmamem的传输)读取DMA(dmamem)数据缓存的数据到用户空间。

1,内核中DMA client相关数据 结构

(1)struct dma_slave_config定义在 include/linux/dmaengine.h 中

struct dma_slave_config 
{
   enum dma_transfer_direction direction;
   phys_addr_t src_addr;
   phys_addr_t dst_addr;
   enum dma_slave_buswidth src_addr_width;
   enum dma_slave_buswidth dst_addr_width;
   u32 src_maxburst;
   u32 dst_maxburst;
   bool device_fc;
   unsigned int slave_id;
};

 (2)struct dma_async_tx_descriptor

struct dma_async_tx_descriptor 
{
   dma_cookie_t cookie;
   enum dma_ctrl_flags flags; /*not a 'long' to pack with cookie*/
   dma_addr_t phys;
   struct dma_chan *chan;
   dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
   int (*desc_free)(struct dma_async_tx_descriptor *tx);
   dma_async_tx_callback callback;
   void *callback_param;
   struct dmaengine_unmap_data *unmap;
   #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
   struct dma_async_tx_descriptor *next;
   struct dma_async_tx_descriptor *parent;
   spinlock_t lock;
   #endif
};

2,Slave-DMA API 的 DMA client实现步骤

      5)等待传输结束
等徃传输可以通过回掉函数,也可以通过 dma_async_is_tx_complete 等函数数查询传输是否
完成。另外可以使用 maengine_pause、dmaengine_resume 函数,暂停、终止传输。

3,驱动代码

#include <linux/dmaengine.h>  
#include <linux/dma-mapping.h>  
#include <linux/types.h>  
#include <linux/slab.h>  
#include <linux/module.h>  
#include <linux/init.h>  
#include <linux/fs.h>  
#include <linux/sched.h>  
#include <linux/miscdevice.h>  
#include <linux/device.h>  
#include <linux/string.h>  
#include <linux/errno.h>  
#include <linux/types.h>  
#include <linux/slab.h>  
#include <linux/of_device.h>  
#include <linux/async_tx.h>
#include <asm/uaccess.h>  
#include <asm/delay.h>  
   
#define DEVICE_NAME "ax_dma"
   
#define MAX_SIZE (512*64)  

static char *src;  
static char *dst;  
dma_addr_t dma_src;  
dma_addr_t dma_dst;  
  
struct ax_dma_drv 
{
    struct dma_chan *chan;  
    struct dma_device *dev;  
    struct dma_async_tx_descriptor *tx;  
    enum dma_ctrl_flags flags;  
    dma_cookie_t cookie;  
};
struct ax_dma_drv ax_dma;
   
void dma_cb(void *dma_async_param)  
{
    if(!memcmp(src, dst, MAX_SIZE))  
    {  
        printk("dma irq test ok\r\n");  
    }  
}  
  
static int dma_open(struct inode *inode, struct file *file)  
{  
    printk("dma_open\r\n");  
    return 0;  
}  
   
static int dma_release(struct inode *indoe, struct file *file)  
{  
    printk("dma_release\r\n");   
    return 0;  
}  
   
static ssize_t dma_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)  
{  
    int ret = 0;  
    printk("dma_read\r\n"); 
    
    ax_dma.tx = ax_dma.dev->device_prep_dma_memcpy(ax_dma.chan, dma_dst, dma_src, MAX_SIZE, ax_dma.flags);  
    if (!ax_dma.tx)
    {  
        printk(KERN_INFO "Failed to prepare DMA memcpy");  
    }  
   
    ax_dma.tx->callback = dma_cb;  
    ax_dma.tx->callback_param = NULL;  
    ax_dma.cookie = ax_dma.tx->tx_submit(ax_dma.tx); 
    if (dma_submit_error(ax_dma.cookie))
    {  
        printk("DMA tx submit failed");  
    }  
    dma_async_issue_pending(ax_dma.chan);  

    return ret;  
}  
   
static struct file_operations ax_fops =  
{  
    .owner   = THIS_MODULE,  
    .open    = dma_open,  
    .read    = dma_read,  
    .release = dma_release,  
};  
   
static struct miscdevice dma_misc =  
{  
    .minor = MISC_DYNAMIC_MINOR,  
    .name  = DEVICE_NAME,  
    .fops  = &ax_fops,  
};  
   
static int __init dma_init(void)  
{  
    int ret=0;   
    dma_cap_mask_t mask;   
    
    ret = misc_register(&dma_misc);  
    if(ret)  
    {  
        printk("misc_register failed!\n");  
        return 0;  
    } 
    printk("drv register ok\n");
    of_dma_configure(dma_misc.this_device, dma_misc.this_device->of_node, true);
    dma_misc.this_device->coherent_dma_mask = 0xffffffff;
    
    //源
    src = dma_alloc_coherent(dma_misc.this_device, MAX_SIZE, &dma_src, GFP_KERNEL);
    if (NULL == src)
    {
        printk("can't alloc buffer for src\n");
        return -ENOMEM;
    }
    //目标
    dst = dma_alloc_coherent(dma_misc.this_device, MAX_SIZE, &dma_dst, GFP_KERNEL); 
    if (NULL == dst)
    {
        dma_free_coherent(NULL, MAX_SIZE, src, dma_src);
        printk("can't alloc buffer for dst\n");
        return -ENOMEM;
    }
    printk("buffer alloc ok\n");
    
    //初始化mask
    dma_cap_zero(mask);  
    dma_cap_set(DMA_MEMCPY, mask);  
    ax_dma.chan = dma_request_channel(mask, NULL, NULL);  
    ax_dma.flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;  
    ax_dma.dev = ax_dma.chan->device;  
    printk("chan request ok\n");
   
    //给源地址一个初值
    memset(src, 0x5A, MAX_SIZE);   
    //给目标地址一个不一样的初值
    memset(dst, 0xA5, MAX_SIZE);  
   
    return 0;  
}  
   
static void __exit dma_exit( void )  
{   
    dma_release_channel(ax_dma.chan);
    dma_free_coherent(dma_misc.this_device, MAX_SIZE, src, dma_src);
    dma_free_coherent(dma_misc.this_device, MAX_SIZE, dst, dma_dst);   
    misc_deregister(&dma_misc);  
}  
  
//驱动入口函数标记  
module_init(dma_init);  
//驱动出口函数标记  
module_exit(dma_exit);  
  
/* 驱动描述信息 */    
MODULE_AUTHOR("subomb");    
MODULE_ALIAS("dma");    
MODULE_DESCRIPTION("DMA driver");    
MODULE_VERSION("v3.0");    
MODULE_LICENSE("GPL");

 4,DMA测试代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include "unistd.h"
int main(int argc, char **argv)
{
    int fd;
    char *filename;
    if(argc != 2)
    {
        printf("Error Usage\r\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("can't open %s\n", filename);
        return -1;
    }
    read(fd, NULL, 0);
    close(fd);
    return 0;
}

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

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

相关文章

Java开源工具库使用之线上监控诊断库Arthas

文章目录 前言一、介绍1.1 功能1.2 原理 二、安装使用2.1 下载2.2 使用 三、常用3.1 实时查看3.2 追踪查看3.3 辅助命令3.4 热更新3.5 监控 四、实战4.1 CPU/内存占用过高4.2 接口耗时高4.3 找到类所在jar4.4 查找类的实例4.5 生成火焰图 参考 前言 在现代软件开发中&#xff…

如何利用ChatGPT撰写学术论文?

在阅读全文前请注意&#xff0c;本文是利用ChatGPT“辅助完成”而不是“帮写”学术论文&#xff0c;请一定要注意学术规范&#xff01; 本文我将介绍如何使用清晰准确的“指令”让ChatGPT帮助我们在论文写作上提高效率&#xff0c;希望通过本文的指导&#xff0c;读者能够充分…

OpenCV入门——概述 || 环境搭建

文章目录 OpenCV与图形学的关系OpenCV与FFmpeg的关系博客内容概览计算机视觉的应用OpenCV简介环境搭建 OpenCV与图形学的关系 从图形中理解其内容&#xff0c;是计算机视觉的任务&#xff1b;通过模型渲染出图像&#xff0c;是图形学的任务。 OpenCV与FFmpeg的关系 博客内容概…

Android开发入门课

设备环境搭建 1. 安装模拟器 雷电模拟器官网 : https://www.ldmnq.com/?n=6000 注:选择一个喜欢的版本安装即可,建议安装3版本、自带系统为安卓7以下。 不过雷电没有mac版本,需要mac版本的模拟器,可以使用夜神模拟器: Mac版本更新信息 | 夜神模拟器帮助中心 2. 安装…

万万没想到,金融人都在考的CFA和CPA在中国人民大学与加拿大女王大学金融硕士的用途这么大?

CFA和CPA是金融行业中两个非常重要的专业认证。它们分别代表了不同的职业方向和技能要求&#xff0c;对于想要在金融行业发展的人来说&#xff0c;拥有这两个认证之一或者两者都具备是非常有帮助的。听说拥有CFA和CPA在中国人民大学与加拿大女王大学金融硕士的用途也很大&#…

基于SPI+DMA方式的ws2812b氛围灯控制

好处&#xff1a;相比于gpio控制&#xff0c;可以大大节省CPU的时间&#xff0c;CPU只要将要传输的数据计算好放入内存中&#xff0c;然后发动DMA传输即可&#xff0c;后续整个过程并不需要CPU干预&#xff0c;CPU可以用于做其他的事情。特别是某些带蓝牙的芯片&#xff0c;需要…

【移远QuecPython】EC800M物联网开发板的MQTT协议腾讯云数据上报

【移远QuecPython】EC800M物联网开发板的MQTT协议腾讯云数据上报 文章目录 导入库初始化设置MQTT注册回调订阅发布功能开启服务发送消息函数打包调用测试效果附录&#xff1a;列表的赋值类型和py打包列表赋值BUG复现代码改进优化总结 py打包 导入库 from TenCentYun import TX…

MongoDB的简单使用

Linux安装MongoDB-CSDN博客 简单使用 /usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf 启动 新建数据库 test MongoDB里 db.createCollection("books") 创建名为books的集合 好比表 db.getCollection(books).find({}) 查询所有 db.books.sa…

【go】报错整理与解决

文章目录 依赖下载失败checksum mismatch启动报错missing go.sum 依赖下载失败checksum mismatch > go get github.com/hibiken/asynqmon go: downloading github.com/hibiken/asynqmon v0.7.2 go: github.com/hibiken/asynqmonv0.7.2: verifying module: checksum mismatc…

QGIS之十六过滤器选择要素导出

效果 步骤 1、准备数据 下面这份数据是中国范围内的市级行政区划范围 2、打开表格 3、选择要素 方法1 从图上能看到选中的图形 方法2 4、导出

批量重命名软件推荐 A Better Finder Rename 12最新 for mac

A Better Finder Rename的大量重命名选项被组织成15个直观的类别&#xff0c;涵盖了一个伟大的文件重命名器所期望的所有文本&#xff0c;字符&#xff0c;位置&#xff0c;转换和截断功能。 除此之外&#xff0c;A Better Finder Rename提供了更多高级功能&#xff0c;可以满…

VMware 虚拟机开启后黑屏问题的解决方式

很好&#xff0c;现在是vm 虚拟机节目的连续剧了 首先&#xff0c;我们安装好了&#xff0c;vm软件。 其次&#xff0c;我们在vm中创建了虚拟机。 再其次&#xff0c;我们解决了&#xff0c;开启虚拟机计算机自动重启的问题。 最后我们遇到了这个问题&#xff1a;虚拟机开启后整…

独立站邮件营销大佬,手把手教你如何做好!

做独立站邮件营销的方式&#xff1f;独立站怎么做邮件营销&#xff1f; 邮件营销&#xff0c;作为独立站营销的重要手段之一&#xff0c;越来越受到卖家的重视。如何才能做好邮件营销呢&#xff1f;蜂邮EDM将手把手教你如何做好独立站邮件营销&#xff0c;让你在电商领域中更上…

RocketMQ(4.9.4)学习笔记 - 安装部署

单机部署&#xff1a; 官网文档地址&#xff1a; https://rocketmq.apache.org/zh/docs/4.x 参考文档&#xff1a; windows安装RocketMQ_rocketmq windows_book多得的博客-CSDN博客 下载地址&#xff1a; https://archive.apache.org/dist/rocketmq/4.9.4/rocketmq-all-4.9.…

kafka单节点创建 topic 超时

1.根据之前的知道&#xff0c;安装kafka的时候改了config的server.properies文件中的listeners配置 之前这一行是没有注释掉的&#xff0c;结果创建topic的时候时钟报错连接超时 结果资料&#xff0c;发现就是因为listeners的问题 https://blog.csdn.net/weixin_42133361/art…

批量整理相同名称文件,高效归类至指定文件夹!

你是否曾经遇到过成百上千的文件名重复&#xff0c;让人无从下手的情况&#xff1f;现在&#xff0c;我们为你带来了一款全新的文件管理工具&#xff0c;它可以让你轻松地将相同名称的文件批量整理归类到指定文件夹中&#xff0c;让你的文件管理更加高效、有序&#xff01; 第…

Java系列之 IDEA 为类 和 方法设置注解模板

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、类方法注解模板 1、File–>settings–…

污水处理系统有哪些设备

污水处理系统通常包括以下几种主要设备&#xff1a; 格栅&#xff1a;用于拦截大颗粒悬浮物&#xff0c;以保护后续处理设备。泵&#xff1a;用于提升污水&#xff0c;使其能够被输送到处理设备的上游。曝气池&#xff1a;通过向污水中注入空气&#xff0c;使污水中的有机物质…

【23真题】坑挖的不错,题目也有质量!

今天分享的是23年西安石油大学810的信号与系统试题及解析。 本套试卷难度分析&#xff1a;22年西安石油810考研真题&#xff0c;我也发布过&#xff0c;若有需要&#xff0c;戳这里自取!本套试题内容难度中等偏下&#xff0c;题量较少&#xff0c;没有考察选填题&#xff0c;通…

Ubuntu 24.04发布日期以定

导读Ubuntu 的下一个长期支持 (LTS) 版本 Ubuntu 24.04 的最终发布日期已确定&#xff0c;计划于 2024 年 4 月 25 日发布。 Ubuntu 的下一个长期支持 (LTS) 版本 Ubuntu 24.04 的最终发布日期已确定&#xff0c;计划于 2024 年 4 月 25 日发布。 除此之外&#xff0c;Ubuntu…