在总线下注册设备及设备注册流程分析

提示:总线里面设备注册和注册流程分析


前言

前面了解过

参考资料

驱动-注册自己的总线并创建属性文件
驱动-总线bus注册流程分析
platform总线注册流程分析
在总线下注册设备实验
设备模型
Linux 在线源码

一、设备注册

设备注册-驱动注册实验

源码程序 dvice.c 设备端源码如下:

     编写驱动文件 device.c,在驱动中,Linux 内核中创建一个自定义设备并将其注册到自定义的总线上

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/sysfs.h>

extern struct bus_type mybus;

void myrelease(struct device *dev)
{
    printk("This is myrelease\n");
};

struct device mydevice = {
    .init_name = "mydevice",      // 设备的初始化名称
    .bus = &mybus,                // 所属总线
    .release = myrelease,         // 设备的释放回调函数
    .devt = ((255 << 20 | 0)),    // 设备号
};

// 模块的初始化函数
static int device_init(void)
{
    int ret;
    ret = device_register(&mydevice);  // 注册设备

    return 0;
}

// 模块退出函数
static void device_exit(void)
{
    device_unregister(&mydevice);      // 取消注册设备
}

module_init(device_init);              // 指定模块的初始化函数
module_exit(device_exit);              // 指定模块的退出函数

MODULE_LICENSE("GPL");                 // 模块使用的许可证
MODULE_AUTHOR("fangchen");               // 模块的作者

驱动层直接用bus的测试程序,源码如下:

    我们编写驱动代码,定义了一个名为 “mybus” 的总线,并实现了总线的匹配回调函数
mybus_match 和设备探测回调函数 mybus_probe。同时,还定义了一个名为 “value” 的属性文
件,并实现了属性的显示回调函数 mybus_show。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/sysfs.h>

int mybus_match(struct device *dev, struct device_driver *drv)
{
    // 检查设备名称和驱动程序名称是否匹配
    return (strcmp(dev_name(dev), drv->name) == 0);
};

int mybus_probe(struct device *dev)
{
    struct device_driver *drv = dev->driver;
    if (drv->probe)
        drv->probe(dev);
    return 0;
};

struct bus_type mybus = {
    .name = "mybus",                 // 总线的名称
    .match = mybus_match,            // 设备和驱动程序匹配的回调函数
    .probe = mybus_probe,            // 设备探测的回调函数
};
EXPORT_SYMBOL_GPL(mybus);             // 导出总线符号

ssize_t mybus_show(struct bus_type *bus, char *buf)
{
    // 在 sysfs 中显示总线的值
    return sprintf(buf, "%s\n", "mybus_show");
};

struct bus_attribute mybus_attr = {
    .attr = {
        .name = "value",             // 属性的名称
        .mode = 0664,                // 属性的访问权限
    },
    .show = mybus_show,               // 属性的 show 回调函数
};

// 模块的初始化函数
static int bus_init(void)
{
    int ret;
    ret = bus_register(&mybus);       // 注册总线
    ret = bus_create_file(&mybus, &mybus_attr);  // 在 sysfs 中创建属性文件

    return 0;
}

// 模块退出函数
static void bus_exit(void)
{
    bus_remove_file(&mybus, &mybus_attr);  // 从 sysfs 中移除属性文件
    bus_unregister(&mybus);                // 取消注册总线
}

module_init(bus_init);                    // 指定模块的初始化函数
module_exit(bus_exit);                    // 指定模块的退出函数

MODULE_LICENSE("GPL");                    // 模块使用的许可证
MODULE_AUTHOR("fangchen");                  // 模块的作者

实验测试结果

当我们编译好驱动.ko 文件 bus.ko 和 device.ko 后,先加载驱动bus.ko 然后加载设备驱动文件 device.ko ,我们看看实际结果:
在这里插入图片描述
在这里插入图片描述
也就是说生成了设备文件:/sys/devices/mydevice 且 在 sys/bus/mybus/devices 下面有了 mydevice 的软链接。

二、设备注册流程分析

如上设备注册的源码:

// 模块的初始化函数
static int device_init(void)
{
    int ret;
    ret = device_register(&mydevice);  // 注册设备

    return 0;
}

