dma-buf 由浅入深(一) —— 最简单的 dma-buf 驱动程序
dma-buf 由浅入深(二) —— kmap / vmap
dma-buf 由浅入深(三) —— map attachment
dma-buf 由浅入深(四) —— mmap
dma-buf 由浅入深(五) —— File
dma-buf 由浅入深(六) —— begin / end cpu_access
dma-buf 由浅入深(七) —— alloc page 版本
dma-buf 由浅入深(八) —— ION 简化版


前言

前面的两篇文章《dma-buf 由浅入深(二) —— kmap/vmap》和《dma-buf 由浅入深(三) —— map attachment》都是在 kernel space 对 dma-buf 进行访问的,本篇我们将一起来学习,如何在 user space 访问 dma-buf。当然,user space 访问 dma-buf 也属于 CPU Access 的一种。

mmap

为了方便应用程序能直接在用户空间读写 dma-buf 的内存,dma_buf_ops 为我们提供了一个 mmap 回调接口,可以把 dma-buf 的物理内存直接映射到用户空间,这样应用程序就可以像访问普通文件那样访问 dma-buf 的物理内存了。

dma-buf: mmap support

在 Linux 设备驱动中,大多数驱动的 mmap 操作接口都是通过调用 remap_pfn_range() 函数来实现的,dma-buf 也不例外。对于此函数不了解的同学,推荐阅读 参考资料 中彭东林的博客,写的非常好!

除了 dma_buf_ops 提供的 mmap 回调接口外,dma-buf 还为我们提供了 dma_buf_mmap() 内核 API,使得我们可以在其他设备驱动中就地取材,直接引用 dma-buf 的 mmap 实现,以此来间接的实现设备驱动的 mmap 文件操作接口。

示例

接下来,我们将通过两个示例来演示如何在 Userspace 访问 dma-buf 的物理内存。

  • 示例一:直接使用 dma-buf 的 fd 做 mmap() 操作
  • 示例二:使用 exporter 的 fd 做 mmap() 操作

示例一

本示例主要演示如何在驱动层实现 dma-buf 的 mmap 回调接口,以及如何在用户空间直接使用 dma-buf 的 fd 进行 mmap() 操作。

exporter 驱动

首先,我们仍然基于第一篇的 exporter-dummy.c 驱动来实现 mmap 回调接口:

exporter-fd.c

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>struct dma_buf *dmabuf_exported;
EXPORT_SYMBOL(dmabuf_exported);static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{void *vaddr = dmabuf->priv;return remap_pfn_range(vma, vma->vm_start, virt_to_pfn(vaddr),PAGE_SIZE, vma->vm_page_prot);
}...static const struct dma_buf_ops exp_dmabuf_ops = {....mmap = exporter_mmap,
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE, GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf = dma_buf_export(&exp_info);sprintf(vaddr, "hello world!");return dmabuf;
}static long exporter_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{int fd = dma_buf_fd(dmabuf_exported, O_CLOEXEC);copy_to_user((int __user *)arg, &fd, sizeof(fd));return 0;
}static struct file_operations exporter_fops = {.owner     = THIS_MODULE,.unlocked_ioctl  = exporter_ioctl,
};static struct miscdevice mdev = {.minor = MISC_DYNAMIC_MINOR,.name = "exporter",.fops = &exporter_fops,
};static int __init exporter_init(void)
{dmabuf_exported = exporter_alloc_page();return misc_register(&mdev);
}static void __exit exporter_exit(void)
{misc_deregister(&mdev);
}module_init(exporter_init);
module_exit(exporter_exit);

从上面的示例可以看到,除了要实现 dma-buf 的 mmap 回调接口外,我们还引入了 misc driver,目的是想通过 misc driver 的 ioctl 接口将 dma-buf 的 fd 传递给上层应用程序,这样才能实现应用程序直接使用这个 dma-buf fd 做 mmap() 操作。

为什么非要通过 ioctl 的方式来传递 fd ?这个问题我会在下一篇《dma-buf 由浅入深(五)—— File》中详细讨论。

ioctl 接口中,我们使用到了 dma_buf_fd() 函数,该函数用于创建一个新的 fd,并与该 dma-buf 的文件相绑定。关于该函数,我也会在下一篇中做详细介绍。

userspace 程序

mmap_dmabuf.c

int main(int argc, char *argv[])
{int fd;int dmabuf_fd = 0;fd = open("/dev/exporter", O_RDONLY);ioctl(fd, 0, &dmabuf_fd);close(fd);char *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED, dmabuf_fd, 0);printf("read from dmabuf mmap: %s\n", str);return 0;
}

可以看到 userspace 的代码非常简单,首先通过 exporter 驱动的 ioctl() 获取到 dma-buf 的 fd,然后直接使用该 fd 做 mmap() 映射,最后使用 printf() 来输出映射后的 buffer 内容。

