pci_alloc_consistent配合mmap实现DMA功能

有这种需求的同行自然明白这2个为什么需要配合起来用。简单说说,我的需求是dma位于pci设备侧,pci主的cpu上应用程序直接mmap获取dma发来的数据。
猜测显卡的dma一般位于pci主,所以我在内核里没有找到pci_alloc_consistent 配合mmap的例子。
这个代码在loongson 3A这个极品芯片上都能通过,相信其他平台毫无障碍。
pci_alloc_consistent得到的是申请到的dma一致性缓冲区的内核态虚拟地址以及pci总线地址。

内核态虚拟地址转成物理地址后需要传递给应用做mmap的最后一个参数。


 
 
  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/pci.h>
  4. #include <linux/init.h>
  5. #include <linux/ioport.h>
  6. #include <linux/netdevice.h>
  7. #include <linux/etherdevice.h>
  8. #include <linux/delay.h>
  9. #include <linux/ethtool.h>
  10. #include <linux/mii.h>
  11. #include <linux/crc32.h>
  12. #include <linux/io.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/module.h>
  15. #include <linux/kernel.h>
  16. #include <linux/types.h>
  17. #include <linux/miscdevice.h>
  18. #include <linux/ioport.h>
  19. #include <linux/fcntl.h>
  20. #include <linux/init.h>
  21. #include <linux/poll.h>
  22. #include <linux/proc_fs.h>
  23. #include <linux/seq_file.h>
  24. #include <linux/spinlock.h>
  25. #include <linux/sched.h>
  26. #include <linux/sysctl.h>
  27. #include <linux/wait.h>
  28. #include <linux/cdev.h>
  29. #include <linux/fs.h>
  30. #include <linux/delay.h>
  31. #include <linux/uaccess.h>
  32. #include <linux/device.h>
  33. #include <linux/err.h>
  34. #include <linux/fs.h>
  35. #include <asm/io.h>
  36. #include <asm/current.h>
  37. #include <asm/system.h>
  38. #include <linux/mm.h>
  39. #include <linux/mman.h>
  40. #include <linux/miscdevice.h>
  41. #include <linux/proc_fs.h>
  42. #include <linux/device.h>
  43. #include <linux/fs.h>
  44. #include <linux/slab.h>
  45. #include <linux/mm.h>
  46. #include <linux/slab.h>
  47. #define DEVICE_NAME "testc"
  48. #define MODNAME "pmc-test"
  49. #define MMAPBUF_LEN (16*1024*1024)
  50. struct my_testdev{
  51. dev_t testc_dev_num;
  52. struct class * testc_class;
  53. unsigned long testc_kernel_virt_addr;
  54. struct cdev test_cdev;
  55. unsigned int current_pointer; /*char device offset ,目前同时只能一个程序读取 */
  56. unsigned long mmap_phyaddr;
  57. dma_addr_t mmap_pcibus_addr;
  58. unsigned long mmap_kvirt_addr;
  59. };
  60. //不得已的一个全局变量
  61. struct my_testdev *priv;
  62. //仅仅是为了测试程序 手头只有这个卡
  63. static DEFINE_PCI_DEVICE_TABLE(netdrv_pci_tbl) = {
  64. { 0x8086, 0x10b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
  65. { 0,}
  66. };
  67. MODULE_DEVICE_TABLE(pci, netdrv_pci_tbl);
  68. static int testc_open(struct inode *inode, struct file *file)
  69. {
  70. return 0;
  71. }
  72. static int testc_release(struct inode *inode, struct file *file)
  73. {
  74. return 0;
  75. }
  76. static ssize_t testc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
  77. {
  78. return 0;
  79. }
  80. static ssize_t testc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  81. {
  82. return 0;
  83. }
  84. static loff_t testc_llseek(struct file * file,loff_t offset,int orig)
  85. {
  86. return 0;
  87. }
  88. static int testc_mmap(struct file *file, struct vm_area_struct *vma)
  89. {
  90. unsigned long size = vma->vm_end - vma->vm_start;
  91. struct my_testdev *pri=priv;
  92. printk( "inmmap ,mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
  93. printk( "start %lx end %lx off %lx \n",vma->vm_start,vma->vm_end,vma->vm_pgoff);
  94. //pci_alloc_consistent申请到的内存已经是物理地址连续的 ,只要一个remap_pfn_range
  95. remap_pfn_range(vma,vma->vm_start,(pri->mmap_phyaddr)>> PAGE_SHIFT,size,vma->vm_page_prot);
  96. vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
  97. return 0;
  98. }
  99. static const struct file_operations testc_fops = {
  100. .read = testc_read,
  101. //.aio_read = generic_file_aio_read,
  102. .write = testc_write,
  103. //.aio_write = blkdev_aio_write,
  104. //.fsync = blkdev_fsync,
  105. .mmap = testc_mmap,
  106. .open = testc_open,
  107. .release = testc_release,
  108. //.unlocked_ioctl = raw_ioctl,
  109. .llseek = testc_llseek,
  110. .owner = THIS_MODULE,
  111. };
  112. static int __ devinit pmc_probe(struct pci_dev *pdev,const struct pci_device_id *ent)
  113. {
  114. int ret;
  115. int i;
  116. struct device *x;
  117. struct my_testdev *pri;
  118. printk( "--------%s %d\n",__FUNCTION__,__LINE__);
  119. pri=kmalloc( sizeof(struct my_testdev),GFP_KERNEL);
  120. memset(pri, 0, sizeof(struct my_testdev));
  121. priv=pri;
  122. pci_set_drvdata(pdev, pri);
  123. //char device
  124. if(alloc_chrdev_region(&(pri->testc_dev_num), 0, 1,DEVICE_NAME))
  125. {
  126. printk( "err----%s %d \n",__FILE__,__LINE__);
  127. return -1;
  128. }
  129. pri->testc_class=class_create(THIS_MODULE,DEVICE_NAME);
  130. cdev_init(&(pri->test_cdev),&testc_fops);
  131. pri->test_cdev.owner=THIS_MODULE;
  132. ret=cdev_add(&(pri->test_cdev),pri->testc_dev_num, 1);
  133. if(ret)
  134. {
  135. printk( "err----%s %d \n",__FILE__,__LINE__);
  136. return -1;
  137. }
  138. //设备节点自动生成
  139. x=device_create(pri->testc_class, NULL,MKDEV(MAJOR(pri->testc_dev_num), 0),pri,DEVICE_NAME);
  140. if(x== NULL)
  141. {
  142. printk( "err----%s %d \n",__FILE__,__LINE__);
  143. return -1;
  144. }
  145. priv=pri;
  146. priv->current_pointer= 0;
  147. //mmap
  148. pri->mmap_kvirt_addr=( unsigned long )pci_alloc_consistent(pdev,MMAPBUF_LEN,&(pri->mmap_pcibus_addr));
  149. if(( void *)pri->mmap_kvirt_addr== NULL)
  150. {
  151. return -1;
  152. }
  153. memset(( void *)pri->mmap_kvirt_addr, 0x0,MMAPBUF_LEN);
  154. //写入有意义的数据,方便在应用里验证确实映射了16MB
  155. for(i= 0;i<MMAPBUF_LEN/ 4;i++)
  156. {
  157. *(((u32 *)pri->mmap_kvirt_addr)+i)=i;
  158. }
  159. pri->mmap_phyaddr=virt_to_phys(( void *)pri->mmap_kvirt_addr);
  160. printk( "mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
  161. return 0;
  162. }
  163. static void __ devexit pmc_remove(struct pci_dev *pdev)
  164. {
  165. struct my_testdev *pri = pci_get_drvdata(pdev);
  166. pci_free_consistent(pdev, MMAPBUF_LEN,( void *)pri->mmap_kvirt_addr, pri->mmap_pcibus_addr);
  167. }
  168. static struct pci_driver netdrv_pci_driver = {
  169. .name = MODNAME,
  170. .id_table = netdrv_pci_tbl,
  171. .probe = pmc_probe,
  172. .remove = __devexit_p(pmc_remove),
  173. };
  174. static int __ init pmc_init_module(void)
  175. {
  176. return pci_register_driver(&netdrv_pci_driver);
  177. }
  178. static void __ exit pmc_cleanup_module(void)
  179. {
  180. pci_unregister_driver(&netdrv_pci_driver);
  181. }
  182. module_init(pmc_init_module);
  183. module_exit(pmc_cleanup_module);
  184. MODULE_AUTHOR( "deep_pro");
  185. MODULE_LICENSE( "GPL");

简单的应用程序,注意mmap的最后一个参数是dma缓冲区的物理地址,演示程序里根据驱动的打印写的硬编码,最终还是要靠ioctl等机制实现自动从驱动取得。


 
 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <errno.h>
  6. #include <limits.h>
  7. #include <linux/kernel.h>
  8. #include <byteswap.h>
  9. #include <unistd.h>
  10. #include <sys/types.h>
  11. #include <sys/mman.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. #include <fcntl.h>
  15. #include <sys/ioctl.h>
  16. #define MMAP_LEN 16*1024*1024
  17. int main()
  18. {
  19. int fd,i,ret;
  20. char buf[ 0x20]={ 0};
  21. unsigned int *mmap_data;
  22. fd=open( "/dev/testc",O_RDWR);
  23. if(fd< 0)
  24. {
  25. perror( "open:");
  26. return -1;
  27. }
  28. //0xf7000000 这个是缓冲区的物理地址,要从驱动里得到
  29. mmap_data=( unsigned int *)mmap( NULL,MMAP_LEN,PROT_READ|PROT_WRITE,MAP_SHARED,fd, 0xf7000000);
  30. if(mmap_data==MAP_FAILED)
  31. {
  32. perror( "mmap");
  33. return -1;
  34. }
  35. //测试mmap后的缓冲区读写 ,先写再读,这样执行第二遍应用就能验证写入成功
  36. for(i= 14* 1024* 1024/ 4;i<( 14* 1024* 1024+ 0x120)/ 4;i++)
  37. {
  38. //mmap_buf[i]=0;
  39. printf( "mmap_buf %02x :%x \n",i,*(mmap_data+i));
  40. *(mmap_data+i)+= 1;
  41. }
  42. munmap(mmap_data,MMAP_LEN);
  43. close(fd);
  44. }


				<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值