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

Posted

tags:

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

    1、misc设备驱动框架源码部分是由内核开发者实现提供的,主要是创建misc类和为驱动开发者提供misc_register函数,来进行创建misc设备。

    这部分的源码在/drvier/char/misc.c里,代码如下:

/*
 * linux/drivers/char/misc.c
 *
 * Generic misc open routine by Johan Myreen
 *
 * Based on code from Linus
 *
 * Teemu Rantanen‘s Microsoft Busmouse support and Derrick Cole‘s
 *   changes incorporated into 0.97pl4
 *   by Peter Cervasio (pete%[email protected]) (08SEP92)
 *   See busmouse.c for particulars.
 *
 * Made things a lot mode modular - easy to compile in just one or two
 * of the misc drivers, as they are now completely independent. Linus.
 *
 * Support for loadable modules. 8-Sep-95 Philip Blundell <[email protected]>
 *
 * Fixed a failing symbol register to free the device registration
 *		Alan Cox <[email protected]> 21-Jan-96
 *
 * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
 *
 * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
 *
 * Handling of mouse minor numbers for kerneld:
 *  Idea by Jacques Gelinas <[email protected]>,
 *  adapted by Bjorn Ekwall <[email protected]>
 *  corrected by Alan Cox <[email protected]>
 *
 * Changes for kmod (from kerneld):
 *	Cyrus Durgin <[email protected]>
 *
 * Added devfs support. Richard Gooch <[email protected]>  10-Jan-1998
 */

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

/*
 * Head entry for the doubly linked miscdevice list
 */
static LIST_HEAD(misc_list);
static DEFINE_MUTEX(misc_mtx);

/*
 * Assigned numbers, used for dynamic minors
 */
#define DYNAMIC_MINORS 64 /* like dynamic majors */
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);

#ifdef CONFIG_PROC_FS
static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
{
	mutex_lock(&misc_mtx);
	return seq_list_start(&misc_list, *pos);
}

static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	return seq_list_next(v, &misc_list, pos);
}

static void misc_seq_stop(struct seq_file *seq, void *v)
{
	mutex_unlock(&misc_mtx);
}

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;
}


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

static int misc_seq_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &misc_seq_ops);
}

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

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);
	
	list_for_each_entry(c, &misc_list, list) {
		if (c->minor == minor) {
			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;
	old_fops = file->f_op;
	file->f_op = new_fops;
	if (file->f_op->open) {
		file->private_data = c;
		err=file->f_op->open(inode,file);
		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;
}

static struct class *misc_class;

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

/**
 *	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.
 */
 
int misc_register(struct miscdevice * misc)
{
	struct miscdevice *c;
	dev_t dev;
	int err = 0;

	INIT_LIST_HEAD(&misc->list);

	mutex_lock(&misc_mtx);
	list_for_each_entry(c, &misc_list, list) {
		if (c->minor == misc->minor) {
			mutex_unlock(&misc_mtx);
			return -EBUSY;
		}
	}

	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->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;
	}

	/*
	 * Add it to the front, so that later devices can "override"
	 * earlier defaults
	 */
	list_add(&misc->list, &misc_list);
 out:
	mutex_unlock(&misc_mtx);
	return err;
}

/**
 *	misc_deregister - unregister a miscellaneous device
 *	@misc: device to unregister
 *
 *	Unregister a miscellaneous device that was previously
 *	successfully registered with misc_register(). Success
 *	is indicated by a zero return, a negative errno code
 *	indicates an error.
 */

int misc_deregister(struct miscdevice *misc)
{
	int i = DYNAMIC_MINORS - misc->minor - 1;

	if (list_empty(&misc->list))
		return -EINVAL;

	mutex_lock(&misc_mtx);
	list_del(&misc->list);
	device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
	if (i < DYNAMIC_MINORS && i >= 0)
		clear_bit(i, misc_minors);
	mutex_unlock(&misc_mtx);
	return 0;
}

EXPORT_SYMBOL(misc_register);
EXPORT_SYMBOL(misc_deregister);

static char *misc_devnode(struct device *dev, mode_t *mode)
{
	struct miscdevice *c = dev_get_drvdata(dev);

	if (mode && c->mode)
		*mode = c->mode;
	if (c->nodename)
		return kstrdup(c->nodename, GFP_KERNEL);
	return NULL;
}

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))
		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);

    misc源码框架本身也是一个模块,内核启动时自动加载。这么做的目的是为了可以加载和卸载,当实际情况中板子上一个misc设备都没有用到,那么我们就可以对内核进行裁剪,将misc驱动框架的代码也卸载掉。因为这个驱动框架的存在的作用就是让其驱动开发着来调用其提供的接口函数来创建misc设备等。如果没有用到misc设备,也就是没有一个misc驱动代码需要依附于misc驱动框架源码,那么这个misc驱动框架源码就可以卸载掉,使内核尽可能的被裁剪。

    怎么知道misc驱动框架源码也是一个模块呢,通过上面的misc驱动框架源码的最后一行代码

subsys_initcall(misc_init);

这个和module_init一样,所以知道misc的驱动框架源码也是模块化的,可以加载和卸载。

