随笔-110  评论-133  文章-4  trackbacks-0
前言:

    记得以前曾研究过Linux内核下i2c子系统,了解了i2c总线上,适配器、设备驱动的注册过程与使用方法,详细请查看:
    1、i2c总线上,适配器、设备驱动注册http://www.cnitblog.com/luofuchong/archive/2008/10/05/49881.html
    2、i2c总线使用http://www.cnitblog.com/luofuchong/archive/2008/10/05/49882.html

    最近因为项目的需要,又重新看了一遍linux下的i2c子系统,有了新的体会。俗话说,好记性不如烂笔头,还是记下来比较好,一来当作总结,二来希望起到启发后人的效果^_^

正文:

    Linux的世界充斥着各种各样的想法,i2c子系统同样,新、旧架构并存。至于旧的架构,网上有非常详细的介绍,本人不打算再去重复,咱们直奔新的架构去。

    Linux下的设备驱动,顾名思义,就是由设备和驱动来组成的,i2c总线上的设备驱动也不例外。

一、i2c设备的注册

第一步:
    记得以前的i2c设备驱动,设备部分喜欢驱动运行的时候动态创建,新式的驱动倾向于向传统的linux下设备驱动看齐,采用静态定义的方式来注册设备,使用接口为:
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);//申请表示i2c设备的结构体空间
        if (!devinfo) {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }
        /* 填写i2c设备描述结构 */
        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        list_add_tail(&devinfo->list, &__i2c_board_list);//添加到全局链表__i2c_board_list中
    }

    mutex_unlock(&__i2c_board_lock);

    return status;
}

第二步:
    系统初始化的时候,会根据板级i2c设备配置信息,创建i2c客户端设备(i2c_client),添加到i2c子系统中:
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) {    //遍历全局链表__i2c_board_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);
}

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);//创建i2c_client设备
    if (!client)
        return NULL;

    client->adapter = adap;

    client->dev.platform_data = info->platform_data;

    if (info->archdata)
        client->dev.archdata = *info->archdata;

    client->flags = info->flags;
    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);//接入设备到i2c总线上
    if (status < 0) {
        kfree(client);
        client = NULL;
    }
    return client;
}

int i2c_attach_client(struct i2c_client *client)
{
    struct i2c_adapter *adapter = client->adapter;
    int res;

    /* Check for address business */
    res = i2c_check_addr(adapter, client->addr);//地址检测
    if (res)
        return res;

    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;

    if (client->driver)
        client->dev.driver = &client->driver->driver;

    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;

    snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
        "%d-%04x", i2c_adapter_id(adapter), client->addr);
    res = device_register(&client->dev);    //注册设备
    if (res)
        goto out_err;

    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\n",
        client->name, client->dev.bus_id);

    if (adapter->client_register)  {
        if (adapter->client_register(client)) {
            dev_dbg(&adapter->dev, "client_register "
                "failed for client [%s] at 0x%02x\n",
                client->name, client->addr);
        }
    }

    return 0;

out_err:
    dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
        "(%d)\n", client->name, client->addr, res);
    return res;
}

注:
    特别要提一下的是这个“i2c_check_addr”,引用<<i2c 源代码情景分析>>里的话:“i2c 设备的7 位地址是就当前i2c 总线而言的,是“相对地址”。不同的i2c 总线上的设备可以使用相同的7 位地址,但是它们所在的i2c 总线不同。所以在系统中一个i2c 设备的“绝对地址”由二元组(i2c 适配器的ID 和设备在该总线上的7 位地址)表示。”,所以这个函数的作用主要是排除同一i2c总线上出现多个地址相同的设备。

二、驱动的注册:

第一步:

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;

    /* Can't register until after driver model init */
    if (unlikely(WARN_ON(!i2c_bus_type.p)))
        return -EAGAIN;

    /* new style driver methods can't mix with legacy ones */
    if (is_newstyle_driver(driver)) {                           //呵呵,新旧架构的驱动可以从这里看出来
        if (driver->attach_adapter || driver->detach_adapter
                || driver->detach_client) {
            printk(KERN_WARNING
                    "i2c-core: driver [%s] is confused\n",
                    driver->driver.name);
            return -EINVAL;
        }
    }

    /* add the driver to the list of i2c drivers in the driver core */
    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.
     */
    res = driver_register(&driver->driver);    //注册驱动
    if (res)
        return res;

    mutex_lock(&core_lock);

    pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

    INIT_LIST_HEAD(&driver->clients);
    /* Walk the adapters that are already present */
    class_for_each_device(&i2c_adapter_class, NULL, driver,
                  __attach_adapter);

    mutex_unlock(&core_lock);
    return 0;
}

第二步:
    还记得在第一部分,我们注册了一i2c设备,其总线类型为i2c_bus_type,现在我们又在这总线上注册一驱动,那不得了,满足了设备+驱动的条件,i2c_bus_type总线上探测函数要被执行了。

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

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));//调用具体i2c设备驱动的探测函数
    if (status)
        client->driver = NULL;
    return status;
}

    OK,剩下的就是具体i2c设备驱动的功夫了,在此不作深入研究。
posted on 2009-10-27 17:54 lfc 阅读(5800) 评论(2)  编辑 收藏 引用

评论:
# re: I2C总线使用方法继续研究 2010-03-08 12:53 | dot.cheng
写得很好,很受启发。

但是我对 IIC 了解太少,刚接触,对其中有一点不清楚,您在文中:
二、驱动的注册:
第二步 还记得在第一部分,我们注册了一i2c设备,其总线类型为i2c_bus_type,现在我们又在这总线上注册一驱动,那不得了,满足了设备+驱动的条件,i2c_bus_type总线上探测函数要被执行了。

所讲“i2c_bus_type总线上探测函数”在哪里调用?

我在 2.6.30 中加入了 saa7113 (saa7115.c),并增加一行i2c_register_board_info 注册设备,启动后又insmod saa7115.ko;

通过打印的调试信息可知,设备已经注册,驱动也注册(至少部分代码被执行);
但是函数 i2c_device_probe 却始终没有被调用,我不知道哪里出了错。

请指点,谢谢。
  回复  更多评论
  
# re: I2C总线使用方法继续研究 2010-04-16 13:45 | Guaranteed Website Traffic
I think there are some codes that is wrong. Pardon me for that. When I used it..an error occurs. I wonder what the cause of error is.  回复  更多评论
  
只有注册用户登录后才能发表评论。