当字符设备不符合预先确定的字符设备范畴时,就可以采用Linux的杂项设备,这类设备用得比较多,misc device用主设备号为10来调用misc_register注册。misc device的实现在drivers/char/misc.c文件中,先看它的init函数。
再来看misc device的核心部分misc_register函数。
misc_deregister函数定义如下:
在misc_register函数中,调用了find_first_zero_bit,这个函数是在misc_minors数组中找到第一个不为0的bit,misc_minors在一开始通过DECLARE_BITMAP宏进行定义。
下面通过一个实例来说明misc device的使用。
驱动的私有数据结构定义:
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);
通过subsys_initcall宏在内核初始化的时候调用misc_init函数,首先通过class_create申请misc_class类,再调用register_chrdev来注册字符设备,其中MISC_MAJOR的值是10。
再来看misc device的核心部分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.
*/
int misc_register(struct miscdevice * misc)
{
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
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);
} else {
struct miscdevice *c;
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
}
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;
}
首先根据传入的miscdevice指针初始化链表头,接下来判断次设备号是否要求动态分配,如果是动态分配,就去misc_minors数组找到第一个不为0的bit,将其赋值给misc->minor,紧接着设置misc_minors数组的第i位。如果次设备号是固定分配,就调用list_for_each_entry查看是否有其他的misc已经注册了该次设备号,若已经分配,则返回错误。最后调用device_create函数根据misc_init函数申请的misc_class来在/dev目录下申请节点,并将驱动的miscdevice加入链表头。
misc_deregister函数定义如下:
/**
* 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 (WARN_ON(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;
}
open操作时,misc_open会在驱动的open方法之前被调用,misc_open实现如下:
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;
}
这个函数最为关键的一点就是file->private_data = c;这条语句将struct file的private_data指针指向驱动程序中的注册的misc device,这样一来,就可以将struct miscdevice结构放到自己定义的结构中去,在open方法中通过container_of宏即可获得自定义结构体的地址。
在misc_register函数中,调用了find_first_zero_bit,这个函数是在misc_minors数组中找到第一个不为0的bit,misc_minors在一开始通过DECLARE_BITMAP宏进行定义。
static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
DECLARE_BITMAP宏定义在include/linux/types.h:
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
BITS_TO_LONGS宏计算一共需要多少个unsigned long型的整数才能存储bits个位。
下面通过一个实例来说明misc device的使用。
驱动的私有数据结构定义:
struct pn547_dev {
bool suspended;
bool irq_enabled;
wait_queue_head_t read_wq;
spinlock_t irq_enabled_lock;
struct mutex pn547_mutex;
struct i2c_client *client;
struct miscdevice pn547_device;
struct regulator *vdd;
struct regulator *vsim1;
struct regulator *vsim2;
struct workqueue_struct *pn547_wq;
struct notifier_block fb_notif;
};
定义file_operations结构体:
static const struct file_operations pn547_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = pn547_dev_read,
.write = pn547_dev_write,
.open = pn547_dev_open,
.unlocked_ioctl = pn547_dev_ioctl,
.release = pn547_dev_release,
};
在驱动的probe函数中注册:
pdata = kzalloc(sizeof(struct pn547_dev), GFP_KERNEL);
if (!pdata) {
dev_err(&client->dev, "%s: Alloc mem fail!", __func__);
return -ENOMEM;
}
pdata->pn547_device.minor = MISC_DYNAMIC_MINOR;
pdata->pn547_device.name = "pn544";
pdata->pn547_device.fops = &pn547_dev_fops;
ret = misc_register(&pdata->pn547_device);
if (ret) {
dev_err(&client->dev, "%s : misc_register failed\n", __FILE__);
misc_deregister(&pdata->pn547_device);
return -EINVAL;
}
如果注册成功,会在/dev/目录下生成pn544节点,之后就可以调用驱动的open,read,write函数了。重点关注open函数:
static int pn547_dev_open(struct inode *inode, struct file *filp)
{
struct pn547_dev *pn547_dev = container_of(filp->private_data,
struct pn547_dev,
pn547_device);
filp->private_data = pn547_dev;
pr_debug("%s : %d,%d\n", __func__, imajor(inode), iminor(inode));
irq_set_irq_wake(pn547_dev->client->irq, 1);
return 0;
}
通过container_of宏,根据pn547_dev结构体的pn547_device成员的地址获得整个pn547_dev结构体的地址。