i2c驱动框架

linux的i2c驱动框架是基于总线模型设计的,就是在内核中会注册一条i2c总线,在总线中提供了注册i2c_driver和i2c_adapter的接口,学过总线模型都知道,总线有两部分device和driver组成,在总线描述结构中有个成员struct subsys_private *p;这个成员里面又有两个链表分别为struct klist klist_devices;,struct klist klist_drivers;这两个链表是用来管理总线下的device和driver成员,在这里i2c_adapter属于device部分,也就是说i2c_adapter会注册到总线的struct klist klist_devices链表中
来,为了需要,i2c总线的device部分还有另外一个结构i2c_client(i2c_driver和i2c_adapter匹配成功后的‘“产物”),既然device部分有两个结构,总线中必然要对它们进行区分,这两个结构中都会包含一个struct device结构,struct device这个结构中有个type成员,i2c_adapter和i2c_client对type这个成员分别赋值i2c_adapter_type、i2c_client_type进行区分,上面说了device部分,自然i2c_driver就属于i2c总线的driver部分。在i2c总线中i2c_adapter对应于一个i2c控制器(当然没有也可以软件模拟一个),i2c_client对应一个真实存在的i2c设备,而i2c_driver并没有与实际的硬件对应,虽然没有对应的硬件设备,但它负责了i2c设备跟linux其他功能模块的对接工作,因为i2c芯片多种多样,功能也各不相同,至于i2c芯片是什么功能,由i2c_driver来处理。先来看下i2c_adapter的注册:
i2c总线提供了两个注册接口分别为int i2c_add_numbered_adapter(struct i2c_adapter *adap)、int i2c_add_adapter(struct i2c_adapter*adapter),这两个接口的区别在于总线id号的分配,存放在struct i2c_adapter结构的nr成员,i2c_add_numbered_adapter是由驱动指定struct i2c_adapter结构的nr成员,为-1时自动分配(所以该接口支持自动分配总线id,和手动设置),而i2c_add_adapter只支持自动分配。它们最终都是调用i2c_register_adapter

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    int res = 0;
    /* Can't register until after driver model init */
    if (unlikely(WARN_ON(!i2c_bus_type.p))) {
        res = -EAGAIN;
        goto out_list;
    }
    /* Sanity checks */
    if (unlikely(adap->name[0] == '\0')) {
        pr_err("i2c-core: Attempt to register an adapter with "
               "no name!\n");
        return -EINVAL;
    }
    if (unlikely(!adap->algo)) {
        pr_err("i2c-core: Attempt to register adapter '%s' with "
               "no algo!\n", adap->name);
        return -EINVAL;
    }
    rt_mutex_init(&adap->bus_lock);
    mutex_init(&adap->userspace_clients_lock);
    INIT_LIST_HEAD(&adap->userspace_clients);
    /* Set default timeout to 1 second if not already set */
    if (adap->timeout == 0)
        adap->timeout = HZ;
    dev_set_name(&adap->dev, "i2c-%d", adap->nr);
    adap->dev.bus = &i2c_bus_type;
    adap->dev.type = &i2c_adapter_type;
    res = device_register(&adap->dev);
    if (res)
        goto out_list;

    dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
#ifdef CONFIG_I2C_COMPAT
    res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
                       adap->dev.parent);
    if (res)
        dev_warn(&adap->dev,
             "Failed to create compatibility class link\n");
#endif
    /* create pre-declared device nodes */
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);
    /* Notify drivers */
    mutex_lock(&core_lock);
    bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
    mutex_unlock(&core_lock);
    return 0;
}

i2c_register_adapter里面主要是调用三个函数首先是调用device_register注册一个device,然后是调用i2c_scan_static_board_info(adap);
和bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);先来看下i2c_scan_static_board_info

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo  *devinfo;
    down_read(&__i2c_board_lock);
    list_for_each_entry(devinfo, &__i2c_board_list, list) {
        if (devinfo->busnum == adapter->nr
                && !i2c_new_device(adapter,
                        &devinfo->board_info))
            dev_err(&adapter->dev,
                "Can't create device at 0x%02x\n",
                devinfo->board_info.addr);
    }
    up_read(&__i2c_board_lock);
}

