调用 device_add():将设备添加到内核的设备层次结构中。 与总线关联,触发设备与驱动的匹配流程。

device_add() 是 Linux 内核设备模型中的核心函数,用于将设备添加到内核的设备层次结构中,并与总线关联,触发设备与驱动的匹配流程。它的功能可以分为以下几个关键步骤:


1. 函数原型


C

int device_add(struct device *dev)

  • dev:指向设备结构体的指针,描述设备的各种信息(如总线类型、设备名称、父设备等)。

2. device_add() 的主要功能

device_add() 的主要职责是:

  1. 将设备注册到内核的设备模型中,建立设备的层次结构(如父子关系)。
  2. 将设备与所属总线关联,触发设备与驱动的匹配流程。
  3. 为设备创建相关的用户空间接口(如 sysfs 文件)。
  4. 通知用户空间(如 udev)有新设备被添加。

3. 函数实现详细解析

以下是 device_add() 的核心代码逻辑逐步解析:

a. 检查设备的必要字段

C

if (!dev->p || !dev->bus) return -EINVAL;

  • 检查设备结构体中是否初始化了必要字段:
    • dev->p:设备的私有数据(struct device_private)。
    • dev->bus:设备所属的总线类型(如 platform_bus_type)。
  • 如果这些字段未初始化,则返回错误。

b. 设置设备的层次关系

C

dev->parent = get_device(dev->parent); kobject_set_name(&dev->kobj, dev_name(dev));

  • 设置父设备

    • 如果该设备有父设备(dev->parent),通过 get_device() 设置父设备,确保父子关系正确。
    • 父设备通常是一个表示设备容器的设备(如 PCI 总线设备、platform 总线设备等)。
  • 设置设备的 kobject 名字

    • kobject_set_name() 给设备的内核对象(kobject)设置一个唯一名字,通常是设备的名字(dev_name(dev))。

c. 添加设备到内核设备模型

C

ret = kobject_add(&dev->kobj, dev->parent ? &dev->parent->kobj : NULL, dev_name(dev)); if (ret < 0) goto Error;

  • kobject_add()

    • 将设备的 kobject 添加到内核的设备层次结构中。
    • 如果设备有父设备,则将其添加到父设备的 kobject 子结构中。
    • kobject 是内核对象的基础结构,用于管理设备的生命周期和 sysfs 挂载点。
  • 错误处理

    • 如果 kobject_add() 失败,则释放资源并返回错误。

d. 与总线关联

C

ret = bus_add_device(dev); if (ret) goto Error;

  • bus_add_device()
    • 将设备添加到设备所属总线(dev->bus)中。
    • 这一步是设备与总线关联的关键,触发设备与驱动的匹配流程。

e. 自动匹配设备与驱动

bus_add_device() 内部会调用 device_attach(),触发设备与驱动的匹配流程:


C

