前言
Linux总线和设备都会注册到总线驱动上,然后在添加新驱动时,会进行匹配查找,将适合的设备和驱动进行绑定,并将设备和驱动注册显示在sys的层级结构种去。
当在使用i2c设备驱动时,遇到一个问题,一般会将定义 i2c_device_id 结构,注册到驱动的 .id_table 中去。再顶一个of_device_id,用于设备树风格的设备驱动匹配。按理来说,如果使用设备树匹配风格,那么不定义 i2c_device_id 结构应该也是可以的。但是在将 i2c_device_id 结构删除后,会发现驱动加载后,probe函数不会被执行。查看网上的说法,是i2c设备驱动必须要定义这个结构,否则就无法匹配。因此跟着跟了一下的注册匹配的流程。发现确实如此。
驱动注册流程
驱动时通过 module_i2c_driver 宏定义进行注册,跟下去发现调用的是 i2c_register_driver 函数。
i2c_register_driver
driver_register
bus_add_driver
driver_attach
bus_for_each_dev
driver_match_device
driver_probe_device
really_probe
dev->bus->probe / drv->probe
driver_bound
driver_deferred_probe_trigger
deferred_probe_work_func
bus_probe_device
device_initial_probe
__device_attach
device_bind_driver
__device_attach_driver
driver_match_device
driver_probe_device
really_probe
以上就是驱动注册的调用流程,驱动会执行驱动总线的match函数,来判断驱动和设备是否匹配。使用i2c设备驱动,那么应该就是使用i2c bus的match函数了。
i2c bus match
在 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;
}
调用的总线的match函数,在i2c总线中,应该是:
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
i2c_device_match:
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
这里会执行三钟方式的匹配,实际只有第一种和第三种有效果。第一种是使用设备树风格的匹配。实际调用:
static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
const struct of_device_id *best_match = NULL;
int score, best_score = 0;
if (!matches)
return NULL;
for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
score = __of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
if (score > best_score) {
best_match = matches;
best_score = score;
}
}
return best_match;
}
依次比较 compatible , type, name 属性,匹配上的会加分,最终得到一个综合得分:
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;
}
选择得分最高的作为最佳匹配。
当设备树匹配失败时,使用id_table 方式匹配。id_table 方式,只匹配id_table 中的name字段和 i2c client 的name。这里i2c client的name应该就是设备树中申明的设备的名称。因此,在声明id_table时,这里不应该使用和compitable属性一样的字符串,应该使用和设备树中设备节点名称一致的字符串。
比如,设备树中如下定义:
htu21d: htu21d@40 {
status = "okay";
compatible = "se,htu21d";
reg = <0x40>;
};
那么使用id_table应该这样定义:
static struct i2c_device_id htu21d_id_table[] = {
{"htu21d", 0}, /* error style: "se,htu21d" */
{ },
};
MODULE_DEVICE_TABLE(i2c, htu21d_id_table);
使用设备树匹配 of_device_id应该这样定义:
static struct of_device_id htu21d_of_match[] = {
{.compatible = "se,htu21d"},
{ },
};
MODULE_DEVICE_TABLE(of, htu21d_of_match);
做实验,可以尝试,只是用id_table进行匹配,会发现,当使用 “se,htu21d” 字符串作为 id_table 的name字段时,匹配是无法成功的,即probe函数不会执行。
分析到这,似乎只使用of_match_table 是可行的。为什么probe不执行呢?接着往下看。
执行 probe
在match成功后,将执行really_probe,其中如果 dev->bus->probe 存在,那么会执行 bus->probe,如果不存在,才会执行 drv->probe 函数。由于是使用的i2c bus驱动注册的,那么将使用i2c bus的probe函数。
问题就出在这儿。
在 i2c_device_probe 中:
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
将对驱动的 probe 函数指针和 id_table 指针进行判断,其中一个为
NULL,那么将直接返回 -ENODEV ,不会继续后面的流程,去执行 driver->probe 函数。因为它要在 driver->probe 中传入匹配的 id_table 项。
显然这是不合理的。如果是使用虚拟总线设备,那么只定义 of_device_id 是没有问题的。因为它将跳过 bus->probe,直接执行 drv->probe,不会有这个问题出现。虽然bus->probe 最终也会去调用 drv->probe。
查阅最新的内核 6.1.2,在 i2c-core-base.c 中,对应的4.14版本文件应该是 i2c-core.c 文件,其中的 i2c_device_probe 函数已经做了修改:
if (!driver->id_table &&
!acpi_driver_match_device(dev, dev->driver) &&
!i2c_of_match_device(dev->driver->of_match_table, client)) {
status = -ENODEV;
goto put_sync_adapter;
}
如果 id_table 为空时,还会检查 of_match_table ,如果都不匹配,才会返回失败。
并且,如果定义了 probe_new,那么也不需要 id_table 作为参数传递给 probe函数参数了。将使用新版的probe函数:
if (driver->probe_new)
status = driver->probe_new(client);
else if (driver->probe)
status = driver->probe(client,
i2c_match_id(driver->id_table, client));
else
status = -EINVAL;
那么在4.14版本中,id_table 属于是强制的。那么我们可以不使用它,但是给他提供一个空的定义,这样可以避免匹配失败的问题,匹配还是使用of_match_table。
2025/06/13:在6.6内核上,之前的驱动编译不过了。发现 probe_new 已经把 probe 取代了:
struct i2c_driver {
unsigned int class;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *client);
void (*remove)(struct i2c_client *client);
probe 函数指针的申明,已经去调了 id_table参数。

1709

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



