2.KMD-3.驱动层-DRM的初始化-《计算机知识》

admin 2025-11-02 22:33:25 系统网络 来源:ZONE.CI 全球网 0 阅读模式
  • 内容和参考
    • 内容
    • 参考
  • drm软件流程
    • drm驱动初始化
      • drm_driver
      • drm_device
      • drm_dev_alloc和drm_dev_release
      • drm_dev_register
      • drm_dev_unregister
      • drm_dev_release
      • 初始化过程疑惑(重点)
        • 到底是card还是render?
        • drm_driver中的major等字段
        • libdrm的ioctrl是怎么掉的?
    • drm_drv初始化
      • 初始化接口
      • 用户态接口
  • 遗留问题
    • dev/dri/xxx节点的创建

    内容和参考

    内容

    学习Linux 中drm驱动编写方法以及drm驱动初始化;

    另外,学习了struct drm_driver 的部分字段是如何使用:

    1. struct drm_driver {
    2. int (*load) (struct drm_device *, unsigned long flags); // 不推荐使用
    3. void (*unload) (struct drm_device *); // 不推荐使用
    4. void (*lastclose) (struct drm_device *); // legacy
    5. void (*release) (struct drm_device *); // 不推荐使用
    6. int major;
    7. int minor;
    8. int patchlevel;
    9. char *name;
    10. char *desc;
    11. char *date;
    12. u32 driver_features;
    13. const struct file_operations *fops;
    14. };

    参考

    • 何小龙-DRM 驱动程序开发(VKMS)
    • 蜗窝Linux graphic subsytem-1 和 蜗窝Linux graphic subsytem-2
    • Linux DRM Internals

    drm软件流程

    文章都是参考 Linux-5.8学习的内容

    1. linux-5.8\drivers\gpu\drm\drm_drv.c
    2. linux-5.8\drivers\gpu\drm\drm_file.c
    3. linux-5.8\drivers\gpu\drm\drm_ioctl.c
    4. linux-5.8\include\drm\drm_drv.h

    drm驱动初始化

    驱动初始化和卸载流程非常简单,基本模板如下: 最小化代码如下:参考%20何小龙-DRM%20驱动程序开发(VKMS)%20和%20Linux%20DRM%20Internals

    #define%20pr_fmt(fmt)%20%20"[%s:%d]%20"%20fmt,%20__func__,%20__LINE__#include%20<drm/drmP.h>#include%20<drm/drm_drv.h>#include%20<drm/drm_gem.h>#include%20<drm/drm_vblank.h>static%20struct%20drm_device%20*vkms_dev;static%20struct%20drm_driver%20vkms_drv%20=%20{%20%20%20%20.name%20%20%20%20%20%20%20%20%20%20%20%20=%20"vkms",%20%20%20%20.desc%20%20%20%20%20%20%20%20%20%20%20%20=%20"Virtual%20Kernel%20Mode%20Setting",%20%20%20%20.date%20%20%20%20%20%20%20%20%20%20%20%20=%20"20180514",};static%20int%20__init%20vkms_init(void){%20%20%20%20pr_info("[E]");%20%20%20%20vkms_dev%20=%20drm_dev_alloc(&vkms_drv,%20NULL);%20%20%20%20if%20(!vkms_dev)%20{%20%20%20%20%20%20%20%20pr_err("[vkms]%20dev%20alloc%20failed");%20%20%20%20%20%20%20%20return%20-ENOMEM;%20%20%20%20}%20%20%20%20drm_dev_register(vkms_dev,%200);%20%20%20%20pr_info("[X]\n");%20%20%20%20return%200;}static%20void%20__exit%20vkms_exit(void){%20%20%20%20pr_info("[E]");%20%20%20%20if%20(vkms_dev)%20{%20%20%20%20%20%20%20%20drm_dev_unregister(vkms_dev);%20%20%20%20%20%20%20%20drm_dev_put(vkms_dev);%20%20%20%20}%20%20%20%20pr_info("[X]\n");}module_init(vkms_init);module_exit(vkms_exit);MODULE_LICENSE("GPL%20v2");MODULE_INFO(supported,%20"Test%20drm%20driver");

    drm_driverstruct%20drm_driver%20{%20%20%20%20//%20属性域%20%20%20%20int%20major;%20%20%20%20int%20minor;%20%20%20%20int%20patchlevel;%20%20%20%20char%20*name;%20%20%20%20char%20*desc;%20%20%20%20char%20*date;%20%20%20%20u32%20driver_features;%20%20%20%20%20%20%20%20//%20!!!%20重点配置:enum%20drm_driver_feature;%20%20%20DRIVER_GEM%20|%20DRIVER_MODESET%20必须有把%20%20%20%20const%20struct%20drm_ioctl_desc%20*ioctls;%20%20%20%20int%20num_ioctls;%20%20%20%20const%20struct%20file_operations%20*fops;};

    drm_devicestruct%20drm_device%20{%20%20%20%20const%20struct%20drm_driver%20*driver;%20//%20在%20drm_dev_init%20中初始化指向struct%20drm_driver};

    drm_dev_alloc和drm_dev_release

    drivers/gpu/drm/drm_drv.c 3.驱动层-DRM的初始化 - 图3这里调用 drm_minor_alloc 会给 struct drm_device dev- > primary/rendor 字段分配 struct drm_minor minor 结构体;

    1. struct drm_minor { // drm_dev_init初始化,根据drm_driver的 driver_features 字段进行分配
    2. int index; // idr_alloc 动态分配一个ID
    3. int type; // DRM_MINOR_RENDER ? DRM_MINOR_PRIMARY ?
    4. struct device *kdev; // !!! drm_minor_register => drm_sysfs_minor_alloc, minor_str=card%d / renderD%d
    5. // !!! 也就是/dev/dri/card%d 和 /dev/dri/renderD%d 后期的由来
    6. struct drm_device *dev; // 指向 struct drm_device
    7. struct dentry *debugfs_root; // drm_minor_register->drm_debugfs_init创建以index为索引的目录
    8. struct list_head debugfs_list;
    9. struct mutex debugfs_lock;
    10. };

    drm_dev_register

    drivers/gpu/drm/drm_drv.c 3.驱动层-DRM的初始化 - 图4

    DRM 框架还为我们做了下面这些事情:

    • 创建设备节点:/dev/dri/card0
    • 创建 sysfs 节点:/sys/class/drm/card0
    • 创建 debugfs 节点:/sys/kernel/debug/dri/0

    其中 drm_minor_register 会在 /sys/kernel/debug/dri/ 下注册 以 struct drm_minor中index字段的调试接口

    1. root@baiy:/sys/kernel/debug/dri# ls
    2. 0 128

    此时,系统启动中有以下打印:

    1. [ 2.573455] [drm] Initialized vmwgfx 2.14.0 20170612 for 0000:00:0f.0 on minor 0
    2. driver->name : vmwgfx
    3. driver->major : 2
    4. driver->minor : 14
    5. driver->patchlevel : 0
    6. driver->date : 20170612
    7. struct drm_device dev->dev ? dev_name(dev->dev) : "virtual device"
    8. struct drm_device dev->primary->index : 0

    drm_dev_unregister

    drivers/gpu/drm/drm_drv.c 3.驱动层-DRM的初始化 - 图5注:因为我们写新的驱动不可能在支持 legacy特性,所以这里不过多描述,lastclose只是注明下

    drm_dev_release

    drivers/gpu/drm/drm_drv.c 3.驱动层-DRM的初始化 - 图6这里的kfree释放设计比较有意思,其实就是释放了自己:

    1. drm_dev_alloc
    2. => drmm_add_final_kfree
    3. => dev->managed.final_kfree = container; == struct drm_device *dev;
    4. drm_dev_release
    5. => kfree(dev->managed.final_kfree); == kfree(struct drm_device);

    初始化过程疑惑(重点)

    到底是card还是render?

    在 drm_dev_init 中

    ls -al /dev/dri/ 有时是cardx,有时是rendorx,这两个有啥区别?蜗窝Linux graphic subsytem-2 中有人提到了这个问题,

    首先在 drm_dev_init 中 ,drm_minor_register => drm_sysfs_minor_alloc 给初始化了 struct drm_minor 的 struct device *kdev;名称然后在 drm_dev_register中,device_add添加了设备,所以有 /dev/dri/cardx和 、/dev/dri/rendorx

    struct drm_driver drv->driver_feature 中 只要有 DRIVER_RENDER属性,就会在 drm_minor_alloc => drm_sysfs_minor_alloc* 会创建一个 renderD%d另外,必须要有 PRIMARY,所以 drm_minor_alloc(dev, DRM_MINOR_PRIMARY); 也会创建一个 card%d其中 %d就是minor从idr分配的一个数字索引;

    /dev/dri/card0 /dev/dri/renderXX 这俩个和/dev/fb 是什么样的千丝万缕的关系. drm会模拟fb,请问这句话是什么意思?

    /dev/dri/card0 显示管理+显示合成(fb)+3D加速 /dev/dri/renderXX 仅仅包括3D加速 drm会模拟fb,/dev/dri/card0在fb功能的使用上跟framebuffer没什么太大区别 card0与renderXX一一对应,可以认为renderXX只是card0的一个子集,renderXX多数情况下用作离线渲染,渲染结果可以交由cardX显示(一般用gbm框架传递数据)。cardX也可以做离线渲染,单独给出一个renderXX的意义目前我也搞不清楚。

    drm_driver中的major等字段

    在 何小龙-DRM 驱动程序开发(VKMS)提供的最简化驱动中有以下字段

    1. static struct drm_driver vkms_driver = {
    2. .name = "vkms",
    3. .desc = "Virtual Kernel Mode Setting",
    4. .date = "20180514",
    5. .major = 1,
    6. .minor = 0,
    7. };

    那么 major,minor, name, desc等作用是啥? 难道只是打印下? 其实在 Linux DRM Internals 中说的很清楚了:其实这几个合起来表明版本信息,在 系统启动时打印,在 调用DRM_IOCTL_VERSION 获取版本描述**

    1. static const struct drm_ioctl_desc drm_ioctls[] = {
    2. DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_RENDER_ALLOW), // drm_version会给用户态传递
    3. ......
    4. }
    5. struct drm_version {
    6. int version_major; /**< Major version */
    7. int version_minor; /**< Minor version */
    8. int version_patchlevel; /**< Patch level */
    9. __kernel_size_t name_len; /**< Length of name buffer */
    10. char __user *name; /**< Name of driver */
    11. __kernel_size_t date_len; /**< Length of date buffer */
    12. char __user *date; /**< User-space buffer to hold date */
    13. __kernel_size_t desc_len; /**< Length of desc buffer */
    14. char __user *desc; /**< User-space buffer to hold desc */
    15. };

    libdrm的ioctrl是怎么掉的?

    参考: drm_drv初始化,实际上调用的是 struct drm_driver *driver->fops;

    drm_drv初始化

    前边先看了 添加drm设备 drm_xxx 的初始化,但有以下疑问?

    • 系统在初始化前给我们做了哪些工作?
    • libdrm调用的ioctrl接口仍然不见?
    • kms这个参数控制仍然没找到?

    初始化接口

    1. drm_core_init
    2. register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops); // drm 字符设备驱动 /proc/devices => 226 drm
    3. drm_sysfs_init(); // 创建 /sys/class/drm

    用户态接口

    1. drm_stub_fops
    2. .open = drm_stub_open,
    3. .llseek = noop_llseek,
    4. drm_stub_open
    5. struct drm_minor *minor = drm_minor_acquire(iminor(inode));
    6. const struct file_operations *new_fops = fops_get(minor->dev->driver->fops); // 获取drm_driver->ops
    7. replace_fops(filp, new_fops); // filp->ops = drm_driver->ops;
    8. filp->f_op->open(inode, filp); // drm_driver->ops->open();
    9. drm_minor_release(minor);

    所以 drm里边的 file->fops在打开时会使用drm_driver->fops来替换;

    1. static const struct file_operations vkms_fops = {
    2. .owner = THIS_MODULE,
    3. .open = drm_open,
    4. .release = drm_release,
    5. .unlocked_ioctl = drm_ioctl,
    6. .poll = drm_poll,
    7. .read = drm_read,
    8. };
    9. static struct drm_driver vkms_driver = {
    10. .fops = &vkms_fops, // drm_stub_open 会替换file->fops = vkms_fops
    11. .name = "vkms",
    12. .desc = "Virtual Kernel Mode Setting",
    13. .date = "20180514",
    14. .major = 1,
    15. .minor = 0,
    16. };

    为什么minor可以根据次设备号获取?struct drm_minor * minor = drm_minor_acquire(iminor(inode));

    1. (base) baiy@baiy:~$ ls -al /dev/dri/*
    2. crw-rw----+ 1 root video 226, 0 May 2 21:42 /dev/dri/card0
    3. crw-rw----+ 1 root video 226, 128 May 2 21:42 /dev/dri/renderD128
    4. 可以看到 次设备号 == minor->index,所以直接用次设备号作为idr的索引即可

    遗留问题

    dev/dri/xxx节点的创建

    /dev/dri/xxx等节点是如何创建的???

    01-shell脚本介绍-《shell脚本》 系统网络

    01-shell脚本介绍-《shell脚本》

    一、shell脚本是什么二、为什么要学shell,而不是其他计算机语言三、学习这门课程的优势四、学了能干什么五、学习什么内容六、学习的技巧七、成长路径八、学习环
    评论:0   参与:  12