i2c_client,i2c_adapter和I2C-core的简介

来源:互联网 发布:数据文员主要做什么的 编辑:程序博客网 时间:2024/06/08 16:36
I2C的主要有两大数据结构,struct i2c_client 和 struct i2c_adapter。

2.1 i2c_client

struct i2c_client {
unsigned short flags; 
unsigned short addr; 
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter
struct i2c_driver *driver
struct device dev
int irq; 
char driver_name[KOBJ_NAME_LEN];
struct list_head list; 
struct completion released;
};

struct i2c_client代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括

  • 该i2c从设备所依附的i2c主设备 struct i2c_adapter *adapter
  • 该i2c从设备的驱动程序struct i2c_driver *driver
  • 作为i2c从设备所通用的成员变量,比如addr, name等
  • 该i2c从设备驱动所特有的数据,依附于dev->driver_data下

2.2 i2c_adapter

struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class;
const struct i2c_algorithm *algo;
void *algo_data;

... ...

};
struct i2c_adapter代表主芯片所支持的一个i2c主设备,该设备所需要的数据结构,

其中,struct i2c_algorithm *algo是该i2c主设备传输数据的一种算法,或者说是在i2c总线上完成主从设备间数据通信的一种能力。

struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data);

u32 (*functionality) (struct i2c_adapter *);
};


