1. 初始化显示子系统核心数据结构

初始化显示子系统核心数据结构 学习卡片

任务说明

本案例要求编写Linux内核DRM显示子系统初始化代码,完成drm_device、drm_minor等核心结构体的初始化,并将设备注册到内核。目标是在内核模块加载时,创建一个最小可用的DRM设备节点,可通过/dev/dri/card0访问。

环境依赖

  • Linux内核源码树(版本≥4.19,本文以5.10为例)
  • 已启用CONFIG_DRM内核配置选项
  • 内核头文件:include/drm/drm_device.h、include/drm/drm_drv.h

核心实现

以下代码实现了一个最小DRM驱动,完成drm_device分配与初始化、drm_minor索引分配、以及设备注册。该驱动不绑定具体硬件,仅用于演示核心数据结构初始化流程。

// minimal_drm.c
#include <linux/module.h>
#include <linux/pci.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_print.h>

static int minor_allocated;

static int fake_drm_load(struct drm_device *dev, unsigned long flags)
{
    // 核心:分配DRM minor索引(即设备节点号)
    int ret = drm_dev_register(dev, flags);
    if (ret) {
        DRM_ERROR("drm_dev_register failed: %d\n", ret);
        return ret;
    }
    minor_allocated = dev->primary->index;
    DRM_INFO("DRM device registered, minor index = %d\n", minor_allocated);
    return 0;
}

static void fake_drm_unload(struct drm_device *dev)
{
    drm_dev_unregister(dev);
    DRM_INFO("DRM device unregistered\n");
}

static const struct drm_driver fake_drm_driver = {
    .driver_features = DRIVER_MODESET | DRIVER_GEM,
    .load = fake_drm_load,
    .unload = fake_drm_unload,
    .name = "fake_drm",
    .desc = "Minimal DRM init example",
    .date = "20250101",
    .major = 1,
    .minor = 0,
};

static struct drm_device *fake_dev;

static int __init fake_drm_init(void)
{
    // 1. 分配并初始化drm_device结构体
    fake_dev = drm_dev_alloc(&fake_drm_driver, NULL);
    if (IS_ERR(fake_dev)) {
        pr_err("drm_dev_alloc failed: %ld\n", PTR_ERR(fake_dev));
        return PTR_ERR(fake_dev);
    }

    // 2. 设置设备参数(此处无真实硬件,仅设置最小必要字段)
    fake_dev->dev = NULL;  // 无真实struct device,仅演示

    // 3. 调用load回调完成注册
    int ret = fake_drm_load(fake_dev, 0);
    if (ret) {
        drm_dev_put(fake_dev);
        return ret;
    }
    return 0;
}

static void __exit fake_drm_exit(void)
{
    if (fake_dev) {
        fake_drm_unload(fake_dev);
        drm_dev_put(fake_dev);
        fake_dev = NULL;
    }
}

module_init(fake_drm_init);
module_exit(fake_drm_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kernel Display Example");
MODULE_DESCRIPTION("Minimal DRM device init example");

编译与加载

将上述代码放入内核源码树drivers/gpu/drm/目录下,创建Kconfig和Makefile条目,或使用独立模块编译方式:

# 独立编译(需内核源码树)
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

# 加载模块
sudo insmod minimal_drm.ko

# 验证设备节点
ls -l /dev/dri/
# 预期输出:crw-rw---- 1 root video 226, 0 ... card0

验证方法

  1. 模块加载:执行dmesg | tail,应看到DRM device registered, minor index = 0日志。
  2. 设备节点:/dev/dri/card0应存在,主设备号226,次设备号0。
  3. 卸载测试:sudo rmmod minimal_drm后,/dev/dri/card0消失,dmesg显示DRM device unregistered。

通过条件:上述三步全部符合预期,且无内核错误日志(dmesg中无BUG/ERROR字样)。

关键说明

  • drm_dev_alloc()内部完成drm_device内存分配和drm_minor结构体初始化,但drm_minor的索引分配发生在drm_dev_register()中。
  • 本示例未绑定真实PCI/USB设备,因此drm_device.dev为NULL,仅用于演示数据结构初始化流程。实际驱动需在probe回调中传入真实struct device。
  • DRIVER_MODESET特性标志使设备节点创建为card0而非renderD128。