i2c内核数据结构之间的关系(编辑修改稿)内容摘要:

/* private data for the adapter */ struct semaphore lock。 unsigned int flags。 /* flags specifying div. data */ struct i2c_client *clients[I2C_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 KERNEL_VERSION(2,1,29) struct proc_dir_entry *proc_entry。 endif endif /* def CONFIG_PROC_FS */ }。 在 i2c_adapter 数据结构中设计了 clients 指针数组,指向该总线上每个设备的 i2c_client 数据结构。 由于一条 i2c 总线上最多只有 I2C_CLENT_MAX 个设备,所以可以使用静态数组(题外话,如果相关数据结构的个数是未知的 ,链表显然是更好的选择)。 lock 信号量用于实现对 i2c 总线的互斥访问:在访问 i2c 总线上的任一设备期间当前进程必须首先获得该信号量,并且在阻塞等待 i2c 操作完成期间不释放。 一个 i2c 适配器上的 i2c 总线通信方法由其驱动程序提供的 i2c_algorithm 数据结构描述,由 algo 指针指向。 i2c_algorithm 数据结构即为 i2c_adapter 数据结构与具体 i2c 适配器的总线通信方法的中间层,由下文可见正是这个中间层使得上层的 i2c 框架代码与与具体 i2c 适配器的总线通信方法无关,从而实现了 i2c 框架 的可移植性和重用性。 当安装具体 i2c 适配器的驱动程序时由相应驱动程序实现具体的 i2c_algorithm 数据结构,其中的函数指针指向操作具体 i2c 适配器的代码(换用面向对象的语言,就是当创建子类对象时将基第 1 章 i2c 核心数据结构之间的关系 7 类中定义的函数调用接口实例化为与具体子类相关的代码。 值得说明的是,在 Linux内核层次中数据结构的设计大量地采用了面向对象的概念来实现框架的可移植性和重用性)。 inc_use 和 dec_use 方法可以用来控制适配器驱动程序的引用计数; client_register 和 client_unregister 函数可以用来完成适配器端的、额外的设备注册和注销工作。 这些函数在当前 pxa255 的 i2c 补丁中都没有实现。 最后 timeout 和 retries 用于超时重传机制。 4. 具体 i2c 适配器的通信方法由 i2c_algorithm 数据结构进行描述: struct i2c_algorithm { char name[32]。 /* textual description */ unsigned int id。 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 mand, int size, union i2c_smbus_data * data)。 int (*slave_send)(struct i2c_adapter *,char*,int)。 int (*slave_recv)(struct i2c_adapter *,char*,int)。 int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long)。 u32 (*functionality) (struct i2c_adapter *)。 }。 master_xfer/smbus_xfer 指针指向 i2c 适配器驱动程序模块实现的 i2c 通信协议或者 smbus 通信协议。 由下文分析可见在用户进程通过 i2cdev 提供的 /dev/i2c/%d 设备节点访问 i2c 设备时,最终是通过调用master_xfer 或者 smbus_xfer 指向的方法完成的。 slave_send/recv函数用于实现当 i2c适配器扮演 slave角色时的传输方法。 由于在 pxa255的现有应用中其 i2c适配器始终为主导 i2c 通信的 master,故补丁中这两个函数都没有实现。 i2c_algorithm 提供了 i2c 适配器的驱动,而 i2c 设备的驱动为 i2c_driver。 内核中静态指针数组 adapters 和drivers 分别记录所有已经注册的 i2c 总线设备和 i2c 设备驱动。 从下文源代码分析可以看到,安装 i2c 总线驱动和 i2c 设备驱动的顺序不确定,因此在安装 i2c设备驱动时必须遍历所有已注册的适配器上的 i2c 设备,以“认领”相应的设备;同理,在安装 i2c 适配器驱动时必须遍历所有已注册的 i2c 设备的驱动程序,让已有驱动程序“认领”新注册的适配器上的所有设备。 5. 假设一条 i2c 总线上有两个使用相同驱 动程序的 i2c 设备,在打开该 i2c 总线的设备结点后相关数据结构之间的逻辑组织关系如下图所示。 在阅读下文时请经常参照下图。 上层的 i2c 框架实现了控制策略,具体 i2c 适配器和设备的驱动实现了使具体设备可用的机制,上层策略和底层机制通过中间的函数调用接口联系。 正是中间的函数调用接口使得上层策略与底层机制无关,从而使得上层策略具有良好的可移植性和重用性。 阅读完全文后可以回过头来总结一下各数据结构的作用、创建时机、由谁创建等,品味这一点是如何通过这些数据结构实现的,进一步在自己的研发过程中积极实践这种思想并享受学 以致用的乐趣:) 第 1 章 i2c 核心数据结构之间的关系 8 i2c_msg i2c_algorithm name id master_xfer smbus_xfer …… algo_control functionality name id flags attach_adapter detach_client mand inc_use dec_use i2c_driver name id flags addr adapter driver data usage_count i2c_client name id flags addr adapter driver data usage_count i2c_client lock wait msg msg_num msg_idx msg_ptr …… name id algo_data algo inc_use dec_use client_register client_unregister data lock flags client_count timeout retries clients pxa_i2c addr flags len buf i2c_pxa_xfer ltc3445_attach_adapter …… addr adapter driver …… i2c_client ltc3445_detach_adapter adapters[ ] drivers[ ] file …… f_op private_data i2cdev_open i2cdev_read i2cdev_write i2cdev_ioctl i2cdev_fops 第 2 章 的初始化 9 第 2 章 的初始化 i2c 框架的主体代码,包括 i2c 适配器驱动和设备驱动的注册、注销管理的框架代码、 i2c通信方法的上层、与具体适配器无关的代码和检测设备地址的上层代码等。 i2c_init 函数 i2ccore 模块的初始化函数如下: static int __init i2c_init(void) { printk(KERN_DEBUG : i2c core module\n)。 memset(adapters,0,sizeof(adapters))。 memset(drivers,0,sizeof(drivers))。 adap_count=0。 driver_count=0。 init_MUTEX(amp。 adap_lock)。 init_MUTEX(amp。 driver_lock)。 在 中定义了内核静态指针数组 adapters 和 drivers,用于注册描述 i2c 适配器及其驱动程序的i2c_adapter 数据结构和描述设备及其驱动程序的 i2c_driver 数据结构。 同时也定义了保护这两个全局指针数组的信号量: static struct i2c_adapter *adapters[I2C_ADAP_MAX]。 static struct i2c_driver *drivers[I2C_DRIVER_MAX]。 struct semaphore adap_lock。 struct semaphore driver_lock。 其中表示适配器和驱动程序最大数量的宏在 linux/ 中均被定义为 16。 由于安装 i2c 适配器驱动程序模块和 i2c 设备驱动程序模块的先后顺序不确定,所以必须通过全局数据结构(比如这里的指针数组或者链表等)来保存所有已安装的数据结构的地址,这样在后安装 i2c 适配器驱动程序时就可将 drivers指针数组中记录的地址传递给 i2c_add_adapter 函数;在后安装 i2c 设备驱动程序时就可将 adapters 指针数组中记录的地址传递给 i2c_add_driver 函数(而这两个函数最终都是通过 attach_adapter 函数“认领”设备的)。 这里首先初始化 adapters 和 drivers 指针数组及相关信号量 adap_lock 和 driver_lock,并将计数器清 0。 i2cproc_init()。 return 0。 } 第 2 章 的初始化 10 然后通过 i2cproc_init 函数创建相应的 /proc/bus/i2c 文件,使得用户进程可以通过该文件得到当前系统上所有已注册的 i2c 总线信息。 i2cproc_init 函数 该函数用于创建描述系统中所有 i2c 总线的 /proc/bus/i2c 文件。 int i2cproc_init(void) { struct proc_dir_entry *proc_bus_i2c。 i2cproc_initialized = 0。 if (! proc_bus) { printk(: /proc/bus/ does not exist)。 i2cproc_cleanup()。 return ENOENT。 } 在 proc 接口初始化程度的静态变量 i2cproc_initialized,其初始值为 0。 内核全局变量 proc_bus 定义于 fs/proc/,声明于 include/linux/,在 fs/proc/ 的 proc_root_init 函数中初始化: struct proc_dir_entry *proc_, *proc_bus, …… void __init proc_root_init(void) { int err = register_filesystem(amp。 proc_fs_type)。 if (err) return。 proc_mnt = kern_mount(amp。 proc_fs_type)。 err = PTR_ERR(proc_mnt)。 if (IS_ERR(proc_mnt)) { unregister_filesystem(amp。 proc_fs_type)。 return。 } …… proc_bus = proc_mkdir(bus, 0)。 } 可见在 proc_root_init函数中注册、挂载 p。
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。