12、设备模型:devicer
device 重点知识点
结构体:device
- 是什么:内核中用来描述一个硬件设备的数据块。
- 作用:保存设备的基本信息(比如名称、类型、状态等),方便内核统一管理设备。
结构体:device_attribute
- 是什么:用来定义设备的一个属性(例如温度、状态、配置参数等)。
- 作用:通过它,内核可以自动为设备生成对应的控制文件(比如
/sys
目录下的文件),让用户或程序能方便地读写设备属性。
函数:device_register
- 做什么:把设备(即
device
结构体)注册到内核中。 - 简单说:告诉内核“这个设备存在了”,内核会接管后续的管理工作(比如匹配驱动、创建目录等)。
函数:device_create_file
- 做什么:为设备生成一个属性文件(比如
/sys
文件系统中的文件)。 - 简单说:通过这个函数,内核会自动生成一个文件,让用户能像操作普通文件一样读写设备的某个属性(例如
cat
或echo
命令)。
一、device_register 函数分析
device_register 函数原型定义在 drivers/base/core.c 文件中, 如下所示:
C++
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);
1
2
3
4
5
6
2
3
4
5
6
这个函数的作用是把设备添加到系统中。它接收一个设备对象作为参数,具体步骤如下:
首先初始化设备:调用device_initialize函数给设备对象做基础配置
然后正式添加设备:调用device_add函数将设备加入系统管理,这个过程会:
- 为设备分配唯一编号
- 在系统里创建对应的设备文件
- 完成其他必要的添加操作
最后返回结果:用数字表示操作是否成功(0代表成功,负数代表出错)
整个过程就像给新硬件办手续:先填好基本资料,再正式登记注册,最后看办理结果。
二、device_add
目录: drivers/base/core.c
在 drivers/base/core.c 文件中的 device_add 函数中调用了 bus_probe_device 函数。
C
int device_add(struct device *dev)
{
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (IS_ERR(kobj)) {
error = PTR_ERR(kobj);
goto parent_error;
}
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error) {
glue_dir = get_glue_dir(dev);
goto Error;
}
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev);
}
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
/*
* Check if any of the other devices (consumers) have been waiting for
* this device (supplier) to be added so that they can create a device
* link to it.
*
* This needs to happen after device_pm_add() because device_link_add()
* requires the supplier be registered before it's called.
*
* But this also needs to happen before bus_probe_device() to make sure
* waiting consumers can link to it before the driver is bound to the
* device and the driver sync_state callback is called for this device.
*/
if (dev->fwnode && !dev->fwnode->dev) {
dev->fwnode->dev = dev;
fw_devlink_link_device(dev);
}
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
done:
put_device(dev);
return error;
SysEntryError:
if (MAJOR(dev->devt))
device_remove_file(dev, &dev_attr_dev);
DevAttrError:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
device_remove_file(dev, &dev_attr_uevent);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
Error:
cleanup_glue_dir(dev, glue_dir);
parent_error:
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
EXPORT_SYMBOL_GPL(device_add);
1
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
2.1、创建设备属性文件:device_create_file
2.2、设备device与总线的关联
drivers/base/bus.c
以下是bus_add_device函数的步骤解释,用更简单易懂的语言重新组织:
- 获取总线信息 先通过设备记录里的总线信息找到对应的总线类型。这个操作会增加总线的"使用计数",确保总线在设备添加过程中不会被系统回收。
- 检查总线有效性 确认找到的总线类型是否有效,如果无效则停止后续操作。
- 记录操作日志 打印调试信息,显示总线名称和设备名称,方便开发者跟踪设备添加过程。
- 添加设备属性 把设备添加到总线的属性组里。这些属性组会在系统文件系统(sysfs)中生成设备的配置文件,供用户查看或修改设备参数。
- 创建总线到设备的链接 在总线目录下创建一个指向设备文件的快捷方式。这样用户可以通过总线目录直接访问设备的配置信息。
- 创建设备到总线的链接 在设备目录下创建一个指向总线管理目录的快捷方式。这建立了设备与总线之间的双向关联,方便系统管理。
- 加入总线设备列表 把设备正式加入到总线管理的设备列表中。这样总线就能随时知道所有连接的设备,进行统一管理。
2.3、bus_probe_device 匹配
bus_porbe_device
-->device_initial_probe
-->__device_attach
重点分析:__device_attach
Bus 中Match的调用
扫描: