I2C的一些函数赏析

i2c总线

先来一张g-sensor原理图
这里写图片描述

struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    /**
        总线上提供了probe,
        根据前面博文的浅析,匹配成功后,会优先调用总线提供的这个probe将会被调用。
        i2c程序中 在总线提供的probe函数中,会调用驱动提供的probe函数
    */
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
    .suspend    = i2c_device_suspend,
    .resume     = i2c_device_resume,
};
: match函数: i2c_device_match,

下面是一张函数调用流程图:
这里写图片描述

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    /**
        struct i2c_client *i2c_verify_client(struct device *dev)
        {
            return (dev->type == &i2c_client_type)? to_i2c_client(dev): NULL;
        }
        #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
        对设备的验证,检查是否是client设备,
        为什么要检查?
        因为有两种类型的设备可以挂接再iic总线上:
            i2c_client   表示 i2c总线下的一个从设备
            i2c_adapter  表示 i2c总线控制器(总线控制器也是一种设备,也挂接再i2c总线的设备链表上,是什么时候挂上去的,下面有分析)
        以上两种设备类型。是根据type来分辨的。
        这里要要匹配的是 总线上挂接的设备i2c_client和对应的驱动。

        下面会有一个函数 : __process_new_driver()它也是来判断设备的类型的,只不过 它检测如果不是i2c_adapter类型就会返回不往下执行了

        static int __process_new_driver(struct device *dev, void *data)
        {
            if (dev->type != &i2c_adapter_type)
                return 0;
            return i2c_do_add_adapter(data, to_i2c_adapter(dev));
        }
    */
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;

    /**
        如果不是i2c_client设备,就立即返回
    */
    if (!client)
    { 
        return 0;
    }   
    /*
        #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
        获取对应的驱动
    */
    driver = to_i2c_driver(drv);

    /**
        driver中的id_table中声明了驱动支持的设备,它是一个数组,因为id_table的出现
        让一个驱动支持多个设备成为了可能,如果只是简单的直接比较name,那么一个驱动只能支持一个驱动
        但是在i2c_match_id()函数中还是利用name来一一匹配的
    */
    if (driver->id_table)
    {
        /**
            看看 i2c_match_id()这个函数干了什么事情
            这个函数主要就是遍历id_table中的每一个表项

            while (id->name[0]) 
            {
            最终还是利用name来判断的。
            if (strcmp(client->name, id->name) == 0)
                return id;
                id++;
            }
            return NULL;
        */
        return i2c_match_id(driver->id_table, client) != NULL;
    }
    return 0;
}
/**
    看一个内核中的例子:
                    static const struct i2c_device_id adt7473_id[] = {
                        { "adt7473", adt7473 },
                        { }
                    };
                    static struct i2c_driver adt7473_driver = {
                            .class      = I2C_CLASS_HWMON,
                            .driver = {
                                .name   = "adt7473",
                            },
                            .probe      = adt7473_probe,
                            .remove     = adt7473_remove,
                            .id_table   = adt7473_id,
                            .detect     = adt7473_detect,
                            .address_data   = &addr_data,
                        };
*/
i2c_device_probe
/**
    这个函数也只是做了,设备与驱动绑定之后调用驱动的probe函数
*/
static int i2c_device_probe(struct device *dev)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;
    int status;
    if (!client)
    {
        return 0;
    }
    driver = to_i2c_driver(dev->driver);
    /**
        如果驱动没有提供probe函数,或者驱动没有提供id_table,就出错返回
    */
    if (!driver->probe || !driver->id_table)
    {
        return -ENODEV;
    }   
    /**
        将设备与驱动绑定
    */  
    client->driver = driver;
    /*
        如果i2c设备可以唤醒,那么系统就唤醒它
    */
    if (!device_can_wakeup(&client->dev))
    {
        device_init_wakeup(&client->dev,client->flags & I2C_CLIENT_WAKE);
    }
    dev_dbg(dev, "probe\n");
    /**
        调用驱动提供的probe函数
        最终还是调用了驱动的probe,
        其他的总线,如 usb、spi...都没有提供probe函数,由于总线上没有提供probe,那么就调用了
        驱动的probe函数,但是iic总线提供的probe函数也没有做其他什么操作,只是简单的检查一下类型、绑定设备与驱动后
        就草草的收场了,下面有一匹配的流程图。
    */
    status = driver->probe(client, i2c_match_id(driver->id_table, client));
    if (status)
    {
        client->driver = NULL;
    }   
    return status;              
}
匹配流程图

