dma_mmap_coherent() 定义于dma-映射.h https://github.com/torvalds/linux/blob/master/include/linux/dma-mapping.h作为 dma_mmap_attrs() 的包装。 dma_mmap_attrs() 尝试查看一组 dma_mmap_ops 是否
与您正在操作的设备(struct device *dev)关联,如果没有,它会调用 dma_common_mmap() ,在将页面保护设置为不可缓存之后,最终会导致调用 remap_pfn_range() (请参阅 dma- 中的 dma_common_mmap() )映射.c)。
至于将内核内存映射到用户空间的工作原理的一般概述,以下是我从用户空间映射 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 缓冲区。释放缓冲区是一项奖励积分的练习!
在应用程序中,您将 open() 文件并获取有效的文件描述符 fd,调用分配 IOCTL 并在执行复制到内核之前设置缓冲区 ID。在 mmap 中,您可以通过 offset 参数给出缓冲区 ID:
mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer_id << PAGE_SHIFT);
PAGE_SHIFT 是内核中固定的依赖于体系结构的编译时宏。
希望这可以帮助。
这不是符合 checkpatch.pl 的代码,也不是最佳实践,但这是我知道如何做到这一点的一种方法。欢迎评论/改进/建议!
请参阅 Linux 设备驱动程序 - 第 15 章:内存映射和 DMA,了解教科书示例以及为感兴趣的读者提供的良好背景信息。