03、platform_device 如何与 platform_driver 配对
从设备树转换得来的 platform_device 会被注册进内核里,以后当我们每 注册一个 platform_driver 时,它们就会调用 platform_match 确定能否配对,如果能配对成功 就调用 platform_driver 的 probe 函数。
C
static 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);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
一、强制绑定机制
如果设备设置了 driver_override,则只与名称完全匹配的驱动绑定
这是一种强制绑定机制,优先级最高
C
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
1
2
3
2
3
二、设备树匹配
尝试使用设备树(Open Firmware)风格的匹配
检查设备树节点与驱动的兼容性字符串是否匹配
C
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
1
2
3
2
3
of_driver_match_device 函数实现如下:
C
/**
* of_driver_match_device - Tell if a driver's of_match_table matches a device.
* @drv: the device_driver structure to test
* @dev: the device structure to match against
*/
static inline int of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
return of_match_device(drv->of_match_table, dev) != NULL;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
of_match_device 是 Linux 内核中用于检查设备是否与设备树匹配表(of_device_id)匹配的关键函数。
C
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
if ((!matches) || (!dev->of_node))
return NULL;
return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
C
/**
* of_match_node - Tell if a device_node has a matching of_match structure
* @matches: array of of device match structures to search in
* @node: the of device structure to match against
*
* Low level utility function used by device matching.
*/
const struct of_device_id *of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
const struct of_device_id *match;
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
match = __of_match_node(matches, node);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return match;
}
EXPORT_SYMBOL(of_match_node);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__of_match_node 中 最后会调用__of_device_is_compatible
C
static int __of_device_is_compatible(const struct device_node *device,
const char *compat, const char *type, const char *name)
{
struct property *prop;
const char *cp;
int index = 0, score = 0;
/* Compatible match has highest priority */
if (compat && compat[0]) {
prop = __of_find_property(device, "compatible", NULL);
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
score = INT_MAX/2 - (index << 2);
break;
}
}
if (!score)
return 0;
}
/* Matching type is better than matching name */
if (type && type[0]) {
if (!device->type || of_node_cmp(type, device->type))
return 0;
score += 2;
}
/* Matching name is a bit better than not */
if (name && name[0]) {
if (!device->name || of_node_cmp(name, device->name))
return 0;
score++;
}
return score;
}
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
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
三、id_table ID表匹配
检查驱动是否提供了 ID 表(id_table)
如果有,则尝试在 ID 表中查找匹配的设备 ID
C
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
1
2
3
2
3
重点platform_match_id分析
C
static const struct platform_device_id *platform_match_id(
const struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
四、匹配名字
最后回退到简单的名称匹配
比较设备名称和驱动名称是否完全相同
C
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
1
2
2