I2C-CORE为linux内核中i2c提供构架核心机制,提供了管理各种重要i2c要素的接口函数,例如struct i2c_adapter、struct i2c_driver等驱动的注册和注销接口,这些重要接口我们将在下一节中介绍。另外就是它还在内核中注册i2c-core时有所作为。在i2c-core.c中有一个i2c_init函数,这个函数在开始了linux内核中i2c的新纪元。首先我们来看看这个函数的源码:
static int __init i2c_init(void)
{
int retval;
retval = bus_register(&i2c_bus_type); //(1)
if (retval)
return retval;
retval = class_register(&i2c_adapter_class); //(2)
if (retval)
goto bus_err;
retval = i2c_add_driver(&dummy_driver); //(3)
if (retval)
goto class_err;
return 0;
class_err:
class_unregister(&i2c_adapter_class);
bus_err:
bus_unregister(&i2c_bus_type);
return retval;
}
从代码中我们很容易看到,这段代码做了三件事情:首先他注册了一种叫i2c_bus_type类型的总线,其次在内核中注册了一种类i2c_adapter_class,最后向内核中加入了一个i2c_driver(dummy_driver),下面我们分别看看这三件事情都做了什么。
总线是新的驱动模型中幕后英雄,为设备和驱动的配匹在内核中一直默默无闻的奋斗着。每当总线上有设备加入或者内核总有驱动加入时它就准备为他们配匹,而当设备卸掉或者驱动从内核总移除时也总是它出来完成善后工作。相对于《非诚勿扰》等类似的工作单位或机构,内核中总线的机制的精神是值得我们推崇的。I2c总线的定义如下:
struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
当内核中同过调用device_register()或者driver_register()向内核中注册设备或者驱动总线就发挥作用。其调用路线图如下:
上图大致描述出设备加入或者驱动加入时内核模型进行的一一系列动作。在bus_for_each_xxx后说明对在每个设备(驱动)加入时,对总线上存在的每个驱动(设备)调用__device_attach(__driver_attach)操作,也就是先调用总线的match函数,match成功后通用总线的probe函数进行总线资源的探测,最后的驱动自身的probe。这里为什么要调用两个probe,总线中probe就想到非诚勿扰中队人品,性格类得探测,驱动中的probe就相当于对一些隐私性的探测,你懂的。
下面我们看看在i2c驱动和设备配匹中发挥关键性作用的两个函数i2c_device_match,和 i2c_device_probe。
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(drv);
/* make legacy i2c drivers bypass driver model probing entirely;
* such drivers scan each i2c adapter/bus themselves.
*/
if (!is_newstyle_driver(driver))
return 0;
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
前面章节中我们已经说过i2c_driver有两种风格的,用到总线类型中probe是新的类型也是以后驱动的发展方向。如驱动中有driver->id_table就调用i2c_match_id。
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;
}
可见i2c中驱动与设备的配匹就看struct i2c_client中的name在struct i2c_driver中是否能找到相应的对照。设备配匹成功以后就调用总线的bus作进一步的探测 i2c_device_probe。
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
int status;
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");
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status)
client->driver = NULL;
return status;
}
该函数主要是绑定设备和驱动然后调用驱动自身的probe,没有过细节的探测。
其次就是向内核中注册了一个名为i2c_adapter的类和空的i2c_driver,这里就不做解释。