前言

在上一篇《关于 DRM 中 DUMB 和 PRIME 名字的由来》 文章中,我们知道了 dumb buffer 名字的由来。本篇,我们将一起来写一个最简单的 GEM 驱动程序。

驱动程序

#include <drm/drmP.h>
#include <drm/drm_gem_cma_helper.h>static struct drm_device drm;static const struct file_operations mygem_fops = {.owner = THIS_MODULE,.open = drm_open,.release = drm_release,.unlocked_ioctl = drm_ioctl,.poll = drm_poll,.read = drm_read,.mmap = drm_gem_cma_mmap,
};static struct drm_driver mygem_driver = {.driver_features        = DRIVER_GEM,.fops                   = &mygem_fops,.dumb_create            = drm_gem_cma_dumb_create,.gem_vm_ops             = &drm_gem_cma_vm_ops,.gem_free_object_unlocked = drm_gem_cma_free_object,.name                   = "my-gem",.desc                   = "My GEM Driver",.date                   = "20200601",.major                  = 1,.minor                  = 0,
};static int __init mygem_init(void)
{drm_dev_init(&drm, &mygem_driver, NULL);drm_dev_register(&drm, 0);return 0;
}module_init(mygem_init);

测试程序

#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>int main(int argc, char **argv)
{int fd;char *vaddr;struct drm_mode_create_dumb create_req = {};struct drm_mode_destroy_dumb destroy_req = {};struct drm_mode_map_dumb map_req = {};fd = open("/dev/dri/card0", O_RDWR);create_req.bpp = 32;create_req.width = 240;create_req.height = 320;drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_req);printf("create dumb: handle = %u, pitch = %u, size = %llu\n",create_req.handle, create_req.pitch, create_req.size);map_req.handle = create_req.handle;drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_req);printf("get mmap offset 0x%llx\n", map_req.offset);vaddr = mmap(0, create_req.size, PROT_WRITE, MAP_SHARED, fd, map_req.offset);strcpy(vaddr, "This is a dumb buffer!");munmap(vaddr, create_req.size);vaddr = mmap(0, create_req.size, PROT_READ, MAP_SHARED, fd, map_req.offset);printf("read from mmap: %s\n", vaddr);munmap(vaddr, create_req.size);getchar();destroy_req.handle = create_req.handle;drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_req);close(fd);return 0;
}

描述:先创建一个 dumb buffer,然后将其 mmap 到 user-space,往里写入一串字符串,然后重新映射,读取并打印 buffer 中的内容。

运行结果

root@ubuntu:~# ./dumb
create dumb: handle = 1, pitch = 960, size = 307200
get mmap offset 0x10000000
read from mmap: This is a dumb buffer!

驱动讲解

  1. DRIVER_GEM:该 feature 告诉 DRM 框架本驱动支持 GEM 操作,如 buffer 的分配和释放,以及 GEM OPEN/FLINK/CLOSE 等操作。
  2. dumb_create:分配 dumb buffer 的回调接口,主要完成三件事:
    (1)创建 gem object
    (2)创建 gem handle
    (3)分配物理 buffer (也可以等到后面再分配)
    本例中直接使用 CMA helper 函数实现,该函数内部会分配最终的物理 buffer。
  3. mmap:创建 dumb buffer 的目的就是要拿去给 CPU 画图,因此没有 mmap 的 dumb buffer 是没有灵魂的,所以必须实现。通常使用 drm_gem_mmap() 来实现。
  4. gem_vm_ops:主要为 mmap 服务,必须实现。下一篇文章会对它做详细介绍。

通常在看 DRM 文档时,还会提到 dumb_map_offsetdumb_destroy 这两个接口,分别对应各自的 ioctl 函数。如果驱动没有实现这两个回调接口, 那么 DRM 框架会使用默认的 drm_gem_dumb_map_offset()drm_gem_dumb_destroy() 代替。

为什么要执行 DRM_IOCTL_MODE_MAP_DUMB ?

许多人刚开始写 dumb buffer 应用程序时都会困惑,明明有 mmap 函数,为什么中间要再插一脚 DRM_IOCTL_MODE_MAP_DUMB ?光看名字很容易让人误以为该 ioctl 是在执行 mmap 的动作,那为什么要添加这个 ioctl 呢?

要回答这个问题其实很简单,设想一下,假如你当前创建了2个 dumb buffer,你要对其中一个做 mmap 操作,请问 mmap 函数应该如何知道你当前要操作的是哪个 buffer ?因为你当前使用的是 card0 的 fd,而不是 dumb buffer 的 fd,所以你没法通过 fd 来对其进行区分。而 size 和 flag 这些参数都不能随意修改,因此只能通过 offset 参数来 workaround,从而告诉 mmap 当前具体要操作的是哪个 dumb buffer。

所以,对 drm device 进行 mmap 操作时,传进去的 offset 参数并不是真正的内存偏移量,而是一个 gem object 的索引值。通过该索引值,drm 驱动就可以准确定位到当前具体要操作的是哪个 gem object,进而获取到与该 object 相对应的物理 buffer,并对其做真正的 mmap 操作。

那如何知道某个 gem object 的索引值呢?所以才有了 DRM_IOCTL_MODE_MAP_DUMB !你给它一个 gem handle,它返回给你一个 offset,就这么简单!而该 ioctl 对应的底层实现就是前面提到的 dumb_map_offset 回调接口。(个人认为该 ioctl 的名字改成 DRM_IOCTL_MODE_CREATE_MAP_OFFSET 可能会更好一点 :-)

