Linux IIC驱动
层次关系
- I2C core:对I2C总线、I2C adapter及I2C driver管理
- I2C adapter :针对不同类型的I2C控制器,实现对总线访问的具体方法
- I2C driver:针对特定I2C设备的具体操作,read、write、ioctl……
core 简化对设备编程,不用考虑硬件的接口,提高驱动的移植性。I2C的core层的代码,就是一堆的标准的API,驱动可以调用,具体的硬件的操作,是由特定的硬件相关的代码实现。
关键结构体
i2c_driver
struct i2c_driver { unsigned int class;//用探测的一种类型的I2C设备 int (*attach_adapter)(struct i2c_adapter *) __deprecated; //关联适配器不推荐使用 int (*detach_adapter)(struct i2c_adapter *) __deprecated;//分离适配器不推荐使用 /* Standard driver model interfaces */ int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); /* driver model interfaces that don't relate to enumeration */ void (*shutdown)(struct i2c_client *);//关机 int (*suspend)(struct i2c_client *, pm_message_t mesg);//暂停 int (*resume)(struct i2c_client *);//恢复 void (*alert)(struct i2c_client *, unsigned int data);//警报回调 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);//总线信号。 struct device_driver driver;//设备驱动模型的驱动 const struct i2c_device_id *id_table;//I2C设备支持的驱动程序列表 /* Device detection callback for automatic device creation */ int (*detect)(struct i2c_client *, struct i2c_board_info *);//设备检测的回调 const unsigned short *address_list;//I2C地址探测(检测) struct list_head clients;//我们创建的clients的探测列表(I2C核心使用的) };
i2c_driver 对应一套驱动方法,其主要成员函数是 probe()、remove()、 suspend()、resume()等,另外id_table是该驱动所支持的I2C设备的ID表。i2c_driver 与 i2c_client 的关系是一对多,一个 i2c_driver 上可以支持多个同等类型的 i2c_client。
i2c_client
struct i2c_client { unsigned short flags; /* 标志 */ unsigned short addr; /* 低 7 位的芯片地址 */ char name[I2C_NAME_SIZE]; /* 设备名称 */ struct i2c_adapter *adapter; /* 依附的 i2c_adapter */ struct i2c_driver *driver; /* 依附的 i2c_driver */ struct device dev; int irq; /* 设备使用的中断号 */ struct list_head detected; };
i2c_client 对应于真实的物理设备,每个 I2C 设备都需要一个 i2c_client 来描述
i2c_adapter
struct i2c_adapter { struct module *owner; /* 所属模块 */ unsigned int id; /* algorithm 的类型,定义于 i2c-id.h,以 I2C_ALGO_开始 */ unsigned int class; const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */ void *algo_data; /* algorithm 数据 */ struct rt_mutex bus_lock; int timeout; /* 超时时间,以 jiffies 为单位 */ int retries; /* 重试次数 */ struct device dev; /* 控制器设备 */ int nr; char name[48]; /* 控制器名称 */ struct completion dev_released; /* 用于同步 */ struct mutex userspace_clients_lock; struct list_head userspace_clients; };
i2c_adapter 对应于物理上的一个控制器。一个 I2C 控制器需要 i2c_algorithm 中提供的通信函数来控制控制器上产生特定的访问周期。
i2c_algorithm
struct i2c_algorithm { /* I2C 传输函数指针 */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); /* smbus 传输函数指针 */ int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); /* 返回控制器支持的功能 */ u32 (*functionality) (struct i2c_adapter *); };
一个 I2C 控制器需要 i2c_algorithm 中提供的通信函数来控制控制器上产生特定的访问周期。 master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。i2c_algorithm是与芯片的i2c_adapter适配器是唯一配对的
i2c_msg
struct i2c_msg { __u16 addr; /* 从设备地址 */ __u16 flags; /* 消息类型 */ __u16 len; /* 消息长度 */ __u8 *buf; /* 消息数据 */ };
i2c_msg 是 I2C 传输的基本单位,它包含了从设备的具体地址,消息的类型 以及要传输的具体数据信息。每个 I2C 消息传输前,都会产生一个开始位,紧接 着传送从设备地址,然后开始数据的发送或接收,对最后的消息还需产生一个停止位。
各数据结构之间的关系
i2c_driver就是一套驱动方法,与硬件接口无关,主要函数attach_adapter()、适配适配器,detach_client(),探测client
i2c_client对应一个真实的物理设备,一个i2c_driver可以支持多个同等类型i2c_client
i2c_adapter,适配器操作的是真实硬件,和真实硬件一一对应,一个适配器可以为多个i2c_client服务
i2c驱动与具体i2c设备对应,通过具体设备描述让core层对接适配器
i2c总线在注册时已经将适配器和相应算法绑定,i2c_transfer()寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正的驱动硬件流程,其本身没有和硬件的交互能力
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) { int ret; if (adap->algo->master_xfer) {//如果master_xfer函数存在,则调用,否则返回错误 ret = adap->algo->master_xfer(adap,msgs,num);//这个函数在硬件相关的代码中给algorithm赋值 return ret; } else { return -ENOSYS; } }
I2C流程
总线上先添加好IIC驱动,i2c.c遍历i2c_boardinfo链表,依次建立i2c_client,并对每一个i2c_client与所有在在这个线上的驱动匹配match,匹配上就调用该驱动的probe函数完成设备文件创建,操作接口注册。
- 总线驱动需要填充core层i2c_add_adapter()和i2c_del_adapter()
- i2c驱动需要i2c_attach_client()关联I2C总线设备
- 算法在总线I2C设备probe时填充,同时probe也完成了对设备文件的创建