2. DRM 设备节点创建与权限配置
任务目标
为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
验证方法与判定标准
- 模块加载:
dmesg | tail应显示minimal DRM fops driver registered,且/dev/dri/下出现新设备节点。 - open验证:运行测试程序后,
dmesg应出现drm device opened, minor=...日志,用户程序输出open success。 - ioctl验证:用户程序输出
ioctl success,同时内核日志出现test_ioctl called from pid ...,证明自定义命令已通过DRM框架分发到驱动回调。 - release验证:程序退出后,
dmesg出现drm device released,证明close()正确触发release回调。
若上述日志与输出全部匹配,则判定文件操作链路实现正确。若设备节点名称不同(如card0),需相应调整测试程序中的路径。