这里写图片描述
i2c_register_board_info

/**
    这个函数没有创建i2c_client,
    为每一个i2c设备分配了一个i2c_devinfo,将i2c_devinfo添加到全局__i2c_board_list链表上。

    那么,
        为什么要把iic设备的信息放到 __i2c_board_list 链表上呢? 
        什么时候才创建i2c_client呢,什么时候才进行注册呢?

     下面在添加iic_adapter的时候 会扫面这条链表,将iic设备的信息取出来
     通过i2c_new_device()->device_register()将这条链表上的iic设备 添加到设备驱动模型中。

    所以 ,不管是那种方式注册iic设备,最终都是会调用i2c_new_device来注册的。
*/
int __init i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
{
    int status;
    /**
        写者用来获取信号量,若没获得时,则调用者睡眠等待。
    */
    down_write(&__i2c_board_lock);

    /**
        动态分配的I2C总线号总是大于静态分配的I2C总线号。
    */
    if (busnum >= __i2c_first_dynamic_bus_num)
    {
        __i2c_first_dynamic_bus_num = busnum + 1;
    }
    /**
        遍历struct i2c_board_info[]数组,
    */
    for (status = 0; len; len--, info++) 
    {
        /**
            struct i2c_devinfo {
                struct list_head    list;  //用于链接到__i2c_board_list链表
                int         busnum;        //保存i2c设备依附的i2c总线号
                struct i2c_board_info   board_info;//用来保存i2c设备的信息
            };
        */
        struct i2c_devinfo  *devinfo;
        /**
            为i2c_devinfo分配内存空间
        */
        devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
        if (!devinfo) 
        {
            pr_debug("i2c-core: can't register boardinfo!\n");
            status = -ENOMEM;
            break;
        }
        /**
            赋值初始化 devinfo。  
        */
        devinfo->busnum = busnum;
        devinfo->board_info = *info;
        /**
            将devinfo 添加到 __i2c_board_list 链表上。
        */
        list_add_tail(&devinfo->list, &__i2c_board_list);
    }
    up_write(&__i2c_board_lock);

    return status;
}
/**
`   下面是内核中使用的例子:
*/
static struct i2c_board_info __initdata h4_i2c_board_info[] = {
        {
            I2C_BOARD_INFO("isp1301_omap",0x2d),
        },
        {
            I2C_BOARD_INFO("24c01",0x52),
        },
        {
            I2C_BOARD_INFO("24c01",0x57),
        }
    };
static void __init omap_h4_init(void)
    {
        ...
        i2c_register_board_info(1,h4_i2c_board_info,ARRAY_SIZE(h4_i2c_board_info));
        ...
    }

i2c_new_device

这里写图片描述
I2C设备是在适配器注册成功之后生成的。

那么如果要再适配器注册完成之后添加i2c设备,怎么办呢?可以使用i2c_new_device()这个函数

