linux设备驱动之misc驱动框架源码分析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux设备驱动之misc驱动框架源码分析相关的知识,希望对你有一定的参考价值。

1、misc_open函数分析

    该函数在driver/char/misc.c中,misc.c是驱动框架实现的,这里面的misc_Open函数是misc驱动框架为应用层提供的一个打开misc设备的一个接口。


    1、首先我们要知道在misc.c中的misc_init函数中,将misc这种类设备注册成了字符设备驱动。代码如下

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
	misc_class = class_create(THIS_MODULE, "misc");
	err = PTR_ERR(misc_class);
	if (IS_ERR(misc_class))
		goto fail_remove;

	err = -EIO;
	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))    //misc类的设备注册为字符设备驱动,因为使用的是register_chrdev函数。
		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;
}

上面代码中的register_chrdev函数参数中可以看出,misc_fops为包含了操作该驱动的方法结构体。结构体内容如下

static const struct file_operations misc_fops = {
	.owner		= THIS_MODULE,
	.open		= misc_open,
};

因此可以知道,驱动框架中的misc_open函数为驱动框架为应用层提供的接口,接口函数是open函数。正常来说,驱动框架中是不会写驱动的操作方法的,应该是留给我们驱动开发的人写的。自己去写这个file_operations结构体中的方法将其填充然后注册。


    2、misc_open函数的代码分析,代码与分析如下

static int misc_open(struct inode * inode, struct file * file)
{
	int minor = iminor(inode);    //从传进来的参数中得到该设备的次设备号,inode是flash中文件的节点,file是打开的那一份的设备文件的路径
	struct miscdevice *c;    
	int err = -ENODEV;
	const struct file_operations *old_fops, *new_fops = NULL; //定义两个file_opreation结构体,这个结构体就不用说了,放的是接口函数,驱动
	                                                            //操作方法

	mutex_lock(&misc_mtx);
	
	list_for_each_entry(c, &misc_list, list) {    //遍历内核misc杂散类设备的链表,如果链表中存在这个设备的次设备号,那么就将内核链表中的
		if (c->minor == minor) {                //这个次设备号对应的设备的fops方法拿出来用,作为新的操作这个设备的方法。
			new_fops = fops_get(c->fops);		
			break;
		}
	}
		
	if (!new_fops) {    //如果在内核misc链表中没有找到这个次设备号对应的fops,那么就request_module一次,在去找一次,如果还找不到,则
		mutex_unlock(&misc_mtx);                                    //跳到fail位置。在注册这个设备的时候,我们是会注册这个设备的fops 
		                                                            //的。
		                                                            //应用层用open操作这个设备的时候,对应到驱动层,就是先找到次设备
		                  //号,因为注册这个驱动的时候,fops已经提供了,同时,这个设备也注册了,所以去内核链表中找这个次设备号,
		                  //找到次设备号后就去找fops方法,找到后就得到这个fops结构体,正常来说是一定能找到的。因为驱动注册是你提供了
		                  //fops,设备创建注册时,也注册了对应的minor次设备号。
		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;    //将打开的文件的fop作为老的fop
	file->f_op = new_fops;    //将从misc内核链表中得到的fops作为新的ops给打开的。
	if (file->f_op->open) {    //如果得到了open的函数方法实例
		file->private_data = c;
		err=file->f_op->open(inode,file);    //然后调用open函数,这是真正的执行的open。上层用open操作misc设备时,最终执行的就是这个函
		                                     //数,这个被绑定到miscdevice结构体中的fops下的open,是写驱动的人提供的,在x210-buzzer.c
		                                     //提供的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;
}

   

    因此应用层使用open操作misc设备的时候,会映射到驱动中的/driver/char/misc.c文件中的misc_open函数执行file->f_op->open函数,这个函数映射的是

/drvier/char/buzzer/x210-buzzer.c中的

static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   x210_pwm_open,
    .release =   x210_pwm_close, 
    .ioctl   =   x210_pwm_ioctl,
};

里的open绑定的x210_pwm_open,x210_pwm_open的函数体是

static int x210_pwm_open(struct inode *inode, struct file *file)
{
	if (!down_trylock(&lock))
		return 0;
	else
		return -EBUSY;
	
}

2、misc在proc中的展现 proc不是很重要,因为proc文件系统现在用的越来越少,因为太乱,这里主要是为了让你知道这个代码是做什么的

    cat /proc/misc文件时,会看到所有注册到misc中的设备的次设备号和名字,这个文件中的内容是通过遍历内核misc_list链表来显示出来的。

这个/proc/misc文件的创建是在/drvier/char/misc.c文件中的misc_init函数中的proc_create时创建的,代码如下

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops); //在/proc目录下创建出misc文件。
	                                                //misc_proc_fops结构体就是操作这个/proc/misc文件时的方法。
