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
:禁止自动探测驱动,需要手动匹配设备与驱动。
这一机制为总线提供了灵活性,允许开发者根据具体的需求控制驱动匹配行为。