Linux驱动框架-以misc为例

Linux  ·  2025-08-22

  • 此文会根据misc测试驱动的代码架构来讲解简单的驱动如何编写。

1.驱动框架与Linux一切皆文件思想

1.1 驱动思想理解

  • Linux系统通过文件抽象实现设备管理,所有硬件设备都通过文件接口进行访问。这种设计体现在
    设备节点以文件形式存在于/dev目录
    使用标准文件操作接口(open/close/read/write)
    驱动开发者通过实现文件操作函数与硬件交互
  • 比如说我们下面的misc测试驱动,就是一个典型的最小驱动,包含Linux最常用的读、写、打开、关闭函数
    核心就是实现设备结构体的file_operations的文件读、写、打开、关闭函数,给出模块入口出口函数
    这个框架就类似JAVA的CRUD,属于是同一类思想,只不过驱动操作的是硬件设备数据,JAVA操作的是软件数据库管理
    理解这个核心,写SPI、I2C,或者其他简单的设备驱动都是大致相同的,首先实现设备结构体,再实现file_operations的函数赋给设备结构体,最后是模块入口出口函数
  • misc(杂项)设备是Linux内核提供的一种简化字符设备驱动开发的机制
    主设备号固定为10,次设备号由内核动态分配(MISC_DYNAMIC_MINOR)
    自动创建设备节点(需配合udev机制)
    相比传统字符设备注册(register_chrdev),简化了开发流程
    适用于功能简单的设备驱动开发

1.2 驱动使用过程生命周期

  • 设备结构体->实现file_operations的文件读、写、打开、关闭函数->给出模块入口出口函数
  • 内核或者用户手动加载驱动->进入模块入口,注册设备结构体,在/dev下新增设备接口
  • 用户的APP启动

          -> 打开设备接口   -> 通过设备结构体的file_operations调用misc_open
          -> 向设备写入数据 -> 通过设备结构体的file_operations调用misc_write
          -> 读设备的数据   -> 通过设备结构体的file_operations调用misc_read
          -> 关闭设备       -> 进入模块出口,释放设备结构体,调用misc_release
    
  • 下列就是一个简单的驱动核心部分
内核驱动函数            说明
misc_open             设备初始化/资源分配
misc_read             从设备获取数据
misc_write            向设备发送数据
misc_release          设备资源释放

// 文件操作结构体
struct file_operations misc_fops = {
    .owner   = THIS_MODULE,
    .open    = misc_open,
    .release = misc_release,
    .read    = misc_read,
    .write   = misc_write,
};

// misc设备结构体
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,  // 动态分配次设备号
    .name  = "hello_misc",        // 设备节点名(/dev/hello_misc)
    .fops  = &misc_fops,          // 关联文件操作
};

module_init(misc_test_init);     模块加载入口函数
module_exit(misc_test_exit);     模块出口函数

1.3 驱动代码

  • 下面的程序我们用这种框架,实现一个简单的misc驱动,当用户加载或者释放驱动,都会有相应的提示,用户写会实现从用户空间拷贝数据,用户读会实现将拷贝来的数据拷贝到用户空间
misc_test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>

//设备打开
int misc_open(struct inode *inode, struct file *file){
    
    printk("misc open\n");
    return 0;
}

//设备关闭
int misc_release(struct inode *inode, struct file *file){
    
    printk("misc release\n");
    return 0;
}

//读设备的数据
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){
    char kbuf[64] = "qerwq";
    
    if(copy_to_user(ubuf,kbuf,strlen(kbuf)) != 0){
        printk("copy to user error\n");
        return -1;
    }
    
    return 0;
    
}

//向设备写数据
ssize_t misc_write(struct file *file, const  char __user *ubuf, size_t size, loff_t *loff_t){

    char kbuf[64] = {0};
    
    if(copy_from_user(kbuf,ubuf,size) != 0){
        printk("copy from user error\n");
        return -1;
    }
    
    printk("write: %s\n",kbuf);
    
    return 0;
    
}


// 文件操作结构体
struct file_operations misc_fops = {
    .owner   = THIS_MODULE,
    .open    = misc_open,
    .release = misc_release,
    .read    = misc_read,
    .write   = misc_write,
};

// misc设备结构体
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,  // 动态分配次设备号
    .name  = "hello_misc",        // 设备节点名(/dev/hello_misc)
    .fops  = &misc_fops,          // 关联文件操作
};

static int misc_test_init(void){
    int ret;
    ret = misc_register(&misc_dev);
    if(ret<0){
        printk("misc init faild\n");    
        return -1;
    }
    printk(KERN_WARNING "misc hello!\n");
    return 0;
}

static void misc_test_exit(void){
    
    misc_deregister(&misc_dev);
    printk(KERN_WARNING "bye!\n");
}

module_init(misc_test_init);
module_exit(misc_test_exit);

MODULE_LICENSE("GPL");

 驱动
评论
LJ` Blog . All Rights Reserved. Theme Jasmine by Kent Liao.
冀ICP备2025127925号 冀公网安备13082402000074号