2. DRM 核心数据结构:drm_device、drm_driver、drm_file 的结构与初始化流程

DRM 子系统通过三个核心数据结构来抽象显示设备、驱动实例与用户态会话。理解 drm_device、drm_driver 和 drm_file 的职责与初始化顺序,是编写稳定显示驱动的第一步。本章从结构体定义出发,逐步拆解其初始化流程与关联关系。

2.1 核心数据结构概览

下表对比了三个结构体的核心定位与生命周期:

结构体 内核对象 生命周期 主要职责
drm_driver 静态全局 模块加载 → 卸载 定义驱动能力、回调函数集、IOCTL 表
drm_device 设备实例 设备绑定 → 解绑 管理硬件资源、模式对象、内存管理器
drm_file 文件句柄 open → close 跟踪用户态会话、权限、私有对象句柄
设计原则: DRM 将驱动描述(drm_driver)、设备实例(drm_device)和用户会话(drm_file)严格分离,使得一个驱动可以同时管理多个 GPU 设备,每个设备又能服务多个独立用户进程。

2.2 drm_driver:驱动能力描述符

drm_driver 是一个静态结构体,通常在驱动模块的入口处定义。它向 DRM 核心声明该驱动支持哪些特性、使用哪些回调函数。

2.2.1 关键字段

字段 类型 说明
driver_features u32 位掩码,声明支持的特性(如 DRIVER_MODESET、DRIVER_ATOMIC)
name const char * 驱动名称,用于 /sys 和调试
desc const char * 驱动描述
date const char * 驱动发布日期
major / minor / patchlevel int 驱动版本号
fops struct file_operations * 字符设备文件操作(open、release、ioctl 等)
gem_create_object 回调 创建 GEM 对象,用于内存管理
prime_handle_to_fd / prime_fd_to_handle 回调 PRIME 缓冲区共享

2.2.2 典型初始化示例

static const struct drm_driver my_drm_driver = {
    .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
    .name           = "mygpu",
    .desc           = "My GPU DRM Driver",
    .date           = "20250101",
    .major          = 1,
    .minor          = 0,
    .patchlevel     = 0,
    .fops           = &my_drm_fops,
    .gem_create_object = my_gem_create_object,
    .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
    .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
};
关键点: driver_features 必须在驱动加载时正确设置。例如,若未设置 DRIVER_ATOMIC,则内核不会注册 atomic IOCTL,用户态将无法使用原子模式设置。

2.3 drm_device:设备实例核心

drm_device 是 DRM 驱动中最重要的运行时结构体,代表一个物理或虚拟显示设备。它包含模式配置、内存管理器、中断处理等所有子系统的入口。

2.3.1 结构体核心字段

字段 说明
dev 指向 struct device(Linux 设备模型)
driver 指向所属的 drm_driver
mode_config 模式配置管理结构体,包含所有 encoder、crtc、connector、plane
primary 主文件节点(通常为 card0)
render 渲染节点(renderD128)
filelist_mutex / filelist 保护已打开文件句柄列表
vblank VBlank 相关数据结构(后续章节重点展开)
irq_enabled 中断是否已启用

2.3.2 初始化流程

  1. 分配 drm_device: 使用 drm_dev_alloc() 或 devm_drm_dev_alloc() 分配并初始化基础字段。
  2. 设置驱动私有数据: 通过 container_of 或内嵌结构体访问驱动自定义数据。
  3. 注册设备: 调用 drm_dev_register() 创建设备节点并注册到文件系统。
  4. 初始化模式配置: 调用 drm_mode_config_init(),然后驱动添加自己的 CRTC、Encoder、Connector、Plane。
  5. 安装中断: 注册 VBlank 中断处理函数。
struct drm_device *ddev;
int ret;

// 步骤1:分配设备
ddev = devm_drm_dev_alloc(&pdev->dev, &my_drm_driver,
                          struct my_drm_private, drm);
if (IS_ERR(ddev))
    return PTR_ERR(ddev);

// 步骤2:初始化模式配置
drm_mode_config_init(ddev);
ddev->mode_config.min_width = 640;
ddev->mode_config.max_width = 4096;
ddev->mode_config.min_height = 480;
ddev->mode_config.max_height = 2160;

