pci_alloc_consistent 配合mmap

110730阅读 0评论2019-03-24 lelv123
分类:嵌入式

有这种需求的同行自然明白这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.     
  79.     return 0;
  80. }    

  81. static ssize_t testc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  82. {

  83.     return 0;
  84. }

  85. static loff_t testc_llseek(struct file * file,loff_t offset,int orig)
  86. {
  87.     return 0;
  88. }


  89. static int testc_mmap(struct file *file, struct vm_area_struct *vma)
  90. {
  91.     
  92.     unsigned long size = vma->vm_end - vma->vm_start;
  93.     struct my_testdev *pri=priv;
  94.     printk("inmmap ,mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
  95.     printk("start %lx end %lx off %lx \n",vma->vm_start,vma->vm_end,vma->vm_pgoff);
  96. //pci_alloc_consistent申请到的内存已经是物理地址连续的 ,只要一个remap_pfn_range
  97.     remap_pfn_range(vma,vma->vm_start,(pri->mmap_phyaddr)>> PAGE_SHIFT,size,vma->vm_page_prot);

  98.     vma->vm_flags |= VM_RESERVED;    /* avoid to swap out this VMA */
  99.     return 0;

  100. }

  101. static const struct file_operations testc_fops = {
  102.     .read        = testc_read,
  103.     //.aio_read    = generic_file_aio_read,
  104.     .write        = testc_write,
  105.     //.aio_write    = blkdev_aio_write,
  106.     //.fsync        = blkdev_fsync,
  107.     .mmap        = testc_mmap,
  108.     .open        = testc_open,
  109.     .release    = testc_release,
  110.     //.unlocked_ioctl = raw_ioctl,
  111.     .llseek        = testc_llseek,
  112.     .owner        = THIS_MODULE,
  113. };


  114. static int __devinit pmc_probe(struct pci_dev *pdev,const struct pci_device_id *ent)
  115. {    
  116.     int ret;
  117.     int i;
  118.     struct device *x;
  119.     struct my_testdev *pri;
  120.     printk("--------%s %d\n",__FUNCTION__,__LINE__);
  121.     pri=kmalloc(sizeof(struct my_testdev),GFP_KERNEL);
  122.     memset(pri,0,sizeof(struct my_testdev));
  123.     priv=pri;
  124.     pci_set_drvdata(pdev, pri);
  125.     
  126.     

  127.     //char device
  128.     if(alloc_chrdev_region(&(pri->testc_dev_num), 0, 1,DEVICE_NAME))
  129.         {
  130.             printk("err----%s %d \n",__FILE__,__LINE__);
  131.             return -1;
  132.         }
  133.     pri->testc_class=class_create(THIS_MODULE,DEVICE_NAME);
  134.     cdev_init(&(pri->test_cdev),&testc_fops);
  135.     pri->test_cdev.owner=THIS_MODULE;

  136.     ret=cdev_add(&(pri->test_cdev),pri->testc_dev_num,1);
  137.     if(ret)
  138.         {
  139.             printk("err----%s %d \n",__FILE__,__LINE__);
  140.             return -1;
  141.         }
  142.     //设备节点自动生成
  143.     x=device_create(pri->testc_class,NULL,MKDEV(MAJOR(pri->testc_dev_num),0),pri,DEVICE_NAME);
  144.     if(x==NULL)
  145.         {
  146.             printk("err----%s %d \n",__FILE__,__LINE__);
  147.             return -1;
  148.         }
  149.     priv=pri;
  150.     priv->current_pointer=0;


  151. //mmap
  152.     pri->mmap_kvirt_addr=(unsigned long )pci_alloc_consistent(pdev,MMAPBUF_LEN,&(pri->mmap_pcibus_addr));
  153.     if((void *)pri->mmap_kvirt_addr==NULL)
  154.         {
  155.             return -1;
  156.         }
  157.     memset((void *)pri->mmap_kvirt_addr,0x0,MMAPBUF_LEN);
  158. //写入有意义的数据,方便在应用里验证确实映射了16MB
  159.     for(i=0;i<MMAPBUF_LEN/4;i++)
  160.         {
  161.             *(((u32 *)pri->mmap_kvirt_addr)+i)=i;
  162.         }

  163.     pri->mmap_phyaddr=virt_to_phys((void *)pri->mmap_kvirt_addr);
  164.     printk("mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
  165.     
  166.     return 0;
  167. }
  168. static void __devexit pmc_remove(struct pci_dev *pdev)
  169. {
  170.     struct my_testdev *pri = pci_get_drvdata(pdev);
  171.     pci_free_consistent(pdev, MMAPBUF_LEN,(void *)pri->mmap_kvirt_addr, pri->mmap_pcibus_addr);
  172.     
  173. }


  174. static struct pci_driver netdrv_pci_driver = {
  175.     .name        = MODNAME,
  176.     .id_table    = netdrv_pci_tbl,
  177.     .probe        = pmc_probe,
  178.     .remove        = __devexit_p(pmc_remove),

  179. };

  180. static int __init pmc_init_module(void)
  181. {
  182.     return pci_register_driver(&netdrv_pci_driver);
  183. }


  184. static void __exit pmc_cleanup_module(void)
  185. {
  186.     pci_unregister_driver(&netdrv_pci_driver);
  187. }

  188. module_init(pmc_init_module);
  189. module_exit(pmc_cleanup_module);


  190. MODULE_AUTHOR("deep_pro");
  191. 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. }
执行2遍应用即能验证读写和确实映射了16MB




上一篇:不用sizeof如何判断系统位数
下一篇:pci 学习笔记