dma_mmap_coherent()在dma-mapping.h中定义,作为dma_mmap_attrs()的包装器。dma_mmap_attrs()尝试查看是否与您正在操作的设备(struct device *dev)相关联的一组dma_mmap_ops,如果没有,则调用dma_common_mmap(),最终导致调用remap_pfn_range(),并将页面保护设置为非缓存(请参见dma-mapping.c中的dma_common_mmap())。
至于从用户空间映射内核内存的概述,以下是我从用户空间映射DMA缓冲区的快速简单方法:
Allocate a Buffer via an IOCTL and designate a buffer ID for each buffer with some flags:
/* 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;
...
}
Define an mmap file ops :
static const struct file_operations my_fops = {
.open = my_open,
.close = my_close,
.mmap = my_mmap,
.unlocked_ioctl = my_ioctl,
};
Do not forget to register the my_fops struct somewhere in your driver's probe function.
Implement mmap file ops :
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) {
}
return 0;
}
使用这个,你的内核驱动程序应该具有分配和映射缓冲区所需的最小限度。释放缓冲区是额外加分的练习!
在应用中,您将打开()文件并获取有效的文件描述符fd,在执行复制到内核之前调用分配IOCTL并设置缓冲区ID。在mmap中,您将通过偏移参数提供缓冲区ID:
mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer_id << PAGE_SHIFT);
PAGE_SHIFT是内核中一个与体系结构相关的编译时宏定义。希望这可以帮到您。
这不是符合checkpatch.pl标准的代码,也不是最佳实践,但这是我知道的一种方法。欢迎评论/改进/建议!
有兴趣的读者可以参考《Linux设备驱动程序》第15章:内存映射和DMA,了解教科书示例和背景信息。