通用思想---随笔 (1)

想设计一个结构类似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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值