3. DRM 帧缓冲设备模拟与内存分配

DRM 帧缓冲设备模拟与内存分配 学习卡片

任务目标

为显卡设备创建DRM minor设备,并注册到DRM子系统,确保生成card0和controlD0设备节点。本案例基于Linux内核DRM框架,使用drm_dev_alloc、drm_dev_register接口完成最小可复现实现。

核心实现

#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 fake_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    struct drm_device *dev;
    int ret;

    /* 1. 分配DRM设备,minor号自动分配 */
    dev = drm_dev_alloc(&fake_driver, &pdev->dev);
    if (IS_ERR(dev))
        return PTR_ERR(dev);

    /* 2. 设置driver_data便于remove时访问 */
    pci_set_drvdata(pdev, dev);

    /* 3. 注册DRM设备,生成card0/controlD0节点 */
    ret = drm_dev_register(dev, 0);
    if (ret) {
        drm_dev_put(dev);
        return ret;
    }

    drm_info(dev, "DRM minor registered: card%d, controlD%d\n",
         dev->primary->index, dev->control->index);
    return 0;
}

static void fake_remove(struct pci_dev *pdev)
{
    struct drm_device *dev = pci_get_drvdata(pdev);

    drm_dev_unregister(dev);
    drm_dev_put(dev);
}

static const struct pci_device_id fake_ids[] = {
    { PCI_DEVICE(0x1234, 0x5678) },
    { }
};

static struct pci_driver fake_driver = {
    .name       = "fake_drm_minor",
    .id_table   = fake_ids,
    .probe      = fake_probe,
    .remove     = fake_remove,
};

/* DRM驱动结构:需提供fops和name */
static const struct file_operations fake_fops = {
    .owner = THIS_MODULE,
};

struct drm_driver fake_driver = {
    .driver_features = DRIVER_GEM | DRIVER_MODESET,
    .fops = &fake_fops,
    .name = "fake_drm_minor",
    .desc = "Fake DRM minor registration example",
    .date = "20250101",
    .major = 1,
    .minor = 0,
};

module_pci_driver(fake_driver);
MODULE_DEVICE_TABLE(pci, fake_ids);
MODULE_LICENSE("GPL");

编译与加载

# 内核树外编译(需内核头文件)
obj-m := fake_drm_minor.o
make -C /lib/modules/$(uname -r)/build M=$PWD modules

# 加载模块(需root)
sudo insmod fake_drm_minor.ko

# 查看生成的设备节点
ls -l /dev/dri/

验证方法

  1. 加载模块后执行ls -l /dev/dri/,预期输出包含card0和controlD0两个节点(若系统已有其他DRM设备,编号可能递增为card1/controlD1等)。
  2. 执行dmesg | tail,应看到内核日志:DRM minor registered: card0, controlD0。
  3. 卸载模块sudo rmmod fake_drm_minor后,/dev/dri/下对应节点消失。

关键说明

  • drm_dev_alloc内部会分配struct drm_device并初始化primary/control/render三个minor结构,但此时尚未创建字符设备节点。
  • drm_dev_register执行时才会真正创建card%d(primary minor)和controlD%d(control minor)设备节点,并关联到drm_fops。
  • 本案例使用PCI驱动框架仅为模拟真实显卡设备的探测入口;若目标平台无PCI设备,可将fake_probe改为在module_init中直接调用drm_dev_alloc+drm_dev_register,但需自行管理设备生命周期。
  • DRIVER_GEM和DRIVER_MODESET特性标志是DRM设备注册的必要条件,缺少会导致drm_dev_register返回-EINVAL。