在我刚开始接触I2C是在单片机,通信协议就不说了。
1.芯片的I2C adapter(适配器),这是硬件相关,所以可以理解为它是芯片固定的硬件功能。
2.core层的通用API函数,在linux中,无处不体现这种设计的思想,比如,我们在驱动中最常见的file_operations的数据结构,我们只要实现它的各种具体的功能就可以了,比如:open ,write ,read,等。至于用户,他们只需要按照file的操作方式,就可以完成。实际上,内核只是一个搬运工。但是他是官方的搬运工,通过这种约束使得用户层不用纠结面向千差万别的操作方式,内核驱动也可以不关心,我的数据怎么送给用户。
3.设备链接到芯片的某个硬件的接口上,那么是不是就是需要根据不同的硬件去实现相应的驱动,如果没有core层,是不是意味着我写驱动的时候,就要考虑当设备链接到(I2C)接口一,我写个驱动,然后如果链接到(I2C)接口二,我再写个驱动,那要是有个7到8个还不纠结到爆。
这么说来。core层就是简化这个过程的。让你的驱动不要纠结怎么使用具体的硬件接口,如芯片的I2C或USB等,这些芯片厂商会帮你搞好,大家都会这么用,除非你有其他的需求。而一个具备I2C协议的外设芯片,就不去关心你是什么CPU,我就按照通用的驱动API编写驱动就可以了。
稍微描述下,一个简单的用户访问一个I2C的设备整个kernel做出的努力,
对于用户可能还是使用file_operations的方法。但是在驱动中,用户对应的是一个client,那么一个client就包含一个驱动和一个设备,驱动实现各种逻辑和调用I2C的通用API,I2C设备就是描述一个挂在I2C适配器总线上的设备,这么说来,I2C的core层的代码,就是一堆的标准的API,驱动可以调用,具体的硬件的操作,是由特定的硬件相关的代码实现!
我需要关心的是:
i2c_driver这个设备驱动程序的数据结构:
struct i2c_driver {
162 unsigned int class;//用探测的一种类型的I2C设备
168 int (*attach_adapter)(struct i2c_adapter *) __deprecated; //关联适配器不推荐使用
169 int (*detach_adapter)(struct i2c_adapter *) __deprecated;//分离适配器不推荐使用
170
171 /* Standard driver model interfaces */
172 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
173 int (*remove)(struct i2c_client *);
174
175 /* driver model interfaces that don't relate to enumeration */
176 void (*shutdown)(struct i2c_client *);//关机
177 int (*suspend)(struct i2c_client *, pm_message_t mesg);//暂停
178 int (*resume)(struct i2c_client *);//恢复
185 void (*alert)(struct i2c_client *, unsigned int data);//警报回调
190 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);//总线信号。
192 struct device_driver driver;//设备驱动模型的驱动
193 const struct i2c_device_id *id_table;//I2C设备支持的驱动程序列表
195 /* Device detection callback for automatic device creation */
196 int (*detect)(struct i2c_client *, struct i2c_board_info *);//设备检测的回调
197 const unsigned short *address_list;//I2C地址探测(检测)
198 struct list_head clients;//我们创建的clients的探测列表(I2C核心使用的)
199};
i2c_client设备数据结构:
struct i2c_client {
221 unsigned short flags; /* div., see below */
222 unsigned short addr; /* 用于连接到母适配器的I2C总线地址chip address - NOTE: 7bit */
223 /* addresses are stored in the */
224 /* _LOWER_ 7 bits */
225 char name[I2C_NAME_SIZE]; //指示设备的类型,通常是一个芯片名称
226 struct i2c_adapter *adapter; /* the adapter we sit on管理托管这I2C器件的总线段 */
227 struct i2c_driver *driver; /* and our access routines设备的驱动程序 */
228 struct device dev; /* the device structure 对应的驱动模型的设备节点 */
229 int irq; /* irq issued by device 表明由该设备产生的中断(如果有的话) */
230 struct list_head detected;
231};
i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体:
352struct i2c_algorithm {
353 /* If an adapter algorithm can't do I2C-level access, set master_xfer
354 to NULL. If an adapter algorithm can do SMBus access, set
355 smbus_xfer. If set to NULL, the SMBus protocol is simulated
356 using common I2C messages */
357 /* master_xfer should return the number of messages successfully
358 processed, or a negative value on error */
359 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
360 int num);
361 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
362 unsigned short flags, char read_write,
363 u8 command, int size, union i2c_smbus_data *data);
364
365 /* To determine what the adapter supports */
366 u32 (*functionality) (struct i2c_adapter *);
367};
i2c_algorithm(i2c的算法)是与芯片的i2c_adapter(适配器是唯一配对的)
也就是说,适配器是需要一个算法来对芯片的具体的属性来处理数据,而适配器提一个函数指针,i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体。所以缺少或算法不对,适配器是无法工作的。
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
那么在注册i2c_driver和i2c_client中,我们要注意:
i2c_driver对应一套驱动方法,其主要函数是attach_adapter()和detach_client(),i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述,i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client.
i2c_adapter和i2c_client,适配器是与硬件的I2C设备一致的,也就是一一对应的,只是一个适配器可能为很多的I2C_client这个类设备工作。
这样一来:编写一个I2C的驱动需要对应的具体的I2C设备,通过这个具体的设备描述来让core层取帮你对接适配器,对于I2C总线在注册的时候就已经将适配器与相应的算法绑定了,也就是core帮你挂载设备到相应的总线。
EXPORT_SYMBOL(i2c_add_adapter);
EXPORT_SYMBOL(i2c_del_adapter);
EXPORT_SYMBOL(i2c_del_driver);
EXPORT_SYMBOL(i2c_attach_client);
EXPORT_SYMBOL(i2c_detach_client);
EXPORT_SYMBOL(i2c_transfer);
i2c_transfer()函数,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总线的驱动也是需要填充core层的i2c_add_adapter()函数和i2c_del_adapter();
I2C驱动需要i2c_attach_client函数来关联I2C总线的设备。
而算法是在总线的I2C设备驱动被probe时填充的。core层维护的是一个I2C总线设备的链表,
client可以遍历的方式来对应的关联,实现驱动。