1、你可以自由地随意修改本文档的任何文字内容及图表声明你可以自由地随意修改本文档的任何文字内容及图表,但是如果你在自己的文档中以任何形式直接引用了本文档的任何原有文字或图表并希望发布你的文档,那么你也得保证让所有得到你的文档的人同时享有你曾经享有过的权利。i2c源代码情景分析(Beta2)作者在上的ID为shrek2欢迎补充,欢迎批评指正!(注意:本文档中的源代码以i2c-2.9.1包及www.arm.linux.org.uk上下载的pxa的i2c适配器的补丁2360-2为准!)第1章 i2c核心数据结构之间的关系i2c总线适配器(adapter)就是一条i2c总线的控制器,在物理连接上若干i2c
2、设备并联于该i2c总线的SCL和SDA线上,如下图所示:那么相应软件数据结构的设计、数据结构之间的关系就至少应该描述硬件物理连接的这种组织关系。Linux的i2c框架中各个部分的关系如下图所示:内核中i2c相关代码可以分为三个层次:1. i2c框架:i2c.h和i2c-core.c为i2c框架的主体,提供了核心数据结构的定义、i2c适配器驱动和设备驱动的注册、注销管理,i2c通信方法上层的、与具体适配器无关的代码、检测设备地址的上层代码等;i2c-dev.c用于创建i2c适配器的/dev/i2c/%d设备节点,提供i2c设备访问方法等。2. i2c总线适配器驱动:定义描述具体i2c总线适配器的
3、i2c_adapter数据结构、实现在具体i2c适配器上的i2c总线通信方法,并由i2c_algorithm数据结构进行描述。3. i2c设备驱动:定义描述具体设备的i2c_client和可能的私有数据结构、借助i2c框架的i2c_probe函数实现注册设备的attach_adapter方法、提供设备可能使用的地址范围、以及设备地址检测成功后创建i2c_client数据结构的回调函数。下面介绍i2c各核心数据结构的定义和它们之间的连接关系。1. 一个i2c设备的驱动程序由i2c_driver数据结构描述,定义于include/linux/i2c.h:struct i2c_driver char
4、 name32; int id; unsigned int flags; int (*attach_adapter)(struct i2c_adapter *); int (*detach_client)(struct i2c_client *); int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); void (*inc_use)(struct i2c_client *client); void (*dec_use)(struct i2c_client *client);其中name为最大长度为32字节的
5、字符串,id可选0xf000到0xffff中的任一数值,flags域可以直接设置为I2C_DF_NOTIFY。attach_adapter回调函数在安装i2c设备驱动程序模块时、或者在安装i2c适配器驱动程序模块时被调用,用于检测、认领设备并为设备分配i2c_client数据结构。detach_client方法在卸载适配器或设备驱动程序模块时被调用,用于从总线上注销设备、并释放i2c_client及相应的私有数据结构。inc_use和dec_use所指向的函数用于改变i2c设备驱动程序模块的引用计数。注意不要直接调用i2c_driver数据结构中的这两个方法,而要通过如下函数调用路径:i2c_
6、use_client i2c_inc_use_client inc_usei2c_release_client i2c_dec_use_client dec_use通过最顶层的i2c_use/release_client函数来同时改变i2c设备和i2c适配器驱动程序模块的引用计数。另外,不能在attach_adapter函数检测到一个i2c设备时就增加驱动程序模块的引用计数,而应该在用户进程访问一个/dev/i2c/%d设备节点时增加模块的引用计数,则关闭设备节点时减少引用计数(但在当前的应用中,适配器和设备的驱动程序都是静态地链接入内核映像的,所以在pxa255的i2c补丁中并没有使用控制引
7、用计数的函数)。2. 一个i2c设备由i2c_client数据结构进行描述:struct i2c_client char name32; int id; unsigned int flags; /* div., see below */ unsigned int addr; /* chip address - NOTE: 7bit addresses are stored in the */ /* _LOWER_ 7 bits of this char */ struct i2c_adapter *adapter; /* the adapter we sit on */ struct i2c_
8、driver *driver; /* and our access routines */ void *data; /* for the clients */ int usage_count; /* How many accesses currently to the client */;在安装适配器或者设备的驱动程序时通过设备驱动程序i2c_driver中的attach_adapter函数检测设备地址。如果检测成功则调用设备驱动程序提供的回调函数创建描述设备的i2c_client数据结构,并将其中的driver指针指向设备驱动程序的i2c_driver数据结构。这样将来就可以使用i2c_dr
9、iver中的注销设备和控制引用计数的方法了。由下文可见在描述i2c适配器的i2c_adapter数据结构中设计了指向该总线上所有i2c设备的i2c_client数据结构的指针数组clients,而每个i2c_client又通过adapter指针回指i2c_adapter。数据结构之间类似的组织关系在Linux内核中屡见不鲜,比如父子进程的PCB之间、父目录及子目录和子文件的dentry之间,等等。每个i2c设备都有唯一的7位地址addr。由于设备可能支持多个地址,所以在设备驱动程序模块中要指出需要检测的地址范围(由i2c_client_address_data二维数组指定),而设备实际使用的地
10、址在检测成功并为之分配i2c_client数据结构时填入。以i2c设备ltc3445为例,硬件支持的地址为1001011或者0101011,即7位地址的高2位由具体的布线方法决定(可以分别接到VCC或者GND)。如果ltc3445驱动程序的开发者知道具体的布线方法,那么在驱动程序中就可以直接指定。否则可以指定地址检测范围为这两个地址,而在加载驱动程序模块时由软件进行地址检测。需要说明的是,i2c设备的7位地址是就当前i2c总线而言的,是“相对地址”。不同的i2c总线上的设备可以使用相同的7位地址,但是它们所在的i2c总线不同。所以在系统中一个i2c设备的“绝对地址”由二元组(i2c适配器的ID
11、和设备在该总线上的7位地址)表示。i2c_client数据结构为描述i2c设备的“模板”,而具体的i2c设备可能需要描述个性的私有数据。私有数据结构由i2c_client数据结构中的data域指向。设备驱动程序开发者可以设计合适的私有数据结构来描述硬件的特性。值得一提的是,目前在Linux内核中常用的表示与具体设备、对象等相关的私有数据结构的方法有两种,一种就是采用void类型的指针data来指向具体的私有数据结构,又比如file结构中的private_data域在设备驱动程序中往往被设置为指向具体的设备数据结构;第二种方法就是采用union域,比如VFS的super_block、inode数
12、据结构。super_block和inode数据结构本身集中描述了各种文件系统的共性,而具体文件系统的个性则放到union中进行描述,在挂载具体的文件系统时实例化为具体的union对象,比如ext2_inode_union或者jffs2_inode_info。有关设计私有数据的的讨论可参见本文末尾的讨论部分。当不同进程访问同一i2c总线时,对i2c总线的互斥访问由i2c_adapter的lock信号量实现,系统调用执行流只有在获得该信号量期间才能调用master_xfer,并且在阻塞期间不释放信号量(类似在读写正规文件期间必须持有inode.i_sem,参见本文末尾的讨论部分)。而usage_c
13、ount域为设备的使用引用计数,在i2c_use_client和i2c_release_client函数中控制usage_count域的值(但是当前pxa255的i2c补丁中并没有使用这两个函数,usage_count的值自初始化后就一直为0)。3. 一个i2c适配器由i2c_adapter数据结构描述:struct i2c_adapter char name32; unsigned int id; /* = is algo-id | hwdep.struct-id, for registered values see below */ struct i2c_algorithm *algo;
14、/* the algorithm to access the bus */ void *algo_data; void (*inc_use)(struct i2c_adapter *); void (*dec_use)(struct i2c_adapter *); int (*client_register)(struct i2c_client *); int (*client_unregister)(struct i2c_client *); void *data; /* private data for the adapter */ struct semaphore lock; unsig
15、ned int flags; /* flags specifying div. data */ struct i2c_client *clientsI2C_CLIENT_MAX; int client_count; int timeout; int retries;#ifdef CONFIG_PROC_FS /* No need to set this when you initialize the adapter */ int inode;#if LINUX_VERSION_CODE read_proc = &read_bus_i2c;#if (LINUX_VERSION_CODE = KE
16、RNEL_VERSION(2,3,27) proc_bus_i2c-owner = THIS_MODULE;#else proc_bus_i2c-fill_inode = &monitor_bus_i2c;#endif /* (LINUX_VERSION_CODE = KERNEL_VERSION(2,3,27) */ i2cproc_initialized += 2; return 0;第3章 安装、卸载pxa255的i2c适配器驱动程序pxa_i2c数据结构linux/i2c.h中定义的i2c_adapter数据结构为描述各种i2c适配器的通用“模板”,它定义了注册总线上所有设备的clients指针数组、指向具体i2c适配器的总线通信方法i2c_algorithm的algo指针、实现i2c总线操作原子性的lock信号量等设施。但在适配器的驱动程序中可以根据具体适配器的需要“扩充”该数据结构。在pxa255的i2c适配器驱动程序补丁中通过pxa_i2c数据结构来描述pxa255的i2c适配器:struct pxa_i2c spinlock_t lock; wait_queue_head_t wait; struct i2c_msg *msg; u
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1