/**
    这个函数一次只能注册一个设备,但是i2c_register_board_info可以一次注册多个设备
    这是他们之间的一个区别,其实iic_register_board_info底层也是通过i2c_new_device来实现的。

    i2c_new_device:断定 设备是已经存在的了,即使地址和设备的地址不符合也会成功。
    如:
        static struct i2c_board_info xxxx = {
            I2C_BOARD_INFO("hmc5883l",0x1111),
        };
    实验中,也是能注册的。
    其实,i2c_new_probed_device 最终也会调用i2c_new_device()函数,
    只是在调用i2c_new_device之前,要检查设备是否存在。    
    它是怎么检查设备是否存在的呢?很简单,请往下面看。

    创建i2c_client的方法有很多,比如:也可以从用户空间来创建设备,
    但是不关是那种方法,最终都是间接或则直接的方式来调用 下面这个函数 i2c_new_device

    这个函数,主要做了以下事情 :
                1 : 分配了一个i2c_client结构体
                1 : 根据传入的参数,对i2c_client进行了赋值
                    指明,i2c_client要挂在的总线
                          i2c_client的类型
                2 : 调用device_register()将这个i2c_client注册到设备驱动模型中  
*/
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client   *client;
    int         status;
    /**
        为i2c_client分配内存空间
    */
    client = kzalloc(sizeof *client, GFP_KERNEL);
    if (!client)
    {
        return NULL;
    }   
    /**
        与适配器绑定
    */
    client->adapter = adap;
    /**

        保存设备数据, 如果有,保存起来,以后需要用的时候,可以从client->dev.platform_data拿出来
        这个和 file->private_data 设计的都很好。
    */
    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));//名字

    /**
        检测地址是否有效.
        对于 7位的设备地址来说,不能大于 0x7f. 下面有一个小例子 来验证这个.

        if (client->addr == 0x00 || client->addr > 0x7f)
            return -EINVAL;
    */
    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;
    }
    /**
        检测指定适配器上该地址状态
    */
    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;    //指定依附的总线
    /**
        本次注册的设备类型是i2c_client_type类型。
        上面也说过,i2c_adapter也挂接在i2c总线上
    */
    client->dev.type = &i2c_client_type;
    client->dev.of_node = info->of_node;

    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;
}
i2c_new_probed_device
/**
    probe函数是可以选择的
    如果没有提供,会使用默认的probe函数。
    这个默认的probe函数,其实就是通过发送一个start信号,看能不能得到一个ack信号
    来判断的。
*/
struct i2c_client *
i2c_new_probed_device(struct i2c_adapter *adap,
              struct i2c_board_info *info,
              unsigned short const *addr_list,
              int (*probe)(struct i2c_adapter *, unsigned short addr))
{
    int i;
    /**
        如果没有提供probe函数,那么就使用默认的probe函数
        上面说了,就是看能不能得到一个ack信号
    */
    if (!probe)
    {
        probe = i2c_default_probe;
    }   
    /**
        遍历地址列表
    */
    for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) 
    {
        /**
            检测地址是否有效
        */  
        if (i2c_check_addr_validity(addr_list[i]) < 0) {
            dev_warn(&adap->dev, "Invalid 7-bit address "
                 "0x%02x\n", addr_list[i]);
            continue;
        }

        /* 检测指定适配器上该地址状态 */
        if (i2c_check_addr_busy(adap, addr_list[i])) {
            dev_dbg(&adap->dev, "Address 0x%02x already in "
                "use, not probing\n", addr_list[i]);
            continue;
        }

        /* 坚持能不能获取到ack信号 */
        if (probe(adap, addr_list[i]))
        {
            break;
        }   
    }
    if (addr_list[i] == I2C_CLIENT_END) 
    {
        dev_dbg(&adap->dev, "Probing failed, no device found\n");
        return NULL;
    }
    /**
        经过上面的判断,地址确切存在,
        那么还是调用了i2c_new_device()函数
    */
    info->addr = addr_list[i];
    return i2c_new_device(adap, info);
}             
i2c设备与驱动的关系

这里写图片描述

/**
    注册i2c设备驱动
*/
#define i2c_add_driver(driver) \
    i2c_register_driver(THIS_MODULE, driver)

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    int res;
    if (unlikely(WARN_ON(!i2c_bus_type.p)))
    {
        return -EAGAIN;
    }
    driver->driver.owner = owner;
    /**
        设置总线为 i2c总线
        设备驱动模型中的 device、driver通常都是嵌入在一个更大的结构体中,表示一些通用的信息
        将设备注入设备驱动模型中 很方便。
    */
    driver->driver.bus = &i2c_bus_type; 
    /**
        所有驱动的注册都会经过这个函数,可以叫做驱动加入设备驱动模型的入口吧。
        那么i2c_bus_type提供的probe函数就会被调用了
    */
    res = driver_register(&driver->driver);
    if(res)
    {
        return res;
    }
    INIT_LIST_HEAD(&driver->clients);
    mutex_lock(&core_lock);
    /**
        下面有一幅图,是注册一个iic设备后的log信息。

        遍历i2c总线上所有的设备(adapter device 和 client device)
        源码 : 
        int bus_for_each_dev(struct bus_type *bus, struct device *start,
             void *data, int (*fn)(struct device *, void *))
        {
            ...
            while ((dev = next_device(&i)) && !error)
                    //__attach_adapter被调用
                error = fn(dev, data);

            klist_iter_exit(&i);
            return error;
        }

        static int __attach_adapter(struct device *dev, void *data)
        {
            struct i2c_adapter *adapter;
            struct i2c_driver *driver = data;

            这里检测如果设备不是适配器,那么就直接返回
            在开头的i2c_device_match()函数也赏析过。

            if (dev->type != &i2c_adapter_type)
            {
                return 0;
            }
            获取适配器   
            adapter = to_i2c_adapter(dev);

            i2c_detect()这个函数会检测:
            如果驱动没有提供detect的话 就不会往下执行了
            if (!driver->detect || !address_data)
            {
                return 0;
            }   

            i2c_detect(adapter, driver);

            如果驱动提供了attach_adapter,那么就调用他。
            在会调用device_create()函数来创建设备
            if (driver->attach_adapter)
            {
                driver->attach_adapter(adapter);
            }
            return 0;
        }   
    */
    bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
    mutex_unlock(&core_lock);

    return 0;
}