运行结果

在 my-qemu 仿真环境中执行如下命令:

# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/exporter-fd.ko
# ./mmap_dmabuf

将看到如下打印结果:

read from dmabuf mmap: hello world!

可以看到,userspace 程序通过 mmap() 接口成功的访问到 dma-buf 的物理内存。

关于应用程序直接使用 dma-buf fd 做 mmap() 操作的案例,Google ADF 的 simple_buffer_alloc 可谓在这一点上发挥的淋漓尽致!详细参考代码如下:

  • kernel-3.18/drivers/video/adf/adf_fops.c
  • android9_r3/bootable/recovery/minui/graphics_adf.cpp

备注:上层 minui 获取到的 surf->fd 其实就是 dma-buf 的 fd。Recovery 模式下应用程序绘图本质上就是 CPU 通过 mmap() 来操作 dma-buf 的物理内存。

示例二

本示例主要演示如何使用 dma_buf_mmap() 内核 API,以此来简化设备驱动的 mmap 文件操作接口的实现。

exporter 驱动

我们基于示例一中的 exporter-fd.c 文件,删除 exporter_ioctl() 函数,新增 exporter_misc_mmap() 函数, 具体修改如下:

exporter-mmap.c

#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>struct dma_buf *dmabuf_exported;
EXPORT_SYMBOL(dmabuf_exported);static int exporter_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{void *vaddr = dmabuf->priv;return remap_pfn_range(vma, vma->vm_start, virt_to_pfn(vaddr),PAGE_SIZE, vma->vm_page_prot);
}...
static const struct dma_buf_ops exp_dmabuf_ops = {....mmap = exporter_mmap,
};static struct dma_buf *exporter_alloc_page(void)
{DEFINE_DMA_BUF_EXPORT_INFO(exp_info);struct dma_buf *dmabuf;void *vaddr;vaddr = kzalloc(PAGE_SIZE, GFP_KERNEL);exp_info.ops = &exp_dmabuf_ops;exp_info.size = PAGE_SIZE;exp_info.flags = O_CLOEXEC;exp_info.priv = vaddr;dmabuf = dma_buf_export(&exp_info);sprintf(vaddr, "hello world!");return dmabuf;
}static int exporter_misc_mmap(struct file *file, struct vm_area_struct *vma)
{return dma_buf_mmap(dmabuf_exported, vma, 0);
}static struct file_operations exporter_fops = {.owner = THIS_MODULE,.mmap    = exporter_misc_mmap,
};static struct miscdevice mdev = {.minor = MISC_DYNAMIC_MINOR,.name = "exporter",.fops = &exporter_fops,
};static int __init exporter_init(void)
{dmabuf_exported = exporter_alloc_page();return misc_register(&mdev);
}static void __exit exporter_exit(void)
{misc_deregister(&mdev);
}module_init(exporter_init);
module_exit(exporter_exit);

与示例一的驱动相比,示例二的驱动不再需要把 dma-buf 的 fd 通过 ioctl 传给上层,而是直接将 dma-buf 的 mmap 回调接口嫁接到 misc driver 的 mmap 文件操作接口上。这样上层在对 misc device 进行 mmap() 操作时,实际映射的是 dma-buf 的物理内存。

userspace 程序

mmap_exporter.c

int main(int argc, char *argv[])
{int fd;fd = open("/dev/exporter", O_RDONLY);char *str = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);printf("read from /dev/exporter mmap: %s\n", str);close(fd);return 0;
}

与示例一的 userspace 程序相比,示例二不再通过 ioctl() 方式获取 dma-buf 的 fd,而是直接使用 exporter misc device 的 fd 进行 mmap() 操作,此时执行的则是 misc driver 的 mmap 文件操作接口。当然最终输出的结果都是一样的。

运行结果

在 my-qemu 仿真环境中执行如下命令:

# insmod /lib/modules/4.14.143/kernel/drivers/dma-buf/exporter-mmap.ko
# ./mmap_exporter

将看到如下打印结果:

read from /dev/exporter mmap: hello world!

开发环境

内核源码 4.14.143
示例源码 hexiaolong2008-GitHub/sample-code/dma-buf/04
hexiaolong2008-GitHub/sample-code/dma-buf/05
开发平台 Ubuntu14.04/16.04
运行平台 my-qemu 仿真环境

参考资料

  1. 认真分析mmap:是什么 为什么 怎么用
  2. 内存映射函数remap_pfn_range学习——示例分析(1)

上一篇:《dma-buf 由浅入深(三)—— map attachment》
下一篇:《dma-buf 由浅入深(五)—— File》
文章汇总:《DRM(Direct Rendering Manager)学习简介》

