目前,我正在使用example driver来学习,并从中我自己定制的驱动程序. mmap代码几乎完全相同,除了我允许用户管理他们自己的请求大小并围绕它进行内存分配以及我在/ dev中自动创建char设备这一事实.
为了解释上下文,对于我的用例,我想缩小我遇到的问题.使用kmalloc内存时,dma_mmap_coherent可以正常工作,但是当我有一个保留的物理地址区域时,我想使用remap_pfn_range,它可以安静地工作,而dmesg不会报告任何错误,但是当我去阅读时,无论如何我在那里写的它总是返回0xff字节.无论我是否使用iowrite&在ioremap内存或尝试使用小型mmap’ing用户态测试写入用户空间后,在内核中进行ioread.
我已经做了尽可能多的关于这个主题的研究.我所能找到的有关remap_pfn_range的文档的是kernel.org page,并且一些内核gmain邮件列表存档在remap_pfn_range上,取代了remap_page_range.至于dma_mmap_coherent,我能够找到更多,including a presentation from the linux archives.
最终必须有所不同;似乎有很多不同的方法将内核内存映射到用户区.我遇到的具体问题是:dma_mmap_coherent和remap_pfn_range有什么区别?
与您正在操作的设备(struct device * dev)相关联,如果不是它调用dma_common_mmap(),在将页面保护设置为不可缓存之后最终导致调用remap_pfn_range()(请参阅dma-中的dma_common_mmap()) mapping.c).
至于mmap的内核内存到用户空间的一般概述,下面是我从用户空间mmap’ed DMA缓冲区的快速而简单的方法:
>通过IOCTL分配缓冲区,并为每个缓冲区指定一个带有一些标志的缓冲区ID:
/* A copy-from-user call needs to be done before in the IOCTL */
static int my_ioctl_alloc(struct my_struct *info,struct alloc_info *alloc)
{
...
info->buf->kvaddr = dma_alloc_coherent(dev,alloc->size,info->buf->phyaddr,GFP_KERNEL);
info->buf->buf_id = alloc->buf_id;
...
}
>定义mmap文件ops:
static const struct file_operations my_fops = {
.open = my_open,.close = my_close,.mmap = my_mmap,.unlocked_ioctl = my_ioctl,};
不要忘记在驱动程序的探测函数中的某处注册my_fops结构.
>实现mmap文件操作:
static int my_mmap(struct file *fptr,struct vm_area_struct *vma)
{
...
desc_id = vma->vm_pgoff;
buf = find_buf_by_id(alloc,desc_id);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
ret = remap_pfn_range(vma,vma->vm_start,buf->phyaddr >> PAGE_SHIFT,vma->vm_end - vma->vm_start,vma->vm_page_prot);
if (ret) {
/* Error Handle */
}
return 0;
}
有了这个,您的内核驱动程序应该具有分配和mmap缓冲区的最小值.释放缓冲区是奖励积分的练习!
在应用程序中,您将打开()文件并获取有效的文件描述符fd,在执行复制到内核之前调用allocate IOCTL并设置缓冲区ID.在mmap中,您将通过offset参数给出缓冲区ID:
mmap(NULL,buf_size,PROT_READ | PROT_WRITE,MAP_SHARED,fd,buffer_id << PAGE_SHIFT);
PAGE_SHIFT是内核中固定的体系结构编译时MACRO.
希望这可以帮助.
这不是checkpatch.pl兼容的代码,也不是最佳实践,但这是我知道如何执行此操作的一种方式.欢迎提出意见/改进/建议!
有关感兴趣的读者,请参阅Linux设备驱动程序 – 第15章:内存映射和DMA以获取教科书示例和良好的背景信息.