Linux设备驱动之I2C架构分析(一)

Linux设备驱动之I2C架构分析
一:前言
I2c是philips提出的外设总线.I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线.另外,I2C是一种多主机控制总线.它和USB总线不同,USB是基于master-slave机制,任何设备的通信必须由主机发起才可以.而I2C是基于multi master机制.一同总线上可允许多个master.关于I2C协议的知识,这里不再赘述.可自行下载spec阅读即可.
 
二:I2C架构概述
在linux中,I2C驱动架构如下所示:
 

如上图所示,每一条I2C对应一个adapter.在kernel中,每一个adapter提供了一个描述的结构(struct i2c_adapter),也定义了adapter支持的操作(struct i2c_adapter).再通过i2c core层将i2c设备与i2c adapter关联起来.
这个图只是提供了一个大概的框架.在下面的代码分析中,从下至上的来分析这个框架图.以下的代码分析是基于linux 2.6.26.分析的代码基本位于: linux-2.6.26.3/drivers/i2c/位置.
 
三:adapter注册
在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已.
对于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);
}
在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅本站<< linux文件系统之文件的读写>>中有关radix tree的分析.
注意一下idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,
然后将对应的id号存放在adapter->nr中.调用i2c_register_adapter(adapter)对这个adapter进行进一步注册.
 
看一下另外一人注册函数: i2c_add_numbered_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;
}
对比一下就知道差别了,在这里它已经指定好了adapter->nr了.如果分配的id不和指定的相等,便返回错误.
 
过一步跟踪i2c_register_adapter().代码如下:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
    int res = 0, dummy;
 
    mutex_init(&adap->bus_lock);
    mutex_init(&adap->clist_lock);
    INIT_LIST_HEAD(&adap->clients);
 
    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.
     */
    if (adap->dev.parent == NULL) {
        adap->dev.parent = &platform_bus;
        pr_debug("I2C adapter driver [%s] forgot to specify "
             "physical device ", 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 ", 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);
 
    /* let legacy drivers scan this bus for matching devices */
    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;
}
首先对adapter和adapter中内嵌的struct device结构进行必须的初始化.之后将adapter内嵌的struct device注册.
在这里注意一下adapter->dev的初始化.它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字为i2c + 总线号.
测试一下:
[eric@mochow i2c]$ cd /sys/class/i2c-adapter/
[eric@mochow i2c-adapter]$ ls
i2c-0
可以看到,在我的PC上,有一个I2C adapter,看下详细信息:
[eric@mochow i2c-adapter]$ tree
.
`-- i2c-0
    |-- device -> ../../../devices/pci0000:00/0000:00:1f.3/i2c-0
    |-- name
    |-- subsystem -> ../../../class/i2c-adapter
    `-- uevent
 
3 directories, 2 files
可以看到,该adapter是一个PCI设备.
继续往下看:
之后,在注释中看到,有两种类型的driver,一种是new-style drivers,另外一种是legacy drivers
New-style drivers是在2.6近版的kernel加入的.它们最主要的区别是在adapter和i2c driver的匹配上.
 
3.1: new-style 形式的adapter注册
对于第一种,也就是new-style drivers,将相关代码再次列出如下:
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);
如果adap->nr 小于__i2c_first_dynamic_bus_num的话,就会进入到i2c_scan_static_board_info().
结合我们之前分析的adapter的两种注册分式: i2c_add_adapter()所分得的总线号肯会不会小于__i2c_first_dynamic_bus_num.只有i2c_add_numbered_adapter()才有可能满足:
(adap->nr < __i2c_first_dynamic_bus_num)
而且必须要调用i2c_register_board_info()将板子上的I2C设备信息预先注册时才会更改__i2c_first_dynamic_bus_num的值.在x86上只没有使用i2c_register_board_info()的.因此,x86平台上的分析可以忽略掉new-style driver的方式.不过,还是详细分析这种情况下.
首先看一下i2c_register_board_info(),如下:
int __init
i2c_register_board_info(int busnum,
    struct i2c_board_info const *info, unsigned len)
{
    int status;
 
