2. 实现DRM驱动的file operations

任务目标

为DRM驱动实现基本的file operations(open、release、ioctl),并验证用户空间程序能够通过open()打开设备节点、通过ioctl()与驱动交互、通过close()触发释放流程。

环境与依赖

  • Linux内核源码树(本案例基于5.15版本验证)
  • 内核编译工具链(gcc、make、libncurses-dev等)
  • 开发板或QEMU虚拟机(用于加载模块并运行测试程序)

核心实现

创建一个最小DRM驱动模块,注册struct drm_driver并实现其open/release回调,同时通过DRM框架提供的drm_ioctl处理用户空间命令。驱动不依赖具体硬件,仅用于演示文件操作链路。

// minimal_drm_fops.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_print.h>

// 自定义ioctl命令编号(从DRM_COMMAND_BASE开始)
#define DRM_TEST_IOCTL   _IO('d', 0x01)

static int test_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
    DRM_INFO("test_ioctl called from pid %d\n", task_pid_nr(current));
    return 0;
}

static const struct drm_ioctl_desc test_ioctls[] = {
    DRM_IOCTL_DEF_DRV(DRM_TEST_IOCTL, test_ioctl, DRM_RENDER_ALLOW),
};

static int fops_open(struct inode *inode, struct file *filp)
{
    struct drm_device *dev = drm_dev_get_from_inode(inode);
    if (!dev)
        return -ENODEV;
    DRM_INFO("drm device opened, minor=%d\n", iminor(inode));
    return drm_open_helper(inode, filp, dev);
}

static int fops_release(struct inode *inode, struct file *filp)
{
    DRM_INFO("drm device released\n");
    return drm_release(inode, filp);
}

static const struct file_operations drm_fops = {
    .owner   = THIS_MODULE,
    .open    = fops_open,
    .release = fops_release,
    .unlocked_ioctl = drm_ioctl,
    .llseek  = noop_llseek,
};

static struct drm_driver min_driver = {
    .driver_features = DRIVER_RENDER,
    .fops            = &drm_fops,
    .ioctls          = test_ioctls,
    .num_ioctls      = ARRAY_SIZE(test_ioctls),
    .name            = "minimal_fops",
    .desc            = "Minimal DRM fops demo",
    .date            = "20250101",
    .major           = 1,
    .minor           = 0,
};

static struct drm_device *dev;

static int __init mod_init(void)
{
    dev = drm_dev_alloc(&min_driver, NULL);
    if (IS_ERR(dev))
        return PTR_ERR(dev);

    int ret = drm_dev_register(dev, 0);
    if (ret) {
        drm_dev_put(dev);
        return ret;
    }
    DRM_INFO("minimal DRM fops driver registered\n");
    return 0;
}

static void __exit mod_exit(void)
{
    drm_dev_unregister(dev);
    drm_dev_put(dev);
    DRM_INFO("minimal DRM fops driver unregistered\n");
}

module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DRM Course");

编译与加载

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

# 加载模块
sudo insmod minimal_drm_fops.ko

# 查看设备节点(应生成 /dev/dri/renderD128 或 card0)
ls -l /dev/dri/

用户空间验证程序

// test_fops.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/drm.h>

#define DRM_TEST_IOCTL   _IO('d', 0x01)

int main(void)
{
    int fd = open("/dev/dri/renderD128", O_RDWR);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    printf("open success, fd=%d\n", fd);

    int ret = ioctl(fd, DRM_TEST_IOCTL, NULL);
    if (ret < 0) {
        perror("ioctl");
        close(fd);
        return 1;
    }
    printf("ioctl success\n");

    close(fd);
    printf("close success\n");
    return 0;
}
# 编译并运行测试程序
gcc -o test_fops test_fops.c
sudo ./test_fops

验证方法与判定标准

  1. 模块加载:dmesg | tail应显示minimal DRM fops driver registered,且/dev/dri/下出现新设备节点。
  2. open验证:运行测试程序后,dmesg应出现drm device opened, minor=...日志,用户程序输出open success。
  3. ioctl验证:用户程序输出ioctl success,同时内核日志出现test_ioctl called from pid ...,证明自定义命令已通过DRM框架分发到驱动回调。
  4. release验证:程序退出后,dmesg出现drm device released,证明close()正确触发release回调。

若上述日志与输出全部匹配,则判定文件操作链路实现正确。若设备节点名称不同(如card0),需相应调整测试程序中的路径。