device_add() 是 Linux 内核设备模型中的核心函数,用于将设备添加到内核的设备层次结构中,并与总线关联,触发设备与驱动的匹配流程。它的功能可以分为以下几个关键步骤:
1. 函数原型
C
int device_add(struct device *dev)
dev:指向设备结构体的指针,描述设备的各种信息(如总线类型、设备名称、父设备等)。
2. device_add() 的主要功能
device_add() 的主要职责是:
- 将设备注册到内核的设备模型中,建立设备的层次结构(如父子关系)。
- 将设备与所属总线关联,触发设备与驱动的匹配流程。
- 为设备创建相关的用户空间接口(如
sysfs文件)。 - 通知用户空间(如
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() 的完整流程如下:
- 检查设备结构体是否有效。
- 设置设备的层次关系(父设备、名字等)。
- 将设备的
kobject添加到内核设备模型。 - 与总线关联,调用
bus_add_device()。 - 自动匹配设备与驱动,调用
device_attach()。 - 为设备创建用户空间属性接口(
sysfs文件)。 - 发送
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:禁止自动探测驱动,需要手动匹配设备与驱动。
这一机制为总线提供了灵活性,允许开发者根据具体的需求控制驱动匹配行为。
667

被折叠的 条评论
为什么被折叠?



