文章目录
👉相关文件:
- drivers/char/misc.c
- include/linux/miscdevice.h
一、杂项设备简介
Linux 内核中的杂项设备(Miscellaneous Devices)是一种通用的设备类型,用于表示那些不适合其他设备类型的设备。这些设备通常是不规则的,没有标准的通信协议或接口。杂项设备提供了一种灵活的机制,允许我们将不同类型的设备注册为杂项设备,并通过统一的接口在用户空间访问它们。
在 Linux 内核中,杂项设备通过struct miscdevice
结构来表示:
c
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
-
minor
: 杂项设备的次设备号。如果将MISC_DYNAMIC_MINOR
分配给此字段,内核将动态分配次设备号。 -
name
: 杂项设备的名称,用于在/dev
文件系统中创建设备节点。 -
fops
: 指向包含设备操作的struct file_operations
结构的指针,定义了对该设备进行的操作。 -
list
: 用于将杂项设备链接到内核中的杂项设备列表中。 -
parent
: 指向父设备的指针,如果杂项设备与其他设备相关联,则指向该设备的父设备。 -
this_device
: 指向表示该杂项设备的struct device
结构的指针。 -
groups
: 用于定义设备属性的指针数组。属性组定义了设备的属性,例如sysfs
上的属性。 -
nodename
: 杂项设备节点的名称,通常与name
字段相同。此字段用于在/sys
文件系统中创建设备节点。 -
mode
: 设备节点的权限模式,指定了用户对设备节点的访问权限。
struct miscdevice
提供了一种将设备注册为杂项设备的机制,并指定了设备的名称、操作以及其他相关属性。杂项设备在 /dev 目录下创建,但是它们的名字与其驱动程序关联,而不是与设备类型直接关联。因此,同一类型的设备可能具有不同的名称,取决于它们所使用的驱动程序。
二、杂项设备API
在内核中,关于杂项设备提供的驱动API较少,仅包含两个API。
1、注册杂项设备
c
int misc_register(struct miscdevice *misc)
- struct miscdevice *misc:杂项设备结构
该函数用于向内核注册一个杂项设备。如果次设备号设置为 MISC_DYNAMIC_MINOR
,则会分配一个次设备号,并将其放置在结构体的 minor
字段中。对于其他情况,使用请求的次设备号。
传递的结构体被链接到内核中,并且在注销之前可能不会被销毁。默认情况下,对设备的 open()
系统调用会将 file->private_data
设置为指向该结构体。驱动程序不需要在 fops
中包含 open
函数。
成功时返回零,失败时返回负的 errno 代码。
2、注销杂项设备
c
int misc_deregister(struct miscdevice *misc)
- struct miscdevice *misc:杂项设备结构
该函数用于注销之前使用 misc_register()
成功注册的杂项设备。
3、杂项设备模块助手函数
提供了一个模块助手函数module_misc_device()
,在编写杂项设备驱动时,可以直接使用该宏函数替换模块的init和exit操作。
三、杂项设备初始化
在Linux内核中都会支持杂项设备,在内核启动过程中,会调用misc_init()
完成杂项设备相关的初始化操作:
c
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);
上述代码主要完成以下三个操作:
- 使用
proc_create()
创建一个名为 "misc" 的 proc 文件,使用指定的文件操作misc_proc_fops
。 - 使用
class_create()
创建一个名为 "misc" 的设备类。 register_chrdev()
注册字符设备
上述代码的目的是初始化杂项设备,包括创建 proc 文件、创建设备类、注册字符设备等。
四、杂项设备示例
我们通常通过以下步骤来创建和注册杂项设备:
- 1、定义和初始化 miscdevice 结构。
- 2、调用 misc_register() 函数注册杂项设备。
- 3、在驱动模块的退出函数中调用 misc_deregister() 函数注销杂项设备。
例如下例代码:
c
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "my_misc_device"
static int my_misc_open(struct inode *inode, struct file *file)
{
// Open device logic
return 0;
}
static int my_misc_release(struct inode *inode, struct file *file)
{
// Release device logic
return 0;
}
static ssize_t my_misc_read(struct file *file, char __user *buf, size_t len, loff_t *offset)
{
// Read from device logic
return 0;
}
static ssize_t my_misc_write(struct file *file, const char __user *buf, size_t len, loff_t *offset)
{
// Write to device logic
return len;
}
static const struct file_operations my_misc_fops = {
.owner = THIS_MODULE,
.open = my_misc_open,
.release = my_misc_release,
.read = my_misc_read,
.write = my_misc_write,
};
static struct miscdevice my_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &my_misc_fops,
};
static int __init my_misc_init(void)
{
int ret = misc_register(&my_misc_device);
if (ret) {
printk("Failed to register misc device\n");
return ret;
}
printk("Registered misc device successfully\n");
return 0;
}
static void __exit my_misc_exit(void)
{
misc_deregister(&my_misc_device);
printk("Deregistered misc device\n");
}
module_init(my_misc_init);
module_exit(my_misc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Iriczhao");
MODULE_DESCRIPTION("A simple miscellaneous device driver");
五、杂项设备和字符设备
在 Linux 内核中,杂项设备(misc device)和字符设备(character device)是两种不同类型的设备。在内核中的实现和使用方式略有不同。
-
杂项设备(Miscellaneous Devices):
- 杂项设备是一种比较通用的设备类型,用于表示不适合其他设备类型的设备。
- 这些设备通常是不规则的,没有标准的通信协议或接口。
- 杂项设备在
/dev
目录下创建,但是它们的名字与其驱动程序关联,而不是与设备类型直接关联。 - 在内核中,杂项设备通过
miscdevice
结构来表示,该结构包含了设备的名称、设备号、文件操作等信息。 - 创建和注册杂项设备通常涉及使用
misc_register()
和misc_deregister()
等函数。
-
字符设备(Character Devices):
- 字符设备是一种基本的设备类型,用于处理以字符为单位的数据流。
- 这些设备通常是顺序访问的,没有固定的大小或边界。
- 字符设备在
/dev
目录下创建,并且其名称直接与设备类型相关联。 - 在内核中,字符设备通过
cdev
结构来表示,该结构包含了设备的文件操作等信息。 - 创建和注册字符设备通常涉及使用
cdev_add()
和cdev_del()
等函数。
总的来说,杂项设备适用于那些不符合标准设备模型的设备,而字符设备则适用于以字符流形式进行通信的设备。在编写内核驱动程序时,我们可以根据设备的特性选择适当的设备类型。
参考链接:
https://docs.kernel.org/driver-api/misc_devices.html?highlight=misc_register#c.misc_register