想设计一个结构类似V4L2实现,用户层的调用接口是不变的,但是对于驱动层来说,设备驱动可能变,但是最终不影响用户调用,分层的思想
首先写一个通用字符设备驱动 (comm_my_core.c)
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/err.h>
#define COMM_MY_DEV_NAME "comm_my"
/* 定义宏命令 */
#define MY_IOCTL_BASE 0xAE
#define MY_IOCTL_CMD1 _IO(MY_IOCTL_BASE, 0) /* 无参数命令 */
#define MY_IOCTL_CMD2 _IOW(MY_IOCTL_BASE, 1, int) /* 写参数命令 */
#define MY_IOCTL_CMD3 _IOR(MY_IOCTL_BASE, 2, int) /* 读参数命令 */
#define MY_IOCTL_CMD4 _IOWR(MY_IOCTL_BASE, 3, int) /* 读写参数命令 */
/* 定义函数指针类型 */
typedef int (*cmd_handler_t)(void *priv, unsigned long arg);
/* 驱动操作表结构 */
struct comm_my_ops {
const char *name; /* 驱动名称 */
cmd_handler_t handlers[4]; /* 对应4个命令的处理函数 */
void *priv; /* 驱动私有数据 */
};
static struct comm_my_ops *g_active_ops = NULL; /* 全局变量,保存当前注册的驱动 */
static DEFINE_MUTEX(ops_lock); /* 保护并发访问的锁 */
/* 注册函数 - 将具体驱动绑定到通用设备 */
int register_common_driver(struct comm_my_ops *ops)
{
int ret = 0;
mutex_lock(&ops_lock);
if (g_active_ops) {
pr_err("comm_my: 已有驱动注册,名称: %s\n", g_active_ops->name);
ret = -EBUSY;
goto out;
}
/* 验证操作表的有效性 */
if (!ops || !ops->name || !ops->handlers[0]) {
pr_err("comm_my: 无效的驱动操作表\n");
ret = -EINVAL;
goto out;
}
g_active_ops = ops; /* 关键绑定操作 */
pr_info("comm_my: 驱动 %s 注册成功\n", ops->name);
out:
mutex_unlock(&ops_lock);
return ret;
}
EXPORT_SYMBOL(register_common_driver); /* 导出符号,允许其他模块使用 */
/* 注销函数 */
void unregister_common_driver(struct comm_my_ops *ops)
{
mutex_lock(&ops_lock);
if (g_active_ops == ops) {
g_active_ops = NULL;
pr_info("comm_my: 驱动 %s 已注销\n", ops->name);
}
mutex_unlock(&ops_lock);
}
EXPORT_SYMBOL(unregister_common_driver); /* 导出符号,允许其他模块使用 */
/* ioctl命令处理函数 */
static long comm_my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = -ENOTTY;
int cmd_index;
mutex_lock(&ops_lock);
if (!g_active_ops) {
pr_err("comm_my: 无注册驱动\n");
ret = -ENODEV;
goto out;
}
/* 计算命令索引 */
if (_IOC_TYPE(cmd) != MY_IOCTL_BASE ||
_IOC_NR(cmd) >= ARRAY_SIZE(g_active_ops->handlers)) {
pr_err("comm_my: 不支持的命令: 0x%08X\n", cmd);
goto out;
}
cmd_index = _IOC_NR(cmd);
/* 检查该命令是否有处理函数 */
if (!g_active_ops->handlers[cmd_index]) {
pr_err("comm_my: 命令 %d 无处理函数\n", cmd_index);
goto out;
}
/* 调用注册的处理函数 */
ret = g_active_ops->handlers[cmd_index](g_active_ops->priv, arg);
out:
mutex_unlock(&ops_lock);
return ret;
}
/* 文件操作结构 */
static struct file_operations comm_my_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = comm_my_ioctl,
.compat_ioctl = comm_my_ioctl, /* 支持32位用户空间在64位内核上运行 */
};
/* 设备相关变量 */
static dev_t comm_my_dev;
static struct cdev comm_my_cdev;
static struct class *comm_my_class;
/* 模块初始化函数 */
static int __init comm_my_init(void)
{
int ret;
/* 分配设备号 */
ret = alloc_chrdev_region(&comm_my_dev, 0, 1, COMM_MY_DEV_NAME);
if (ret < 0) {
pr_err("comm_my: 分配设备号失败\n");
return ret;
}
/* 初始化字符设备 */
cdev_init(&comm_my_cdev, &comm_my_fops);
comm_my_cdev.owner = THIS_MODULE;
/* 添加字符设备 */
ret = cdev_add(&comm_my_cdev, comm_my_dev, 1);
if (ret < 0) {
pr_err("comm_my: 添加字符设备失败\n");
unregister_chrdev_region(comm_my_dev, 1);
return ret;
}
/* 创建类 */
comm_my_class = class_create(THIS_MODULE, COMM_MY_DEV_NAME);
if (IS_ERR(comm_my_class)) {
pr_err("comm_my: 创建类失败\n");
cdev_del(&comm_my_cdev);
unregister_chrdev_region(comm_my_dev, 1);
return PTR_ERR(comm_my_class);
}
/* 创建设备节点 */
device_create(comm_my_class, NULL, comm_my_dev, NULL, COMM_MY_DEV_NAME);
pr_info("comm_my: 通用字符设备初始化成功,设备节点: /dev/%s\n", COMM_MY_DEV_NAME);
return 0;
}
/* 模块退出函数 */
static void __exit comm_my_exit(void)
{
/* 确保所有驱动已注销 */
if (g_active_ops) {
pr_warn("comm_my: 模块退出时仍有驱动注册,强制注销\n");
g_active_ops = NULL;
}
/* 清理设备 */
device_destroy(comm_my_class, comm_my_dev);
class_destroy(comm_my_class);
cdev_del(&comm_my_cdev);
unregister_chrdev_region(comm_my_dev, 1);
pr_info("comm_my: 通用字符设备已退出\n");
}
module_init(comm_my_init);
module_exit(comm_my_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("通用字符设备驱动框架");
MODULE_AUTHOR("Doubao");
通用头文件 (comm_my_core.h)
#ifndef _COMM_MY_CORE_H_
#define _COMM_MY_CORE_H_
#include <linux/types.h>
/* 定义宏命令 */
#define MY_IOCTL_BASE 0xAE
#define MY_IOCTL_CMD1 _IO(MY_IOCTL_BASE, 0)
#define MY_IOCTL_CMD2 _IOW(MY_IOCTL_BASE, 1, int)
#define MY_IOCTL_CMD3 _IOR(MY_IOCTL_BASE, 2, int)
#define MY_IOCTL_CMD4 _IOWR(MY_IOCTL_BASE, 3, int)
/* 定义函数指针类型 */
typedef int (*cmd_handler_t)(void *priv, unsigned long arg);
/* 驱动操作表结构 */
struct comm_my_ops {
const char *name; /* 驱动名称 */
cmd_handler_t handlers[4]; /* 对应4个命令的处理函数 */
void *priv; /* 驱动私有数据 */
};
/* 注册/注销函数声明 */
extern int register_common_driver(struct comm_my_ops *ops);
extern void unregister_common_driver(struct comm_my_ops *ops);
#endif /* _COMM_MY_CORE_H_ */
具体驱动示例 (comm_my_driver_example.c)
#include <linux/module.h>
#include "comm_my_core.h" /* 包含通用驱动的头文件 */
/* 驱动私有数据结构 */
struct my_driver_data {
int counter;
char name[32];
};
static struct my_driver_data my_data = {
.counter = 0,
.name = "example_driver",
};
/* 实现命令处理函数 */
static int cmd1_handler(void *priv, unsigned long arg)
{
struct my_driver_data *data = priv;
pr_info("cmd1_handler: 驱动 %s 收到命令1, counter=%d\n",
data->name, data->counter++);
return 0;
}
static int cmd2_handler(void *priv, unsigned long arg)
{
struct my_driver_data *data = priv;
int val = (int)arg;
pr_info("cmd2_handler: 驱动 %s 收到命令2, 参数=%d\n",
data->name, val);
data->counter += val;
return 0;
}
static int cmd3_handler(void *priv, unsigned long arg)
{
struct my_driver_data *data = priv;
int *val = (int *)arg;
pr_info("cmd3_handler: 驱动 %s 收到命令3, 返回counter=%d\n",
data->name, data->counter);
if (val)
copy_to_user(val, &data->counter, sizeof(int));
return 0;
}
static int cmd4_handler(void *priv, unsigned long arg)
{
struct my_driver_data *data = priv;
int val;
if (copy_from_user(&val, (int *)arg, sizeof(int)))
return -EFAULT;
pr_info("cmd4_handler: 驱动 %s 收到命令4, 原counter=%d, 新值=%d\n",
data->name, data->counter, val);
data->counter = val;
/* 返回新值给用户空间 */
if (copy_to_user((int *)arg, &data->counter, sizeof(int)))
return -EFAULT;
return 0;
}
/* 定义驱动操作表 */
static struct comm_my_ops my_ops = {
.name = "example_driver",
.priv = &my_data,
.handlers = {
[0] = cmd1_handler, /* 对应 MY_IOCTL_CMD1 */
[1] = cmd2_handler, /* 对应 MY_IOCTL_CMD2 */
[2] = cmd3_handler, /* 对应 MY_IOCTL_CMD3 */
[3] = cmd4_handler, /* 对应 MY_IOCTL_CMD4 */
}
};
/* 驱动初始化函数 */
static int __init my_driver_init(void)
{
int ret;
/* 注册驱动到通用设备 */
ret = register_common_driver(&my_ops);
if (ret) {
pr_err("my_driver: 注册失败, 错误码=%d\n", ret);
return ret;
}
pr_info("my_driver: 驱动已加载并注册到 /dev/comm_my\n");
return 0;
}
/* 驱动退出函数 */
static void __exit my_driver_exit(void)
{
/* 从通用设备注销驱动 */
unregister_common_driver(&my_ops);
pr_info("my_driver: 驱动已卸载\n");
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Comm_My 示例驱动");
MODULE_AUTHOR("Doubao");
用户空间测试程序 (user_test.c)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
/* 定义与内核一致的宏命令 */
#define MY_IOCTL_BASE 0xAE
#define MY_IOCTL_CMD1 _IO(MY_IOCTL_BASE, 0)
#define MY_IOCTL_CMD2 _IOW(MY_IOCTL_BASE, 1, int)
#define MY_IOCTL_CMD3 _IOR(MY_IOCTL_BASE, 2, int)
#define MY_IOCTL_CMD4 _IOWR(MY_IOCTL_BASE, 3, int)
int main(int argc, char *argv[])
{
int fd;
int val = 100;
int ret;
/* 打开通用设备 */
fd = open("/dev/comm_my", O_RDWR);
if (fd < 0) {
perror("无法打开设备");
return -1;
}
printf("=== 测试开始 ===\n");
/* 测试命令1 (无参数) */
printf("发送命令1 (无参数)...\n");
ret = ioctl(fd, MY_IOCTL_CMD1);
if (ret < 0) {
perror("命令1失败");
close(fd);
return -1;
}
/* 测试命令2 (写参数) */
val = 200;
printf("发送命令2 (写参数: %d)...\n", val);
ret = ioctl(fd, MY_IOCTL_CMD2, val);
if (ret < 0) {
perror("命令2失败");
close(fd);
return -1;
}
/* 测试命令3 (读参数) */
val = 0; /* 初始化为0 */
printf("发送命令3 (读参数)...\n");
ret = ioctl(fd, MY_IOCTL_CMD3, &val);
if (ret < 0) {
perror("命令3失败");
close(fd);
return -1;
}
printf("命令3返回值: %d\n", val);
/* 测试命令4 (读写参数) */
val = 300; /* 设置新值 */
printf("发送命令4 (写参数: %d 并读取)...\n", val);
ret = ioctl(fd, MY_IOCTL_CMD4, &val);
if (ret < 0) {
perror("命令4失败");
close(fd);
return -1;
}
printf("命令4返回值: %d\n", val);
close(fd);
printf("=== 测试完成 ===\n");
return 0;
}
加载驱动
insmod comm_my_core.ko # 加载通用驱动
insmod comm_my_driver_example.ko # 加载具体驱动
运行测试程序
./user_test
1306

被折叠的 条评论
为什么被折叠?



