i2c设备与驱动的匹配,既可以在i2c设备注册时匹配相应驱动,也可以在i2c驱动注册时匹配相应设备,本文将从i2c设备注册匹配分析、i2c驱动注册匹配分析两方面着手。
1.i2c设备注册匹配分析
前文提到,i2c适配器在注册时会扫描设备树上的i2c设备,也在扫描静态(代码中)添加的i2c设备,最后都是通过i2c_new_device函数将i2c设备注册。
我们即从i2c_new_device函数(driver/i2c/i2c-core-base.c)开始分析:
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
client->flags = info->flags;
client->addr = info->addr;
client->init_irq = info->irq;
client->irq = client->init_irq;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = of_node_get(info->of_node);
client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client, info);
status = device_register(&client->dev);
}
这个函数将info结构体中的信息填充到 i2c_client 结构体,并且设置了 device 成员内部成员属性。值得注意的是,它设置了 divece.type 为 i2c_client_type,表明这个设备是iic设备,在前面提到iic设备和iic适配器都会通过device挂载到iic总线,它们之间区分便通过这个属性。
函数最后调用了 device_register 将设备注册到系统,注意看,这里只传入了 i2c_client 结构体中的 device 成员,device_register 直接调用了device_add,前文分析i2c适配器的时将要分析过此函数,现在在详细分析下:
int device_add(struct device *dev)
{
device_add_class_symlinks(dev);//创建类符号链接,相互创建dev和class之间的连接文件
device_add_attrs(dev);//创建sys目录下设备其他属性文件 ,添加设备属性文件
bus_add_device(dev);//将设备添加到对应总线
dpm_sysfs_add(dev);//电源管理相关
device_pm_add(dev);//添加设备到激活设备列表中,用于电源管理
if (dev->bus)//呼唤通知链表,通知注册监听该总线的设备,有新设备加入
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);
//产生一个内核uevent事件,该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);//在总线上寻找对应的driver
}
其中bus_add_device函数将设备添加到总线设备链表中
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
if (bus) {
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
}
bus_probe_device函数:
void bus_probe_device(struct device *dev)
{
if (bus->p->drivers_autoprobe)// drivers_autoprobe为1,在bus_register函数赋值
device_initial_probe(dev);
}
继续分析device_initial_probe –>__device_attach:
static int __device_attach(struct device *dev, bool allow_async)
{
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
}
将设备与总线上所有driver进行匹配,匹配函数是 __device_attach_driver ,其代码简化如下:
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
if (dev->driver)//如果设备已经匹配,则返回
return -EBUSY;
ret = driver_match_device(drv, dev);//检测设备与驱动是否匹配,返回大于0表示匹配,其它则进行下一个driver匹配!
if (ret == 0) {
/* no match */
return 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 */
return driver_probe_device(drv, dev);//匹配成功后,执行总线的probe函数,然后再调用i2c驱动的probe函数
}
先看 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指针是否为空,不为空则执行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,
};
所以将会执行 i2c_device_match 函数,代码如下:
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);//判断当前device是不是iic设备,是的话返回i2c_client结构体
struct i2c_driver *driver;
/* Attempt an OF style match */ //使用drv内部的of_match_table列表来匹配,一般使用设备树注册设备时才会使用这种匹配
if (i2c_of_match_device(drv->of_match_table, client))用设备树中的 compatible 属性和i2c驱动driver成员中的 of_match_table 来匹配
return 1;
/* Then ACPI style match */ //ACPI专用匹配,较少用不关心
if (acpi_driver_match_device(dev, drv))
return 1;
driver = to_i2c_driver(drv);
/* Finally an I2C match */
if (i2c_match_id(driver->id_table, client)) // match_id匹配方式
return 1;
return 0;
}
struct i2c_client *i2c_verify_client(struct device *dev)
{
return (dev->type == &i2c_client_type)? to_i2c_client(dev): NULL;
}//这个函数很有意思,它会先判断这个device是不是iic设备,对应之前提到的iic适配器(iic控制器)也会作为设备挂在总线上!所以当iic适配器注册到总线时候,虽然也会调用总线匹配函数,但直接就结束了!
可以看到i2c_device_match给出了3中设备与驱动的匹配方式,相比于平台总线匹配,少了一种通过设备名字与驱动名字匹配的方式,所以iic驱动中的名字不重要!
- 第一种是设备树类型的匹配方式,device_driver 结构体(表示设备驱动)中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数就会执行。
- 第二种不关注
- 第三种是id的匹配方式,每个i2c_driver结构体有一个 id_table成员变量,顾名思义,保存了很多 id 信息,用于匹配。
如果匹配成功,那么回到之前的__device_attach_driver函数中,会继续执行driver_probe_device函数:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
if (initcall_debug)
ret = really_probe_debug(dev, drv);
else
ret = really_probe(dev, drv);
}
主要看下really_probe函数,简化如下:
static int really_probe(struct device *dev, struct device_driver *drv)
{
dev->driver = drv;//将设备中驱动指针指向匹配到的驱动!
if (dev->bus->probe) {//执行总线的probe函数!
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);//驱动绑定设备
}
可以看到这里执行了i2c_bus_type中的.probe函数,对应的是i2c_device_probe函数,简化后如下:
static int i2c_device_probe(struct device *dev)
{
if (driver->probe_new)
status = driver->probe_new(client);
else if (driver->probe)
status = driver->probe(client, i2c_match_id(driver->id_table, client));
}
先判断iic驱动中是否使用 probe_new ,如果没有则执行当前的 probe 函数,也就是iic驱动中自己写的probe函数!
2.i2c驱动匹配分析
驱动注册使用的是 i2c_add_driver 宏,它的定义是:
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)
接着看i2c_register_driver:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
driver->driver.bus = &i2c_bus_type;//添加总线
res = driver_register(&driver->driver);//驱动注册核心函数,注意只传入了driver成员
/* 遍历所有挂在总线上的iic适配器,用它们去探测driver中指定的iic设备地址列表 */
i2c_for_each_dev(driver, __process_new_driver);
}
i2c_for_each_dev 函数是用于探测未注册的设备,并将其注册后匹配,我们一般很少用到它这里不展开讲解。我们一般使用的是 driver_register 函数,其代码简化后如下:
int driver_register(struct device_driver *drv)
{
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);
}
将驱动添加至总线使用 bus_add_driver 函数,其代码简化后如下
int bus_add_driver(struct device_driver *drv)
{
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);//将驱动添加到总线
if (drv->bus->p->drivers_autoprobe) {
if (driver_allows_async_probing(drv)) {
pr_debug("bus: '%s': probing driver %s asynchronously\n",drv->bus->name, drv->name);
async_schedule(driver_attach_async, drv);
} else {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
}
}
将驱动添加到总线后执行 driver_attach 函数,其代码(简化)如下:
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *))
{
klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i))) //遍历总线上所有设备,分别执行__driver_attach函数
error = fn(dev, data);
}
从上面可以看出,驱动加载后会去遍历总线上所有设备,分别执行 __driver_attach 函数,其代码简化后如下:
static int __driver_attach(struct device *dev, void *data)
{
ret = driver_match_device(drv, dev);//调用总线的match函数,检测设备与驱动是否匹配,返回大于0表示匹配,其它则进行下一个device匹配!
if (ret == 0) {
/* no match */
return 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 (!dev->driver)//如果匹配成功,则要判断这个设备是否已经绑定了驱动,举个例子,之前有个设备已经和别的驱动绑定了,但同时也支持这个驱动!
driver_probe_device(drv, dev);//匹配成功后,执行总线的probe函数,然后再调用i2c驱动的probe函数(自己写的probe)
}
__driver_attach的代码看起来是不是很熟悉?没错,和__device_attach_driver函数基本一致。都是先去调用i2c总线的match函数看看设备与驱动是否匹配,如果匹配就调用i2c总线的probe函数,然后在总线的probe函数中调用i2c驱动的probe函数,在我们自己写的probe函数中注册字符设备,创建设备节点,实现fops集等等,这里就不再多讲解了。.
参考文章:
【linux iic子系统】i2c设备与驱动匹配过程(三)_i2c_add_driver 如何和clent匹配-CSDN博客