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

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

    1. struct virt_dma_desc
    2. {
    3. struct dma_async_tx_descriptor tx;
    4. /* protected by vc.lock */
    5. struct list_head node;
    6. };
    7. struct virt_dma_chan
    8. {
    9. struct dma_chan chan;
    10. struct tasklet_struct task;
    11. void (*desc_free)(struct virt_dma_desc *);
    12. spinlock_t lock;
    13. /* protected by vc.lock */
    14. struct list_head desc_allocated;
    15. struct list_head desc_submitted;
    16. struct list_head desc_issued;
    17. struct list_head desc_completed;
    18. struct virt_dma_desc *cyclic;
    19. };

    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 中

    1. struct dma_slave_config
    2. {
    3. enum dma_transfer_direction direction;
    4. phys_addr_t src_addr;
    5. phys_addr_t dst_addr;
    6. enum dma_slave_buswidth src_addr_width;
    7. enum dma_slave_buswidth dst_addr_width;
    8. u32 src_maxburst;
    9. u32 dst_maxburst;
    10. bool device_fc;
    11. unsigned int slave_id;
    12. };

     (2)struct dma_async_tx_descriptor

    1. struct dma_async_tx_descriptor
    2. {
    3. dma_cookie_t cookie;
    4. enum dma_ctrl_flags flags; /*not a 'long' to pack with cookie*/
    5. dma_addr_t phys;
    6. struct dma_chan *chan;
    7. dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
    8. int (*desc_free)(struct dma_async_tx_descriptor *tx);
    9. dma_async_tx_callback callback;
    10. void *callback_param;
    11. struct dmaengine_unmap_data *unmap;
    12. #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
    13. struct dma_async_tx_descriptor *next;
    14. struct dma_async_tx_descriptor *parent;
    15. spinlock_t lock;
    16. #endif
    17. };

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

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

    3,驱动代码

    1. #include <linux/dmaengine.h>
    2. #include <linux/dma-mapping.h>
    3. #include <linux/types.h>
    4. #include <linux/slab.h>
    5. #include <linux/module.h>
    6. #include <linux/init.h>
    7. #include <linux/fs.h>
    8. #include <linux/sched.h>
    9. #include <linux/miscdevice.h>
    10. #include <linux/device.h>
    11. #include <linux/string.h>
    12. #include <linux/errno.h>
    13. #include <linux/types.h>
    14. #include <linux/slab.h>
    15. #include <linux/of_device.h>
    16. #include <linux/async_tx.h>
    17. #include <asm/uaccess.h>
    18. #include <asm/delay.h>
    19. #define DEVICE_NAME "ax_dma"
    20. #define MAX_SIZE (512*64)
    21. static char *src;
    22. static char *dst;
    23. dma_addr_t dma_src;
    24. dma_addr_t dma_dst;
    25. struct ax_dma_drv
    26. {
    27. struct dma_chan *chan;
    28. struct dma_device *dev;
    29. struct dma_async_tx_descriptor *tx;
    30. enum dma_ctrl_flags flags;
    31. dma_cookie_t cookie;
    32. };
    33. struct ax_dma_drv ax_dma;
    34. void dma_cb(void *dma_async_param)
    35. {
    36. if(!memcmp(src, dst, MAX_SIZE))
    37. {
    38. printk("dma irq test ok\r\n");
    39. }
    40. }
    41. static int dma_open(struct inode *inode, struct file *file)
    42. {
    43. printk("dma_open\r\n");
    44. return 0;
    45. }
    46. static int dma_release(struct inode *indoe, struct file *file)
    47. {
    48. printk("dma_release\r\n");
    49. return 0;
    50. }
    51. static ssize_t dma_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
    52. {
    53. int ret = 0;
    54. printk("dma_read\r\n");
    55. ax_dma.tx = ax_dma.dev->device_prep_dma_memcpy(ax_dma.chan, dma_dst, dma_src, MAX_SIZE, ax_dma.flags);
    56. if (!ax_dma.tx)
    57. {
    58. printk(KERN_INFO "Failed to prepare DMA memcpy");
    59. }
    60. ax_dma.tx->callback = dma_cb;
    61. ax_dma.tx->callback_param = NULL;
    62. ax_dma.cookie = ax_dma.tx->tx_submit(ax_dma.tx);
    63. if (dma_submit_error(ax_dma.cookie))
    64. {
    65. printk("DMA tx submit failed");
    66. }
    67. dma_async_issue_pending(ax_dma.chan);
    68. return ret;
    69. }
    70. static struct file_operations ax_fops =
    71. {
    72. .owner = THIS_MODULE,
    73. .open = dma_open,
    74. .read = dma_read,
    75. .release = dma_release,
    76. };
    77. static struct miscdevice dma_misc =
    78. {
    79. .minor = MISC_DYNAMIC_MINOR,
    80. .name = DEVICE_NAME,
    81. .fops = &ax_fops,
    82. };
    83. static int __init dma_init(void)
    84. {
    85. int ret=0;
    86. dma_cap_mask_t mask;
    87. ret = misc_register(&dma_misc);
    88. if(ret)
    89. {
    90. printk("misc_register failed!\n");
    91. return 0;
    92. }
    93. printk("drv register ok\n");
    94. of_dma_configure(dma_misc.this_device, dma_misc.this_device->of_node, true);
    95. dma_misc.this_device->coherent_dma_mask = 0xffffffff;
    96. //
    97. src = dma_alloc_coherent(dma_misc.this_device, MAX_SIZE, &dma_src, GFP_KERNEL);
    98. if (NULL == src)
    99. {
    100. printk("can't alloc buffer for src\n");
    101. return -ENOMEM;
    102. }
    103. //目标
    104. dst = dma_alloc_coherent(dma_misc.this_device, MAX_SIZE, &dma_dst, GFP_KERNEL);
    105. if (NULL == dst)
    106. {
    107. dma_free_coherent(NULL, MAX_SIZE, src, dma_src);
    108. printk("can't alloc buffer for dst\n");
    109. return -ENOMEM;
    110. }
    111. printk("buffer alloc ok\n");
    112. //初始化mask
    113. dma_cap_zero(mask);
    114. dma_cap_set(DMA_MEMCPY, mask);
    115. ax_dma.chan = dma_request_channel(mask, NULL, NULL);
    116. ax_dma.flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
    117. ax_dma.dev = ax_dma.chan->device;
    118. printk("chan request ok\n");
    119. //给源地址一个初值
    120. memset(src, 0x5A, MAX_SIZE);
    121. //给目标地址一个不一样的初值
    122. memset(dst, 0xA5, MAX_SIZE);
    123. return 0;
    124. }
    125. static void __exit dma_exit( void )
    126. {
    127. dma_release_channel(ax_dma.chan);
    128. dma_free_coherent(dma_misc.this_device, MAX_SIZE, src, dma_src);
    129. dma_free_coherent(dma_misc.this_device, MAX_SIZE, dst, dma_dst);
    130. misc_deregister(&dma_misc);
    131. }
    132. //驱动入口函数标记
    133. module_init(dma_init);
    134. //驱动出口函数标记
    135. module_exit(dma_exit);
    136. /* 驱动描述信息 */
    137. MODULE_AUTHOR("subomb");
    138. MODULE_ALIAS("dma");
    139. MODULE_DESCRIPTION("DMA driver");
    140. MODULE_VERSION("v3.0");
    141. MODULE_LICENSE("GPL");

     4,DMA测试代码

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. #include <fcntl.h>
    5. #include <sys/ioctl.h>
    6. #include <string.h>
    7. #include "unistd.h"
    8. int main(int argc, char **argv)
    9. {
    10. int fd;
    11. char *filename;
    12. if(argc != 2)
    13. {
    14. printf("Error Usage\r\n");
    15. return -1;
    16. }
    17. filename = argv[1];
    18. fd = open(filename, O_RDWR);
    19. if (fd < 0)
    20. {
    21. printf("can't open %s\n", filename);
    22. return -1;
    23. }
    24. read(fd, NULL, 0);
    25. close(fd);
    26. return 0;
    27. }

  • 相关阅读:
    使用注解的方式装配Bean
    总结 CNN 模型:将焦点转移到基于注意力的架构
    【GitHub】Watch、Star、Fork、Follow 有什么区别?
    Bugku sql注入
    uni-app云打包(安卓)及push模块添加
    【快应用】二级页面如何携带参数返回一级页面?
    Flink学习4 - 富函数 + 数据重分区操作 + sink 操作(kafka、redis、jdbc)
    《Linux运维总结:Centos7.6之OpenSSH7.4升级版本至9.4》
    数字孪生3d智慧核电可视化场景应用展示,包括:智能计算,智能运维
    原型链污染攻击也称JavaScript Prototype 污染攻击
  • 原文地址:https://blog.csdn.net/wangjie36/article/details/134398539