由此可见从__i2c_board_list中取出前面已经注册的struct i2c_devinfo结构,通过判断devinfo->busnum == adapter->nr是否相等来识别该dev与i2c_adapter的是否匹配,如果匹配,则调用i2c_new_device创建一个i2c设备,再来看下bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);bus_for_each_drv这个函数的主要作用是遍历已注册进i2c总线的所以i2c_driver然后调用__process_new_adapter对每一个i2c_driver与adap进行匹配,__process_new_adapter这个函数里面的调用比较多,所以这里以主要函数调用流程来说明它们的匹配过程:

i2c_do_add_adapter
    i2c_detect(adap, driver);//
        for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) 
        {
            temp_client->addr = address_list[i];
            i2c_detect_address(temp_client, driver);    
                    i2c_check_addr_validity//检测该设备地址的有效性,无效则返回
                    i2c_check_addr_busy//检测该设备地址是否已经注册,是则返回
                    i2c_default_probe//硬件的识别
                     driver->detect(temp_client,&info);//调用i2c_driver的detect函数,根据临时分配的struct i2c_client *temp_client结构构造一个struct i2c_board_info info(单板信息),下面会根据这个单板信息创建一个设备
                     i2c_new_device//产生另一个结构i2c_client,把i2c_driver跟i2c_adapter关联起来
       }

如上用函数调用过程了解了大概过程,但有些细节还是要用文字描述下,首先是调用i2c_do_add_adapter转接一下,然后是调用i2c_detect,由上可见,这个函数传进来的参数有两个,分别为adap和driver,再这个函数里面首先会临时分配一个struct i2c_client *temp_client结构,并把adap赋值给temp_client的adapter成员,在硬件的识别过程需要用到,取出i2c_driver里面的address_list,这是个指针,里面存放有该driver支持的i2c设备地址,逐个取出该address_list,并赋值给temp_client结构的addr成员,然后再调用i2c_detect_address把这个临时分配的结构当作参数传送进去,进去这个函数首先是调用i2c_check_addr_validity检测该设备地址的有效性,无效则返回,再调用 i2c_check_addr_busy检测该设备地址是否已经注册,是则返回,然后调用i2c_default_probe进行真正的硬件识别(识别原理就是先发送i2c的start信号,发送地址,然后判断有没有收到回应信号来识别,这里需要注意一下,数据传输接口由adapter来提供,如果adapter没有提供会调用i2c总线默认的数据传输接口),之后调用driver的detect函数根据这个临时分配的个struct i2c_client结构构造出另一个结构struct i2c_board_info info,再就是调用i2c_new_device(adapter, &info);根据刚构造出的struct i2c_board_info info创建一个设备。这就完成了一个i2c_adapter的注册。由上面可知在注册一个i2c_adapter的过程中,有两处地方会调用 i2c_new_device来创建一个i2c设备,并产生另外一个结构i2c_client,来看下它的实现:

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;
    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));
    /* Check for address validity */
    status = i2c_check_client_addr_validity(client);
    if (status) {
        dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
            client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
        goto out_err_silent;
    }
    /* Check for address business */
    status = i2c_check_addr_busy(adap, client->addr);
    if (status)
        goto out_err;
    client->dev.parent = &client->adapter->dev;
    client->dev.bus = &i2c_bus_type;
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;
    /* For 10-bit clients, add an arbitrary offset to avoid collisions */
    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
             client->addr | ((client->flags & I2C_CLIENT_TEN)
                     ? 0xa000 : 0));
    status = device_register(&client->dev);
    if (status)
        goto out_err;
    dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
        client->name, dev_name(&client->dev));
    return client;
}

这里可以看到首先是构造一个struct i2c_client结构,并对它进行初始化,上面可以看到client->dev.parent = &client->adapter->dev;把adapter的dev作为该设备的parent,然后调用device_register注册一个设备,也就是把它作为一个子项注册进adapter的dev中(这个在上层通过adapter访问一个具体的i2c设备时检测一个设备是否存在时用到,也就是遍历该adapter的dev下的子项来检测,后面会讲),这个只是注册一个设备,还需要调用device_create才能能生成一个设备文件,在i2c中,创建一个设备文件已经做成一个接口注册进i2c总线中了,由上可见,在调用i2c_register_adapter和i2c_new_device都会调用device_register注册一个设备会调用device_add,在device_add中又有一个判断并调用
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);i2c的设备文件的创建就是在这里完成的(但需要注意并不是两处的device_register调用注册都会创建一个设备,只有在注册i2c_adapter的时候才会生成,因为它里面会有判断),创建一个i2c设备文件接口是在i2c_dev.c里面实现的,应用层访问一个i2c设备文件的入口也是在这里实现,来看下它的具体实现:

