4. drm_driver 结构体详解:关键回调函数的作用与配置
struct drm_driver 是 DRM 框架中驱动侧的核心结构体,它定义了驱动程序的能力、文件操作接口、内存管理方式以及私有数据生命周期。每个 DRM 驱动都必须实例化并填充该结构体,然后通过 drm_dev_alloc() 或 drm_dev_register() 注册到内核。本节将深入解析 driver_features、fops、gem_ops 和 prime_ops 四个关键成员。
drm_driver 是驱动与 DRM 核心之间的契约,它决定了用户空间能调用哪些 ioctl、驱动使用哪种内存模型(GEM 还是 TTM)、以及如何导出/导入缓冲区。
4.1 driver_features:驱动能力位图
driver_features 是一个 u32 类型的位掩码,用于向 DRM 核心声明驱动支持的特性。DRM 核心在初始化设备时会根据该字段启用或禁用相应的内部逻辑。
| 特性标志 | 值 | 说明 |
|---|---|---|
DRIVER_GEM |
0x1 | 支持 GEM(Graphics Execution Manager)内存管理。现代 DRM 驱动必须设置此标志。 |
DRIVER_MODESET |
0x2 | 支持 KMS(Kernel Mode Setting)。显示驱动必须设置此标志。 |
DRIVER_RENDER |
0x4 | 支持渲染节点(render node),允许非特权用户空间直接访问 GPU 计算能力。 |
DRIVER_ATOMIC |
0x8 | 支持原子模式设置(Atomic Modeset),提供事务性、无撕裂的显示更新。 |
DRIVER_SYNCOBJ |
0x10 | 支持同步对象(syncobj),用于用户空间同步 GPU 和显示操作。 |
DRIVER_GEM | DRIVER_MODESET。如果硬件支持原子提交,强烈建议同时设置 DRIVER_ATOMIC。
4.2 fops:文件操作回调表
fops 是一个指向 struct file_operations 的指针,它定义了用户空间通过 open()、ioctl()、mmap() 等系统调用与 DRM 设备交互时的行为。DRM 核心提供了通用的 drm_open()、drm_ioctl()、drm_mmap() 等函数,驱动通常只需填充 owner 字段,其余直接使用 DRM 核心实现。
| fops 成员 | 推荐值 | 说明 |
|---|---|---|
owner |
THIS_MODULE |
内核模块所有者,防止模块在使用中被卸载。 |
open |
drm_open |
打开设备文件,分配并初始化 drm_file 结构体。 |
release |
drm_release |
关闭设备文件,清理文件私有数据。 |
ioctl |
drm_ioctl |
分发 ioctl 请求到对应的 DRM 核心或驱动回调。 |
mmap |
drm_mmap |
将 GEM 对象映射到用户空间地址。 |
poll |
drm_poll |
支持用户空间通过 poll/select 等待事件(如 VBlank)。 |
drm_ioctl 作为 unlocked_ioctl,而忽略了 compat_ioctl 的配置。对于 32 位兼容层,应同时设置 .compat_ioctl = drm_compat_ioctl。
4.3 gem_ops:GEM 对象生命周期管理
gem_ops 是一个指向 struct drm_gem_object_funcs 的指针,它定义了 GEM 对象的创建、销毁、内存映射、导出等操作。从 Linux 4.19 内核开始,DRM 核心推荐驱动使用 gem_ops 替代旧的 drm_driver 级别的 GEM 回调。
| 回调函数 | 作用 | 是否必须 |
|---|---|---|
free |
释放 GEM 对象及其关联的物理内存。 | 是 |
open |
当 GEM 对象被新的文件描述符引用时调用,用于增加引用计数。 | 否 |
close |
当文件描述符关闭对 GEM 对象的引用时调用。 | 否 |
print_info |
用于 debugfs 输出 GEM 对象信息。 | 否 |
export |
将 GEM 对象导出为 dma-buf,用于跨设备共享。 | 否(但推荐) |
pin |
将 GEM 对象固定到物理内存,防止被换出。 | 否 |
unpin |
解除 GEM 对象的固定状态。 | 否 |
vmap |
将 GEM 对象映射到内核虚拟地址空间。 | 否 |
vunmap |
解除内核虚拟地址映射。 | 否 |
// 典型的 gem_ops 配置示例
static const struct drm_gem_object_funcs my_gem_funcs = {
.free = my_gem_free_object,
.open = my_gem_open,
.close = my_gem_close,
.print_info = my_gem_print_info,
.export = my_gem_export,
.pin = my_gem_pin,
.unpin = my_gem_unpin,
.vmap = my_gem_vmap,
.vunmap = my_gem_vunmap,
};
// 在 drm_driver 中赋值
static struct drm_driver my_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET,
.gem_ops = &my_gem_funcs,
// ... 其他成员
};
gem_ops 初始化为一个非空指针。DRM 核心会检查 gem_ops 是否为 NULL,如果为 NULL 则回退到旧的 drm_driver 级别回调,这可能导致行为不一致。
4.4 prime_ops:dma-buf 导入/导出回调
prime_ops 是一个指向 struct drm_prime_ops 的指针,它定义了驱动如何处理 dma-buf 的导入和导出操作。PRIME 是 DRM 框架中实现跨设备缓冲区共享的机制,基于内核的 dma-buf 子系统。
| 回调函数 | 作用 | 典型实现 |
|---|---|---|
init_object |
初始化一个空的 GEM 对象,准备从 dma-buf 导入数据。 | 分配 GEM 对象结构体,但不分配物理内存 |
begin_cpu_access |
在 CPU 访问 dma-buf 前调用,确保缓存一致性。 | 执行 dma_sync_single_for_cpu() |
end_cpu_access |
在 CPU 访问 dma-buf 结束后调用。 | 执行 dma_sync_single_for_device() |
gem_prime_export |
将 GEM 对象导出为 dma-buf 文件描述符。 | 调用 drm_gem_prime_export() |
gem_prime_import |
从 dma-buf 文件描述符导入并创建 GEM 对象。 | 调用 drm_gem_prime_import() |
4.5 关键回调函数配置总结
下表汇总了 drm_driver 中四个关键成员的配置要点和常见陷阱:
| 成员 | 类型 | 必须配置 | 常见陷阱 |
|---|---|---|---|
driver_features |
u32 |
是 | 忘记设置 DRIVER_GEM 导致 GEM 功能不可用;未设置 DRIVER_ATOMIC 导致原子提交失败 |
fops |
struct file_operations * |
是 | 未设置 .compat_ioctl 导致 32 位应用无法使用;直接使用自定义 ioctl 绕过 DRM 核心分发 |
gem_ops |
struct drm_gem_object_funcs * |
推荐 | 设为 NULL 导致回退到旧回调;未实现 free 导致内存泄漏 |
prime_ops |
struct drm_prime_ops * |
需要 PRIME 时 | 未实现 begin_cpu_access 导致缓存一致性问题;导入时未正确初始化 GEM 对象 |
gem_ops 而非旧的 drm_driver 级别 GEM 回调。这不仅能获得更好的代码组织,还能利用 DRM 核心提供的默认实现,减少重复代码。对于 PRIME 支持,如果驱动不需要跨设备共享缓冲区,可以将 prime_ops 设为 NULL,DRM 核心会提供基本的 dma-buf 支持。