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%q106fm.uucp@wupost.wustl.edu) (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 <pjb27@cam.ac.uk>
 *
 * Fixed a failing symbol register to free the device registration
 *		Alan Cox <alan@lxorguk.ukuu.org.uk> 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 <jack@solucorp.qc.ca>,
 *  adapted by Bjorn Ekwall <bj0rn@blox.se>
 *  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
 *
 * Changes for kmod (from kerneld):
 *	Cyrus Durgin <cider@speakeasy.org>
 *
 * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au>  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是个链表。

INIT_LIST_HEAD(&misc->list);


static LIST_HEAD(misc_list);    //解析这个

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

//struct list_head结构体类型是
struct list_head {
	struct list_head *next, *prev;
};

//所以static LIST_HEAD(misc_list); 就是static struct list_head misc_list = { &(misc_list ), &(misc_list ) }
//所以在/drvier/char/misc.c文件中static LIST_HEAD(misc_list);的意思就是定义一个list_head类型的内核链表变量misc_list,并将next指针和prev指针都指向自己,初始化链表。

    1、这里为什么要用static LIST_HEAD(mist_list)定义一个mist_list链表呢?因为内核要用链表来管理misc这种杂散类设备,用来记录内核中所有注册了的杂散类设备。

当我们向内核注册一个杂散类设备的时候,内核就会向mist_list链表中insert一个节点,表示一个misc设备。链表用来管理的好处这里就不去说了。

    在/drvier/char/misc.c的misc_register函数中,开始的代码

INIT_LIST_HEAD(&misc->list);

    意思就是将mist_register函数参数传进来的misc变量中的list,list头节点的pre指针和next指针都先指针自己list。注册一个misc设备的时候,首先将这misc设备中的list链表的prev指针和next指针指向list。这个链表是是在miscdevice表示mist设备的结构体变量中的,最后将这个miscdevice中的链表挂接到内核链表mist_list中。

    有点乱,其实目的是为了将内核链表mist_list内嵌到一个有意义的结构体中,这个结构体就是miscdevice。这是内核链表的使用方法。

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

在misc_register函数中上面代码的意思是,注册一个misc设备的时候,会传进来一个参数misc,表示要注册的设备。list_for_each_entry函数是遍历内核链表,在if判断中,当内核链表中的minor次设备号和要注册的设备的minor次设备号一样的话,说明有问题,就返回EBUSY,表示注册的设备的次设备号已经在内核链表中存在了,说明已经注册过。

	if (misc->minor == MISC_DYNAMIC_MINOR) {
		int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); //在位图中,找到位置为0的bit。位图中如果有bit位为0,表示这个次设备号没有被用到。
		                                                          //misc_minors,就是存放所有注册了的misc设备的次设备的位图。
		if (i >= DYNAMIC_MINORS) {
			mutex_unlock(&misc_mtx);
			return -EBUSY;
		}
		misc->minor = DYNAMIC_MINORS - i - 1; //当找到可以使用的位图位置时,说明有次设备号可以分配给这个设备,这句话的意思是向后分配,次设备号从大到校
		                                       //分
		set_bit(i, misc_minors);    //这句话的意思,因为新的设备使用了一个次设备号,那么就要在位图中将相应位,表示这个次设备号的位置的bit给设置1,表示
		                             //位图中该bit位对应的次设备号已经占用了。
	}

上面代码的意思就是,当要注册的设备的次设备号minor是MISC_DYNAMIC_MINOR宏的值时(255),表示要让内核动态的帮我们给这个要注册的设备分配一个次设备号。

上面两个代码就是给新的misc设备用来自动分配次设备号的。

dev = MKDEV(MISC_MAJOR, misc->minor); //用主设备号和次设备号共同组成一个号码存放到dev变量中。


misc->this_device = device_create(misc_class, misc->parent, dev,
					  misc, "%s", misc->name);//之后这个代码就是创建misc设备到misc_class中,内核的驱动框架中已经做过了class的create了,用户
					  //需要做的就是device_create,这句代码就是我们要做的,但是用户不会直接调用device_create,用户直接调用的是misc_register
					  //函数。misc_class参数,表示将这个设备注册到misc的class中,dev表示主次设备号组成的数字。misc是这个设备,misc-name是
					  //设备的名字。
list_add(&misc->list, &misc_list);//mist_register函数中最后的这句代码意思就是将,新注册的这个misc设备的list链表,添加到misc_list内核链表中。
                                   //新的设备作为一个节点,将这个节点添加到内核链表misc_list中。



总结:misc_register函数到底做了什么呢?

    根据上面额分析,可以知道,misc_register函数的工作就是,先判断一下要注册的这个misc设备的次设备号是否是合法的,判断是否是255(表示要内核来为其分配次设备号),

之后将次设备号和主设备号按照一定的算法组成一个数字放到dev_t(unsigned long)类型的dev变量中,之后在用device_create函数,将这个设备注册到misc的class中,并告诉

主次设备号是多少(将dev用算法解析出主次设备号)和名字是什么什么,最后将这个设备(节点)添加到内核mist_list杂散类链表中。


3、主设备号和次设备号的作用?

一个主设备号表示一类设备

次设备号表示一类设备中的具体个体。可以在将这个设备注册到class的时候自己分配次设备号,也可以将minor设置为255让内核来为我们动态分配次设备号。