static int __init i2c_dev_init(void)
{
    int res;
    printk(KERN_INFO "i2c /dev entries driver\n");
    res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
    if (res)
        goto out;
    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
    if (IS_ERR(i2c_dev_class)) {
        res = PTR_ERR(i2c_dev_class);
        goto out_unreg_chrdev;
    }
    /* Keep track of adapters which will be added or removed later */
    res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
    if (res)
        goto out_unreg_class;
    /* Bind to already existing adapters right away */
    i2c_for_each_dev(NULL, i2cdev_attach_adapter);
    return 0;
}

i2cdev_fops是i2c设备文件上层访问入口函数集,调用register_chrdev(I2C_MAJOR, “i2c”, &i2cdev_fops);把它跟 i2c主设备号I2C_MAJOR进行绑定,因为这个是系统分配给i2c的主设备号,注册一个i2c设备都会使用这个主设备号来创建设备文件,这使得上层应用i2c设备文件的访问入口为 i2cdev_fops提供的函数集(应用并不是直接就访问到这里来,而是需要通过虚拟文件系统的映射才会访问到这里来),然后这里是注册i2c设备文件的创建或销毁接口res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);看下代码

static struct notifier_block i2cdev_notifier = {
    .notifier_call = i2cdev_notifier_call,
};
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,void *data)      
{
    struct device *dev = data;
    switch (action) {
    case BUS_NOTIFY_ADD_DEVICE:
        return i2cdev_attach_adapter(dev, NULL);
    case BUS_NOTIFY_DEL_DEVICE:
        return i2cdev_detach_adapter(dev, NULL);
    }
    return 0;
}
static int i2cdev_detach_adapter(struct device *dev, void *dummy)
{
    struct i2c_adapter *adap;
    struct i2c_dev *i2c_dev;
    if (dev->type != &i2c_adapter_type)//对dev的type进行判断,不是i2c_adapter_type类型返回
        return 0;
    adap = to_i2c_adapter(dev);
    i2c_dev = i2c_dev_get_by_minor(adap->nr);
    if (!i2c_dev) /* attach_adapter must have failed */
        return 0;
    device_remove_file(i2c_dev->dev, &dev_attr_name);
    return_i2c_dev(i2c_dev);
    device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
    pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
    return 0;
}

这里提供了创建和销毁一个i2c设备文件的接口,在上面提到的blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);调用,就会调用到这里的i2cdev_attach_adapter(dev, NULL);函数进行一个i2c设备文件的创建,由上面代码可见,i2cdev_attach_adapter首先就是对dev的type成员变量进行判断,不是i2c_adapter_type类型的就返回,不进行设备文件的创建,是则调用device_create进行设备文件的创建。了解了i2c_adapter和i2c设备文件的创建流程,现在回到i2c_driver中来,i2c框架是基于总线模型设计的,必然会遵守总线模型的双匹配机制,也就是在注册一个i2c_driver也会来匹配注册进i2c总线的i2c_adapter,在总线中也提供另一个用来注册i2c_driver的接口i2c_register_driver,这个函数做两件事情:1、调用driver_register(&driver->driver);注册一个driver,这是任何总线注册一个driver都会做的事情,这里不加以讨论2、i2c_for_each_dev(driver, __process_new_driver);这个跟注册i2c_adapter调用的是相反的,在这里是逐个取出i2c总线下已注册的i2c_adapter然后进行该i2c_driver address_list下的设备地址进行识别,识别过程跟上面的是一样的。
i2c总线是基于数据协议传输的,势必会涉及到数据的收发,在i2c总线中,对数据协议传输分为两个层面:一个是硬件层面(与底层硬件直接打交道,由i2c_adapter成员来完成)、另一个属软件层面(通过软件进行封装,最终找到对应的i2c_adapter进行数据的收发),在一开始就讲到,i2c_adapter对应于一个i2c控制器,自然与i2c设备的通讯由它来完成,内核用一个数据结构来对一个i2c控制器进行描述

struct i2c_adapter {
    struct module *owner;
    unsigned int class;       /* classes to allow probing for */
    const struct i2c_algorithm *algo; /* the algorithm to access the bus 这个结构用来提供与i2c设备通讯的接口*/
    void *algo_data;
    /* data fields that are valid for all devices   */
    struct rt_mutex bus_lock;
    int timeout;            /* in jiffies 重复超时时间*/
    int retries;                    //失败重复尝试次数
    struct device dev;      /* the adapter device */
    int nr;
    char name[48];
    struct completion dev_released;
    struct mutex userspace_clients_lock;
    struct list_head userspace_clients;
};主要看下struct i2c_algorithm
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);
    /* To determine what the adapter supports */
    u32 (*functionality) (struct i2c_adapter *);
};