subsys_initcall的启动顺序比module_Init的启动顺序要早,因为肯定要先将misc杂散类设备的驱动框架代码进行模块加载初始化成功后,才能写misc设备的驱动代码,module_init加载的是驱动代码,驱动框架如果都没有初始化加载好,怎么调用驱动框架写misc设备的驱动代码,只有驱动框架初始化加载好了,才能在驱动代码中利用驱动框架去注册你的设备。

   

subsys_initcall(misc_init);

    这个subsys_initcall的参数misc_init,就是这个驱动框架代码被加载的时候执行的函数。misc_init的代码如下并进行分析:

static int __init misc_init(void)
{
	int err;

#ifdef CONFIG_PROC_FS
	proc_create("misc", 0, NULL, &misc_proc_fops);    //如果当前内核需要使用proc虚拟文件系统,那么就在proc目录下创建misc文件
	                                                    //通过这个文件,我们可以在用户空间来知道我们当前系统中注册了那些杂散类设备
#endif                                                    //proc虚拟文件系统实现的没有sysfs文件系统实现的好,在内核为2.4版本的时候较为流行,
                                                        //因为里面太乱
                                                        
	misc_class = class_create(THIS_MODULE, "misc");    //创建一个类,名字叫做misc,在/sys/class/目录中,此时创建完这个misc类后目录的内
	                                                //容是空的。但是当我们调用device_create创建一个misc设备的时候,这misc目录里面就会多
	                                                //一个设备。device_create是在misc_register函数中被调用的,misc_register是misc的驱动
	                                                //框架源码,驱动开发者调用misc_register来创建一个misc设备,之后就会在misc目录里面看到
	                                                //创建出来的设备,这里class_create创建出来的类目录的内容和device_create函数调用后
	                                                //后创建出来的设备是绑定的,创建出来的设备就会在class_create创建出来的类目录中体现。
	err = PTR_ERR(misc_class);
	if (IS_ERR(misc_class))
		goto fail_remove;

	err = -EIO;
	
	//字符设备驱动的创建,主设备号是10,名字是misc,将misc类设备驱动注册为了字符设备驱动。
	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))    //这个register_chrdev是一个老接口,利用这接口注册设备的时候是只有主设备号没有
	                                                    //次设备号的,直接调用这个接口注册的时候,就相当于将所有的misc设备的次设备号注册
	                                                    //了。
	                                                     //MISC_MAJOR宏的值是10,表示主设备号,是固定的。
		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;
}

 上面的代码,misc驱动框架代码被加载后的效果,就是创建misc类,注册字符设备驱动。这是驱动框架源码实现的部分,驱动开发者在将来一旦调用了misc_register函数注册一个misc设备结构体后就会间接调用device_create创建一个misc设备,udev或者mdev的机制就会自动在/dev目录下创建出一个设备文件节点。我们就可以使用这个设备文件节点来操作这个misc设备了。

    misc_register函数是提供给驱动开发者使用的,功能是将misc设备注册到misc驱动框架中,注册misc设备成功后,会在/sys/class/misc/目录中看到注册的设备文件。

    misc_register函数如下:

int misc_register(struct miscdevice * misc)
{
	struct miscdevice *c;
	dev_t dev;
	int err = 0;

	INIT_LIST_HEAD(&misc->list);

	mutex_lock(&misc_mtx);
	list_for_each_entry(c, &misc_list, list) {
		if (c->minor == misc->minor) {
			mutex_unlock(&misc_mtx);
			return -EBUSY;
		}
	}

	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->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;
	}

	/*
	 * Add it to the front, so that later devices can "override"
	 * earlier defaults
	 */
	list_add(&misc->list, &misc_list);
 out:
	mutex_unlock(&misc_mtx);
	return err;
}

这个函数的参数是struct miscdevice类型的结构体指针,这个结构体类型内容为:

struct miscdevice  {
	int minor;
	const char *name;
	const struct file_operations *fops;
	struct list_head list;
	struct device *parent;
	struct device *this_device;
	const char *nodename;
	mode_t mode;
};

看这个misc_register函数和提供的参数可以知道,驱动开发者要将一个设备注册到misc驱动框架中就需要使用这个函数,需要填充struct miscdevice类型的变量,然后进行注册,表示一个misc设备,注册设备完毕后,会在/sys/class/misc/目录中看到注册的设备文件。udev或mdev会在/dev目录下创建出设备文件节点来让应用层进行操作。

    其实驱动框架说白了,就是内核(驱动框架)给驱动开发者提供了一个结构体以及注册和注销相关的函数接口,驱动开发者只需要用内核(驱动框架)提供的结构体来定义变量然后进行合理填充,然后利用内核(驱动框架)提供的注册或注销相关的函数接口来进行注册或注销这个结构体变量,则表示将设备注册到了驱动框架中,注册到了一类中,这样udev或mdev就可以在/dev/下创建出设备文件节点,应用层就可以用这个设备文件节点来进行操作设备。

    驱动框架具有强烈的面向对象的思维逻辑,利用驱动框架写驱动代码,逻辑思维上就是利用驱动框架提供的类,new出来一个设备对象然后进行合理填充,填充后利用类中提供的方法来将new出来的对象进行注册到驱动框架中。


    2、misc_list链表的作用

    在misc_register函数中,用到了一个全局变量misc_list,misc_list是个链表。


本文出自 “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驱动框架源码分析串口应用程序编写)