猜测显卡的dma一般位于pci主,所以我在内核里没有找到pci_alloc_consistent 配合mmap的例子。
这个代码在loongson 3A这个极品芯片上都能通过,相信其他平台毫无障碍。
pci_alloc_consistent得到的是申请到的dma一致性缓冲区的内核态虚拟地址以及pci总线地址。
内核态虚拟地址转成物理地址后需要传递给应用做mmap的最后一个参数。
以下是简单驱动代码:
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/pci.h>
- #include <linux/init.h>
- #include <linux/ioport.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/delay.h>
- #include <linux/ethtool.h>
- #include <linux/mii.h>
- #include <linux/crc32.h>
- #include <linux/io.h>
- #include <linux/interrupt.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/miscdevice.h>
- #include <linux/ioport.h>
- #include <linux/fcntl.h>
- #include <linux/init.h>
- #include <linux/poll.h>
- #include <linux/proc_fs.h>
- #include <linux/seq_file.h>
- #include <linux/spinlock.h>
- #include <linux/sched.h>
- #include <linux/sysctl.h>
- #include <linux/wait.h>
- #include <linux/cdev.h>
- #include <linux/fs.h>
- #include <linux/delay.h>
- #include <linux/uaccess.h>
- #include <linux/device.h>
- #include <linux/err.h>
- #include <linux/fs.h>
- #include <asm/io.h>
- #include <asm/current.h>
- #include <asm/system.h>
- #include <linux/mm.h>
- #include <linux/mman.h>
- #include <linux/miscdevice.h>
- #include <linux/proc_fs.h>
- #include <linux/device.h>
- #include <linux/fs.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #define DEVICE_NAME "testc"
- #define MODNAME "pmc-test"
- #define MMAPBUF_LEN (16*1024*1024)
- struct my_testdev{
- dev_t testc_dev_num;
- struct class * testc_class;
- unsigned long testc_kernel_virt_addr;
- struct cdev test_cdev;
- unsigned int current_pointer; /*char device offset ,目前同时只能一个程序读取 */
- unsigned long mmap_phyaddr;
- dma_addr_t mmap_pcibus_addr;
- unsigned long mmap_kvirt_addr;
- };
- //不得已的一个全局变量
- struct my_testdev *priv;
- //仅仅是为了测试程序 手头只有这个卡
- static DEFINE_PCI_DEVICE_TABLE(netdrv_pci_tbl) = {
- {0x8086, 0x10b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- {0,}
- };
- MODULE_DEVICE_TABLE(pci, netdrv_pci_tbl);
- static int testc_open(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static int testc_release(struct inode *inode, struct file *file)
- {
- return 0;
- }
- static ssize_t testc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
- {
-
- return 0;
- }
- static ssize_t testc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
- {
- return 0;
- }
- static loff_t testc_llseek(struct file * file,loff_t offset,int orig)
- {
- return 0;
- }
- static int testc_mmap(struct file *file, struct vm_area_struct *vma)
- {
-
- unsigned long size = vma->vm_end - vma->vm_start;
- struct my_testdev *pri=priv;
- printk("inmmap ,mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
- printk("start %lx end %lx off %lx \n",vma->vm_start,vma->vm_end,vma->vm_pgoff);
- //pci_alloc_consistent申请到的内存已经是物理地址连续的 ,只要一个remap_pfn_range
- remap_pfn_range(vma,vma->vm_start,(pri->mmap_phyaddr)>> PAGE_SHIFT,size,vma->vm_page_prot);
- vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
- return 0;
- }
- static const struct file_operations testc_fops = {
- .read = testc_read,
- //.aio_read = generic_file_aio_read,
- .write = testc_write,
- //.aio_write = blkdev_aio_write,
- //.fsync = blkdev_fsync,
- .mmap = testc_mmap,
- .open = testc_open,
- .release = testc_release,
- //.unlocked_ioctl = raw_ioctl,
- .llseek = testc_llseek,
- .owner = THIS_MODULE,
- };
- static int __devinit pmc_probe(struct pci_dev *pdev,const struct pci_device_id *ent)
- {
- int ret;
- int i;
- struct device *x;
- struct my_testdev *pri;
- printk("--------%s %d\n",__FUNCTION__,__LINE__);
- pri=kmalloc(sizeof(struct my_testdev),GFP_KERNEL);
- memset(pri,0,sizeof(struct my_testdev));
- priv=pri;
- pci_set_drvdata(pdev, pri);
-
-
- //char device
- if(alloc_chrdev_region(&(pri->testc_dev_num), 0, 1,DEVICE_NAME))
- {
- printk("err----%s %d \n",__FILE__,__LINE__);
- return -1;
- }
- pri->testc_class=class_create(THIS_MODULE,DEVICE_NAME);
- cdev_init(&(pri->test_cdev),&testc_fops);
- pri->test_cdev.owner=THIS_MODULE;
- ret=cdev_add(&(pri->test_cdev),pri->testc_dev_num,1);
- if(ret)
- {
- printk("err----%s %d \n",__FILE__,__LINE__);
- return -1;
- }
- //设备节点自动生成
- x=device_create(pri->testc_class,NULL,MKDEV(MAJOR(pri->testc_dev_num),0),pri,DEVICE_NAME);
- if(x==NULL)
- {
- printk("err----%s %d \n",__FILE__,__LINE__);
- return -1;
- }
- priv=pri;
- priv->current_pointer=0;
- //mmap
- pri->mmap_kvirt_addr=(unsigned long )pci_alloc_consistent(pdev,MMAPBUF_LEN,&(pri->mmap_pcibus_addr));
- if((void *)pri->mmap_kvirt_addr==NULL)
- {
- return -1;
- }
- memset((void *)pri->mmap_kvirt_addr,0x0,MMAPBUF_LEN);
- //写入有意义的数据,方便在应用里验证确实映射了16MB
- for(i=0;i<MMAPBUF_LEN/4;i++)
- {
- *(((u32 *)pri->mmap_kvirt_addr)+i)=i;
- }
- pri->mmap_phyaddr=virt_to_phys((void *)pri->mmap_kvirt_addr);
- printk("mmap_phyaddr %lx , mmap_kvirt_addr %lx \n",pri->mmap_phyaddr,pri->mmap_kvirt_addr);
-
- return 0;
- }
- static void __devexit pmc_remove(struct pci_dev *pdev)
- {
- struct my_testdev *pri = pci_get_drvdata(pdev);
- pci_free_consistent(pdev, MMAPBUF_LEN,(void *)pri->mmap_kvirt_addr, pri->mmap_pcibus_addr);
-
- }
- static struct pci_driver netdrv_pci_driver = {
- .name = MODNAME,
- .id_table = netdrv_pci_tbl,
- .probe = pmc_probe,
- .remove = __devexit_p(pmc_remove),
- };
- static int __init pmc_init_module(void)
- {
- return pci_register_driver(&netdrv_pci_driver);
- }
- static void __exit pmc_cleanup_module(void)
- {
- pci_unregister_driver(&netdrv_pci_driver);
- }
- module_init(pmc_init_module);
- module_exit(pmc_cleanup_module);
- MODULE_AUTHOR("deep_pro");
- MODULE_LICENSE("GPL");
简单的应用程序,注意mmap的最后一个参数是dma缓冲区的物理地址,演示程序里根据驱动的打印写的硬编码,最终还是要靠ioctl等机制实现自动从驱动取得。
点击(此处)折叠或打开
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <limits.h>
- #include <linux/kernel.h>
- #include <byteswap.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/mman.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/ioctl.h>
- #define MMAP_LEN 16*1024*1024
- int main()
- {
- int fd,i,ret;
- char buf[0x20]={0};
- unsigned int *mmap_data;
- fd=open("/dev/testc",O_RDWR);
- if(fd<0)
- {
- perror("open:");
- return -1;
- }
- //0xf7000000 这个是缓冲区的物理地址,要从驱动里得到
- mmap_data=(unsigned int *)mmap(NULL,MMAP_LEN,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0xf7000000);
- if(mmap_data==MAP_FAILED)
- {
- perror("mmap");
- return -1;
- }
- //测试mmap后的缓冲区读写 ,先写再读,这样执行第二遍应用就能验证写入成功
- for(i=14*1024*1024/4;i<(14*1024*1024+0x120)/4;i++)
- {
- //mmap_buf[i]=0;
- printf("mmap_buf %02x :%x \n",i,*(mmap_data+i));
- *(mmap_data+i)+=1;
- }
- munmap(mmap_data,MMAP_LEN);
- close(fd);
- }