dma-buf 由浅入深(四) —— mmap相关推荐

  1. v4l2 use V4L2_MEMORY_MMAP方式导出为 DMA BUF fd 方式使用

    V4L2_MEMORY_MMAP 导出 fd 需要使用 vb2_ioctl_expbuf (只能使用于VB2_MEMORY_MMAP 方式). int buffer_export(int v4lfd, ...

  2. DMA中的四种控制信号:DRQ、DACK、HRQ、HLDA

    转载请注明来源:imred的专栏 | http://blog.csdn.net/imred/article/details/50357819 使用DMA方式进行数据传输时,主要有四种控制信号:DRQ( ...

  3. Java 两种zero-copy零拷贝技术mmap和sendfile的介绍

    详细介绍了两种zero-copy零拷贝技术mmap和sendfile的概念和基本原理. 文章目录 1 标准IO 2 零拷贝 2.1 sendfile调用 2.1 mmap调用 2.2 MQ中的应用 1 ...

  4. 【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)

    前言: 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1.芯片: STM32F407ZET6/ STM32F103ZET6 ...

  5. camera申请buf流程

    camera通过v4l2框架申请buf流程 以下内容,以linux4.9版本代码arm架构进行介绍. VIDIOC_REQBUFS 在应用层通过ioctl传输VIDIOC_REQBUFS命令,将会传递 ...

  6. Linux mmap内存映射

    将最近网上搜索的资料统一整理下,方便后续复查. 一.什么是mmap mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一 ...

  7. 【mmap】深度分析mmap:是什么 为什么 怎么用 性能总结

    目录 有什么用? 1.文件映射 2.分配内存(匿名文件映射) mmap基础概念 mmap内存映射原理 mmap和常规文件操作的区别 mmap优点总结 mmap相关函数 mmap使用细节 性能总结 效率 ...

  8. 杂谈 linux DMA内存

    dma的大小 从开机开始看起 bootmem_init --- dma_contiguous_reserve(arm64_dma_phys_limit):     if (size_cmdline ! ...

  9. LWN 翻译:DMA-BUF cache handling: Off the DMA API map (part 2)

    声明:本文非原创,只是翻译! 原文:https://lwn.net/Articles/822521/ 作者:John Stultz ( Linaro 成员,kernel timekeeping mai ...

  10. STM32学习笔记(七) ADC模数转换测电平(普通和DMA模式)

    嵌入式系统在微控制领域(温度,湿度,压力检测,四轴飞行器)中占据着重要地位,这些功能的实现是由微处理器cpu(如stm32)和传感器以及控制器共同完成的,而连接他们,使它们能够互相正常交流的正是本小节 ...

最新文章

  1. 列表推导式 python原理_Python进阶-列表推导式详解总结
  2. 独家 | 王海峰:百度大数据与人工智能(附PPT下载)
  3. vetur插件_6款让开发效率“起飞的”VS code插件,哪个才是你的最爱
  4. linux mysql主主复制_MySQL主从复制与主主复制
  5. jquery 过滤html代码,jquery – 如何使指令使用过滤的HTML属性?
  6. const char *转wstring 方法
  7. 【背包问题】基于matlab离散粒子群算法求解背包问题【含Matlab源码 423期】
  8. SolidWorks、inventor、UG...我该学哪个?主流三维机械设计软件对比
  9. 关于复数i本质的探讨
  10. 小米5如何进入开发者模式
  11. amd服务器cpu性能排行榜,AMD 32核服务器CPU完胜Intel 22核顶级CPU
  12. 国产化服务器兼容系统,必须兼容中国芯,国产操作系统再迎来发展机遇?
  13. 电脑运行卡顿?六个方法打开任务管理器解决
  14. oracle从入门到跑路
  15. zhaowei -列表知识问答(王者农药)
  16. 网易视频云:HBase优化实战
  17. 基于kettle的数据集成平台(三)
  18. ios使用SARUnArchiveANY 解压rar文件(oc和swift版本)
  19. 从零开始学python的第19天
  20. 【游戏建模】3DMAX插件安装与详细说明

热门文章

  1. Python(1)字符串
  2. 【转载】导航定位技术方案
  3. 使用流量时微信联系不到服务器,iphone6在流量模式下也就是关闭无线用4G模式收不到任何信息通知,QQ微信啥都没有!什么原因...
  4. shell 检测文件是否存在
  5. 前端实现聊天对话框页面
  6. 3Dmax模型导入unity3d
  7. 名帖343 怀素 草书《王献之王洽王珣书评》
  8. CDP, DCP, SDP的区别(USB)
  9. 空调行业如何破局?TCL空调双十一战绩可窥见端倪
  10. 双十一第十年:新零售未来已来,消费分级成最大看点