这个结构提供了两个数据传输接口master_xfer、smbus_xfer,master_xfer这个是通用的i2c传输接口,而smbus_xfer接口是为了应用层对i2c的操作更加的通用性扩展出来smbus数据传输协议(属软件协议)。可根据需要去实现这个接口,另外还提供了一个用来检测该i2c_adapter支持的功能的接口functionality,下面以一个具体的实例进行这些功能接口的实现,如下是s3c2410平台实现的一个struct i2c_algorithm

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
    .master_xfer        = s3c24xx_i2c_xfer,
    .functionality      = s3c24xx_i2c_func,
};
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)     
{
    struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
    int retry;
    int ret;
    pm_runtime_get_sync(&adap->dev);
    clk_enable(i2c->clk);
    for (retry = 0; retry < adap->retries; retry++) {//失败重复发送
        ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
        if (ret != -EAGAIN) {
            clk_disable(i2c->clk);
            pm_runtime_put_sync(&adap->dev);
            return ret;
        }
        dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
        udelay(100);
    }
    return -EREMOTEIO;
}

这里主要是调用s3c24xx_i2c_doxfer把数据包传给struct s3c24xx_i2c结构的msg成员,然后启动数据发送(即是触发中断),发送失败按设定的重复次数进行重复启动传输,真正的传输在中断完成,中断处理函数比较长(主要是数据的收发),这里就不贴出来了,贴下i2c协议以及注意事项:
i2c总线在传送数据过程中有3种类型信号:开始信号、结束信号和响应信号
1、开始信号(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据
2、结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据
3、响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA电平
数据传输的注意事项:
1、SDA上传输的数据必须在SCL为高电平期间保持稳定,SDA上的数据只能在SCL为低电平期间变化
2、并非每传输8位数据之后都会有ACK信号,有以下三种例外
(1)当从机不能响应从机地址时(例如它正忙于其他事而无法响应I2C总线的操作,或这个地址没有对应的从机),这时,主机发出一个P信号终止传输或者重新发出一个S信号开始新的传输
(2)如果从机接收器在传输过程中不能接受更多的数据时,它也不会发出ACK信号。这样,主机就可以意识到这点,从而发出一个P信号终止传输或者重新发出一个S信号开始新的传输
(3)主机接收器在接收到最后一个字节后,也不会发出ACK信号,于是,从机发送器释放SDA线,以允许主机发出P信号结束传输
3、发送到SDA线上的每个字节必须是8位的,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB)。如果从机要完成一些其他功能后才能继续接收或发送下一个字节,从机可以拉低SCL迫使主机进入等待状态。当从机准备好接收下一个数据并释放SCL后,数据继续传输。如果主机在传输数据期间也需要完成一些其他功能,也可以拉低SCL以占住总线
启动传输时,即发出一个S信号,接着发出从机的地址(从机地址有7位和10位,如果为10位分两字节传输,这里要注意每次传输的8位数据中最高位(MSB)先发送)和一位表示传输方向的位(0表示写,1表示读)

如上讲了硬件层面的数据传输,i2c_adapter直接与硬件打交道。但是在写i2c_driver驱动的需要从i2c设备中读取或发送数据怎么办?在i2c总线中也封装了一些i2c数据传输的接口(这就是上面说的,数据传输的软件层面),在i2c_core.c文件下有i2c_master_send、i2c_master_recv、i2c_smbus_read_byte、i2c_smbus_read_byte、i2c_smbus_read_byte_data、i2c_smbus_write_byte_data、i2c_smbus_read_word_data、i2c_smbus_write_word_data,i2c总线主要为驱动编程人员封装了这几个i2c数据传输的接口,可根据需要去具体使用,对于这些接口的实现,主要还是通过找到adapter->algo->smbus_xfer来进行数据传输。
讲了这么多还是很有必要了解下应用层访问一个i2c设备的具体过程,在上面讲创建一个i2c设备文件的时候,略微提了一下i2c的上层访问入口函数操作集i2cdev_fops,下面看下它的实现

