11、设备模型:device_driver
设备驱动的重要知识点
- 结构体:device_driver
- 结构体:driver_attribute
- 驱动的注册即可driver_register
- 驱动属性文件driver_create_file
一、驱动注册过程分析driver_register
drivers/base/driver.c
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
// 检查总线是否已初始化
if (!drv->bus->p) {
pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
drv->name, drv->bus->name);
return -EINVAL;
}
// 检查驱动程序的方法是否需要更新
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
// 检查驱动程序是否已被注册
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv); // 将驱动程序添加到总线
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);// 添加驱动程序的组属性
if (ret) {
bus_remove_driver(drv);// 移除已添加的驱动程序
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);// 发送内核对象事件, 通知驱动程序添加成功
return ret;// 返回注册结果
}
EXPORT_SYMBOL_GPL(driver_register);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
driver_register函数的作用是把设备驱动程序添加到对应的总线中,具体流程如下:
首先,它会检查总线是否已经正常初始化。如果驱动关联的总线内部指针为"空",说明总线还没准备好,就会报错并返回"参数无效"(-EINVAL)。
接着,它会检查驱动的方法是否需要升级。如果发现驱动和总线的probe/remove/shutdown方法同时存在冲突,就会提示开发者需要更新驱动代码,用总线提供的标准方法代替。
然后,它会检查是否已经有同名驱动被注册过。通过查找总线中的驱动列表,如果发现重复名称,就会报错并返回"设备忙"(-EBUSY)。
确认无误后,函数会正式将驱动添加到总线中。如果这一步失败,比如资源不足,就会直接返回对应的错误码。
成功添加后,会继续注册驱动的属性接口。如果属性注册失败,就会立即撤销之前的操作,把驱动从总线中移除并返回错误。
最后,系统会发送一个事件通知,告诉其他模块该驱动已经成功加入系统。
整个过程就像给设备驱动办"入网手续":先检查材料是否齐全,再确认没有重复申请,最后完成注册并通知相关部门。每个步骤都会在出错时及时终止流程并反馈具体问题。
二、bus_add_driver
在上面代码中, 调用 bus_add_driver 函数将驱动程序添加到总线。 我们来详细分析下bus_add_driver 函数。
drivers/base/bus.c
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
// 获取总线对象
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;// 返回无效参数错误码
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
// 分配并初始化驱动程序私有数据
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
// 初始化并添加驱动程序的内核对象
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
// 将驱动程序添加到总线的驱动程序列表
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
if (drv->bus->p->drivers_autoprobe) {//默认就是1
error = driver_attach(drv);
if (error)
goto out_unregister;
}
// 将驱动程序添加到模块
module_add_driver(drv->owner, drv);
// 创建驱动程序的 uevent 属性文件
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
// 添加驱动程序的组属性
error = driver_add_groups(drv, bus->drv_groups);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
__func__, drv->name);
}
// 如果驱动程序不禁止绑定属性文件, 则添加绑定属性文件
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
/* drv->p is freed in driver_release() */
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2.1、driver_attach
在上述函数中, 使用 driver_attach 函数来探测设备, 我们进一步分析下 driver_attach 函数。
目录:base/dd.c
/**
* driver_attach - try to bind driver to devices.
* @drv: driver.
*
* Walk the list of devices that the bus has on it and try to
* match the driver with each one. If driver_probe_device()
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
2
3
4
5
6
7
8
9
10
11
12
13
14
bus_for_each_dev 函数实现如下所示:
目录:base/bus.c
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
// 检查总线对象是否存在
if (!bus || !bus->p)
return -EINVAL; // 返回无效参数错误码
// 初始化设备列表迭代器
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
// 遍历设备列表并执行指定的函数
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
// 退出设备列表迭代器
klist_iter_exit(&i);
return error;// 返回执行过程中的错误码(如果有)
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
以下是更直白的改写版本:
这个函数的作用是逐个检查指定总线上的所有设备,并对每个设备运行一个特定操作。以下是关键点说明:
参数说明:
- 总线(bus):要检查的设备总线
- 起始设备(start):从哪个设备开始检查。如果设为"空",就从第一个设备开始
- 附加数据(data):传递给操作函数的额外信息
- 操作函数(fn):要执行的核心操作,需要接收设备和附加数据,返回错误代码
执行流程:
参数检查:先检查总线和它的数据是否存在,如果不存在就返回错误代码
准备遍历:初始化一个设备列表的遍历工具,准备好逐个处理设备
逐个处理:
- 获取下一个设备
- 运行操作函数(比如
__driver_attach
) - 如果操作出错,记录错误码并停止处理
清理工作:处理完所有设备后,释放遍历工具占用的资源
核心功能: 这个函数就像一个自动化设备处理助手,能帮驱动程序快速完成"检查所有设备→对每个设备做某事→处理完自动收尾"这类常见操作。特别适合需要统一管理多个设备的场景,比如设备驱动初始化时的批量操作。
补充说明: 这里用到的操作函数通常是__driver_attach
,它的作用是把设备和驱动程序进行匹配绑定,类似"把正确的钥匙插入对应的锁孔"。
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;// 传入的数据参数作为设备驱动对象
int ret;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
ret = driver_match_device(drv, dev);// 尝试将驱动程序绑定到设备上
if (ret == 0) {
/* no match */
return 0;// 如果没有匹配, 则返回 0
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
driver_deferred_probe_add(dev);// 请求推迟探测设备
} else if (ret < 0) {
dev_dbg(dev, "Bus failed to match device: %d", ret);
return ret;// 总线无法匹配设备, 返回错误码
} /* ret > 0 means positive match */
if (driver_allows_async_probing(drv)) {
/*
* Instead of probing the device synchronously we will
* probe it asynchronously to allow for more parallelism.
*
* We only take the device lock here in order to guarantee
* that the dev->driver and async_driver fields are protected
*/
dev_dbg(dev, "probing driver %s asynchronously\n", drv->name);
device_lock(dev);// 锁定设备以保护 dev->driver 和 async_driver 字段
if (!dev->driver) {
get_device(dev);
dev->p->async_driver = drv;// 设置设备的异步驱动程序
async_schedule(__driver_attach_async_helper, dev);// 异步调度驱动程序的附加处理函数
}
device_unlock(dev);// 解锁设备
return 0;
}
device_driver_attach(drv, dev);// 同步探测设备并绑定驱动程序
return 0;// 返回 0 表示成功执行驱动程序附加操作
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
上述函数中使用 driver_match_device 函数尝试将驱动程序绑定到设备上, 我们再来看看driver_match_device 函数。
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
2
3
4
5
以下是更直白的解释:
这个driver_match_device
函数用来判断设备和驱动是否匹配。它的工作流程如下:
检查总线是否有匹配函数 首先看设备所在的总线(比如USB、PCI等)是否提供了自己的匹配规则(
match
函数)。- 如果总线有这个规则: 就用这个规则来对比设备和驱动,看看是否匹配。
- 如果总线没提供这个规则: 直接认为设备和驱动是匹配的(默认返回成功)。
执行匹配判断
- 如果总线的
match
函数返回了"0"(不匹配): 整个函数直接返回0,表示不匹配。 - 如果返回了非零值(比如1,表示匹配): 函数返回1,表示匹配成功。
- 如果总线的
后续操作 当设备和驱动匹配成功后,会继续执行
device_driver_attach
函数,完成驱动和设备的绑定。
简单总结: 函数先看总线有没有自己的匹配规则,有的话就用规则检查,没有的话默认认为匹配。根据检查结果返回0或1,决定是否继续后续操作。
/**
* device_driver_attach - attach a specific driver to a specific device
* @drv: Driver to attach
* @dev: Device to attach it to
*
* Manually attach driver to a device. Will acquire both @dev lock and
* @dev->parent lock if needed.
*/
int device_driver_attach(struct device_driver *drv, struct device *dev)
{
int ret = 0;
__device_driver_lock(dev, dev->parent);
/*
* If device has been removed or someone has already successfully
* bound a driver before us just skip the driver probe call.
*/
if (!dev->p->dead && !dev->driver)
ret = driver_probe_device(drv, dev);
__device_driver_unlock(dev, dev->parent);
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
driver_probe_device 函数, 如下所示:
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* This function returns -ENODEV if the device is not registered,
* 1 if the device is bound successfully and 0 otherwise.
*
* This function must be called with @dev lock held. When called for a
* USB interface, @dev->parent lock must be held as well.
*
* If the device has a parent, runtime-resume the parent before driver probing.
*/
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
// 检查设备是否已注册, 如果未注册则返回错误码 -ENODEV
if (!device_is_registered(dev))
return -ENODEV;
// 打印调试信息, 表示设备与驱动程序匹配
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
// 获取设备供应商的运行时引用计数
pm_runtime_get_suppliers(dev);
// 如果设备有父设备, 获取父设备的同步运行时引用计数
if (dev->parent)
pm_runtime_get_sync(dev->parent);
// 等待设备的运行时状态达到稳定
pm_runtime_barrier(dev);
// 根据初始化调试标志选择调用真实的探测函数
if (initcall_debug)
ret = really_probe_debug(dev, drv);
else
ret = really_probe(dev, drv);
// 请求设备进入空闲状态(省电模式)
pm_request_idle(dev);
// 如果设备有父设备, 释放父设备的运行时引用计数
if (dev->parent)
pm_runtime_put(dev->parent);
// 释放设备供应商的运行时引用计数
pm_runtime_put_suppliers(dev);
// 返回探测函数的执行结果
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
经过前面代码的分析, 总结设备和驱动匹配流程函数, 如下图所示
To be determined
暂时无法在飞书文档外展示此内容
probe 函数的执行, 我们来分析 really_probe 函数。
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = -EPROBE_DEFER;// 初始化返回值为延迟探测
// 获取当前延迟探测计数
int local_trigger_count = atomic_read(&deferred_trigger_count);
// 判断是否启用了驱动移除测试
bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
!drv->suppress_bind_attrs;
if (defer_all_probes) {
/*
* defer_all_probes 的值只能通过 device_defer_all_probes_enable() 设置,
* 而该函数会紧接着调用 wait_for_device_probe(), 以避免任何竞争情况。
*/
dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
driver_deferred_probe_add(dev);
return ret;
}
ret = device_links_check_suppliers(dev);// 检查设备的供应者链路
if (ret == -EPROBE_DEFER)
driver_deferred_probe_add_trigger(dev, local_trigger_count);// 将设备添加到延迟探测触发列表
if (ret)
return ret;
atomic_inc(&probe_count);// 增加探测计数
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
if (!list_empty(&dev->devres_head)) {
dev_crit(dev, "Resources present before probing\n");
ret = -EBUSY;
goto done;
}
re_probe:
dev->driver = drv;
/* 如果使用了 pinctrl, 绑定引脚 */
ret = pinctrl_bind_pins(dev);
if (ret)
goto pinctrl_bind_failed;
ret = dma_configure(dev);// 配置 DMA
if (ret)
goto probe_failed;
if (driver_sysfs_add(dev)) {// 添加驱动的 sysfs
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->pm_domain && dev->pm_domain->activate) {
// 如果设备有电源管理域并且存在激活函数,激活电源管理域
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
// 如果总线有探测函数, 调用总线的探测函数
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
// 否则调用驱动的探测函数
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
// 如果启用了驱动移除测试
if (test_remove) {
test_remove = false;
// 如果总线有移除函数, 调用总线的移除函数
if (dev->bus->remove)
dev->bus->remove(dev);
// 否则调用驱动的移除函数
else if (drv->remove)
drv->remove(dev);
// 释放设备的资源
devres_release_all(dev);
// 移除驱动的 sysfs
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
// 如果设备有电源管理域并且存在解除函数, 解除电源管理域
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
// 重新初始化电源管理运行时
pm_runtime_reinit(dev);
// 重新进行探测
goto re_probe;
}
// 完成 pinctrl 的初始化
pinctrl_init_done(dev);
// 如果设备有电源管理域并且存在同步函数, 同步电源管理域
if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);
// 驱动绑定成功
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
pinctrl_bind_failed:
device_links_no_driver(dev);
devres_release_all(dev);
dma_deconfigure(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);
switch (ret) {
case -EPROBE_DEFER:
/* Driver requested deferred probing */
dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add_trigger(dev, local_trigger_count);
break;
case -ENODEV:
case -ENXIO:
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
break;
default:
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up_all(&probe_waitqueue);
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146