int bus_add_device(struct device *dev) { struct bus_type *bus = dev->bus; int error = 0; // 自动驱动匹配 if (bus->p->drivers_autoprobe) { error = device_attach(dev); // 关键步骤 } return error; }

device_attach()
  • device_attach() 是设备模型中用于尝试匹配设备和驱动的函数。
  • 它会遍历总线上的所有驱动,调用驱动的 match() 函数检查是否与设备匹配。

关键代码:


C

int device_attach(struct device *dev) { return __device_attach(dev, false); } static int __device_attach(struct device *dev, bool allow_async) { struct bus_type *bus = dev->bus; struct device_driver *drv; // 遍历总线上的所有驱动 if (bus && bus->match) { list_for_each_entry(drv, &bus->p->drivers_list, p->drivers_list) { if (bus->match(dev, drv)) { // 匹配设备与驱动 ret = driver_probe_device(drv, dev); // 绑定驱动到设备 break; } } } return ret; }

驱动匹配逻辑
  • 总线提供 match() 函数,用来匹配设备与驱动。
  • 对于 platform_bus,匹配逻辑通常是设备名字和驱动名字是否相同。

匹配函数:


C

int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); // 匹配设备名字和驱动名字 return strcmp(pdev->name, pdrv->driver.name) == 0; }

如果匹配成功,就会调用 driver_probe_device(),将驱动绑定到设备。


f. 创建用户空间接口

C

ret = device_add_groups(dev, dev->groups); if (ret) goto Error;

  • device_add_groups()
    • 为设备创建相关的属性组(sysfs 文件)。
    • 用户空间可以通过 sysfs 访问设备的属性。

g. 通知用户空间

C

kobject_uevent(&dev->kobj, KOBJ_ADD);

  • kobject_uevent()
    • 向用户空间发送 KOBJ_ADD 事件,通知设备被添加。
    • 用户空间工具(如 udev)可以捕获这个事件,进行设备的管理和初始化。

4. 总结流程

device_add() 的完整流程如下:

  1. 检查设备结构体是否有效。
  2. 设置设备的层次关系(父设备、名字等)。
  3. 将设备的 kobject 添加到内核设备模型。
  4. 与总线关联,调用 bus_add_device()
  5. 自动匹配设备与驱动,调用 device_attach()
  6. 为设备创建用户空间属性接口(sysfs 文件)。
  7. 发送 KOBJ_ADD 事件,通知用户空间设备已添加。

通过这些步骤,设备被完整地注册到内核的设备模型中,并与总线和驱动程序建立了关联。

bus->p->drivers_autoprobe 这个是什么类型?

🤖

在 Linux 内核中,bus->p->drivers_autoprobe 是一个布尔类型(bool),表示是否允许总线自动探测驱动程序与设备之间的匹配关系。


1. 定义位置

drivers_autoprobe 是在 struct bus_type_private 中定义的,bus->p 是指向总线的私有数据结构(struct bus_type_private)的指针。


C

struct bus_type_private { struct kset subsys; // 总线的 kobject 集合 struct kset drivers_kset; // 驱动的 kobject 集合 struct kset devices_kset; // 设备的 kobject 集合 struct klist klist_devices; // 总线上的设备列表 struct klist klist_drivers; // 总线上的驱动列表 bool drivers_autoprobe; // 是否启用驱动自动探测 };

drivers_autoprobe 是一个布尔变量,用来控制是否允许总线自动探测驱动与设备的匹配关系。


2. 作用

drivers_autoprobe 的主要作用是在设备注册到总线时,控制是否自动调用 device_attach() 来尝试匹配设备和驱动。

当 drivers_autoprobe 为 true 时:

  • 内核会在设备注册到总线时自动调用 device_attach()
  • device_attach() 会遍历总线上的所有驱动,尝试找到与设备匹配的驱动,并调用驱动的 probe() 函数。

当 drivers_autoprobe 为 false 时:

  • 内核不会主动尝试匹配设备与驱动。
  • 驱动与设备的绑定需要开发者手动触发(通常是通过其他机制,如用户空间工具)。

3. 示例:在注册总线时初始化

drivers_autoprobe 的值通常在总线类型注册时初始化。以下是 platform_bus_type 的定义示例:


C

struct bus_type platform_bus_type = { .name = "platform", .match = platform_match, // 设备与驱动的匹配函数 .p = &(struct bus_type_private) { .drivers_autoprobe = true, // 启用自动探测驱动 }, };

在这里,platform_bus_type 的私有数据中,将 drivers_autoprobe 设置为 true,表示 platform 总线支持驱动与设备的自动匹配。


4. 使用场景

drivers_autoprobe 的典型使用场景包括:

  • 自动匹配驱动
    如果 drivers_autoprobe = true,当设备通过 device_add() 被注册到总线时,内核会自动尝试匹配驱动。
  • 手动匹配驱动
    如果 drivers_autoprobe = false,设备注册到总线后不会自动匹配,开发者需要手动调用相关函数(如 device_attach())来触发匹配逻辑。

5. 结合 bus_add_device() 的逻辑

当 drivers_autoprobe 为 true 时,device_add() 调用 bus_add_device() 会自动调用 device_attach(),进入设备与驱动的匹配流程:


C

int bus_add_device(struct device *dev) { struct bus_type *bus = dev->bus; int error = 0; // 如果总线允许自动探测,则尝试匹配驱动 if (bus->p->drivers_autoprobe) { error = device_attach(dev); // 自动匹配设备与驱动 } return error; }


6. 小结

  • bus->p->drivers_autoprobe 是一个布尔变量(bool),定义在 struct bus_type_private 中。
  • 它用于控制是否允许总线自动探测设备与驱动的匹配关系。
  • 典型值:
    • true:允许自动探测驱动。
    • false:禁止自动探测驱动,需要手动匹配设备与驱动。

这一机制为总线提供了灵活性,允许开发者根据具体的需求控制驱动匹配行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值