版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/88909555
I2C核心(drivers/i2c/i2c-core.c) 中提供了一组不依赖于硬件平台的接口函数, 这个文件一般不需要被工程师修改, 但是理解其中的主要函数非常关键, 因为I2C总线驱动和设备驱动之间以I2C核心作为纽带。
首先看一下i2c核心层的创建函数。
static int __init i2c_init(void);
static void __exit i2c_exit(void);
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}
static void __exit i2c_exit(void)
{
i2c_del_driver(&dummy_driver);
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
#endif
bus_unregister(&i2c_bus_type);
}
postcore_initcall(i2c_init);
module_exit(i2c_exit);
这里要注意的是总线的注册
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
总线提供的match方法:match方法用来进行 device 和driver 的匹配,在向总线注册设备或是驱动的的时候会调用此方法。
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;
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 i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
用struct i2c_client 来描述一个具体的IIC设备,这里指的是client device 。
如果IIC驱动的id_table 存在的话,使用i2c_match_id 进行函数进行匹配。匹配的方法是拿id_table 中的每一项与client 的name 进行匹配,如果名字相同则匹配成功。从这里我们可以看出IIC总线的匹配方式与platform 总线的匹配方式是不同的:
- IIC总线根据设备名字和驱动中的id_table进行匹配(这里adapoer类似设备驱动)
- platform总线根据设备名字和设备驱动名字进行匹配
总线提供的probe方法:probe方法在完成设备和驱动的配对之后调用执行。
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver; //设备匹配到驱动后,把驱动绑定在设备上
if (!device_can_wakeup(&client->dev)) //提供了电源管理相关的则,设置相关标志
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
/* 设备和驱动匹配上后,执行驱动程序的probe函数 */
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
/* 当然这里这里的probe函数,有个参数是匹配表的某一项
* 因为一个驱动通常可以支持多个设备,每个设备可能有自己一些特殊的设置,
* 这里用匹配表刚好也可进行参数传递
*/
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
/* 这个就是我在内核中搜到的匹配表的的其中之一,
* 第一个参数名字用来和设备匹配
* 第二个参数刚好用来给驱动程序传递参数
*/
static const struct i2c_device_id adm1021_id[] = {
{ "adm1021", adm1021 },
{ "adm1023", adm1023 },
{ "max1617", max1617 },
{ "max1617a", max1617a },
{ "thmc10", thmc10 },
{ "lm84", lm84 },
{ "gl523sm", gl523sm },
{ "mc1066", mc1066 },
{ }
};