Linux 字符设备分析2

Linux 字符设备分析2(基于Linux6.6)---混杂设备介绍

 

 

 

一、Linux Misc 设备概述

在 Linux 系统中,Misc 设备(Miscellaneous Devices) 是一种特殊的设备类型,通常用于处理一些不符合标准字符设备或块设备类型的硬件或软件资源。它们通常是针对特定用途而设计的设备,能够提供与内核直接交互的简单接口。misc 设备主要通过 /dev 目录下的设备节点与用户空间程序进行交互。

1.1、Misc 设备的定义

在 Linux 中,设备分为不同类型,如字符设备、块设备和网络设备等。而 misc 设备则作为一个特别的设备类别,主要用于处理一些没有固定接口的设备。这些设备可能不会经常使用,但却有特定的功能或提供某些特定的服务。

字符设备(Character Device)块设备(Block Device) 是常见的设备类型,它们分别处理流数据和存储数据。而 Misc 设备 不属于这两类,通常用于处理一些复杂或特定的操作,如一些硬件接口或软件接口。

1.2、Misc 设备的用途

misc 设备通常用于提供一个简单的接口来管理某些硬件资源或内核功能,以下是常见的使用场景:

  • 简单的硬件接口:一些硬件设备无法完全按照字符设备或块设备的规范进行驱动,它们可以使用 misc 设备进行管理。例如,一些定制的硬件接口可能只有简单的读写操作。

  • 驱动程序和内核模块的交互:许多 Linux 内核模块需要与用户空间交互,这时通过 misc 设备提供一个简单的接口来进行通信。

  • 虚拟设备:有些虚拟设备(如虚拟串口、虚拟磁盘等)通过 misc 设备进行管理。

  • 控制接口:一些内核模块可能需要提供与用户空间的控制接口,而不依赖于字符设备或块设备的复杂性。例如,调试、日志记录、内核配置等功能可能会用到 misc 设备。

 

二、混杂设备驱动模型相关的结构体变量

混杂设备驱动模型的结构体为miscdevice,仅这一个结构体变量,从其结构体定义也可以看出

该驱动模型的实现比较简洁明了。

struct miscdevice 

该结构体的定义如下,主要涉及如下几个方面:

  1. 通过fops指针,指向该字符设备文件的文件操作接口(open、read、write、ioctl、close等),通过该指针会实现与文件描述符中的f_op指针的关联,当调用系统调用接口open、read、write等对该文件进行操作时,即可实现对该文件的操作;
  2. 通过list成员,将系统中所有已注册的混杂字符设备链接在一起,该链表主要是混杂设备驱动模型内部的变量,通过该变量可实现对系统中已注册的混杂设备的查找,以及在混杂设备注册时的查重;
  3. 通过parent变量,实现将该混杂设备对应的device及其父device关联,从而可将该字符设备对应的device变量链入LINUX系统中的device树中;
  4. 通过this_device,并借助device_create、device_destroy接口,可借助LINUX设备-总线-驱动模型中的uevent机制,通过netlink向应用层发送设备添加或移除的事件消息,而应用层的udev/mdev在接收到该事件消息后,根据该device变量的devt成员,通过调用mknod系统调用接口,实现字符设备文件节点的创建或移除。
  5. 而nodename、mode则可通过uevent事件传递给应用层。

include/linux/miscdevice.h 

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

三、混杂设备驱动模型的设备号区间申请以及cdev的添加

   在上一节中,介绍了如何申请设备号区间以及进行cdev的添加,此处再次重复一遍,步骤如下:

  1. 调用alloc_chrdev_region/register_chrdev_region实现设备号的创建;
  2. 调用cdev_init/cdev_alloc、cdev_add,实现cdev的创建以及注册至cdev_map;
  3. 调用class_create创建一个类别;
  4. 调用device_create/device_add实现device类型变量的注册,该步主要目的是向应用层发送uevent,借助udev/mdev,从而实现字符设备文件的创建。

而针对混杂字符设备驱动模型,在该驱动模型的初始化接口中,即完成上述4步的操作。在接口misc_init中,通过调用register_chrdev申请了主设备号为10,次设备号区间0-255的设备号区间,并通过调用cdev_alloc、cdev_add接口完成了cdev变量的创建以及注册至cdev_map中。同时该接口也创建了一个名为“misc”的类,混杂字符设备对应的device类型变量均会链接至该类。

下图为申请完字符设备区间后,chrdevs变量的示意图(其中蓝色虚线框为混杂设备驱动模型申请的设备号区间)

dc1bae01e7b84ff29b142b4bfd84beb1.png

下图为执行cdev_alloc、cdev_add后,cdev_map中增加针对混杂字符设备相关的关联,其中蓝色虚线框为混杂设备驱动相关的内容(该混杂设备对应的cdev变量的销毁借助设备驱动模型中kref变量,以及函数cdev_dynamic_release,在引用计数为时,实现该cdev变量内存空间的释放),该cdev的ops指向misc_fops,而misc_fops中定义了misc_open接口。

 

 

71c42bc8cfe8473da6ec855df4a5a943.png 

执行完以上操作后,即完成了混杂字符设备驱动模型的设备号区间申请,cdev的注册等功能。至此混杂字符设备驱动模型中与字符设备驱动模块的相关的工作基本上已完成,下面主要是提供次设备创建的接口。

