内容和参考
内容
学习Linux 中drm驱动编写方法以及drm驱动初始化;
另外,学习了struct drm_driver 的部分字段是如何使用:
struct drm_driver {int (*load) (struct drm_device *, unsigned long flags); // 不推荐使用void (*unload) (struct drm_device *); // 不推荐使用void (*lastclose) (struct drm_device *); // legacyvoid (*release) (struct drm_device *); // 不推荐使用int major;int minor;int patchlevel;char *name;char *desc;char *date;u32 driver_features;const struct file_operations *fops;};
参考
drm软件流程
文章都是参考 Linux-5.8学习的内容
linux-5.8\drivers\gpu\drm\drm_drv.clinux-5.8\drivers\gpu\drm\drm_file.clinux-5.8\drivers\gpu\drm\drm_ioctl.clinux-5.8\include\drm\drm_drv.h
drm驱动初始化
驱动初始化和卸载流程非常简单,基本模板如下:
最小化代码如下:参考 何小龙-DRM 驱动程序开发(VKMS) 和 Linux DRM Internals
#define pr_fmt(fmt) "[%s:%d] " fmt, __func__, __LINE__#include <drm/drmP.h>#include <drm/drm_drv.h>#include <drm/drm_gem.h>#include <drm/drm_vblank.h>static struct drm_device *vkms_dev;static struct drm_driver vkms_drv = {.name = "vkms",.desc = "Virtual Kernel Mode Setting",.date = "20180514",};static int __init vkms_init(void){pr_info("[E]");vkms_dev = drm_dev_alloc(&vkms_drv, NULL);if (!vkms_dev) {pr_err("[vkms] dev alloc failed");return -ENOMEM;}drm_dev_register(vkms_dev, 0);pr_info("[X]\n");return 0;}static void __exit vkms_exit(void){pr_info("[E]");if (vkms_dev) {drm_dev_unregister(vkms_dev);drm_dev_put(vkms_dev);}pr_info("[X]\n");}module_init(vkms_init);module_exit(vkms_exit);MODULE_LICENSE("GPL v2");MODULE_INFO(supported, "Test drm driver");
drm_driver
struct drm_driver {// 属性域int major;int minor;int patchlevel;char *name;char *desc;char *date;u32 driver_features; // !!! 重点配置:enum drm_driver_feature; DRIVER_GEM | DRIVER_MODESET 必须有把const struct drm_ioctl_desc *ioctls;int num_ioctls;const struct file_operations *fops;};
drm_device
struct drm_device {const struct drm_driver *driver; // 在 drm_dev_init 中初始化指向struct drm_driver};
drm_dev_alloc和drm_dev_release
drivers/gpu/drm/drm_drv.c
这里调用 drm_minor_alloc 会给 struct drm_device dev- > primary/rendor 字段分配 struct drm_minor minor 结构体;
struct drm_minor { // drm_dev_init初始化,根据drm_driver的 driver_features 字段进行分配int index; // idr_alloc 动态分配一个IDint type; // DRM_MINOR_RENDER ? DRM_MINOR_PRIMARY ?struct device *kdev; // !!! drm_minor_register => drm_sysfs_minor_alloc, minor_str=card%d / renderD%d// !!! 也就是/dev/dri/card%d 和 /dev/dri/renderD%d 后期的由来struct drm_device *dev; // 指向 struct drm_devicestruct dentry *debugfs_root; // drm_minor_register->drm_debugfs_init创建以index为索引的目录struct list_head debugfs_list;struct mutex debugfs_lock;};
drm_dev_register
drivers/gpu/drm/drm_drv.c

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字段的调试接口
root@baiy:/sys/kernel/debug/dri# ls0 128
此时,系统启动中有以下打印:
[ 2.573455] [drm] Initialized vmwgfx 2.14.0 20170612 for 0000:00:0f.0 on minor 0driver->name : vmwgfxdriver->major : 2driver->minor : 14driver->patchlevel : 0driver->date : 20170612struct drm_device dev->dev ? dev_name(dev->dev) : "virtual device"struct drm_device dev->primary->index : 0
drm_dev_unregister
drivers/gpu/drm/drm_drv.c
注:因为我们写新的驱动不可能在支持 legacy特性,所以这里不过多描述,lastclose只是注明下
drm_dev_release
drivers/gpu/drm/drm_drv.c
这里的kfree释放设计比较有意思,其实就是释放了自己:
drm_dev_alloc=> drmm_add_final_kfree=> dev->managed.final_kfree = container; == struct drm_device *dev;drm_dev_release=> 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)提供的最简化驱动中有以下字段
static struct drm_driver vkms_driver = {.name = "vkms",.desc = "Virtual Kernel Mode Setting",.date = "20180514",.major = 1,.minor = 0,};
那么 major,minor, name, desc等作用是啥? 难道只是打印下?
其实在 Linux DRM Internals 中说的很清楚了:
其实这几个合起来表明版本信息,在 系统启动时打印,在 调用DRM_IOCTL_VERSION 获取版本描述**
static const struct drm_ioctl_desc drm_ioctls[] = {DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_RENDER_ALLOW), // drm_version会给用户态传递......}struct drm_version {int version_major; /**< Major version */int version_minor; /**< Minor version */int version_patchlevel; /**< Patch level */__kernel_size_t name_len; /**< Length of name buffer */char __user *name; /**< Name of driver */__kernel_size_t date_len; /**< Length of date buffer */char __user *date; /**< User-space buffer to hold date */__kernel_size_t desc_len; /**< Length of desc buffer */char __user *desc; /**< User-space buffer to hold desc */};
libdrm的ioctrl是怎么掉的?
参考: drm_drv初始化,实际上调用的是 struct drm_driver *driver->fops;
drm_drv初始化
前边先看了 添加drm设备 drm_xxx 的初始化,但有以下疑问?
- 系统在初始化前给我们做了哪些工作?
- libdrm调用的ioctrl接口仍然不见?
- kms这个参数控制仍然没找到?
初始化接口
drm_core_initregister_chrdev(DRM_MAJOR, "drm", &drm_stub_fops); // drm 字符设备驱动 /proc/devices => 226 drmdrm_sysfs_init(); // 创建 /sys/class/drm
用户态接口
drm_stub_fops.open = drm_stub_open,.llseek = noop_llseek,drm_stub_openstruct drm_minor *minor = drm_minor_acquire(iminor(inode));const struct file_operations *new_fops = fops_get(minor->dev->driver->fops); // 获取drm_driver->opsreplace_fops(filp, new_fops); // filp->ops = drm_driver->ops;filp->f_op->open(inode, filp); // drm_driver->ops->open();drm_minor_release(minor);
所以 drm里边的 file->fops在打开时会使用drm_driver->fops来替换;
static const struct file_operations vkms_fops = {.owner = THIS_MODULE,.open = drm_open,.release = drm_release,.unlocked_ioctl = drm_ioctl,.poll = drm_poll,.read = drm_read,};static struct drm_driver vkms_driver = {.fops = &vkms_fops, // drm_stub_open 会替换file->fops = vkms_fops.name = "vkms",.desc = "Virtual Kernel Mode Setting",.date = "20180514",.major = 1,.minor = 0,};
为什么minor可以根据次设备号获取?
struct drm_minor * minor = drm_minor_acquire(iminor(inode));
(base) baiy@baiy:~$ ls -al /dev/dri/*crw-rw----+ 1 root video 226, 0 May 2 21:42 /dev/dri/card0crw-rw----+ 1 root video 226, 128 May 2 21:42 /dev/dri/renderD128可以看到 次设备号 == minor->index,所以直接用次设备号作为idr的索引即可
遗留问题
dev/dri/xxx节点的创建
/dev/dri/xxx等节点是如何创建的???