// 步骤3:添加 CRTC、Encoder、Connector(驱动实现)
my_drm_crtc_init(ddev);
my_drm_encoder_init(ddev);
my_drm_connector_init(ddev);

// 步骤4:注册设备
ret = drm_dev_register(ddev, 0);
if (ret)
    goto err_unregister;
常见错误: 在调用 drm_dev_register() 之前,必须确保所有模式对象(CRTC、Encoder、Connector)已初始化完毕。否则用户态在打开设备节点后可能立即访问到未就绪的对象。

2.4 drm_file:用户态会话跟踪

每次用户态进程打开 DRM 设备节点(如 /dev/dri/card0)时,内核会创建一个 drm_file 实例。它用于隔离不同进程的权限、对象句柄空间和状态。

2.4.1 核心字段

字段 说明
authenticated 是否通过权限认证(Master 权限)
is_master 是否为当前 DRM Master
table_lock / object_idr 管理该会话创建的 GEM 对象句柄
event_list 待发送给用户态的事件链表(如 VBlank 事件)
minor 指向所属的 drm_minor(主节点或渲染节点)

2.4.2 创建流程

当用户态调用 open() 时,DRM 核心的 drm_open() 函数被调用:

static int drm_open(struct inode *inode, struct file *filp)
{
    struct drm_device *ddev = drm_minor_get_device(inode);
    struct drm_file *priv;
    int ret;

    // 分配 drm_file
    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    // 初始化
    priv->minor = drm_minor_acquire(drm_minor_from_inode(inode));
    priv->authenticated = 0;
    priv->is_master = 0;
    idr_init(&priv->object_idr);
    mutex_init(&priv->table_lock);
    INIT_LIST_HEAD(&priv->event_list);

    // 调用驱动回调
    if (ddev->driver->open)
        ret = ddev->driver->open(ddev, priv);
    if (ret)
        goto out_free;

    filp->private_data = priv;
    return 0;

out_free:
    kfree(priv);
    return ret;
}

2.5 三者的关联关系

下面的 SVG 图展示了 drm_driver、drm_device 和 drm_file 之间的引用与生命周期关系:

DRM 核心数据结构关系图 DRM 核心数据结构关联关系 drm_driver 静态全局,定义回调与能力 生命周期:模块加载→卸载 drm_device 设备实例,管理硬件资源 生命周期:设备绑定→解绑 包含 mode_config、vblank 等 drm_file 用户态会话句柄 生命周期:open→close driver 字段引用 filelist 链表管理 driver->open() 回调 初始化顺序 ① drm_driver 静态定义 ② drm_dev_alloc() 创建设备 ③ open() 创建 drm_file

2.6 初始化流程总结

完整的初始化时序如下:

  1. 模块加载: 驱动注册 PCI/platform 驱动,drm_driver 静态结构体已就绪。
  2. 设备探测: 内核调用驱动的 probe() 函数,在其中分配 drm_device,初始化模式配置,注册设备。
  3. 设备注册: drm_dev_register() 创建设备节点 /dev/dri/card0,用户态可开始访问。
  4. 用户态打开: 每次 open() 调用触发 drm_open(),分配并初始化 drm_file,调用驱动的 open 回调。
  5. 用户态操作: 通过 IOCTL 与 drm_device 交互,drm_file 跟踪会话状态。
  6. 设备移除: drm_dev_unregister() 销毁设备节点,释放 drm_device。
调试建议: 在 probe() 和 open() 回调中添加 DRM_DEBUG_DRIVER() 日志,可以清晰追踪每个数据结构的初始化状态。使用 cat /sys/kernel/debug/dri/0/name 可验证设备是否注册成功。

2.7 本章小结

  • drm_driver 是静态描述符,定义驱动能力和回调;drm_device 是运行时设备实例,管理所有模式对象;drm_file 是用户态会话句柄,实现进程隔离。
  • 初始化顺序严格遵循:驱动定义 → 设备分配 → 模式对象初始化 → 设备注册 → 用户态打开。
  • 三个结构体通过指针和链表相互引用,形成完整的 DRM 驱动骨架。