2. 实现DRM驱动加载与卸载

任务目标

编写一个最小DRM驱动内核模块,在模块加载时完成DRM设备对象的分配与注册,在卸载时执行逆序释放,并通过内核日志验证加载/卸载流程正确执行。

环境依赖

  • Linux内核源码树(含DRM子系统头文件),版本 ≥ 4.19
  • 内核头文件包(linux-headers-$(uname -r))
  • Make工具与C编译器

关键完整代码

创建文件 drm_load_unload.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 struct drm_device *dev;

static int drm_load_probe(struct pci_dev *pdev,
                          const struct pci_device_id *id)
{
    int ret;

    dev = drm_dev_alloc(NULL, &pdev->dev);
    if (IS_ERR(dev))
        return PTR_ERR(dev);

    ret = drm_dev_register(dev, 0);
    if (ret) {
        drm_dev_put(dev);
        dev = NULL;
        return ret;
    }

    pci_set_drvdata(pdev, dev);
    drm_info(dev, "DRM device registered successfully\n");
    return 0;
}

static void drm_load_remove(struct pci_dev *pdev)
{
    struct drm_device *drm_dev = pci_get_drvdata(pdev);

    if (!drm_dev)
        return;

    drm_dev_unregister(drm_dev);
    drm_dev_put(drm_dev);
    pci_set_drvdata(pdev, NULL);
    dev = NULL;
    pr_info("DRM device unregistered and released\n");
}

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

static struct pci_driver drm_load_driver = {
    .name       = "drm_load_unload",
    .id_table   = drm_load_pci_ids,
    .probe      = drm_load_probe,
    .remove     = drm_load_remove,
};

module_pci_driver(drm_load_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("DRM Course");
MODULE_DESCRIPTION("Minimal DRM load/unload example");

创建 Makefile:

obj-m := drm_load_unload.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

编译与加载

make
sudo insmod drm_load_unload.ko
sudo rmmod drm_load_unload

验证方法

加载后执行 dmesg | tail -5,应看到:

[timestamp] drm_load_unload 0000:00:00.0: DRM device registered successfully

卸载后再次执行 dmesg | tail -5,应看到:

[timestamp] DRM device unregistered and released

同时确认 ls /sys/bus/pci/drivers/drm_load_unload/ 在加载期间存在,卸载后消失。

说明

  • 代码使用虚拟PCI设备ID 0x1234:0x5678,实际硬件需替换为真实设备ID;若仅验证模块生命周期,可改用module_init/module_exit直接调用drm_dev_alloc/register,但当前实现更贴近真实DRM驱动的PCI探测流程。
  • 资源释放顺序必须为:先drm_dev_unregister注销设备,再drm_dev_put释放引用,不可颠倒。
  • 若系统无匹配PCI设备,probe不会触发,此时可添加new_id或使用虚拟化设备(如QEMU的virtio-gpu)验证。