static int __init i2c_dev_init(void)
{
    int res;
    printk(KERN_INFO "i2c /dev entries driver\n");
    /**
        注册一个字符设备,他向vfs层提供的接口是 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,
        };
    */
    ret = register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops);
    if(ret)
    {
        goto out;
    }
    /**
        创建类
    */
    i2c_dev_class = class_create(THIS_MODULE,"i2c-dev");
    if(IS_ERR(i2c_dev_class))
    {
        ret = PTR_ERR(i2c_dev_class);
        goto out_unreg_chrdev;
    }
    /**
        注册一个i2c_driver
        他是对i2c_register_driver()的封装,上面简单的赏析过。
        看看这个i2cdev_driver
        static struct i2c_driver i2cdev_driver = {
            .driver = {
                .name   = "dev_driver",
            },
            .attach_adapter = i2cdev_attach_adapter,
            .detach_adapter = i2cdev_detach_adapter,
        };
    */
    res = i2c_add_driver(&i2cdev_driver);
    if(res)
    {
        goto out_unreg_class;
    }

    return 0;   
}

注册一个iic设备后 ,iic-core 打印出来的log信息
这里写图片描述

iic的读写过程

这里写图片描述这里写图片描述


/**
    1 : 这个中断处理函数是在那里、什么时候被注册的?
    在s3c24xx_i2c_probe()函数中会注册这个中断处理函数

    static int s3c24xx_i2c_probe(struct platform_device *pdev)
    {
        struct s3c24xx_i2c *i2c;

        i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
        i2c->adap.owner   = THIS_MODULE;
        i2c->adap.algo    = &s3c24xx_i2c_algorithm;
        i2c->adap.retries = 2;
        i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
        i2c->tx_setup     = 50;

        .....

        ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);
        .....
    }

    2 : 那么这个中断处理函数会在什么时候被调用呢?
        有下面几种情况
                      1 : 总线仲裁错误.(1:控制器、从设备同时控制总线,2:多个从设备同时控制总线)
                      2 : 发送、接受一个字节的时候会发生中断

*/
static irqreturn_t s3c24xx_i2c_irq(int irqno,void * dev_id)
{
    /**
        这个dev_id有一个作用:
            在被中断的进程 与 中断处理程序中传递数据
            前面的博文已经分析过。

        这个dev_id  是在s3c24xx_i2c_probe()函数中封装好的 s3c24xx_i2c
        请参考上面的s3c24xx_i2c_probe函数
    */
    struct s3c24xx_i2c * i2c = dev_id;

    unsigned long status;
    unsigned long tmp;

    /**
        读取IICSTAT寄存器的值
        这个IICSTAT寄存器:
                bit[5] 表示总线空闲、忙....

        下面的函数 s3c24xx_i2c_set_master()函数会根据这一位来判断
        总线的空、忙状态。
        因为适配器要发送数据到 iic总线,首先要判断总线是不是空闲状态
        而且他会每隔 1 ms 尝试一次,一共会尝试 400次。
        下面会有讲解
    */
    status = readl(i2c->regs + S3C2410_IICSTAT);

    /**
        #define S3C2410_IICSTAT_ARBITR      (1<<3)
        这里判断的是 由总线仲裁发生的中断
    */
    if (status & S3C2410_IICSTAT_ARBITR)
    {
        dev_err(i2c->dev,"deal with arbitration loss\n");
    }

    /**
        如果总线处于空闲状态,这里可能是因为不是读写而引发的中断
        所以要清除中断
    */
    if (i2c->state == STATE_IDLE)
    {
        dev_dbg(i2c->dev,"IRQ : error i2c->state == IDLE\n");

        /**
            读取IICCON寄存器
            #define S3C2410_IICCON_IRQPEND      (1<<4)
            清[4]为0
        */
        tmp = readl(i2c->regs + S3C2410_IICCON);
        tmp = readl(i2c->regs + S3C2410_IICCON);
        tmp &= ~S3C2410_IICCON_IRQPEND;
        writel(tmp,i2c->regs + S3C2410_IICCON);

        goto out;
    }
    /**
        这个函数很重要,下面详细分析
    */
    i2s_s3c_irq_nextbyte(i2c,status);
out:
    return IRQ_HANDLED;
}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值