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 },
{ }
};