linux DMA设备驱动详解

 一,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_SWITCHstruct 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");//初始化maskdma_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.hqwc.cn/news/175584.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023-11-14 LeetCode每日一题(阈值距离内邻居最少的城市)

2023-11-14每日一题 一、题目编号 1334. 阈值距离内邻居最少的城市二、题目链接 点击跳转到题目位置 三、题目描述 有 n 个城市&#xff0c;按从 0 到 n-1 编号。给你一个边数组 edges&#xff0c;其中 edges[i] [fromi, toi, weighti] 代表 fromi 和 toi 两个城市之间的…

2023年亚太杯数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 赛题思路算法介绍FP树表示法构建FP树实现代码 建模资料 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#…

分库分表之后,主键ID如何处理?

前言 当关系型数据库数据量过大时&#xff0c;通常会采用分库分表降低数据库查表压力。分库分表有多种&#xff0c;有分一个库多张分表额&#xff0c;有分多个库多张表的。一般分库分表使用ShardingSphere分表&#xff0c;建分片键等。但是分库分表之后&#xff0c;主键ID如何处…

简易电路特性测试仪的设计(论文+源码)

1. 系统设计 系统主要由 STM32 单片机、DDS 模块、调理模块、特定放大器模块和电源模块 等组成。 DDS 模块使用低价格的 AD9833 芯片设计&#xff0c;产生调理模块需要的正弦信号。调理模块主要包 括特定放大器电路前端调理和后端调理&#xff1a;因为特定放大器进行性能…

Accelerate 0.24.0文档 二:DeepSpeed集成

文章目录 一、 DeepSpeed简介二、DeepSpeed集成&#xff08;Accelerate 0.24.0&#xff09;2.1 DeepSpeed安装2.2 Accelerate DeepSpeed Plugin2.2.1 ZeRO Stage-22.2.2 ZeRO Stage-3 with CPU Offload2.2.3 accelerate launch参数 2.3 DeepSpeed Config File2.3.1 ZeRO Stage-…

可以通过电脑远程控制安卓设备的软件

有些时候&#xff0c;我们需要用电脑远程控制安卓设备&#xff0c;比如远程维护门店设备、安卓系统的户外广告牌等等。我们来探索和比较几款允许用户通过电脑远程控制安卓设备的软件。 1、Splashtop Business Splashtop 是一种多功能远程访问解决方案&#xff0c;以其高性能流…

Istio学习笔记-部署模型

参考&#xff1a;Istioldie 1.18 / 部署模型 当您将 Istio 用于生产环境部署时&#xff0c;需要确定一系列的问题。 网格将被限制在单个集群中还是分布在多个集群中&#xff1f; 是将所有服务都放置在单个完全连接的网络中&#xff0c;还是需要网关来跨多个网络连接服务&#…

CNN进展:AlexNet、VGGNet、ResNet 和 Inception

一、说明 对于初学者来说&#xff0c;神经网络进展的历程有无概念&#xff1f;该文综合叙述了深度神经网络的革命性突破&#xff0c;从AlexNet开始&#xff0c;然后深度VGG的改进&#xff0c;然后是残差网络ResNet和 Inception&#xff0c;如果能讲出各种特色改进点的和改进理由…

fileread任意文件读取学习笔记

任意文件读取概述 一些网站的需求&#xff0c;可能会提供文件查看与下载的功能。如果对用户查看或下载的文件没有限制或者限制绕过&#xff0c;就可以查看或下载任意文件。这些文件可以是源代码文件&#xff0c;配置文件&#xff0c;敏感文件等等。 任意文件读取会造成&#x…

Android 12 intent-filter添加android:exported后任然报错解决方法

Android 12 或更高版本为目标平台&#xff0c;且包含使用intent-filter 过滤器的 activity、service或receiver&#xff0c;您必须为这些应用组件显式声明 android:exported 属性。 常规操作 查看AndroidManifest.xml文件&#xff0c;搜索intent-filter&#xff0c;然后添加好…

金属五要素微型气象仪科技小物大智慧

WX-WQX5S 天气是我们生活中不可忽视的一个因素。冷暖交替&#xff0c;阴晴不定&#xff0c;这款金属五要素微型气象仪&#xff0c;让你随时掌握天气变化&#xff0c;成为生活的智者。 一、外观设计 这款气象仪采用金属材质&#xff0c;不仅耐用&#xff0c;而且具有很高的美观…

3.1 Linux 前置知识

1、硬件 我们知道&#xff0c;组成计算机的硬件主要有“主机”和“输入/输出设备”。 主机包括机箱、电源、主板、CPU&#xff08;Central Processing Unit&#xff0c;中央处理器&#xff09;、内存、显卡、声卡、网卡、 硬盘、光驱等。输入/输出设备包括显示器、键盘、鼠标…