红联Linux门户
Linux帮助

Android/Linux I2C的驱动框架

发布时间:2016-09-15 10:27:52来源:linux网站作者:lsn946803746
在我刚开始接触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编写驱动就可以了。
Android/Linux I2C的驱动框架
 
稍微描述下,一个简单的用户访问一个I2C的设备整个kernel做出的努力,
对于用户可能还是使用file_operations的方法。但是在驱动中,用户对应的是一个client,那么一个client就包含一个驱动和一个设备,驱动实现各种逻辑和调用I2C的通用API,I2C设备就是描述一个挂在I2C适配器总线上的设备,这么说来,I2C的core层的代码,就是一堆的标准的API,驱动可以调用,具体的硬件的操作,是由特定的硬件相关的代码实现!
Android/Linux I2C的驱动框架
 
我需要关心的是:
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可以遍历的方式来对应的关联,实现驱动。
 
本文永久更新地址:http://www.linuxdiyf.com/linux/24179.html