3. DRM 基础数据结构:drm_device、drm_driver、drm_file、drm_minor 详解
DRM 子系统的核心由四个基础数据结构支撑:drm_device 代表一个显示设备实例,drm_driver 描述驱动行为,drm_file 管理用户态文件句柄上下文,drm_minor 提供设备节点访问入口。理解这四个结构体及其关系,是掌握 DRM 框架的基石。
3.1 drm_device:显示设备的核心抽象
drm_device 是 DRM 框架中最高层级的设备抽象,每个物理或虚拟显示设备对应一个实例。它聚合了所有子组件,是驱动初始化时第一个被创建的对象。
| 成员 | 类型 | 说明 |
|---|---|---|
dev | struct device * | 内核通用设备指针,关联到硬件平台设备 |
driver | struct drm_driver * | 指向驱动描述结构体,定义回调函数集 |
primary | struct drm_minor * | 主设备节点(/dev/dri/cardX) |
render | struct drm_minor * | 渲染节点(/dev/dri/renderD128) |
mode_config | struct drm_mode_config | 显示模式配置管理,包含 CRTC/Encoder/Connector 链表 |
filelist | struct list_head | 已打开文件句柄链表,遍历所有 drm_file |
event_lock | spinlock_t | 保护事件队列的自旋锁 |
关键要点: drm_device 的生命周期从 drm_dev_alloc() 开始,经过 drm_dev_register() 暴露给用户态,最后通过 drm_dev_unregister() 和 drm_dev_put() 销毁。驱动通常将其嵌入到自己的私有结构体中,使用 container_of 宏访问。
3.2 drm_driver:驱动行为描述符
drm_driver 是一个静态常量结构体,定义了驱动的名称、版本、支持的 IOCTL 以及一系列回调函数。它不存储运行时状态,而是作为驱动与 DRM 核心之间的契约。
| 回调函数 | 触发时机 | 典型实现 |
|---|---|---|
load | 设备注册时调用(已废弃,推荐使用 devm_drm_dev_init) | 初始化硬件、分配资源 |
open | 用户态打开设备节点时 | 创建私有数据、初始化文件上下文 |
postclose | 用户态关闭文件描述符时 | 清理 GEM 句柄、释放资源 |
lastclose | 最后一个文件句柄关闭时 | 恢复显示模式、关闭显示输出 |
irq_handler | 硬件中断触发时 | 处理 VBlank 等显示中断 |
gem_create_object | 创建 GEM 对象时 | 分配驱动私有 GEM 结构体 |
最佳实践: 现代 DRM 驱动推荐使用 drmm_ 系列 API(Managed DRM)替代传统的 load/unload 回调,利用设备资源管理自动处理生命周期。
3.3 drm_file:用户态文件句柄上下文
每次用户态进程通过 open() 系统调用打开 DRM 设备节点时,内核都会创建一个 drm_file 实例。它保存了该文件描述符的私有状态,包括权限、对象句柄映射、事件订阅等。
| 成员 | 说明 |
|---|---|
authenticated | 是否通过 Magic 认证,控制主节点访问权限 |
universal_planes | 是否启用通用平面(Universal Planes)模式 |
event_list | 待发送给用户态的事件链表(如 VBlank 事件) |
table_lock | 保护句柄表的读写锁 |
object_idr | IDR 映射表,将用户态句柄映射到内核对象指针 |
minor | 指向所属的 drm_minor,标识设备节点类型 |
安全注意: drm_file->authenticated 标志控制着对主节点(primary)的访问权限。只有经过 Magic 认证的进程才能执行模式设置(Mode Setting)操作。渲染节点(render)默认不需要认证,但无法进行显示控制。
3.4 drm_minor:设备节点与访问入口
drm_minor 是 DRM 设备节点在内核中的表示,每个 drm_minor 对应一个 /dev/dri/ 下的设备文件。它负责将用户态的文件操作(open/close/ioctl)路由到正确的 drm_device 和 drm_driver 回调。
| 类型 | 设备节点路径 | 次设备号范围 | 功能 |
|---|---|---|---|
DRM_MINOR_PRIMARY | /dev/dri/card0 | 0–63 | 主节点,支持模式设置和渲染 |
DRM_MINOR_CONTROL | /dev/dri/controlD64 | 64–127 | 控制节点,仅用于模式设置(已废弃) |
DRM_MINOR_RENDER | /dev/dri/renderD128 | 128–191 | 渲染节点,仅支持 GPU 渲染操作 |
3.5 四个结构体的协作流程
当用户态程序调用 open("/dev/dri/card0") 时,内核中的协作流程如下:
- VFS 层 根据设备号找到对应的
drm_minor实例。 drm_minor通过minor->dev指针找到所属的drm_device。drm_device调用dev->driver->open()回调,创建drm_file实例。drm_file被添加到dev->filelist链表中,并初始化句柄映射表。- 用户态通过
ioctl()发送命令时,DRM 核心根据drm_file->minor->type判断权限,然后路由到对应的 IOCTL 处理函数。
核心关系总结:
drm_device是“容器”,包含所有子对象。drm_driver是“行为”,定义驱动如何响应事件。drm_minor是“入口”,提供用户态访问路径。drm_file是“会话”,管理每个打开文件的状态。
3.6 代码示例:驱动初始化中的结构体使用
// 定义驱动回调
static const struct drm_driver my_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.open = my_driver_open,
.postclose = my_driver_postclose,
.lastclose = my_driver_lastclose,
.gem_create_object = my_gem_create_object,
.name = "my_drm_driver",
.desc = "My DRM Driver",
.date = "20250101",
.major = 1,
.minor = 0,
};
// 在 probe 函数中创建设备
static int my_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct drm_device *dev;
int ret;
// 1. 分配 drm_device
dev = drm_dev_alloc(&my_driver, &pdev->dev);
if (IS_ERR(dev))
return PTR_ERR(dev);
// 2. 初始化硬件(私有数据通过 dev->dev_private 访问)
pci_set_drvdata(pdev, dev);
// 3. 注册设备,创建 drm_minor 节点
ret = drm_dev_register(dev, ent->driver_data);
if (ret) {
drm_dev_put(dev);
return ret;
}
return 0;
}
// open 回调:创建 drm_file 私有数据
static int my_driver_open(struct drm_device *dev, struct drm_file *file)
{
struct my_file_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
file->driver_priv = priv;
return 0;
}
调试技巧: 可以通过 /sys/kernel/debug/dri/ 下的 debugfs 文件查看当前系统中所有 drm_device、drm_minor 和 drm_file 的状态。例如 cat /sys/kernel/debug/dri/0/name 可以查看显卡驱动名称。