#endif

misc_proc_fops结构体内容为

static const struct file_operations misc_proc_fops = {
	.owner	 = THIS_MODULE,
	.open    = misc_seq_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = seq_release,
};

cat /proc/misc文件时对应的方法应该是

static const struct seq_operations misc_seq_ops = {
	.start = misc_seq_start,
	.next  = misc_seq_next,
	.stop  = misc_seq_stop,
	.show  = misc_seq_show,
};

中的misc_seq_show方法,内容如下

static int misc_seq_show(struct seq_file *seq, void *v)
{
	const struct miscdevice *p = list_entry(v, struct miscdevice, list);

	seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
	return 0;
}



3、内核互斥锁

    (1)何为互斥锁?

            和应用中的互斥锁其实差不多。别人访问时我不能访问,访问时别人访问,需要上锁和解锁,不多说,

    (2)内核中定义互斥锁:DEFINE_MUTEX

    (3)上锁:mutex_lock和解锁:mutex_unlock

    (4)内核防止竞争状态的手段:原子访问、自旋锁、互斥锁、信号量

    (5)原子访问主要是用来计数(访问过程不能被打断)、自旋锁后面说、互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。

    

原子操作:

比如你定义一个变量count,然后count++,实际上这个count++是可以被打断的,因为count++转换成可能是三句代码,当执行了两句突然被打断时(可能是时间片时间到了),被打断时又改了这个count的值,那么这个时候这个count++的值肯定是不对的了,所以为了防止这种现象就有了原子操作,原子操作的计数不可以被打断。


自旋锁:自旋锁跟原子操作是有本质区别的,在现在的驱动中用的越来越多。是在多核CPU的年代发明的,专门应对这种多处理器的CPU的,所以自旋锁会用的越来越多,后面会详细说。


信号量:和互斥锁基本相似,信号量用来计数的,比如说一个设备只能被打开7次,每个人打开这个设备就会记一次数,当第八打开时,数为大于7了,就不能打开这个设备了。这就是用信号量来做的一个打开计数的手段。


互斥锁:互斥锁就是一种特殊的信号,他只能被打开一次。一个人打开了,数值可能就是1了,另一个人就不能打开了,当这个人解锁的时候,这个值就为0了,另一个人就可以打开或访问了。所以互斥锁和信号量的区别就是次数的区别。能用互斥锁的时候用互斥锁不要信号量。照猫画虎就行。如果驱动是你自己写的,你自己要去选择用互斥锁和信号量的时候,这个时候你已经不会纠结了,因为你有了自己不去照别人的驱动写驱动功力的时候,你就已经知道什么时候用互斥锁什么时候用信号量了。


misc.c这个内核人写的,写驱动框架人写的代码已经分析的差不多了,这部分代码只要理解就行,并不是驱动工程师需要去写的,我们要写的是驱动,而不是驱动框架代码。

后面分析的蜂鸣器的驱动代码,这部分代码是驱动工程师需要去写的。

本文出自 “whylinux” 博客,谢绝转载!

以上是关于linux设备驱动之misc驱动框架源码分析的主要内容,如果未能解决你的问题,请参考以下文章

内核的misc驱动框架详解:内核源码分析蜂鸣器驱动分析

Linux misc设备misc驱动框架

Linux驱动修炼之道-SPI驱动框架源码分析(上)转

Linux——Linux驱动之iMX6ULL平台下串口UART驱动实现RS232数据通信开发实战(UART驱动框架源码分析串口应用程序编写)

Linux——Linux驱动之iMX6ULL平台下串口UART驱动实现RS232数据通信开发实战(UART驱动框架源码分析串口应用程序编写)

Linux——Linux驱动之iMX6ULL平台下串口UART驱动实现RS232数据通信开发实战(UART驱动框架源码分析串口应用程序编写)