四、混杂字符设备注册与注销接口

针对次设备号的创建主要就如下两种方式:

  1. 在应用层直接调用mknod命令实现字符设备文件inode的创建;
  2. 借助linux设备-总线-驱动模型中的uevent,通过调用device_add接口,创建一个device类型的设备变量,并设置该设备变量对应的设备号,然后即会向应用程序发送设备添加的uevent(通过netlink传递该uevent信息),而应用层程序通过udev进行接收uevent事件(或者通过mdev接收udeve事件),当确认该设备存在设备号以及设备添加event后,即自动调用mknod系统调用,实现字符设备文件的创建。

而针对混杂字符设备驱动模型而言,为了让其字符设备的创建自动化,因此其选择上述b中描述的方式,分析下其注册函数misc_register的实现,其实现流程如下图所示,主要的功能如下:

  1. 调用device_create创建device类型的变量,从而借助设备模型的uevent事件信息,实现字符设备文件节点的创建;
  2. 将混杂设备链接至misc_list上,以便实现查找与查重。

1a63e99fb39c42329aab2dce4932d300.png 

 

针对misc_list以及各混杂设备之间的关联,其关联图如下所示:

61b1458f0a3f4730ac7ff95909fc397a.png

 

针对注销接口misc_deregister,其实现的功能与注册刚好相反,也就是将该混杂设备从misc_list移除,调用device_destroy从devices_kset上移除该混杂设备对应device变量,且解除与“misc”类的链接(若次设备号在范围0-63内,则位动态申请的次设备后,则需要clear变量misc_minors对应位,以便继续使用)。

五、如何执行到具体混杂设备驱动的文件操作接口

针对申请了多个次设备号的cdev而言,可在其注册的open接口中,根据各自模块维护的次设备变量,实现具体次设备的操作。如spi通用字符设备中的open接口中通过device_list链表,并根据设备号查找到对应spidev_data类型的变量,从而完成针对具体spi device的通信;而在i2c通用字符设备驱动中的open接口中,通过i2c_dev_list链表并借助次设备号,查找到i2c_dev类型的变量,从而借助对应的i2c_adapter,实现数据通信。

而针对混杂设备驱动模型而言,其也是使用类似的操作,在其open接口misc_open中,借助链表misc_list以及传递的次设备号,查找对应的混杂设备,从而调用其fops指针中的文件操作接口,实现具体设备对应的操作接口。

 

 

59c3fa2aaf4e4c5e89f89dd2c1e0969c.png

六、驱动中如何实现混杂设备的注册

6.1、混杂设备(Misc Device)注册的基本流程:

  1. 定义文件操作函数:与设备交互时,需要定义设备文件操作(file_operations)函数,例如 openreadwrite 等。
  2. 创建 miscdevice 结构:通过 miscdevice 结构体描述设备的信息,如设备名称、文件操作、设备的 minor 号等。
  3. 注册设备:使用 misc_register() 函数将设备注册到系统中。
  4. 注销设备:使用 misc_deregister() 注销设备。

6.2、如何实现混杂设备注册的具体步骤:

1. 定义文件操作(File Operations)

你需要定义与设备交互的文件操作结构体,通常包含如下几个函数:

  • open:打开设备。
  • read:从设备读取数据。
  • write:向设备写入数据。
  • ioctl:控制设备操作。
  • close:关闭设备。

例如,定义一个简单的 readopen 函数:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>  // For copy_to_user, copy_from_user

#define DEVICE_NAME "misc_example"

static int misc_example_open(struct inode *inode, struct file *file)
{
    pr_info("Misc device opened\n");
    return 0;
}

static ssize_t misc_example_read(struct file *file, char __user *buf,
                                  size_t count, loff_t *ppos)
{
    const char *message = "Hello from misc device!\n";
    size_t len = strlen(message);

    if (count < len)
        return -EINVAL;

    if (copy_to_user(buf, message, len))
        return -EFAULT;

    return len;
}

static const struct file_operations misc_example_fops = {
    .owner = THIS_MODULE,
    .open = misc_example_open,
    .read = misc_example_read,
};

2. 定义和注册 miscdevice 结构

接下来,你需要定义一个 miscdevice 结构,并初始化它。miscdevice 结构包含设备的 minor 号、设备名称、文件操作函数等。

static struct miscdevice misc_example_device = {
    .minor = MISC_DYNAMIC_MINOR,  // 动态分配 minor 号
    .name = DEVICE_NAME,          // 设备名称
    .fops = &misc_example_fops,   // 文件操作结构体
};

3. 注册混杂设备

在模块初始化时,你可以调用 misc_register() 函数将设备注册到系统中。

static int __init misc_example_init(void)
{
    int ret;

    // 注册设备
    ret = misc_register(&misc_example_device);
    if (ret)
    {
        pr_err("Failed to register misc device\n");
        return ret;
    }

    pr_info("Misc device registered with minor %d\n", misc_example_device.minor);
    return 0;
}

4. 注销设备

当设备模块被卸载时,需要调用 misc_deregister() 注销设备。

static void __exit misc_example_exit(void)
{
    misc_deregister(&misc_example_device);
    pr_info("Misc device unregistered\n");
}

module_init(misc_example_init);
module_exit(misc_example_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple misc device driver");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值