这里要重点分析的流程就是 device_register(&mydevice); 设备是如何注册到总线上去的?

源码device_register

/**
 * device_register - register a device with the system.
 * @dev: pointer to the device structure
 *
 * This happens in two clean steps - initialize the device
 * and add it to the system. The two steps can be called
 * separately, but this is the easiest and most common.
 * I.e. you should only call the two helpers separately if
 * have a clearly defined need to use and refcount the device
 * before it is added to the hierarchy.
 *
 * For more information, see the kerneldoc for device_initialize()
 * and device_add().
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up the
 * reference initialized in this function instead.
 */
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}

慢慢发现驱动C的代码注释其实蛮清晰的,特别清晰 有时候。 就是说 设备注册分为两步,initialize 初始化设备和把设备添加到系统里面去。

device_initialize 设备初始化

device_initialize() 函数
device_initialize() 负责初始化设备结构的基本字段:

void device_initialize(struct device *dev)
{
    dev->kobj.kset = devices_kset;  // 设置设备所属的kset
    kobject_init(&dev->kobj, &device_ktype); // 初始化kobject
    INIT_LIST_HEAD(&dev->dma_pools); // 初始化DMA池链表
    mutex_init(&dev->mutex); // 初始化互斥锁
    lockdep_set_novalidate_class(&dev->mutex);
    spin_lock_init(&dev->devres_lock); // 初始化自旋锁
    INIT_LIST_HEAD(&dev->devres_head); // 初始化资源链表
    device_pm_init(dev); // 初始化电源管理
    set_dev_node(dev, -1); // 设置NUMA节点
}

device_add 添加设备到系统里面去

device_add() 是设备注册的核心函数,主要完成以下工作:

int device_add(struct device *dev)
{
    struct device *parent = NULL;
    struct kobject *kobj;
    struct class_interface *class_intf;
    int error = -EINVAL;
    
    // 1. 参数检查和基本设置
    dev = get_device(dev);
    if (!dev->p) {
        error = device_private_init(dev);
        if (error)
            goto done;
    }
    
    // 2. 设置父设备
    if (dev->parent)
        parent = get_device(dev->parent);
    else if (dev->bus && dev->bus->dev_root)
        parent = get_device(dev->bus->dev_root);
    
    dev->parent = parent;
    
    // 3. 设置设备名称
    if (dev->init_name) {
        dev_set_name(dev, "%s", dev->init_name);
        dev->init_name = NULL;
    }
    
    // 4. 在sysfs中注册设备
    error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev));
    if (error)
        goto Error;
    
    // 5. 添加到设备层次结构中
    if (parent)
        klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children);
    
    // 6. 添加到总线的设备列表
    if (dev->bus) {
        mutex_lock(&dev->bus->p->mutex);
        klist_add_tail(&dev->p->knode_bus, &dev->bus->p->klist_devices);
        mutex_unlock(&dev->bus->p->mutex);
    }
    
    // 7. 触发uevent事件
    kobject_uevent(&dev->kobj, KOBJ_ADD);
    
    // 8. 绑定驱动程序
    bus_probe_device(dev);
    if (parent)
        klist_add_tail(&dev->p->knode_driver, &parent->p->klist_drivers);
    
    // 9. 添加到类设备列表
    if (dev->class) {
        mutex_lock(&dev->class->p->mutex);
        klist_add_tail(&dev->p->knode_class, &dev->class->p->klist_devices);
        class_dir_update(dev->class);
        mutex_unlock(&dev->class->p->mutex);
    }
    
    // 10. 通知接口
    list_for_each_entry(class_intf, &dev->class->p->interfaces, node)
        if (class_intf->add_dev)
            class_intf->add_dev(dev, class_intf);
    
    return 0;
    
Error:
    // 错误处理
    ...
    return error;
}

文件创建分析

当设备通过 device_add 注册后,内核会在 /sys/devices/ 下创建对应的设备目录(如 /sys/devices/mydevice),并自动创建一些标准文件和子目录:

dev 文件创建

dev 文件是字符设备和块设备的主次设备号表示,格式为 major:minor。

创建过程:

  • 在 device_add 中调用 devtmpfs_create_node 创建设备节点
  • 通过 device_create_file 创建 dev 属性文件
  • 实际由 dev_attr_show 函数实现读取操作

