• 正文
  • 相关推荐
申请入驻 产业图谱

GEM 驱动程序开发(dumb)

01/05 13:25
129
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

先来看下驱动程序

#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);

DRIVER_GEM:该 feature 告诉 DRM 框架本驱动支持 GEM 操作,如 buffer 的分配和释放,以及 GEM OPEN/FLINK/CLOSE 等操作。

dumb_create:分配 dumb buffer 的回调接口,主要完成三件事:

创建 gem object

创建 gem handle

分配物理 buffer (也可以等到后面再分配),本例中直接使用 CMA helper 函数实现,该函数内部会分配最终的物理 buffer。

mmap:创建 dumb buffer 的目的就是要拿去给 CPU 画图,因此没有 mmap 的 dumb buffer 是没有灵魂的,所以必须实现。通常使用 drm_gem_mmap() 来实现。

gem_vm_ops:主要为 mmap 服务,必须实现。

再来看下测试程序:

#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 = %llun",                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%llxn", 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: %sn", 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 = 307200get mmap offset 0x10000000read from mmap: This is a dumb buffer!

为什么要执行 DRM_IOCTL_MODE_MAP_DUMB ?

许多开发者初次编写 dumb buffer 应用时,常对 mmap与 DRM_IOCTL_MODE_MAP_DUMB的分工感到困惑——明明已有 mmap函数,为何需额外调用该 ioctl?核心矛盾在于:当进程仅持有 DRM 设备(如 card0)的文件描述符(fd)时,若创建多个 dumb buffer,mmap无法通过 fd 区分目标 buffer(因所有 buffer 共享同一设备 fd)。此时,size和 flag等参数无法灵活调整,只能依赖 offset参数传递标识。但 mmap接收的 offset并非真实内存偏移,而是 DRM 驱动定义的“gem object 索引”(伪偏移),用于定位具体的 buffer。

为解决“如何通过设备 fd 关联目标 gem object”的问题,DRM_IOCTL_MODE_MAP_DUMB应运而生:其功能是接收一个 gem handle(buffer 的唯一标识),返回该 buffer 对应的“伪偏移”(如示例中的 0x10000000)。这一偏移值本质是 gem object 在 DRM 驱动内部的索引标记,驱动通过该索引可精准找到对应的物理 buffer,最终完成真实的 mmap映射。简言之,该 ioctl 的核心作用是“将用户态的 gem handle 转换为 mmap 可用的伪偏移”,其底层由驱动的 dumb_map_offset回调实现(若命名为 DRM_IOCTL_MODE_CREATE_MAP_OFFSET或更直观)。

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录