static const struct file_operations i2cdev_fops = {
    .owner      = THIS_MODULE,
    .llseek     = no_llseek,
    .read       = i2cdev_read,
    .write      = i2cdev_write,
    .unlocked_ioctl = i2cdev_ioctl,
    .open       = i2cdev_open,
    .release    = i2cdev_release,
};

上层应用打开一个i2c设备就会调用这里的i2cdev_open函数(通过虚拟文件系统映射),先看下打开一个i2c设备的过程:

static int i2cdev_open(struct inode *inode, struct file *file)
{
    unsigned int minor = iminor(inode);
    struct i2c_client *client;
    struct i2c_adapter *adap;
    struct i2c_dev *i2c_dev;
    i2c_dev = i2c_dev_get_by_minor(minor);
    if (!i2c_dev)
        return -ENODEV;
    adap = i2c_get_adapter(i2c_dev->adap->nr);
    if (!adap)
        return -ENODEV;
    /* This creates an anonymous i2c_client, which may later be
     * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
     *
     * This client is ** NEVER REGISTERED ** with the driver model
     * or I2C core code!!  It just holds private copies of addressing
     * information and maybe a PEC flag.
     */
    client = kzalloc(sizeof(*client), GFP_KERNEL);
    if (!client) {
        i2c_put_adapter(adap);
        return -ENOMEM;
    }
    snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
    client->adapter = adap;
    file->private_data = client;
    return 0;
}

首先是调用i2c_dev_get_by_minor通过设备的次设备号找到它的struct i2c_dev,内核有个i2c_dev_list链表对i2c_dev成员进行管理,这个函数的主要实现就是从i2c_dev_list链表中逐个取出i2c_dev成员,进行i2c_dev->adap->nr 和该设备次设备号的比较,相等则返回i2c_dev,然后调用i2c_get_adapter找到它的adapter,构造一个struct i2c_client *client;并把adapter赋值给client的adapter成员,然后把client成员放在文件描述结构file的私有数据private_data成员里面,方便在读写函数中直接从文件描述结file中private_data取出该设备对应的struct i2c_client结构。对于读写数据,都差不多,只要去看它的一个实现就够了,看下它的写函数:

static ssize_t i2cdev_write(struct file *file, const char __user *buf,size_t count, loff_t *offset) 
{
    int ret;
    char *tmp;
    struct i2c_client *client = file->private_data;
    if (count > 8192)
        count = 8192;
    tmp = memdup_user(buf, count);
    if (IS_ERR(tmp))
        return PTR_ERR(tmp);
    pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",iminor(file->f_path.dentry->d_inode), count);
    ret = i2c_master_send(client, tmp, count);
    kfree(tmp);
    return ret;
}首先取出在open时构造的struct i2c_client *client结构,然后是对写长度的判断,由此可见,对于i2c设备一次最大只能写8192个字节,调用memdup_user从用户空间映射到内核空间(为了安全考虑),再是调用i2c_master_send发送数据,这个函数是在i2c_core.c里实现的
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
    int ret;
    struct i2c_adapter *adap = client->adapter;
    struct i2c_msg msg;

    msg.addr = client->addr;
    msg.flags = client->flags & I2C_M_TEN;
    msg.len = count;
    msg.buf = (char *)buf;
    ret = i2c_transfer(adap, &msg, 1);
    /*
     * If everything went ok (i.e. 1 msg transmitted), return #bytes
     * transmitted, else error code.
     */
    return (ret == 1) ? count : ret;
}

在这里需要注意一点,从client中取出设备地址,但是在前面打开一个i2c设备,只是构造一个struct i2c_client并把i2c_adapter赋值给它的adapter成员,没有做任何的初始化,那么就要考虑client中addr的来源了(稍后再讲),这里是构造一个数据包,然后调用i2c_transfer进行发送,这个也是在i2c_core.c里面提供

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    unsigned long orig_jiffies;
    int ret, try;

    /* REVISIT the fault reporting model here is weak:
     *
     *  - When we get an error after receiving N bytes from a slave,
     *    there is no way to report "N".
     *
     *  - When we get a NAK after transmitting N bytes to a slave,
     *    there is no way to report "N" ... or to let the master
     *    continue executing the rest of this combined message, if
     *    that's the appropriate response.
     *
     *  - When for example "num" is two and we successfully complete
     *    the first message but get an error part way through the
     *    second, it's unclear whether that should be reported as
     *    one (discarding status on the second message) or errno
     *    (discarding status on the first one).
     */

    if (adap->algo->master_xfer) {
#ifdef DEBUG
        for (ret = 0; ret < num; ret++) {
            dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
                "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
                ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
                (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
        }
#endif

        if (in_atomic() || irqs_disabled()) {
            ret = i2c_trylock_adapter(adap);
            if (!ret)
                /* I2C activity is ongoing. */
                return -EAGAIN;
        } else {
            i2c_lock_adapter(adap);
        }

        /* Retry automatically on arbitration loss */
        orig_jiffies = jiffies;
        for (ret = 0, try = 0; try <= adap->retries; try++) {
            ret = adap->algo->master_xfer(adap, msgs, num);
            if (ret != -EAGAIN)
                break;
            if (time_after(jiffies, orig_jiffies + adap->timeout))
                break;
        }
        i2c_unlock_adapter(adap);

        return ret;
    } else {
        dev_dbg(&adap->dev, "I2C level transfers not supported\n");
        return -EOPNOTSUPP;
    }
}

