内核的misc驱动框架详解:内核源码分析蜂鸣器驱动分析
Posted 正在起飞的蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核的misc驱动框架详解:内核源码分析蜂鸣器驱动分析相关的知识,希望对你有一定的参考价值。
1、什么是misc类设备
(1)misc类设备就是杂项类设备,那些功能不太复杂,不太好归类到已经创建好的类中,这些设备都可以归类到misc类设备。misc类设备就好像一个收纳盒子,不知道怎么归类的设备就归类到misc类设备里;
(2)misc类设备本质上是字符类设备,而且归类是建议不是强制,你想把设备归到哪一类设备都是可以的,只有按照相应驱动框架的注册方式去注册驱动,但是良好的归类习惯,能让人更容易理解你写的代码;
2、misc驱动框架的加载
static const struct file_operations misc_fops =
.owner = THIS_MODULE,
.open = misc_open,
;
static int __init misc_init(void)
int err;
#ifdef CONFIG_PROC_FS
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
//创建misc类
misc_class = class_create(THIS_MODULE, "misc");
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
//注册misc字符设备,主设备号是10,次设备号范围[0,255]
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk:
printk("unable to get major %d for misc devices\\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
subsys_initcall(misc_init);
(1)CONFIG_PROC_FS宏:内核是否开启了proc文件系统,如果定义了该宏,会看到"/proc/misc"文件,里面记录了注册的misc类设备,可以用cat命令查看;
(2)创建了misc类,在sysfs目录下可以看到"/sys/class/misc"目录,将来misc类设备都放到该目录下;
(3)注册了名字为misc、主设备号是10的字符设备,设备节点的操作方法是misc_fops变量定义的,所以misc类设备的主设备号都是10;
(4)subsys_initcall(misc_init):将misc_init函数放到".initcall4.init"段,在内核启动过程中会自动调用,宏的分析参考博客:《内核加载驱动机制详解(module_init & module_exit)》;
3、misc类设备驱动的注册
3.1、struct miscdevice结构体
struct miscdevice
int minor; //次设备号
const char *name; //misc设备的名字
const struct file_operations *fops; //设备节点的操作方法
struct list_head list; //用来挂接的链表节点
struct device *parent; //设备的父节点
struct device *this_device;
const char *nodename;
mode_t mode;
;
构建表格 | |
---|---|
minor | 次设备号 |
name | misc设备的名字,在./sys/class/misc目录中可以看到 |
fops | 设备节点的操作方法 |
list | 用来挂接的链表节点 |
3.2、misc驱动框架注册函数
int misc_register(struct miscdevice * misc)
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
//遍历已经注册的misc类设备,查看次设备号是否已经被注册
list_for_each_entry(c, &misc_list, list)
if (c->minor == misc->minor)
mutex_unlock(&misc_mtx);
return -EBUSY;
//如果此设备是MISC_DYNAMIC_MINOR,表示自动分配次设备号
if (misc->minor == MISC_DYNAMIC_MINOR)
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS)
mutex_unlock(&misc_mtx);
return -EBUSY;
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
//构建主次设备号
dev = MKDEV(MISC_MAJOR, misc->minor);
//在misc类下面创建设备
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
if (IS_ERR(misc->this_device))
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device);
goto out;
//添加到内核管理misc类设备的链表中
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
(1)将misc驱动中构建好的struct miscdevice结构体通过misc_register()函数注册到内核的misc驱动框架中;
(2)遍历已经注册的misc类设备,查看次设备号是否已经被注册;
(3)如果此设备是255,misc驱动框架将自动分配次设备号;
(4)构建主次设备号;
(5)在sysfs中创建misc类的设备;
3.3、misc设备的次设备号分配问题
#define DYNAMIC_MINORS 64 /* like dynamic majors */
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
int misc_register(struct miscdevice * misc)
······
//如果此设备是MISC_DYNAMIC_MINOR,表示自动分配次设备号
if (misc->minor == MISC_DYNAMIC_MINOR)
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); //返回位图中bit值第一个为0的下标
if (i >= DYNAMIC_MINORS)
mutex_unlock(&misc_mtx);
return -EBUSY;
//换算得到次设备号
misc->minor = DYNAMIC_MINORS - i - 1;
//根据申请到的次设备号,位图中将对应的bit值置1
set_bit(i, misc_minors);
······
(1)在注册misc驱动时,将次设备号填255,就代表由misc驱动框架自动分配次设备号;
(2)自动分配的次设备号范围是[0,63],并且是按从大到小开始分配的,具体分析上面的代码;
(3)对位图不熟悉的参考博客:https://blog.csdn.net/weixin_42031299/article/details/124853167;
3.4、内核如何记录注册的misc类驱动
int misc_register(struct miscdevice * misc)
······
//遍历已经注册的misc类设备,查看次设备号是否已经被注册
list_for_each_entry(c, &misc_list, list)
if (c->minor == misc->minor)
mutex_unlock(&misc_mtx);
return -EBUSY;
······
//将misc类设备的struct miscdevice结构体添加到misc_list链表中
list_add(&misc->list, &misc_list);
······
static int misc_open(struct inode * inode, struct file * file)
······
//根据次设备号在注册的misc驱动程序中进行匹配
list_for_each_entry(c, &misc_list, list)
if (c->minor == minor)
new_fops = fops_get(c->fops); //获取匹配到的misc驱动的fops
break;
······
(1)在内核中用链表来管理misc类设备,在注册时添加到链表,在open操作misc设备时进行遍历链表;
(2)在misc设备的管理链表中,通过次设备号来唯一的标记设备;
4、misc设备的操作分析
4.1、共用主设备号的影响
(1)在加载misc驱动框架的时候,调用register_chrdev()函数注册了主设备号为10、名字是misc的字符驱动,我们后续通过misc_register()函数
注册的misc驱动都是主设备号相同,次设备号不同;
(2)主设备号相同的设备,都是共用struct file_operations结构体的,也就是misc_fops变量,这里面只实现了open方法,所以misc类的设备节点的open方法都是同一个函数;
(3)不同的misc设备会有不同的read、write等方法,明显不能共用struct file_operations结构体,并且misc_fops变量还只实现了open方法;
(4)所以misc_fops变量的open方法,肯定会想办法将设备节点的fops操作方法,替换成misc驱动的fops;
4.2、如何通过次设备号区分各个misc设备
static int misc_open(struct inode * inode, struct file * file)
//解析处次设备号
int minor = iminor(inode);
······
//根据次设备号在注册的misc驱动程序中进行匹配
list_for_each_entry(c, &misc_list, list)
if (c->minor == minor)
new_fops = fops_get(c->fops); //获取匹配到的misc驱动的fops
break;
······
(1)在调用misc_register()注册misc驱动时,函数内部调用了device_create()函数创建了"/dev/xxx"设备节点,设备节点的inode结构体里就保存了主次设备号,在open打开设备节点时,内核是调用的misc_open()函数,会传入设备节点的struct inode结构体;
(2)根据解析得到的此设备号,在记录misc设备的链表中找到之前注册的struct miscdevice结构体;
(3)主次设备号更详细的内容参见博客:https://blog.csdn.net/weixin_42031299/article/details/124557522;
4.3、open操作
static int misc_open(struct inode * inode, struct file * file)
//解析出次设备号
int minor = iminor(inode);
struct miscdevice *c;
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
mutex_lock(&misc_mtx);
//根据次设备号在注册的misc驱动程序中进行匹配,得到注册的驱动的fops
list_for_each_entry(c, &misc_list, list)
if (c->minor == minor)
new_fops = fops_get(c->fops); //获取匹配到的misc驱动的fops
break;
//上一步中没有查找到,尝试手动加载对应主次设备号的驱动,然后再次去匹配
if (!new_fops)
mutex_unlock(&misc_mtx);
request_module("char-major-%d-%d", MISC_MAJOR, minor); //尝试手动加载对应主次设备号的驱动
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list)
if (c->minor == minor)
new_fops = fops_get(c->fops);
break;
if (!new_fops)
goto fail;
err = 0;
old_fops = file->f_op;
//将设备节点文件的fops替换成对应misc设备的fops
file->f_op = new_fops;
if (file->f_op->open)
//将misc驱动框架的struct miscdevice结构体保存在struct file结构体中,后续会用
file->private_data = c;
//调用注册的misc驱动的fops的open方法
err=file->f_op->open(inode,file); //调用新的fops的open方法
if (err)
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
fops_put(old_fops);
fail:
mutex_unlock(&misc_mtx);
return err;
(1)解析出次设备号,然后根据次设备号去找到注册驱动时的struct miscdevice结构体;
(2)将设备节点文件的struct file结构体的f_op替换成misc驱动struct miscdevice结构体的fops;
(3)调用misc驱动真正的open函数;
4.4、read、write等操作
经过open函数的替换,后续的read、write等操作方法,都是调用我们在注册misc驱动时,填充的struct miscdevice结构体里的fops,
这个是和具体硬件相关的,后面以蜂鸣器的驱动具体分析;
5、X210开发板的蜂鸣器驱动分析
创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖后续补充
以上是关于内核的misc驱动框架详解:内核源码分析蜂鸣器驱动分析的主要内容,如果未能解决你的问题,请参考以下文章
hisi3516dv300芯片基于hwmon驱动框架的温度获取驱动源码分析