这也是为什么上面的示例运行结果中,获取到的 offset 值为 0x10000000,而不是我们通常认为的偏移 0,原因就在于此(DRM 文档中将其称为 fake offset,即伪偏移)。

总结

  1. 哪里有 dumb_create,哪里就有 mmap
  2. 哪里有 mmap,哪里就有 gem_vm_ops
  3. mmap 中的 offset 参数是个假的。
  4. 没有 DRIVER_MODESETDRIVER_GEM 一样可以做成一个独立的 DRM 驱动。

源码下载

Github: driver,test

参考资料

  1. 杨枫_mind CSDN 博客
  2. DRM Memory Management

文章汇总: DRM (Direct Rendering Manager) 学习简介

DRM GEM 驱动程序开发(dumb)相关推荐

  1. DRM 驱动程序开发(开篇)

    前言 在前面的<最简单的DRM应用程序>系列文章中,我们学习了如何使用 libdrm 接口编写 DRM 应用程序.从本篇开始,我们将进入一个全新的世界,一起来学习如何在 kernel 空间 ...

  2. WindML相关知识和图形设备驱动程序开发(一)

    1.介绍 WindML即Wind Media Library(媒体库),它支持多媒体程序运行于嵌入式操作系统,风河公司设计它主要是用来提供基本的图形.视频和声频技术以及提供一个设计标准设备驱动程序框架 ...

  3. 《精通Linux设备驱动程序开发》——1.5 Linux发行版

    本节书摘来自异步社区<精通Linux设备驱动程序开发>一书中的第1章,第1.5节,作者:[印]Sreekrishnan Venkateswaran(斯里克里斯汉 温卡特斯瓦兰)著,更多章节 ...

  4. 《精通Linux设备驱动程序开发》——1.7 编译内核

    本节书摘来自异步社区<精通Linux设备驱动程序开发>一书中的第1章,第1.7节,作者:[印]Sreekrishnan Venkateswaran(斯里克里斯汉 温卡特斯瓦兰)著,更多章节 ...

  5. 嵌入式Linux LED,键盘,AD驱动程序开发

                  LED,键盘,AD驱动程序开发 原文: http://blog.sina.com.cn/s/blog_4083b2d70100bnlf.html 一:硬件平台及系统平台 C ...

  6. linux 网络dma驱动,S3C2410的Linux下DMA驱动程序开发

    网上介绍Linux下的一般驱动程序开发示例浩如烟海,或是因为简单,关于DMA驱动的介绍却寥寥无几:近期因工作需要,花了几日时间开发了某设备在S3C2410处理器Linux下DMA通信的驱动程序,有感于 ...

  7. poll接口《来自Linux驱动程序开发实例》

    您所在的位置:读书频道 > 操作系统 > Linux > 1.2.7 poll接口 1.2.7 poll接口 2012-05-22 13:38 冯国进 机械工业出版社 我要评论(0) ...

  8. 异步通知《来自Linux驱动程序开发实例》

    您所在的位置:读书频道 > 操作系统 > Linux > 1.2.8 异步通知 1.2.8 异步通知 2012-05-22 13:38 冯国进 机械工业出版社 我要评论(0) 字号: ...

  9. 基于DM6467的TVP7002 Linux驱动程序开发

    在Linux中,使用V4L2框架管理所有的视频编解码设备.针对我们开发板的V4L2框架结构已经在之前的TVP5150驱动程序编写和OV5642驱动程序编写的说明文档中进行了详细的分析,所以这里不再对整 ...

最新文章

  1. python手机版ios-使用Python写iOS自动化测试
  2. rtx3090能组成超级计算机吗,「必看分析」技嘉rtx3090超级雕评测?功能真的不好吗...
  3. 案例让一个命令在开机的时候自动运行的方法
  4. Python爬虫入门之使用Redis+Flask维护动态代理池
  5. kali php服务器,在云服务器上搭建公网kali linux2.0
  6. android style theme
  7. 简单python脚本实例-你用 Python 写过哪些有趣的脚本?
  8. 医学图像分割--U-Net: Convolutional Networks for Biomedical Image Segmentation
  9. win10启动修复_在win10桌面建立高级启动选项快捷方式,修复电脑故障不用愁
  10. 用Linux命令备份oracle表,Linux系统上Oracle数据库备份和还原操作说明
  11. OA系统(海信集团)双因素身份认证解决方案
  12. 硬币找钱问题(最小硬币和问题)详解与代码实现
  13. 复化辛浦生求积算法C++实现
  14. git:info: detecting host provider for ‘https://gitee.com/‘...
  15. python北京房价预测_Python爬虫告诉你北京房价有多高
  16. 从MDK分散加载文件学习STM32启动流程
  17. 【水滴石穿】ES must与should组合使用的正确方式
  18. 教你文件重命名快速操作
  19. 十五、2021-11-16Hadoop集群问题记录
  20. neo4j安装详细步骤(小白版)

热门文章

  1. amdr7-4700linux,Yoga14s 2021 ARH R7 4800H 安装Arch Linux
  2. 家谱树 (并查集拓扑排序)
  3. 使用机器学习来进行应用识别
  4. ​历经 33 天,终于拿到了心仪的 Offer
  5. 架构设计实践思路:什么是架构,怎么画架构图?
  6. Linux(CentOS)搭建redmine项目管理系统
  7. str中的join方法,fromkeys(),set集合,深浅拷贝(重点)
  8. java中有测试方法主方法不运行_java – 我的Eclipse无法再运行(或调试)我的JUnit测试...
  9. 谷歌网盘云盘google drive扩容方法
  10. untiy打包发布WebGL