目录
Linux里面的misc杂项设备是主设备号为10的驱动设备,它的注册跟使用比较的简单,所以比较适用于功能简单的设备。正因为简单,所以它通常嵌套在platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。
基于linux2.6.22
1.misc_init函数分析
misc_init函数是misc驱动框架模块注册时的一个初始化函数,只有执行了初始化,我们才能够利用misc提供的框架来进行编写misc设备驱动程序和管理misc类设备。
misc_init函数是misc驱动框架的入口函数。
static int __init misc_init(void)
{
#ifdef CONFIG_PROC_FS /* CONFIG_PROC_FS用来控制我们的系统中是否需要proc虚拟文件系统 */
struct proc_dir_entry *ent;
ent = create_proc_entry("misc", 0, NULL);/*在proc文件系统下创建一个名为 misc 的文件*/
if (ent)
ent->proc_fops = &misc_proc_fops;
#endif
misc_class = class_create(THIS_MODULE, "misc");/*在sys文件系统下创建 misc 设备类*/
if (IS_ERR(misc_class))
return PTR_ERR(misc_class);
/*注册misc 字符设备 主设备号10 misc_fops*/
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
printk("unable to get major %d for misc devices\n",
MISC_MAJOR);
class_destroy(misc_class);
return -EIO;
}
return 0;
}
(1)proc文件系统在2.4版本中用的比较流行,现在主要用的就是sys文件系统,因为sys文件系统比proc文件系统做的更好,功能更加齐全,目录层次设计的很好
所以现在proc文件系统成为了一个可以选择添加或者删除的一个选项了,可以通过在内核配置的时候进行相应的配置。
(2)misc_register函数与misc_deregister函数
misc_register函数是misc驱动框架提供给驱动工程师编写misc类设备时的注册函数,一个重要的接口,misc_deregister就是相对应的卸载函数
一些需要注意的细节部分
(1)misc_init函数中调用的注册字符设备的函数 register_chrdev
register_chrdev(MISC_MAJOR,"misc",&misc_fops),从这里可以看出来 misc_fops 就是传入的一个file_operations结构体,之前说过了这个结构体在注册
字符设备时的一个重要性,这里就不再重复了,misc_fops 如下:
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
};
从上面可以看出来结构体中只实现了open函数,而没有实现其他的函数,因为具体的驱动实现的open、read、write函数在他们的file_operations结构体中,并不在这里实现,
我们需要通过这里的open函数去找到具体的要打开的硬件设备,然后找到他下面的file_operations结构体,调用结构体中实现的open函数,并且将要打开的设备的file_operations结构体替换当前要操作的这个结构体,之后我们就可以通过这个结构体来调用设备的其他操作函数,例如read、write....等函数。
为什么会有这样的一种操作模式呢? 其原因就是字符设备的管理的问题,调用register_chrdev函数一次就是注册了一个设备组,而这一个设备组共用了一个file_operations,所以打开这个
设备组中的任何一个设备节点最开始是对应到这个共用的file_operations,所以我们需要通过这个file_operations中的函数找到我们需要正真打开的设备的对应函数。
先来看看misc_open函数:
static int misc_open(struct inode * inode, struct file * file)
{
/*由传进了的inode结构体找到设备的次设备号
inode结构体之前说了它里面有一个元素记录的就是设备号,
由上层传下来的,之前已经讲过
*/
int minor = iminor(inode);
struct miscdevice *c;// 定义一个miscdevice指针
int err = -ENODEV;
// 定义两个file_operations指针
const struct file_operations *old_fops, *new_fops = NULL;
// 互斥锁上锁
mutex_lock(&misc_mtx);
//遍历我们的misc_list链表找到次设备号与当前需要打开的设备的次设备号相同的
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
// 然后获取这个设备的fops结构体 放入new_fops
new_fops = fops_get(c->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;
//将file中的fops先放在 old_fops , 这是用来以免后面出错的时候能够恢复的一种手段
old_fops = file->f_op;
//将我们的获取到的fops放入 file->fops中,也就是替换 那么之后操作file时,对应的就是我们上面获取到的具体的设备的fops
file->f_op = new_fops;
if (file->f_op->open) {// 如果我们的open函数存在
err=file->f_op->open(inode,file);// 打开次设备的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;
}
2.misc_register函数分析
/**
* misc_register - register a miscellaneous device
* @misc: device structure
*
* Register a miscellaneous device with the kernel. If the minor
* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
* and placed in the minor field of the structure. For other cases
* the minor number requested is used.
*
* The structure passed is linked into the kernel and may not be
* destroyed until it has been unregistered.
*
* A zero is returned on success and a negative errno code for
* failure.
*/
/*
主要的功能有:给设备分配次设备号;根据设备号在/dev目录下新建设备节点;
将杂项设备加入misc_list链表
*/
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;// 定义一个 miscdevice 结构体指针
dev_t dev;// 设备号
int err = 0;
INIT_LIST_HEAD(&misc->list);/*初始化misc_list链表*/
mutex_lock(&misc_mtx);// 上锁
/*
遍历misc_list链表,看这个次设备号以前有没有被用过,
如果次设备号已被占有则退出
*/
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;// 如果存在直接退出
}
}
/*
*#define DYNAMIC_MINORS 64
*static unsigned char misc_minors[DYNAMIC_MINORS / 8];
*这里存在一个次设备号的位图,一共64位,下边是遍历每一位;
*如果这位为0,表示没有被占有,可以使用,为1表示被占用。
*/
// misc->minor == 255 表示 自动分配次设备号
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = DYNAMIC_MINORS;
while (--i >= 0)
if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
break;
if (i<0) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
//得到可用的次设备号
misc->minor = i;
}
if (misc->minor < DYNAMIC_MINORS)
//设置位图中相应为1
misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
//计算出设备号
dev = MKDEV(MISC_MAJOR, misc->minor);
/*
在/dev下创建设备节点,这就是有些驱动程序没有显式调用device_create,
却出现了设备节点的原因
*/
misc->this_device = device_create(misc_class, misc->parent, dev,
"%s", misc->name);
if (IS_ERR(misc->this_device)) {
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
/*将这个miscdevice添加到misc_list链表中*/
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}
1.misc_list是misc驱动框架中提供的用来挂载所有的已经注册的misc设备的一个链表,当我们 cat /proc/misc 时查看系统中注册的所有misc类设备就是通过遍历
这个链表来实现的。与字符设备的用数组管理的方式一定要区分开来,misc设备的主设备号在这个数组中也占有一个位置,不要将他们之间的关系脱离了。
2.对代码中宏的解析
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
原式子:static LIST_HEAD(misc_list);
展开后:static struct list_head misc_list = { &(misc_list), &(misc_list) } // 其实就是定义了一个链表,next指针和prev指针都指向本身
3.流程图