linux 自定义字符设备,混杂设备与一般字符设备

混杂设备是字符设备的一种,它们共享一个主设备号(10),但次设备号不同,所有混杂设备形成一个链表,对设备发给你问时内核根据次设备号查找到相应的miscdevice设备。这样做的好处,节约主设备号,将某些设备用链表的形式链接在一起,最后通过查找次设备区分。

miscdevice混杂设备用主设备号无法匹配出设备驱动,只能找到链表,再通过次设备号,才能找到设备驱动,而一般字符设备,通过主设备号,就能找到设备驱动了。

通过代码,我们简单了解一下miscdevice混杂设备(只截取有关内容)

include/linux/miscdevice.h:

#define MISC_DYNAMIC_MINOR 255

struct

device;

struct miscdevice  {

int minor; //次设备号

const char *name;//设备名称,即/dev创建的设备文件名称

const struct file_operations *fops;//对应设备文件的一组操作

struct list_head list;//最终会链接到内核维持的misc_list链表中,方便查找。

struct device *parent;

struct device *this_device;//指向当前创建的设备文件

const char *nodename;

umode_t mode;

};

extern int misc_register(struct miscdevice * misc);//混杂设备注册

extern int misc_deregister(struct miscdevice *misc);//混杂设备注销

drivers/char/misc.c

static int

misc_open(struct inode * inode, struct file * file)

{

int minor = iminor(inode);

struct miscdevice *c;

int err = -ENODEV;

const struct file_operations *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;

replace_fops(file, new_fops);

if (file->f_op->open) {

file->private_data = c;

err =

file->f_op->open(inode,file);

}

fail:

mutex_unlock(&misc_mtx);

return err;

}

static const struct

file_operations misc_proc_fops = {

.owner = THIS_MODULE,

.open    = misc_seq_open,

//在之后的定义操作,无须对open赋值,而一般字符设备必须初始化open操作

.read    = seq_read,

.llseek  = seq_lseek,

.release = seq_release,

};

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

err = -EBUSY;

goto out;

}

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

err = -EBUSY;

goto out;

}

}

}

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;

}

list_add(&misc->list,

&misc_list);

out:

mutex_unlock(&misc_mtx);

return err;

}

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;

}

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

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

然后,简单的介绍一下主要的数据结构:

1. struct file_operations :

采用面向对象的思想,将一组系统调用封装到一个结构体中。在写设备驱动时,将自定义的操作赋值相应的系统调用。当对该设备文件操作时,内核自动选择相应的系统调用。

2. struct inode : 在内部表示文件。在设备驱动中,一般只使用dev_t i_rdev和struct

cdev *i_cdev。

3. struct file :

表示打开的文件。它是由内核open时创建,并传递给在该文件上进行操作的所有函数,直到最后的close函数。

各自成员请参考《linux设备驱动程序》第三章。

接下来,比较混杂设备和一般字符设备

1.

数据结构:在混杂设备中自定义的数据结构不必包含miscdevice结构体,而一般字符设备中自定义的数据结构得包含cdev结构体。

2. 设备编号:

在混杂设备中主设备编号恒为10,次设备编号在调用misc_register()时分配。而在一般字符设备提供了两种分配设备编号的方法,一是当明确知道所需要的设备编号时,调用register_chrdev_region()分配次设备号,二是当不明确主设备号时,则采用alloc_chrdev_region()动态主设备号。

3.

注册字符设备:在混杂字符设备中仅需调用misc_register()即可注册,而一般字符设备,则需要通过cdev_init()初始化包含struct

cdev的数据结构和cdev_add()通知内核字符设备的信息。

4.

自定义系统调用:混杂设备中无需自定义open系统调用,而一般字符设备中,则需要自定义open系统调用。

5.

设备与操作数据结构相关联:在混杂设备中只需在初始化miscdevice结构赋值即可,而字符设备则需要cdev_init()相关联并且显式赋值cdev结构体中ops成员。

6. 卸载设备:

在混杂字符设备中,仅需调用misc_deregister()即可,而在一般字符设备中,需要通过cdev_del()移除字符设备和通过unregister_chrdev_region释放已分配的设备号。

7.

实际的设备文件:在混杂设备中,自动创建实际的设备文件;卸载混杂设备模块时,自动删除实际设备文件,原因请看上述源代码。在一般字符设备中,这需要通过/proc/devices文件显式创建实际的设备文件;卸载一般字符设备模块之后,并没有删除实际的设备文件,需显式删除设备文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值