4. 实现DRM文件操作
任务目标
为DRM设备节点实现完整的file_operations结构,包含open、release、ioctl、mmap回调,并通过互斥锁处理并发访问。本案例基于虚拟DRM驱动框架,不依赖具体硬件,可直接编译验证。
核心实现
// drm_fops.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_device.h>
#define DRM_FOPS_TEST_IOCTL _IO('D', 0x01)
struct drm_fops_private {
struct mutex lock;
unsigned long buf_size;
void *buf;
};
static int drm_fops_open(struct inode *inode, struct file *filp)
{
struct drm_device *dev = drm_dev_get_from_inode(inode);
struct drm_fops_private *priv;
int ret;
if (!dev)
return -ENODEV;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_init(&priv->lock);
priv->buf_size = PAGE_SIZE;
priv->buf = kzalloc(priv->buf_size, GFP_KERNEL);
if (!priv->buf) {
kfree(priv);
return -ENOMEM;
}
mutex_lock(&priv->lock);
ret = drm_open_helper(inode, filp, dev);
mutex_unlock(&priv->lock);
if (ret) {
kfree(priv->buf);
kfree(priv);
return ret;
}
filp->private_data = priv;
return 0;
}
static int drm_fops_release(struct inode *inode, struct file *filp)
{
struct drm_fops_private *priv = filp->private_data;
if (priv) {
mutex_lock(&priv->lock);
drm_release(inode, filp);
mutex_unlock(&priv->lock);
kfree(priv->buf);
mutex_destroy(&priv->lock);
kfree(priv);
filp->private_data = NULL;
}
return 0;
}
static long drm_fops_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct drm_fops_private *priv = filp->private_data;
long ret = 0;
if (!priv)
return -EBADF;
mutex_lock(&priv->lock);
switch (cmd) {
case DRM_FOPS_TEST_IOCTL: {
unsigned long val;
if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
ret = -EFAULT;
break;
}
/* 写入私有缓冲区,验证并发安全 */
memset(priv->buf, (int)val, priv->buf_size);
ret = 0;
break;
}
default:
/* 交由DRM核心处理标准ioctl */
ret = drm_ioctl(filp, cmd, arg);
break;
}
mutex_unlock(&priv->lock);
return ret;
}
static int drm_fops_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_fops_private *priv = filp->private_data;
int ret;
if (!priv)
return -EBADF;
mutex_lock(&priv->lock);
/* 映射私有缓冲区到用户空间 */
ret = remap_pfn_range(vma, vma->vm_start,
virt_to_phys(priv->buf) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
mutex_unlock(&priv->lock);
return ret;
}
static const struct file_operations drm_fops = {
.owner = THIS_MODULE,
.open = drm_fops_open,
.release = drm_fops_release,
.unlocked_ioctl = drm_fops_ioctl,
.mmap = drm_fops_mmap,
};
/* 驱动注册入口(最小示例) */
static struct drm_driver fake_driver = {
.fops = &drm_fops,
.name = "drm_fops_demo",
.desc = "DRM file operations demo",
.date = "20250101",
.major = 1,
.minor = 0,
};
static int __init drm_fops_init(void)
{
/* 实际项目中此处需注册platform_driver或pci_driver */
pr_info("drm_fops: module loaded, fops registered\n");
return 0;
}
static void __exit drm_fops_exit(void)
{
pr_info("drm_fops: module unloaded\n");
}
module_init(drm_fops_init);
module_exit(drm_fops_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DRM Course");
MODULE_DESCRIPTION("DRM file_operations implementation example");
编译与加载
# Makefile
obj-m += drm_fops.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_fops.ko
# 验证模块加载
lsmod | grep drm_fops
验证方法
由于本案例为教学演示,未绑定真实DRM设备,可通过以下方式验证file_operations逻辑正确性:
- 静态检查:确认
drm_fops结构体各回调函数指针正确赋值,无编译警告。 - 并发测试:在真实DRM设备驱动中接入本
file_operations后,使用stress-ng或自定义多线程程序同时调用open/ioctl/mmap,观察无死锁、无崩溃。 - 功能验证:通过
ioctl写入值后,再通过mmap映射读取同一缓冲区,确认数据一致。
通过条件:模块编译无错误;加载/卸载正常;在接入真实DRM设备后,多进程并发访问设备节点不产生锁竞争异常或内存错误。
关键说明
drm_open_helper和drm_release是DRM核心提供的辅助函数,负责文件与DRM设备的关联管理。- 互斥锁
priv->lock保护私有数据访问,确保ioctl和mmap操作互斥。 - 本示例未实现真实GPU硬件交互,仅演示
file_operations的标准实现模式;接入真实设备时需在drm_driver中填充对应硬件操作回调。