• 流式DMA映射实践3:dmaengine与memcpy


    前言


            本次是第三篇。

            第一篇,写一个通用框架,做到拿来就能用。

            第二篇,实现mmap功能,内核中的read_buf和write_buf都映射到用户空间,然后呢。写read_buf和write_buf的最后一个字节为‘R’和'W',然后再release函数中打印这两个字节。更加复杂的验证,根据需要自行添加,写的太复杂,意义不大。

            第三篇,通过测试app,控制复制src_buf到dst_buf,复制方式可以使用DMA引擎和memcpy,并计算复制过程中消耗的微秒数,并在测试app中验证复制是否准确,尽最大努力保证整个流程的准确无误。

    一 dmaengine标准API

            Linux内核目前推荐使用dmaengine的驱动架构来编写DMA控制器的驱动,同时外设的驱动使用标准的dmaengine API进行DMA的准备、发起和完成时的回调工作。和中断一样,在使用DMA之前,设备驱动程序需首先向dmaengine系统申请DMA通道,申请DMA通道的函数如下:

    1. struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name);
    2. struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
    3. dma_filter_fn fn, void *fn_param);

    使用完DMA通道后,应该利用如下函数释放该通道:

    void dma_release_channel(struct dma_chan *chan);

            之后,一般通过如代码清单11.15的方法初始化并发起一次DMA操作。它通过dmaengine_prep_slave_single()准备好一些DMA描述符,并填充其完成回调为xxx_dma_fini_callback(),之后通过dmaengine_submit()把这个描述符插入队列,再通过dma_async_issue_pending()发起这次DMA动作。DMA完成后,xxx_dma_fini_callback()函数会被dmaengine驱动自动调用。

    1. static void xxx_dma_fini_callback(void *data)
    2. {
    3. struct completion *dma_complete = data;
    4. complete(dma_complete);
    5. }
    6. issue_xxx_dma(...)
    7. {
    8. rx_desc = dmaengine_prep_slave_single(xxx->rx_chan,
    9. xxx->dst_start, t->len, DMA_DEV_TO_MEM,
    10. DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    11. rx_desc->callback = xxx_dma_fini_callback;
    12. rx_desc->callback_param = &xxx->rx_done;
    13. dmaengine_submit(rx_desc);
    14. dma_async_issue_pending(xxx->rx_chan);
    15. }

    这点知识不够用啊(⊙o⊙)…。

    二 开始写代码

    头文件Dmaengine.h (include\linux) 

    dmaengine_prep_slave_single的函数原型

    1. static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
    2. struct dma_chan *chan, dma_addr_t buf, size_t len,
    3. enum dma_transfer_direction dir, unsigned long flags)
    4. {
    5. struct scatterlist sg;
    6. sg_init_table(&sg, 1);
    7. sg_dma_address(&sg) = buf;
    8. sg_dma_len(&sg) = len;
    9. return chan->device->device_prep_slave_sg(chan, &sg, 1,
    10. dir, flags, NULL);
    11. }

    为了让这个函数工作起来,我们要定义如下几个变量

    1. struct dma_async_tx_descriptor *rx_desc;
    2. struct dma_chan rx_chan;
    3. dma_addr_t dst_start;
    4. size_t len = CSS_DMA_IMAGE_SIZE;
    5. enum dma_transfer_direction dir = DMA_MEM_TO_MEM;

    大概写完的伪代码就是这样的

    1. static void css_dma_fini_callback(void *data)
    2. {
    3. struct _css_dev_ *css_dev = (struct _css_dev_ *)data;
    4. //struct completion *dma_complete = data;
    5. DEBUG_CSS("css_dev->name",css_dev->name);
    6. //complete(dma_complete);
    7. }
    8. static int css_dma_init(struct _css_dev_ *css_dev)
    9. {
    10. struct dma_async_tx_descriptor *rx_desc;
    11. struct dma_chan rx_chan;
    12. dma_addr_t dst_start;
    13. size_t len = CSS_DMA_IMAGE_SIZE;
    14. enum dma_transfer_direction dir = DMA_DEV_TO_MEM;
    15. rx_desc = dmaengine_prep_slave_single(&rx_chan,
    16. dst_start, len, dir,
    17. DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
    18. rx_desc->callback = xxx_dma_fini_callback;
    19. rx_desc->callback_param = css_dev;
    20. dmaengine_submit(rx_desc);
    21. dma_async_issue_pending(css_dev);
    22. }

    三 测试代码

    应用代码:csi_single_mmap.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #define DEBUG_INFO(format,...) printf("%s:%d"format"\n",\
    10. __func__,__LINE__,##__VA_ARGS__);
    11. #define _CSS_DMA_DEV_NAME "/dev/css_dma"
    12. #define CSS_DMA_IMAGE_SIZE (1280*800)
    13. #define CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL 0x1001
    14. #define CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL 0x1002
    15. #define CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL 0x1003
    16. enum _css_dev_buf_type{
    17. _CSS_DEV_READ_BUF = 0,
    18. _CSS_DEV_WRITE_BUF,
    19. _CSS_DEV_UNKNOWN_BUF_TYPE,
    20. _CSS_DEV_MAX_BUF_TYPE,
    21. };
    22. struct __css_dev_ {
    23. char *read_buf;
    24. char *write_buf;
    25. }_css_dev_;
    26. static int cycle_dmaengine_test(struct __css_dev_ *css_dev,int fd,int count){
    27. int i = 0,j = 0;
    28. char c = 'a' - 1;
    29. for(i = 0;i < count;i++){
    30. c = c + 1;
    31. if(ioctl(fd,
    32. CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL,
    33. &c) < 0)
    34. {
    35. perror("ioctl");
    36. DEBUG_INFO("ioctl CSI_DMA_FILL_CHAR_IOCTL fail");
    37. return -1;
    38. }
    39. for(j = 0;j < CSS_DMA_IMAGE_SIZE;j++){
    40. if(css_dev->read_buf[j] != c || css_dev->write_buf[j] != c){
    41. DEBUG_INFO("error:css_dev->read_buf[%d] = %c,css_dev->write_buf[%d] = %c",
    42. j,css_dev->read_buf[j],
    43. j,css_dev->write_buf[j]);
    44. return -1;
    45. }
    46. }
    47. DEBUG_INFO("set c = %c ok",c);
    48. if(c == 'z'){
    49. c = 'a' - 1;
    50. }
    51. }
    52. }
    53. static int cycle_memcpy_test(struct __css_dev_ *css_dev,int fd,int count){
    54. int i = 0,j = 0;
    55. char c = 'a' - 1;
    56. for(i = 0;i < count;i++){
    57. c = c + 1;
    58. if(ioctl(fd,
    59. CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL,
    60. &c) < 0)
    61. {
    62. perror("ioctl");
    63. DEBUG_INFO("ioctl CSI_DMA_FILL_CHAR_IOCTL fail");
    64. return -1;
    65. }
    66. for(j = 0;j < CSS_DMA_IMAGE_SIZE;j++){
    67. if(css_dev->read_buf[j] != c || css_dev->write_buf[j] != c){
    68. DEBUG_INFO("error:css_dev->read_buf[%d] = %c,css_dev->write_buf[%d] = %c",
    69. j,css_dev->read_buf[j],
    70. j,css_dev->write_buf[j]);
    71. return -1;
    72. }
    73. }
    74. DEBUG_INFO("set c = %c ok",c);
    75. if(c == 'z'){
    76. c = 'a' - 1;
    77. }
    78. }
    79. }
    80. int main(int argc,char *argv[])
    81. {
    82. struct __css_dev_ *css_dev = &_css_dev_;
    83. char *name = _CSS_DMA_DEV_NAME;
    84. int fd = open(name,O_RDWR);
    85. if(fd < 0){
    86. DEBUG_INFO("open %s",name);
    87. return -1;
    88. }
    89. DEBUG_INFO("open %s ok",name);
    90. if(ioctl(fd,
    91. CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL,
    92. _CSS_DEV_READ_BUF) < 0)
    93. {
    94. perror("ioctl");
    95. DEBUG_INFO("ioctl fail");
    96. goto fail;
    97. }
    98. css_dev->read_buf = (char*)mmap(NULL,
    99. CSS_DMA_IMAGE_SIZE,
    100. PROT_READ|PROT_WRITE,
    101. MAP_SHARED,fd,0);
    102. if(css_dev->read_buf == NULL){
    103. perror("mmap");
    104. DEBUG_INFO("mmap fail");
    105. goto fail;
    106. }
    107. DEBUG_INFO("css_dev->read_buf = %p",css_dev->read_buf);
    108. if(ioctl(fd,
    109. CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL,
    110. _CSS_DEV_WRITE_BUF) < 0)
    111. {
    112. perror("ioctl");
    113. DEBUG_INFO("ioctl fail");
    114. goto fail;
    115. }
    116. css_dev->write_buf = (char*)mmap(NULL,
    117. CSS_DMA_IMAGE_SIZE,
    118. PROT_READ|PROT_WRITE,
    119. MAP_SHARED,fd,0);
    120. if(css_dev->write_buf == NULL){
    121. perror("mmap");
    122. DEBUG_INFO("mmap fail");
    123. goto fail;
    124. }
    125. DEBUG_INFO("css_dev->write_buf = %p",css_dev->write_buf);
    126. cycle_dmaengine_test(css_dev,fd,10);
    127. cycle_memcpy_test(css_dev,fd,10);
    128. fail:
    129. close(fd);
    130. return 0;
    131. }

    驱动代码:csi_single.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #define DEBUG_CSS(format,...)\
    19. printk(KERN_INFO"info:%s:%s:%d: "format"\n",\
    20. __FILE__,__func__,__LINE__,\
    21. ##__VA_ARGS__)
    22. #define DEBUG_CSS_ERR(format,...)\
    23. printk("\001" "1""error:%s:%s:%d: "format"\n",\
    24. __FILE__,__func__,__LINE__,\
    25. ##__VA_ARGS__)
    26. #define CSS_DMA_IMAGE_SIZE (1280*800)
    27. #define CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL 0x1001
    28. #define CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL 0x1002
    29. #define CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL 0x1003
    30. enum _css_dev_buf_type{
    31. _CSS_DEV_READ_BUF = 0,
    32. _CSS_DEV_WRITE_BUF,
    33. _CSS_DEV_UNKNOWN_BUF_TYPE,
    34. _CSS_DEV_MAX_BUF_TYPE,
    35. };
    36. struct _css_dev_{
    37. struct file_operations _css_fops;
    38. struct miscdevice misc;
    39. int buf_size;
    40. int buf_size_order;
    41. char *src_buf;
    42. char *dst_buf;
    43. char *user_src_buf_vaddr;
    44. char *user_dst_buf_vaddr;
    45. dma_addr_t src_addr;
    46. dma_addr_t dst_addr;
    47. struct spinlock slock;
    48. struct mutex open_lock;
    49. char name[10];
    50. enum _css_dev_buf_type buf_type;
    51. struct device *dev;
    52. struct dma_chan * dma_m2m_chan;
    53. struct completion dma_m2m_ok;
    54. struct imx_dma_data m2m_dma_data;
    55. struct timeval tv_start,tv_end;
    56. struct device *chan_dev;
    57. struct dma_device *dma_dev;
    58. struct dma_async_tx_descriptor *dma_m2m_desc;
    59. };
    60. #define _to_css_dev_(file) (struct _css_dev_ *)container_of(file->f_op,struct _css_dev_,_css_fops)
    61. static int _css_open(struct inode *inode, struct file *file)
    62. {
    63. struct _css_dev_ *css_dev = _to_css_dev_(file);
    64. DEBUG_CSS("css_dev->name = %s",css_dev->name);
    65. return 0;
    66. }
    67. static ssize_t _css_read(struct file *file, char __user *ubuf, size_t size, loff_t *ppos)
    68. {
    69. struct _css_dev_ *css_dev = _to_css_dev_(file);
    70. DEBUG_CSS("css_dev->name = %s",css_dev->name);
    71. return 0;
    72. }
    73. static int _css_mmap (struct file *file, struct vm_area_struct *vma)
    74. {
    75. struct _css_dev_ *css_dev = _to_css_dev_(file);
    76. char *p = NULL;
    77. char **user_addr;
    78. switch(css_dev->buf_type){
    79. case _CSS_DEV_READ_BUF:
    80. p = css_dev->src_buf;
    81. user_addr = (char**)&css_dev->user_src_buf_vaddr;
    82. break;
    83. case _CSS_DEV_WRITE_BUF:
    84. p = css_dev->dst_buf;
    85. user_addr = (char**)&css_dev->user_dst_buf_vaddr;
    86. break;
    87. default:
    88. p = NULL;
    89. return -EINVAL;
    90. break;
    91. }
    92. if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(p) >> PAGE_SHIFT,
    93. vma->vm_end-vma->vm_start, vma->vm_page_prot)) {
    94. DEBUG_CSS_ERR( "remap_pfn_range error\n");
    95. return -EAGAIN;
    96. }
    97. css_dev->buf_type = _CSS_DEV_UNKNOWN_BUF_TYPE;
    98. *user_addr = (void*)vma->vm_start;
    99. DEBUG_CSS("mmap ok user_addr = %p kernel addr = %p",*user_addr,p);
    100. return 0;
    101. }
    102. static ssize_t _css_write(struct file *file, const char __user *ubuf, size_t size, loff_t *ppos)
    103. {
    104. struct _css_dev_ *css_dev = _to_css_dev_(file);
    105. DEBUG_CSS("css_dev->name = %s",css_dev->name);
    106. return size;
    107. }
    108. static int _css_release (struct inode *inode, struct file *file)
    109. {
    110. struct _css_dev_ *css_dev = _to_css_dev_(file);
    111. DEBUG_CSS("css_dev->name = %s",css_dev->name);
    112. DEBUG_CSS("css_dev->src_buf[%d] = %c",css_dev->buf_size - 1,
    113. ((char*)css_dev->src_buf)[css_dev->buf_size - 1]);
    114. DEBUG_CSS("css_dev->dst_buf[%d] = %c",css_dev->buf_size - 1,
    115. ((char*)css_dev->dst_buf)[css_dev->buf_size - 1]);
    116. return 0;
    117. }
    118. static int _css_set_buf_type(struct file *file, enum _css_dev_buf_type buf_type)
    119. {
    120. unsigned long flags;
    121. struct _css_dev_ *css_dev = _to_css_dev_(file);
    122. DEBUG_CSS("buf_type=%d",buf_type);
    123. if(buf_type >= _CSS_DEV_MAX_BUF_TYPE){
    124. DEBUG_CSS_ERR("invalid buf type");
    125. return -EINVAL;
    126. }
    127. spin_lock_irqsave(&css_dev->slock,flags);
    128. css_dev->buf_type = buf_type;
    129. spin_unlock_irqrestore(&css_dev->slock,flags);
    130. return 0;
    131. }
    132. static void css_dma_async_tx_callback(void *dma_async_param)
    133. {
    134. struct _css_dev_ *css_dev = (struct _css_dev_ *)dma_async_param;
    135. complete(&css_dev->dma_m2m_ok);
    136. }
    137. static int css_dmaengine_cpy(struct _css_dev_ *css_dev)
    138. {
    139. dma_cookie_t cookie;
    140. enum dma_status dma_status;
    141. do_gettimeofday(&css_dev->tv_start);
    142. css_dev->src_addr = dma_map_single(css_dev->chan_dev, css_dev->src_buf, css_dev->buf_size, DMA_TO_DEVICE);
    143. css_dev->dst_addr = dma_map_single(css_dev->chan_dev, css_dev->dst_buf, css_dev->buf_size, DMA_FROM_DEVICE);
    144. //DEBUG_CSS("32bit:css_dev->src_addr = %x,css_dev->dst_addr = %x",css_dev->src_addr,css_dev->dst_addr);
    145. css_dev->dma_m2m_desc = css_dev->dma_dev->device_prep_dma_memcpy(css_dev->dma_m2m_chan,
    146. css_dev->dst_addr,
    147. css_dev->src_addr,
    148. css_dev->buf_size,0);
    149. css_dev->dma_m2m_desc->callback = css_dma_async_tx_callback;
    150. css_dev->dma_m2m_desc->callback_param = css_dev;
    151. init_completion(&css_dev->dma_m2m_ok);
    152. cookie = dmaengine_submit(css_dev->dma_m2m_desc);
    153. if(dma_submit_error(cookie)){
    154. DEBUG_CSS("dmaengine_submit error");
    155. return -EINVAL;
    156. }
    157. dma_async_issue_pending(css_dev->dma_m2m_chan);
    158. wait_for_completion(&css_dev->dma_m2m_ok);
    159. dma_status = dma_async_is_tx_complete(css_dev->dma_m2m_chan,cookie,NULL,NULL);
    160. if(DMA_COMPLETE != dma_status){
    161. DEBUG_CSS("error:dma_status = %d",dma_status);
    162. }
    163. dma_unmap_single(css_dev->chan_dev,css_dev->src_addr,css_dev->buf_size,DMA_TO_DEVICE);
    164. dma_unmap_single(css_dev->chan_dev,css_dev->dst_addr,css_dev->buf_size,DMA_FROM_DEVICE);
    165. do_gettimeofday(&css_dev->tv_end);
    166. DEBUG_CSS("dma used time = %ld us",
    167. (css_dev->tv_end.tv_sec - css_dev->tv_start.tv_sec)*1000000
    168. + (css_dev->tv_end.tv_usec - css_dev->tv_start.tv_usec));
    169. return 0;
    170. }
    171. static long _css_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    172. {
    173. int ret = 0;
    174. struct _css_dev_ *css_dev = _to_css_dev_(file);
    175. char *p;
    176. switch(cmd){
    177. case CSI_DMA_SET_CUR_MAP_BUF_TYPE_IOCTL:
    178. ret = _css_set_buf_type(file,(enum _css_dev_buf_type)arg);
    179. break;
    180. case CSI_DMA_FILL_CHAR_AND_RUN_DMAENGIEN_IOCTL:
    181. p = (char*)arg;
    182. memset(css_dev->src_buf,*p,css_dev->buf_size);
    183. css_dmaengine_cpy(css_dev);
    184. break;
    185. case CSI_DMA_FILL_CHAR_RUN_MEMCPY_IOCTL:
    186. p = (char*)arg;
    187. do_gettimeofday(&css_dev->tv_start);
    188. memset(css_dev->src_buf,*p,css_dev->buf_size);
    189. do_gettimeofday(&css_dev->tv_end);
    190. DEBUG_CSS("memset used time = %ld us",
    191. (css_dev->tv_end.tv_sec - css_dev->tv_start.tv_sec)*1000000
    192. + (css_dev->tv_end.tv_usec - css_dev->tv_start.tv_usec));
    193. do_gettimeofday(&css_dev->tv_start);
    194. memcpy(css_dev->dst_buf,css_dev->src_buf,css_dev->buf_size);
    195. do_gettimeofday(&css_dev->tv_end);
    196. DEBUG_CSS("memcpy used time = %ld us",
    197. (css_dev->tv_end.tv_sec - css_dev->tv_start.tv_sec)*1000000
    198. + (css_dev->tv_end.tv_usec - css_dev->tv_start.tv_usec));
    199. break;
    200. default:
    201. DEBUG_CSS_ERR("unknown cmd = %x",cmd);
    202. ret = -EINVAL;
    203. break;
    204. }
    205. return ret;
    206. }
    207. static struct _css_dev_ _global_css_dev = {
    208. .name = "lkmao",
    209. ._css_fops = {
    210. .owner = THIS_MODULE,
    211. .mmap = _css_mmap,
    212. .open = _css_open,
    213. .release = _css_release,
    214. .read = _css_read,
    215. .write = _css_write,
    216. .unlocked_ioctl = _css_unlocked_ioctl,
    217. },
    218. .misc = {
    219. .minor = MISC_DYNAMIC_MINOR,
    220. .name = "css_dma",
    221. },
    222. .buf_type = _CSS_DEV_UNKNOWN_BUF_TYPE,
    223. .user_src_buf_vaddr = NULL,
    224. .user_dst_buf_vaddr = NULL,
    225. .m2m_dma_data = {
    226. .peripheral_type = IMX_DMATYPE_MEMORY,
    227. .priority = DMA_PRIO_HIGH,
    228. },
    229. };
    230. static int css_dev_get_dma_addr(struct _css_dev_ *css_dev,char **vaddr,dma_addr_t *phys, int direction)
    231. {
    232. char *p;
    233. dma_addr_t dma_addr;
    234. p = (char*)__get_free_pages(GFP_KERNEL|GFP_DMA,css_dev->buf_size_order);
    235. if(p == NULL || IS_ERR(p)){
    236. DEBUG_CSS("devm_kmalloc error");
    237. return -ENOMEM;
    238. }
    239. dma_addr = dma_map_single(css_dev->dev, p, css_dev->buf_size, direction);
    240. *vaddr = p;
    241. *phys = dma_addr;
    242. DEBUG_CSS("32bit:p = %p,dma_addr = %x",p,dma_addr);
    243. return 0;
    244. }
    245. static bool css_dma_filter_fn(struct dma_chan *chan, void *filter_param)
    246. {
    247. if(!imx_dma_is_general_purpose(chan)){
    248. DEBUG_CSS("css_dma_filter_fn error");
    249. return false;
    250. }
    251. chan->private = filter_param;
    252. return true;
    253. }
    254. static int css_dmaengine_init(struct _css_dev_ *css_dev)
    255. {
    256. dma_cap_mask_t dma_m2m_mask;
    257. struct dma_slave_config dma_m2m_config = {0};
    258. css_dev->m2m_dma_data.peripheral_type = IMX_DMATYPE_MEMORY;
    259. css_dev->m2m_dma_data.priority = DMA_PRIO_HIGH;
    260. dma_cap_zero(dma_m2m_mask);
    261. dma_cap_set(DMA_MEMCPY,dma_m2m_mask);
    262. css_dev->dma_m2m_chan = dma_request_channel(dma_m2m_mask,css_dma_filter_fn,&css_dev->m2m_dma_data);
    263. if(!css_dev->dma_m2m_chan){
    264. DEBUG_CSS("dma_request_channel error");
    265. return -EINVAL;
    266. }
    267. dma_m2m_config.direction = DMA_MEM_TO_MEM;
    268. dma_m2m_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    269. dmaengine_slave_config(css_dev->dma_m2m_chan,&dma_m2m_config);
    270. css_dev->dma_dev = css_dev->dma_m2m_chan->device;
    271. css_dev->chan_dev = css_dev->dma_m2m_chan->device->dev;
    272. return 0;
    273. }
    274. static int css_dev_init(struct platform_device *pdev,struct _css_dev_ *css_dev)
    275. {
    276. css_dev->misc.fops = &css_dev->_css_fops;
    277. pr_debug("css_init init ok");
    278. mutex_init(&css_dev->open_lock);
    279. spin_lock_init(&css_dev->slock);
    280. printk("KERN_ALERT = %s",KERN_ALERT);
    281. css_dev->dev = &pdev->dev;
    282. css_dev->buf_size = CSS_DMA_IMAGE_SIZE;
    283. css_dev->buf_size_order = get_order(css_dev->buf_size);
    284. if(css_dev_get_dma_addr(css_dev,&css_dev->src_buf,&css_dev->src_addr,DMA_TO_DEVICE)){
    285. return -ENOMEM;
    286. }
    287. css_dev->src_buf = (char*)__get_free_pages(GFP_KERNEL|GFP_DMA,css_dev->buf_size_order);
    288. if(css_dev->src_buf == NULL || IS_ERR(css_dev->src_buf)){
    289. DEBUG_CSS("devm_kmalloc error");
    290. return -ENOMEM;
    291. }
    292. css_dev->dst_buf = (char*)__get_free_pages(GFP_KERNEL|GFP_DMA,css_dev->buf_size_order);
    293. if(css_dev->dst_buf == NULL || IS_ERR(css_dev->dst_buf)){
    294. DEBUG_CSS("devm_kmalloc error");
    295. return -ENOMEM;
    296. }
    297. DEBUG_CSS("32bit:css_dev->src_buf = %p,css_dev->dst_buf = %p",css_dev->src_buf,css_dev->dst_buf);
    298. if(misc_register(&css_dev->misc) != 0){
    299. DEBUG_CSS("misc_register error");
    300. return -EINVAL;
    301. }
    302. platform_set_drvdata(pdev,css_dev);
    303. css_dmaengine_init(css_dev);
    304. return 0;
    305. }
    306. static int css_probe(struct platform_device *pdev)
    307. {
    308. struct _css_dev_ *css_dev = (struct _css_dev_ *)&_global_css_dev;
    309. if(css_dev_init(pdev,css_dev)){
    310. return -EINVAL;
    311. }
    312. DEBUG_CSS("init ok");
    313. return 0;
    314. }
    315. static int css_remove(struct platform_device *pdev)
    316. {
    317. struct _css_dev_ *css_dev = &_global_css_dev;
    318. //dma_unmap_single(css_dev->dev,css_dev->src_addr,css_dev->buf_size,DMA_FROM_DEVICE);
    319. //dma_unmap_single(css_dev->dev,css_dev->dst_addr,css_dev->buf_size,DMA_FROM_DEVICE);
    320. free_page((unsigned long )css_dev->dst_buf);
    321. free_page((unsigned long )css_dev->src_buf);
    322. misc_deregister(&css_dev->misc);
    323. dma_release_channel(css_dev->dma_m2m_chan);
    324. DEBUG_CSS("exit ok");
    325. return 0;
    326. }
    327. static const struct of_device_id css_of_ids[] = {
    328. {.compatible = "css_dma"},
    329. {},
    330. };
    331. MODULE_DEVICE_TABLE(of,css_of_ids);
    332. static struct platform_driver css_platform_driver = {
    333. .probe = css_probe,
    334. .remove = css_remove,
    335. .driver = {
    336. .name = "css_dma",
    337. .of_match_table = css_of_ids,
    338. .owner = THIS_MODULE,
    339. },
    340. };
    341. static int __init css_init(void)
    342. {
    343. int ret_val;
    344. ret_val = platform_driver_register(&css_platform_driver);
    345. if(ret_val != 0){
    346. DEBUG_CSS("platform_driver_register error");
    347. return ret_val;
    348. }
    349. DEBUG_CSS("platform_driver_register ok");
    350. return 0;
    351. }
    352. static void __exit css_exit(void)
    353. {
    354. platform_driver_unregister(&css_platform_driver);
    355. }
    356. module_init(css_init);
    357. module_exit(css_exit);
    358. MODULE_LICENSE("GPL");

    Makefile:

    1. export ARCH=arm
    2. export CROSS_COMPILE=arm-linux-gnueabihf-
    3. KERN_DIR = /home/lkmao/imx/linux/linux-imx
    4. FILE_NAME=csi_single
    5. obj-m += $(FILE_NAME).o
    6. APP_NAME=csi_single_mmap
    7. all:
    8. make -C $(KERN_DIR) M=$(shell pwd) modules
    9. sudo cp $(FILE_NAME).ko /big/nfsroot/jiaocheng_rootfs/home/root/
    10. sudo scp $(FILE_NAME).ko root@192.168.0.3:/home/root/
    11. arm-linux-gnueabihf-gcc -o $(APP_NAME) $(APP_NAME).c
    12. sudo cp $(APP_NAME) /big/nfsroot/jiaocheng_rootfs/home/root/
    13. sudo scp $(APP_NAME) root@192.168.0.3:/home/root/
    14. .PHONY:clean
    15. clean:
    16. make -C $(KERN_DIR) M=$(shell pwd) clean
    17. rm $(APP_NAME) -rf

    设备树配置

    1. sdma_m2m{
    2. compatible = "css_dma";
    3. };

    测试过程和结果:

    1 make编译Makefile

    2 进入开发板加载模块:insmod csi_single.ko

    1. root@ATK-IMX6U:~# insmod csi_single.ko
    2. [ 41.430795] KERN_ALERT = 1
    3. [ 41.433620] info:/big/csi_driver/css_dma/csi_single.c:css_dev_get_dma_addr:267: 32bit:p = 94c00000,dma_addr = 94c00000
    4. [ 41.447390] info:/big/csi_driver/css_dma/csi_single.c:css_dev_init:337: 32bit:css_dev->src_buf = 94d00000,css_dev->dst_buf = 94e00000
    5. [ 41.463653] info:/big/csi_driver/css_dma/csi_single.c:css_probe:355: init ok
    6. [ 41.474842] info:/big/csi_driver/css_dma/csi_single.c:css_init:398: platform_driver_register ok
    7. root@ATK-IMX6U:~#

    3 运行测试应用:./csi_single_mmap

    执行结果如下图所示:

    测试分两个部分,一个是dma引擎复制数据的部分,第二个是memcpy复制数据的部分

    驱动中的调试信息

     应用输出的调试信息:

     小结

            我对这个测试结果是表示怀疑的,DMA引擎20多毫秒,memcpy1.7毫秒,memset 0.7毫秒。我一度怀疑是代码出了问题。还得想办法做更多的测试才行。

  • 相关阅读:
    【Linux C】Linux如何执行一个程序(程序存储空间、系统调用、内核调用)
    DB307S-ASEMI整流桥DB307S
    【路径规划】基于模糊逻辑系统实现车辆的自主导航附matlab代码
    攻防演练-安全监测岗都做哪些工作
    C++ explicit关键字的用法
    MySQL XA事务文档翻译
    AWR1843+DCA1000+mmwave_studio 采集原始数据
    月薪2w+的大数据就业岗位有哪些?
    把 vs Code 添加到右键菜单 ( 其他程序一样 )
    【IEEE出版顺利申请中】2024年第四届电子信息工程与计算机科学国际会议(EIECS 2024)
  • 原文地址:https://blog.csdn.net/yueni_zhao/article/details/127678694