【linux kernel】杂项(misc)设备驱动总结

发布于:2024-05-09 ⋅ 阅读:(29) ⋅ 点赞:(0)


👉相关文件:

  • drivers/char/misc.c
  • include/linux/miscdevice.h

一、杂项设备简介

Linux 内核中的杂项设备(Miscellaneous Devices)是一种通用的设备类型,用于表示那些不适合其他设备类型的设备。这些设备通常是不规则的,没有标准的通信协议或接口。杂项设备提供了一种灵活的机制,允许我们将不同类型的设备注册为杂项设备,并通过统一的接口在用户空间访问它们。

在 Linux 内核中,杂项设备通过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 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、注册杂项设备

int misc_register(struct miscdevice *misc)
  • struct miscdevice *misc:杂项设备结构

该函数用于向内核注册一个杂项设备。如果次设备号设置为 MISC_DYNAMIC_MINOR,则会分配一个次设备号,并将其放置在结构体的 minor 字段中。对于其他情况,使用请求的次设备号。

传递的结构体被链接到内核中,并且在注销之前可能不会被销毁。默认情况下,对设备的 open() 系统调用会将 file->private_data 设置为指向该结构体。驱动程序不需要在 fops 中包含 open 函数。

成功时返回零,失败时返回负的 errno 代码。

2、注销杂项设备

int misc_deregister(struct miscdevice *misc)
  • struct miscdevice *misc:杂项设备结构

该函数用于注销之前使用 misc_register() 成功注册的杂项设备。

3、杂项设备模块助手函数

提供了一个模块助手函数module_misc_device(),在编写杂项设备驱动时,可以直接使用该宏函数替换模块的init和exit操作。

三、杂项设备初始化

在Linux内核中都会支持杂项设备,在内核启动过程中,会调用misc_init()完成杂项设备相关的初始化操作:

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() 函数注销杂项设备。

例如下例代码:

#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)是两种不同类型的设备。在内核中的实现和使用方式略有不同。

  1. 杂项设备(Miscellaneous Devices)

    • 杂项设备是一种比较通用的设备类型,用于表示不适合其他设备类型的设备。
    • 这些设备通常是不规则的,没有标准的通信协议或接口。
    • 杂项设备在 /dev 目录下创建,但是它们的名字与其驱动程序关联,而不是与设备类型直接关联。
    • 在内核中,杂项设备通过 miscdevice 结构来表示,该结构包含了设备的名称、设备号、文件操作等信息。
    • 创建和注册杂项设备通常涉及使用 misc_register()misc_deregister() 等函数。
  2. 字符设备(Character Devices)

    • 字符设备是一种基本的设备类型,用于处理以字符为单位的数据流。
    • 这些设备通常是顺序访问的,没有固定的大小或边界。
    • 字符设备在 /dev 目录下创建,并且其名称直接与设备类型相关联。
    • 在内核中,字符设备通过 cdev 结构来表示,该结构包含了设备的文件操作等信息。
    • 创建和注册字符设备通常涉及使用 cdev_add()cdev_del() 等函数。

总的来说,杂项设备适用于那些不符合标准设备模型的设备,而字符设备则适用于以字符流形式进行通信的设备。在编写内核驱动程序时,我们可以根据设备的特性选择适当的设备类型。

参考链接:
https://docs.kernel.org/driver-api/misc_devices.html?highlight=misc_register#c.misc_register