在i2c-core架构中struct i2c_adapter和struct i2c_algorithm是为适配器服务的,也就是i2c总线控制器驱动。注册一个适配器驱动就是要把一个struct i2c_adapter 加入到内核中,前面我们说过i2c-core中的i2c_adapter_idr是专门用来管理注册到i2c-core中的struct i2c_adapter结构的。struct i2c_adapter中有指向具体struct i2c_algorithm的指针。struct i2c_algorithm是i2c的具体操作方法函数。内核中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已。当在实现的具体struct i2c_algorithm中一般也提供了相类似的接口i2c_xxx_add_numbered_adapter和i2c_xxx_add_adapter。 对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败.分别来看一下这两个函数的代码:
int i2c_add_adapter(struct i2c_adapter *adapter)
{
int id, res = 0;retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh */
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
mutex_unlock(&core_lock);if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}adapter->nr = id;
return i2c_register_adapter(adapter);
}int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
int status;if (adap->nr & ~MAX_ID_MASK)
return -EINVAL;retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh;
* we need the "equal to" result to force the result
*/
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
if (status == 0 && id != adap->nr) {
status = -EBUSY;
idr_remove(&i2c_adapter_idr, id);
}
mutex_unlock(&core_lock);
if (status == -EAGAIN)
goto retry;if (status == 0)
status = i2c_register_adapter(adap);
return status;
}至于idr的使用方法,在前节中已经介绍。对比两个代码的区别在于前者没有指定adap->nr,而是从某一起始数字开始动态分配而后者直接指定了nr,如果分配的id不和指定的相等,便返回错误。接着我们继续追踪i2c_register_adapter。
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;mutex_init(&adap->bus_lock); //初始化保护i2c适配器的互斥锁
mutex_init(&adap->clist_lock); //初始化保护adap->clients的锁
INIT_LIST_HEAD(&adap->clients); //初始化i2c适配器上介入的设备(client)链表mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*///初始化adap->dev然后注册该设备
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev);
if (res)
goto out_list;dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
//适配器驱动注册进内核后,对系统中现有的两种设备进行绑定。
/* create pre-declared device nodes for new-style drivers */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);/* Notify drivers */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter);out_unlock:
mutex_unlock(&core_lock);
return res;out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}内核中I2C设备的加入有两种方式,一种是在soc上板级是利用i2c_register_board_info注册在__i2c_board_list链表上的i2c设备(由__i2c_board保护)。另外一种就是通过i2c_driver驱动加入的设备。下面我们分别看看两种情况
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;mutex_lock(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
i2c_adapter_id(adapter),
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}第一种是对&__i2c_board_list中的每个struct i2c_devinfo对比,若busnum与适配器编号相同则调用i2c_new_device把设备绑定到适配器。
对第二种通过设备驱动注册进的设备调用的函数为bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter)对i2c总线上的每个驱动调用i2c_do_add_adapter(drv,adap)函数,下面我们看看i2c_do_add_adapter函数。
static int i2c_do_add_adapter(struct device_driver *d, void *data)
{
struct i2c_driver *driver = to_i2c_driver(d);
struct i2c_adapter *adap = data;/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver); //最终调用i2c_new_device把设备绑定到适配器/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) {
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}第二方式应对的是以驱动方式注册进系统的i2c设备,因为i2c驱动由两种方式因此这里有有两种应对方式,而且新旧两种方式中实现的对应函数也有所不同,新中以probe函数为代表,而旧中以attach_adapter和detach_adapter为代表(旧方法我们在这里不再追踪)。
----------------------------未完待续