    mutex_lock(&__i2c_board_lock);
 
    /* dynamic bus numbers will be assigned after the last static one */
    if (busnum >= __i2c_first_dynamic_bus_num)
        __i2c_first_dynamic_bus_num = busnum + 1;
 
    for (status = 0; len; len--, info++) {
        struct i2c_devinfo  *devinfo;
 
        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo! ");
            status = -ENOMEM;
            break;
        }
 
        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);
    }
 
    mutex_unlock(&__i2c_board_lock);
 
    return status;
}
这个函数比较简单, struct i2c_board_info用来表示I2C设备的一些情况,比如所在的总线.名称,地址,中断号等.最后,这些信息会被存放到__i2c_board_list链表.
 
跟踪i2c_scan_static_board_info():代码如下:
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 ",
                i2c_adapter_id(adapter),
                devinfo->board_info.addr);
    }
    mutex_unlock(&__i2c_board_lock);
}
该函数遍历挂在__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息.
如果指定设备是位于adapter所在的I2C总线上,那么,就调用i2c_new_device().代码如下:
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client   *client;
    int         status;
 
    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client)
        return NULL;
 
    client->adapter = adap;
 
    client->dev.platform_data = info->platform_data;
    device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);
 
    client->flags = info->flags & ~I2C_CLIENT_WAKE;
    client->addr = info->addr;
    client->irq = info->irq;
 
    strlcpy(client->name, info->type, sizeof(client->name));
 
    /* a new style driver may be bound to this device when we
     * return from this function, or any later moment (e.g. maybe
     * hotplugging will load the driver module).  and the device
     * refcount model is the standard driver model one.
     */
    status = i2c_attach_client(client);
    if (status < 0) {
        kfree(client);
        client = NULL;
    }
    return client;
}
我们又遇到了一个新的结构:struct i2c_client,不要被这个结构吓倒了,其实它就是一个嵌入struct device的I2C设备的封装.它和我们之前遇到的struct usb_device结构的作用是一样的.
首先,在clinet里保存该设备的相关消息.特别的, client->adapter指向了它所在的adapter.
特别的,clinet->name为info->name.也是指定好了的.
一切初始化完成之后,便会调用i2c_attach_client( ).看这个函数的字面意思,是将clinet关联起来.到底怎么样关联呢?继续往下看:
int i2c_attach_client(struct i2c_client *client)
{
    struct i2c_adapter *adapter = client->adapter;
    int res = 0;
 
    //初始化client内嵌的dev结构
    //父结点为所在的adapter,所在bus为i2c_bus_type
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
 
    //如果client已经指定了driver,将driver和内嵌的dev关联起来 
    if (client->driver)
        client->dev.driver = &client->driver->driver;
    //指定了driver, 但不是newstyle的
    if (client->driver && !is_newstyle_driver(client->driver)) {
        client->dev.release = i2c_client_release;
        client->dev.uevent_suppress = 1;
    } else
        client->dev.release = i2c_client_dev_release;
 
    //clinet->dev的名称
    snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
        "%d-%04x", i2c_adapter_id(adapter), client->addr);
    //将内嵌的dev注册
    res = device_register(&client->dev);
    if (res)
        goto out_err;
 
    //将clinet链到adapter->clients中
    mutex_lock(&adapter->clist_lock);
    list_add_tail(&client->list, &adapter->clients);
    mutex_unlock(&adapter->clist_lock);
 
    dev_dbg(&adapter->dev, "client [%s] registered with bus id %s ",
        client->name, client->dev.bus_id);
    //如果adapter->cleinet_reqister存在,就调用它
    if (adapter->client_register)  {
        if (adapter->client_register(client)) {
            dev_dbg(&adapter->dev, "client_register "
                "failed for client [%s] at 0x%02x ",
                client->name, client->addr);
        }
    }
 
    return 0;
 
out_err:
    dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
        "(%d) ", client->name, client->addr, res);
    return res;
}
参考上面添加的注释,应该很容易理解这段代码了,就不加详细分析了.这个函数的名字不是i2c_attach_client()么?怎么没看到它的关系过程呢?
这是因为:在代码中设置了client->dev所在的bus为i2c_bus_type .以为只需要有bus为i2c_bus_type的driver注册,就会产生probe了.这个过程呆后面分析i2c driver的时候再来详细分析.
 