power 目录创建

power 目录包含与设备电源管理相关的文件,如:

  • wakeup - 控制设备是否可唤醒系统
  • control - 电源状态控制
  • runtime_status - 运行时电源状态信息

创建过程:

  • 由 dpm_sysfs_add 函数在 device_pm_add 中调用
  • 使用 sysfs_create_group 创建 power 属性组

subsystem 符号链接 创建

subsystem 是一个指向设备所属总线的符号链接(如 /sys/bus/pci)。

创建过程:

  • 在 device_add 中调用 bus_add_device
  • 由 sysfs_create_link 创建符号链接
  • 如果设备没有父设备,还会创建到 devices 目录的链接

uevent 文件 创建

uevent 文件用于触发或显示设备的 uevent 信息。

创建过程:

  • 在 device_add 中调用 device_create_file 创建
  • 由 uevent_attr 定义,使用 uevent_show 和 uevent_store 作为操作函数
  • 写入此文件可以手动触发 uevent 事件

实现流程详解

以下是 device_add 中与 sysfs 创建相关的主要调用流程:

device_add(struct device *dev)
    |
    |--> device_initialize(dev); // 初始化设备基础属性
    |
    |--> dev_set_name(dev, "%s", dev->name); // 设置设备名称
    |
    |--> device_create_sys_dev_entry(dev); // 创建设备号文件
    |
    |--> device_add_class_symlinks(dev); // 创建类符号链接
    |
    |--> device_add_attrs(dev); // 添加设备属性
    |
    |--> bus_add_device(dev); // 将设备添加到总线
    |    |
    |    |--> sysfs_create_link(&dev->kobj, &bus->subsys.kobj, "subsystem");
    |    |--> sysfs_create_link(&bus->subsys.kobj, &dev->kobj, dev_name(dev));
    |
    |--> dpm_sysfs_add(dev); // 添加电源管理属性
    |    |
    |    |--> sysfs_create_group(&dev->kobj, &pm_attr_group);
    |
    |--> device_create_file(dev, &dev_attr_uevent); // 创建uevent文件
    |
    |--> device_create_file(dev, &dev_attr_dev); // 创建dev文件
    |
    |--> kobject_uevent(&dev->kobj, KOBJ_ADD); // 发送uevent事件

通过以上机制,Linux 设备模型在 device_add 时自动创建了标准化的 sysfs 接口,为设备管理和用户空间交互提供了统一的访问方式。

总结

注册设备及设备注册流程分析,通过设备注册实验+注册流程中几个文件或文件夹创建来逐步分析流程。

【RIS 辅助的 THz 混合场波束斜视下的信道估计与定位】在混合场波束斜视效应下,利用太赫兹超大可重构智能表面感知用户信道与位置(Matlab代码实现)内容概要:本文围绕“IS 辅助的 THz 混合场波束斜视下的信道估计与定位”展开,重点研究在太赫兹(THz)通信中,由于超大可重构智能表面(RIS)引起的混合近场-远场(混合场)波束斜视效应,对用户信道感知与位置估计带来的挑战。文中提出利用RIS调控电磁波传播特性,结合先进的信号处理算法,在波束斜视影响下实现高精度的信道估计与用户定位,并提供了基于Matlab的代码实现,支持科研复现与进一步优化。研究对于提升未来6G超高速无线通信系统的感知与定位能力具有重要意义。; 适合人群:具备通信工程、信号处理或电子信息等相关专业背景,熟悉Matlab编程,从事太赫兹通信、智能反射面(RIS)或无线定位方向研究的研究生、科研人员及工程师。; 使用场景及目标:① 理解并复现混合场波束斜视效应下的信道建模方法;② 掌握基于RIS的太赫兹系统中信道估计与联合定位算法的设计与实现;③ 为后续开展智能超表面辅助的ISAC(通感一体化)研究提供技术参考和代码基础。; 阅读建议:建议读者结合Matlab代码,深入理解文档中提出的系统模型与算法流程,重点关注波束斜视的数学表征、信道估计算法设计及定位性能评估部分,可通过调整参数进行仿真验证,以加深对关键技术难点和解决方案的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值