分类: LINUX
2013-09-07 17:30:05
/*
* 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 () (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 <>
*
* Fixed a failing symbol register to free the device registration
* Alan Cox <> 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 <>,
* adapted by Bjorn Ekwall <>
* corrected by Alan Cox <>
*
* Changes for kmod (from kerneld):
* Cyrus Durgin <>
*
* Added devfs support. Richard Gooch <> 10-Jan-1998
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* 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 unsigned char misc_minors[DYNAMIC_MINORS / 8]; /* 用于保存已经注册的小于64的次设备号 */
extern int pmu_device_init(void);
#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 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 = { /* 混杂设备操作函数,其对应的是/proc目录下的设备文件 */
.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;
lock_kernel();
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) { /* 遍历混杂设备链表,找到次设备相等的那个设备节点 */
if (c->minor == minor) {
new_fops = fops_get(c->fops); /* 获取找到的设备的file_operations结构的操作函数 */
break;
}
}
if (!new_fops) { /*如果 获取找到的设备的file_operations结构的操作函数没有成功 */
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);/* 获取找到的设备的file_operations结构的操作函数 */
break;
}
}
if (!new_fops) /* 如果还是没有 获取找到的设备的file_operations结构的操作函数,则做出错处理*/
goto fail;
}
err = 0;
old_fops = file->f_op; /* 保存设备文件的老的操作函数指针 */
file->f_op = new_fops; /* 将找 设备文件的file_operations操作函数集赋值file->f_op ,此后对设备文件进行读写就会操作到对应的设备*/
if (file->f_op->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);
unlock_kernel();
return err;
}
static struct class *misc_class;
static const struct file_operations misc_fops = { /* 字符设备操作函数,当用户空间进行系统调用open时,会调用该misc_open函数 */
.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) { /* 如果次设备号等于MISC_DYNAMIC_MINOR=255,则说明要为设备动态分配一个此设备号 */
int i = DYNAMIC_MINORS; /* i=64 */
while (--i >= 0)
if ( (misc_minors[i>>3] & (1 << (i&7))) == 0) /* 说明有设备号可以分配 */
break;
if (i<0) {/* 如果没有找到符合条件misc_minors[i>>3] & (1 << (i&7))) 的i则返回错误*/
mutex_unlock(&misc_mtx);
return -EBUSY;
}
misc->minor = i; /* 将找到的这个数(小于64)赋给 misc->minor*/
}
if (misc->minor < DYNAMIC_MINORS) /* 如果次设备号小于64,这说明小于64的次设备号最多可以有8个 */
misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7); /* 则数组 misc_minors以下标为misc->minor >> 3的数组项就等于1 << (misc->minor & 7)*/
dev = MKDEV(MISC_MAJOR, misc->minor); /* 获取设备的设备号 */
misc->this_device = device_create(misc_class, misc->parent, dev, NULL, /* 创建设备节点,即/dev/ misc->name*/
"%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
*/
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 = misc->minor; /* 获取设备的次设备号 */
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) {
misc_minors[i>>3] &= ~(1 << (misc->minor & 7)); /* 将数组项中相应的设备号标志位取反 */
}
mutex_unlock(&misc_mtx);
return 0;
}
EXPORT_SYMBOL(misc_register);
EXPORT_SYMBOL(misc_deregister);
/* 混杂设备子系统初始化 */
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS
proc_create("misc", 0, NULL, &misc_proc_fops); /* 在proc文件系统中创建设备文件,文件名为misc即/proc/misc */
#endif
misc_class = class_create(THIS_MODULE, "misc"); /* 在/sys/class下面创建目录misc即/sys/class/misc*/
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) /* 注册字符设备 ,主设备号是10,设备文件名为misc*/
goto fail_printk;
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); /* 这在系统初始化时被调用 */