从代码可见,最终还是调用adap->algo->master_xfer,这就回到了i2c_adapter部分,在前面可以知道i2c_adapter主要是负责硬件数据传输的启动,最终在i2c_driver里面注册的i2c中断来完成数据传输的过程。到这里,上层应用访问一个i2c设备到硬件的数据传输就贯通起来了,在上面还有一个疑点,client->addr从哪里来?其实这在open函数已经有注释了,如下是内核在i2cdev_open中的一段注释:
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is * NEVER REGISTERED * with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/
意思就是打开一个i2c设备后可以通过系统调用ioctl发送 I2C_SLAVE或者I2C_SLAVE_FORCE.命令来设置i2c设备的地址,最终是调用这里的函数:

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct i2c_client *client = file->private_data;
    unsigned long funcs;

    dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
        cmd, arg);

    switch (cmd) {
    case I2C_SLAVE:
    case I2C_SLAVE_FORCE://设置i2c设备地址
        /* NOTE:  devices set up to work with "new style" drivers
         * can't use I2C_SLAVE, even when the device node is not
         * bound to a driver.  Only I2C_SLAVE_FORCE will work.
         *
         * Setting the PEC flag here won't affect kernel drivers,
         * which will be using the i2c_client node registered with
         * the driver model core.  Likewise, when that client has
         * the PEC flag already set, the i2c-dev driver won't see
         * (or use) this setting.
         */
        if ((arg > 0x3ff) ||
            (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
            return -EINVAL;
        if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
            return -EBUSY;
        /* REVISIT: address could become busy later */
        client->addr = arg;
        return 0;
    case I2C_TENBIT:
        if (arg)
            client->flags |= I2C_M_TEN;
        else
            client->flags &= ~I2C_M_TEN;
        return 0;
    case I2C_PEC:
        if (arg)
            client->flags |= I2C_CLIENT_PEC;
        else
            client->flags &= ~I2C_CLIENT_PEC;
        return 0;
    case I2C_FUNCS://获取该设备下的i2c控制器支持的功能
        funcs = i2c_get_functionality(client->adapter);
        return put_user(funcs, (unsigned long __user *)arg);

    case I2C_RDWR://读写数据除了调用read、write外,这里还提供了一个读写数据命令
        return i2cdev_ioctl_rdrw(client, arg);

    case I2C_SMBUS:
        return i2cdev_ioctl_smbus(client, arg);

    case I2C_RETRIES://设置重复发送次数
        client->adapter->retries = arg;
        break;
    case I2C_TIMEOUT://设置设备响应的超时时间
        /* For historical reasons, user-space sets the timeout
         * value in units of 10 ms.
         */
        client->adapter->timeout = msecs_to_jiffies(arg * 10);
        break;
    default:
        /* NOTE:  returning a fault code here could cause trouble
         * in buggy userspace code.  Some old kernel bugs returned
         * zero in this case, and userspace code might accidentally
         * have depended on that bug.
         */
        return -ENOTTY;
    }
    return 0;
}

由上代码可见除了发送 I2C_SLAVE或者I2C_SLAVE_FORCE来设置地址外,内核还提供了其他的命令来访问和设置一些重要的信息,看注释,这里还需要注意一下,在发送I2C_SLAVE设置设备地址会去调用 i2cdev_check_addr(client->adapter, arg)来检测这个设备是否已经注册进该adapter,是才设置,否则返回busy标志,而发送I2C_SLAVE_FORCE设置地址不会去检测。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值