最近学习
Linux的i2c 框架中各个部分的关系如下图所示:
内核中 i2c 相关代码可以分为三个层次:
1. i2c 框架:i2c.h 和i2c-core.c 为i2c 框架的主体,提供了核心数据结构的定义、i2c 适配器驱动和设备驱动的注册、注销管理,i2c 通信方法上层的、与具体适配器无关的代码、检测设备地址的上层代码等;i2c-dev.c 用于创建i2c 适配器的/dev/i2c/%d 设备节点,提供i2c 设备访问方法等。
2. i2c 总线适配器驱动:定义描述具体i2c 总线适配器的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 name[32]; //最大32字节的字符串
int id; //id 可选0xf000 到0xffff 中的任一数值
unsigned int flags; //一般直接设置为I2C_DF_NOTIFY
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);
};
现在讲解一下这个结构体:
(1).attach_adapter 回调函数在安装i2c 设备驱动程序模块时、或者在安装i2c 适配器驱动
程序模块时被调用,用于检测、认领设备并为设备分配i2c_client 数据结构.
(2).detach_client 方法在卸载适配器或设备驱动程序模块时被调用,用于从总线上注销设备、并释放i2c_client 及相应的私有数据结构。
(3).inc_use 和dec_use 所指向的函数用于改变i2c 设备驱动程序模块的引用计数。注意不要直接调用i2c_driver数据结构中的这两个方法,而要通过如下函数调用路径:
i2c_use_client > i2c_inc_use_client > inc_use
i2c_release_client > i2c_dec_use_client > dec_use
2. 一个 i2c 设备由i2c_client 数据结构进行描述:
struct i2c_client {
char name[32];
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_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_driver 中的注销设备和控制引用计数的方法了。
3. 一个 i2c 适配器由i2c_adapter 数据结构描述:
struct i2c_adapter {
char name[32];
unsigned int id; /* == is algo->id | hwdep.struct->id, for registered values see below */
struct i2c_algorithm *algo; /* 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;
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 操作完成期间不释放。
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 command, 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 *);
};
现在讲解一下这个结构体:
(1).master_xfer/smbus_xfer 指针指向i2c 适配器驱动程序模块实现的i2c 通信协议或者smbus 通信协议。由下文分析可见在用户进程通过i2c-dev 提供的/dev/i2c/%d 设备节点访问i2c 设备时,最终是通过调用master_xfer 或者smbus_xfer 指向的方法完成的。
(2).slave_send/recv 函数用于实现当i2c 适配器扮演slave 角色时的传输方法。