2.1Linux driver misc设备驱动理解

目录

1.misc_init函数分析

一些需要注意的细节部分

2.misc_register函数分析

3.流程图


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.流程图

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 中的 misc 是一种杂项设备类型,它可以用来创建一些没有特定驱动程序的设备文件,例如 /dev/null 和 /dev/random。 要创建一个 misc 设备,可以使用 misc_register() 函数,该函数需要一个 miscdevice 结构体作为参数,其中包含设备的名称、设备号等信息。然后,可以使用 misc_deregister() 函数来注销设备。 下面是一个简单的示例程序,用于创建一个名为 mymiscmisc 设备: ```c #include <linux/module.h> #include <linux/miscdevice.h> static int mymisc_open(struct inode *inode, struct file *file) { printk(KERN_INFO "mymisc: device opened\n"); return 0; } static int mymisc_release(struct inode *inode, struct file *file) { printk(KERN_INFO "mymisc: device closed\n"); return 0; } static const struct file_operations mymisc_fops = { .owner = THIS_MODULE, .open = mymisc_open, .release = mymisc_release, }; static struct miscdevice mymisc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "mymisc", .fops = &mymisc_fops, }; static int __init mymisc_init(void) { int ret; ret = misc_register(&mymisc_device); if (ret) { printk(KERN_ERR "mymisc: unable to register device\n"); return ret; } printk(KERN_INFO "mymisc: device registered\n"); return 0; } static void __exit mymisc_exit(void) { misc_deregister(&mymisc_device); printk(KERN_INFO "mymisc: device unregistered\n"); } module_init(mymisc_init); module_exit(mymisc_exit); MODULE_LICENSE("GPL"); ``` 编译并安装模块后,可以使用以下命令来查看设备文件: ``` $ ls -l /dev/mymisc crw------- 1 root root 10, 58 May 22 15:08 /dev/mymisc ``` 可以使用 cat 命令来测试设备: ``` $ cat /dev/mymisc mymisc: device opened mymisc: device closed ``` 这里的输出是由 mymisc_open() 和 mymisc_release() 函数生成的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值