3.2: legacy形式的adapter注册
Legacy形式的adapter注册代码片段如下:
    dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
                 i2c_do_add_adapter);
这段代码遍历挂在i2c_bus_type上的驱动,然后对每一个驱动和adapter调用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;
 
    if (driver->attach_adapter) {
        /* We ignore the return code; if it fails, too bad */
        driver->attach_adapter(adap);
    }
    return 0;
}
该函数很简单,就是调用driver的attach_adapter()接口.
到此为止,adapter的注册已经分析完了.
 
四:i2c driver注册
在分析i2c driver的时候,有必要先分析一下i2c架构的初始化
代码如下:
static int __init i2c_init(void)
{
    int retval;
 
    retval = bus_register(&i2c_bus_type);
    if (retval)
        return retval;
    retval = class_register(&i2c_adapter_class);
    if (retval)
        goto bus_err;
    retval = i2c_add_driver(&dummy_driver);
    if (retval)
        goto class_err;
    return 0;
 
class_err:
    class_unregister(&i2c_adapter_class);
bus_err:
    bus_unregister(&i2c_bus_type);
    return retval;
}
subsys_initcall(i2c_init);
很明显,i2c_init()会在系统初始化的时候被调用.
在i2c_init中,先注册了i2c_bus_type的bus,i2c_adapter_class的class.然后再调用i2c_add_driver()注册了一个i2c driver.
 
I2c_bus_type结构如下:
static 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,
};
 
这个结构先放在这里吧,以后还会用到里面的信息的.
从上面的初始化函数里也看到了,注册i2c driver的接口为i2c_add_driver().代码如下:
static inline int i2c_add_driver(struct i2c_driver *driver)
{
    return i2c_register_driver(THIS_MODULE, driver);
}
继续跟踪:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;
 
    /* new style driver methods can't mix with legacy ones */
    //如果是一个newstyle的driver.但又定义了attach_adapter/detach_adapter.非法
    if (is_newstyle_driver(driver)) {
        if (driver->attach_adapter || driver->detach_adapter
                || driver->detach_client) {
            printk(KERN_WARNING
                    "i2c-core: driver [%s] is confused ",
                    driver->driver.name);
            return -EINVAL;
        }
    }
 
    /* add the driver to the list of i2c drivers in the driver core */
    //关联到i2c_bus_types
    driver->driver.owner = owner;
    driver->driver.bus = &i2c_bus_type;
 
    /* for new style drivers, when registration returns the driver core
     * will have called probe() for all matching-but-unbound devices.
     */
     //注册内嵌的driver
    res = driver_register(&driver->driver);
    if (res)
        return res;
 
    mutex_lock(&core_lock);
 
    pr_debug("i2c-core: driver [%s] registered ", driver->driver.name);
 
    /* legacy drivers scan i2c busses directly */
    //遍历所有的adapter,对其都调用driver->attach_adapter
    if (driver->attach_adapter) {
        struct i2c_adapter *adapter;
 
        down(&i2c_adapter_class.sem);
        list_for_each_entry(adapter, &i2c_adapter_class.devices,
                    dev.node) {
            driver->attach_adapter(adapter);
        }
        up(&i2c_adapter_class.sem);
    }
 
    mutex_unlock(&core_lock);
    return 0;
}
这里也有两种形式的区分,对于第一种,只需要将内嵌的driver注册就可以了,对于legacy的情况,对每一个adapter都调用driver->attach_adapter().
现在,我们可以将adapter和i2c driver关联起来考虑一下了:
1:如果是news style形式的,在注册adapter的时候,将它上面的i2c 设备转换成了struct client.struct client->dev->bus又指定了和i2c driver同一个bus.因为,它们可以发生probe.
2:如果是legacy形式,就直接找到对应的对象,调用driver->attach_adapter().
 

你可能感兴趣的:(linux,架构,驱动,休闲,设备)