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\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);       /* 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 下一页

相关内容