接下来,要实现整个i2c子系统的驱动,便围绕这两个数据结构展开,其主要步骤可总结为以下三步,

  • 实现i2c主设备驱动 (drivers/i2c/bus

    static int __init tegra_i2c_init_driver(void)
    {
    return platform_driver_register(&tegra_i2c_driver);
    }

    static struct platform_driver tegra_i2c_driver = {
    .probe = tegra_i2c_probe,
    .remove = tegra_i2c_remove,
    .driver = {
    .name = "tegra-i2c",
    .owner = THIS_MODULE,
    .pm = TEGRA_I2C_DEV_PM_OPS,
    },
    };


    static int tegra_i2c_probe(struct platform_device *dev)
    {
    struct tegra_i2c_dev *i2c_dev;
    i2c->adap.algo = tegra_i2c_algo; // 提供该i2c主设备与从设备间完成数据通信的能力

    i2c_add_numbered_adapter(&i2c->adap); // 调用i2c-core.c中的接口函数,完成该i2c_adapter和i2c_client的注册

    ... ...
    }

    static const struct i2c_algorithm tegra_i2c_algo = {
    .master_xfer = tegra_i2c_xfer, // 根据tegra具体芯片的要求,完成i2c数据传输
    .functionality = tegra_i2c_func,
    };


    4 I2C-core

    内核目录drivers/i2c下的i2c-core.c,顾名思义,是内核为I2C提供的统一系统接口。

    看看i2c_add_numbered_adapter做了些什么,

    int i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
    ... ...
    status = i2c_register_adapter(adap);
    return status;
    }

    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
    ... ...

    device_register(&adap->dev); //完成I2C主设备adapter的注册,即注册object和发送uevent等

    i2c_scan_static_board_info(adap);

    ... ...
    }
    i2c_scan_static_board_info(adap),此函数为整个I2C子系统的核心,它会去遍历一个由I2C从设备组成的双向循环链表,并完成所有I2C从设备的i2c_client的注册,具体过程如下,

    static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
    {
    struct i2c_devinfo *devinfo//已经建立好了的I2C从设备链表


    list_for_each_entry(devinfo, &__i2c_board_list, list) {
    i2c_new_device(adapter,&devinfo->board_info);
    ... ...
    }
    }

    struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    {
    ... ...
    i2c_attach_client(client);
    ... ...

    }
    int i2c_attach_client(struct i2c_client *client)
    {
    ... ...
    device_register(&client->dev); //完成I2C从设备client的注册
    ... ...
    }

    那么,这个I2C从设备组成的双向循环链表,是什么时候通过什么方式建立起来的呢?

    以某重力感应设备为例,

    static void __init saar_init(void)

    {

    ... ...

    i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_bma220_info));

    ... ...

    }

    static struct i2c_board_info saar_i2c_bma220_info[] = {
    {
    .driver_name = "bma220",
    .addr = 0x0B,
    .irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO15)), 
    },
    };

    int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
    {

    ... ...

    struct i2c_devinfo *devinfo;
    devinfo->board_info = *info;
    list_add_tail(&devinfo->list, &__i2c_board_list); //将I2C从设备加入该链表中
    ... ...
    }

    所以,在系统初始化的过程中,我们可以通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。

    • 5 Slave Driver

      如果说硬件方面,I2C主设备已经集成在主芯片内,软件方面,linux也为 我们提供了相应的驱动程序,位于drivers/i2c/bus下,那么接下来I2C从设备驱动就变得容易得多。既然系统加载I2C主设备驱动时已经注册 了i2c_adapter和i2c_client,那么I2C从设备主要完成三大任务,

      • 系统初始化时添加以i2c_board_info为结构的I2C从设备的信息
      • 在I2C从设备驱动程序里使用i2c_adapter里所提供的算法,即实现I2C通信。
      • 将I2C从设备的特有数据结构挂在到i2c_client.dev->driver_data下。

      以重力感应装置为例,

      static int __init BMA220_init(void)
      {
      return i2c_add_driver(&bma220_driver);
      }

      static struct i2c_driver bma220_driver = { 
      .driver = {
      .owner = THIS_MODULE, 
      .name = "bma220",
      },
      .class = I2C_CLASS_HWMON,
      .probe = bma220_probe,
      .remove = bma220_remove,
      };

      static int bma220_probe(struct i2c_client *client, const struct i2c_device_id *id)
      {
      struct bma220_data *data; 
      i2c_check_functionality(client->adapter, I2C_FUNC_I2C)

      i2c_smbus_read_word_data(client, 0x00); // i2c-core提供的接口,利用i2c_adapter的算法实现I2C通信
      i2c_set_clientdata(bma220_client, data); // 将设备的数据结构挂到i2c_client.dev->driver_data下
      misc_register(&bma_device);
      request_irq(client->irq, bma220_irq_handler, IRQF_TRIGGER_RISING, "bma220", &data->bma220);
      bma220_set_en_tt_xyz(0);
      bma220_reset_int();

      ... ...
      }

    • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    • another:


  • 在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);

    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);

    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);

    //初始化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);

    //适配器驱动注册进内核后,对系统中现有的两种设备进行绑定。


    if (adap->nr < __i2c_first_dynamic_bus_num)
    i2c_scan_static_board_info(adap);


    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-x\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;


    i2c_detect(adap, driver); 
    //最终调用i2c_new_device把设备绑定到适配器


    if (driver->attach_adapter) {

    driver->attach_adapter(adap);
    }
    return 0;
    }

    第 二方式应对的是以驱动方式注册进系统的i2c设备,因为i2c驱动由两种方式因此这里有有两种应对方式,而且新旧两种方式中实现的对应函数也有所不同,新 中以probe函数为代表,而旧中以attach_adapter和detach_adapter为代表(旧方法我们在这里不再追踪)。

  • 在struct i2c_adapter 注册到内核中后,内核时怎样把已经注册进系统中的i2c设备设备与刚注册进内核的适配器进行绑定的。分两种情况一是在板级用 i2c_register_board_info注册的,其二是通过各种struct i2c_driver注册的。其中驱动注册又有两种方法,一种是新的总线式驱动一种是老式的,这里我们对老式的方法不做介绍,老式的方法在内核中也慢慢的 消亡。下面我们看看新式的方法中的绑定函数i2c_detect。

    在分析i2c_detect之前先看看一个数据结构struct i2c_client_address_data,内核总有很多的适配器,而且适配器上可以接有很多指定地址的设备,一个驱动对设备的在何地址上是有指导 性的,因此在探测设备时(i2c_detect)我们要参考驱动的提示去做。

    struct i2c_client_address_data { 
    const unsigned short *normal_i2c; //该数组指定对每个适配器上的指定地址都进行探测
    const unsigned short *probe; //该数组是匹对出现的只对指定适配器的指定地址进行探测,前一个数是适配器后面是指该适配器的上的一个地址
    const unsigned short *ignore; //在进行normal_i2c探测是看看此中是否忽略,若忽略则放弃探测,其也是成对出现的,前者指适配器后者指地址
    const unsigned short * const *forces; 
    //强制使用的地址,确定某个地址代 表 的设备已经连接在总线上了。这个指针指向一个指针数组,每一个成员所

    //指的内容也是每个适配器号后紧跟一个地址。
    };

    下面可以看i2c_detect的源码:

    static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
    {
    const struct i2c_client_address_data *address_data;
    struct i2c_client *temp_client;
    int i, err = 0;
    int adap_id = i2c_adapter_id(adapter);
     //得到适配器的编号

    address_data = driver->address_data;
    if (!driver->detect || !address_data) //新式驱动这两个成员必须存在
    return 0;


    temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); //分配一个设备结构体
    if (!temp_client)
    return -ENOMEM;
    temp_client->adapter = adapter;
     //让设备与适配器绑定


    if (address_data->forces) {
    const unsigned short * const *forces = address_data->forces;
    int kind;

    for (kind = 0; forces[kind]; kind++) {

    //下面代码首先在forces存在的情况下对其中适配器编号与现在加入的适配器编号相对应的或者指定任何适配器编号ANY_I2C_BUS项相对应的地址调用i2c_detect_address进行探测
    for (i = 0; forces[kind][i] != I2C_CLIENT_END;
    i += 2) {
    if (forces[kind][i] == adap_id
    || forces[kind][i] == ANY_I2C_BUS) {
    dev_dbg(&adapter->dev, "found force "
    "parameter for adapter %d, "
    "addr 0xx, kind %d\n",
    adap_id, forces[kind][i + 1],
    kind);
    temp_client->addr = forces[kind][i + 1];
    err = i2c_detect_address(temp_client,
    kind, driver);
    if (err)
    goto exit_free;
    }
    }
    }
    }


    if (!(adapter->class & driver->class)) //类型不匹配
    goto exit_free;

    //如果adapter不支持I2C_FUNC_SMBUS_QUICK不能够遍历这个adapter上面的设备
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
    if (address_data->probe[0] == I2C_CLIENT_END
    && address_data->normal_i2c[0] == I2C_CLIENT_END) //无需探测
    goto exit_free;

    dev_warn(&adapter->dev, "SMBus Quick command not supported, "
    "can't probe for chips\n");
    err = -EOPNOTSUPP;
    goto exit_free;
    }

    //probe情况下对匹配的适配器的响应地址进行探测(i2c_detect_address)
    for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
    if (address_data->probe[i] == adap_id
    || address_data->probe[i] == ANY_I2C_BUS) {
    dev_dbg(&adapter->dev, "found probe parameter for "
    "adapter %d, addr 0xx\n", adap_id,
    address_data->probe[i + 1]);
    temp_client->addr = address_data->probe[i + 1];
    err = i2c_detect_address(temp_client, -1, driver);
    if (err)
    goto exit_free;
    }
    }

    //对normal_i2c指定的所以地址对每个适配器进行探测,但在ingore中指定的适配器对应的地址不做探测
    for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
    int j, ignore;

    ignore = 0;
    for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
    j += 2) {
    if ((address_data->ignore[j] == adap_id ||
    address_data->ignore[j] == ANY_I2C_BUS)
    && address_data->ignore[j + 1]
    == address_data->normal_i2c[i]) {
    dev_dbg(&adapter->dev, "found ignore "
    "parameter for adapter %d, "
    "addr 0xx\n", adap_id,
    address_data->ignore[j + 1]);
    ignore = 1;
    break;
    }
    }
    if (ignore)
    continue;

    dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
    "addr 0xx\n", adap_id,
    address_data->normal_i2c[i]);
    temp_client->addr = address_data->normal_i2c[i];
    err = i2c_detect_address(temp_client, -1, driver);
    if (err)
    goto exit_free;
    }

    exit_free:
    kfree(temp_client);
    return err;
    }

    i2c_detect就是对驱动传递下的设备地址列表类别做相应的处理,具体的探测工作由i2c_detect_address来完成,下面我们看具体的代码

    static int i2c_detect_address(struct i2c_client *temp_client, int kind,
    struct i2c_driver *driver)
    {
    struct i2c_board_info info;
    struct i2c_adapter *adapter = temp_client->adapter;
    int addr = temp_client->addr;
    int err;


    if (addr < 0x03 || addr > 0x77) {
    dev_warn(&adapter->dev, "Invalid probe address 0xx\n",
    addr);
    return -EINVAL;
    }


    //对适配器中的所有设备进行检验,看该地址是否被使用过
    if (i2c_check_addr(adapter, addr))
    return 0;


    //当kind <0 时检查设备上是否有这个设备,而对forces而言并不做物理探测
    //在上个函数的探测中probe 和 normal_i2c都是把该值指定为 -1
    if (kind < 0) {
    if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
    I2C_SMBUS_QUICK, NULL) < 0)
    return 0;


    if ((addr & ~0x0f) == 0x50)
    i2c_smbus_xfer(adapter, addr, 0, 0, 0,
    I2C_SMBUS_QUICK, NULL);
    }


    //构建i2c_board_info结构
    memset(&info, 0, sizeof(struct i2c_board_info));
    info.addr = addr;
    err = driver->detect(temp_client, kind, &info);
    if (err) {

    return err == -ENODEV ? 0 : err;
    }


    //用i2c_board_info结构,调用i2c_new_device进行设备和适配器的绑定。
    if (info.type[0] == '\0') {
    dev_err(&adapter->dev, "%s detection function provided "
    "no name for 0x%x\n", driver->driver.name,
    addr);
    } else {
    struct i2c_client *client;


    dev_dbg(&adapter->dev, "Creating %s at 0xx\n",
    info.type, info.addr);
    client = i2c_new_device(adapter, &info);
    if (client)
    list_add_tail(&client->detected, &driver->clients);
    else
    dev_err(&adapter->dev, "Failed creating %s at 0xx\n",
    info.type, info.addr);
    }
    return 0;
    }

    首 先,对传入的参数进行一系列的合法性检查.另外,如果该adapter上已经有了这个地址的设备了.也会返回失败.所有adapter下面的设备都是以 adapter->dev为父结点的.因此只需要遍历adapter->dev下面的子设备就可以得到当前地址是不是被占用了.如果kind < 0.还得要adapter检查该总线是否有这个地址的设备.方法是向这个地址发送一个Read的Quick请求.如果该地址有应答,则说明这个地址上有这 个设备.另外还有一种情况是在24RF08设备的特例。i2c_smbus_xfer是与具体的协议相关的,在后面的章节中再分析。最后我们看看设备也适 配器的绑定函数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;

    client->flags = info->flags;
    client->addr = info->addr;
    client->irq = info->irq;

    strlcpy(client->name, info->type, sizeof(client->name));


    status = i2c_attach_client(client);
    if (status < 0) {
    kfree(client);
    client = NULL;
    }
    return client;
    }

    分 配一个struct i2c_client,并用i2c_board_info中的信息初始化它,然后调用i2c_attach_client,这个函数对其成员dev做一些 初始化加入内核,然把client加入到adapter的设备列表中。再然后i2c的总线机制就发挥作用了(device_register调用)。这里 留下一个疑问,当设备驱动先注册,而适配器后注册是两者又是如何绑定的呢?

0 0