Linux IIC驱动

Linux的i2c体系结构分为三个部分:

1、i2c核心;

2、i2c总线驱动;

3、i2c设备驱动。

 

 

 

1、i2c核心

i2c核心提供了i2c总线驱动和设备驱动的注册、注销方法,i2c通信方法上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。

(1)增加/删除 i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adap);

int i2c_del_adapter(struct i2c_adapter *adap);

(2)增加/删除 i2c_driver

int i2c_register_driver(struct module *owner, struct i2c_driver *driver);

int i2c_del_driver(struct i2c_driver *driver);

inline int i2c_add_driver(struct i2c_driver *driver);

(3) i2c_client 依附/脱离

int i2c_attach_client(struct i2c_client *client);

int i2c_detach_client(struct i2c_client *client);

(4) I2C 传输、发送和接收

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);

/*用于进行i2c适配器和i2c设备之间的一组消息交互,本身并不具备驱动适配器物理硬件实现消息交互的能力,通过寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm中的master_sfer()函数实现真正驱动硬件流程。*/

 

int i2c_master_send(struct i2c_client *client,const char *buf ,int count);

int i2c_master_recv(struct i2c_client *client, char *buf ,int count);

 

2、i2c总线驱动

i2c总线驱动是对硬件体系结构中适配器端的实现,适配器可以由CPU控制,甚至直接集成在CPU中。

i2c总线驱动主要包含i2c适配器数据结构i2c_adapter、i2c通信方法数据结构i2c_algorithm和控制i2c适配器产生通信信号的函数,主要实现i2c_algorithm 的 master_xfer()函数和functionality()函数。

经由i2c总线驱动的代码,可以控制i2c适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。

(1)master_xfer()函数

static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,int num)

(2)functionality()函数

用 于 返 回 algorithm 所 支 持 的 通 信 协 议 , 如 I2C_FUNC_I2C 、I2C_FUNC_10BIT_ADDR、 I2C_FUNC_SMBUS_READ_BYTE、 I2C_FUNC_SMBUS_WRITE_BYTE等。

多数 I2C 总线驱动会定义一个 xxx_i2c 结构体,作为 i2c_adapter 的 algo_data(类似“私有数据”),其中包含 I2C 消息数组指针、数组索引及 I2C 适配器 algorithm 访问控制用的自旋锁、等待队列等。

 

3、i2c设备驱动

i2c设备驱动时对硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的i2c适配器上,通过i2c适配器与CPU进行交换数据。

i2c设备驱动主要包含数据结构i2c_client和i2c_driver,需要根据具体设备实现其中的成员函数。 i2c_client 一般被包含在设备的私有信息结构体 yyy_data 中,而 i2c_driver 则适合被定义为全局变量并初始化。

i2c_add_driver(&yyy_driver)的执行会引发 i2c_driver 结构体中 yyy_attach_adapter()函数的执行,我们可以在 yyy_attach_adapter()函数里探测物理设备。为了实现探测, yyy_attach_adapter()函数里面也只需简单地调用 I2C 核心的 i2c_probe()函数i2c_probe(adapter, &addr_data, yyy_detect);i2c_probe()函数的第 1 个参数是 i2c_adapter 指针,第 2 个参数是要探测的地址数据,第 3 个参数是具体的探测函数。i2c_probe()函数会引发 yyy_detect()函数的调用,可以在 yyy_detect()函数中初始化 i2c_client。

所有的i2c设备都在sysfs文件系统中显示,存于/sys/bus/i2c/目录,以适配器地址和芯片地址的形式列出。

 

i2c设备驱动的模块加载连锁反应

 

 

i2c设备驱动的模块卸载连锁反应

 

Linux内核源代码中的driver目录下包含一个i2c目录,而i2c目录包含如下文件和文件夹:

1、i2c-core.c ——实现i2c核心的功能以及/proc/bus/i2c*接口;

2、i2c-dev.c ——实现i2c适配器设备文件的功能,每一个适配器都被分配一个设备。通过适配器访问设备时的主设备号为89;

3、chips文件夹 ——包含一些特定的i2c设备驱动;

4、busses文件夹 ——包含一些总线的驱动;

5、algos文件夹 ——实现i2c总线适配器的algorithm。

 

i2c_driver、 i2c_client、 i2c_adapter 和 i2c_algorithm数据结构分析及相互之间的关系:

  1. struct i2c_adapter {  
  2.  struct module *owner;//所属模块  
  3.  unsigned int id;//algorithm的类型,定义于i2c-id.h,  
  4.  unsigned int class;      
  5.  const struct i2c_algorithm *algo; //总线通信方法结构体指针  
  6.  void *algo_data;//algorithm数据  
  7.  struct rt_mutex bus_lock;//控制并发访问的自旋锁  
  8.  int timeout;     
  9.  int retries;//重试次数  
  10.  struct device dev; //适配器设备   
  11.  int nr;  
  12.  char name[48];//适配器名称  
  13.  struct completion dev_released;//用于同步  
  14.  struct list_head userspace_clients;//client链表头  
  15. };  

 

  1. struct i2c_algorithm {  
  2.     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//I2C传输函数指针  
  3. int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union   
  4.     i2c_smbus_data *data);//smbus传输函数指针  
  5.     u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能  
  6. };  

 

 

 

  1. struct i2c_driver {  
  2. unsigned int class;  
  3. int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针  
  4. int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针  
  5. int (*probe)(struct i2c_client *, const struct i2c_device_id *);  
  6. int (*remove)(struct i2c_client *);  
  7. void (*shutdown)(struct i2c_client *);  
  8. int (*suspend)(struct i2c_client *, pm_message_t mesg);  
  9. int (*resume)(struct i2c_client *);  
  10. void (*alert)(struct i2c_client *, unsigned int data);  
  11. int (*command)(struct i2c_client *client, unsigned int cmd, void*arg);//命令列表  
  12. struct device_driver driver;  
  13. const struct i2c_device_id *id_table;//该驱动所支持的设备ID表  
  14. int (*detect)(struct i2c_client *, struct i2c_board_info *);  
  15. const unsigned short *address_list;  
  16. struct list_head clients;  
  17. };  

 

 

 

  1. struct i2c_client {  
  2.  unsigned short flags;//标志    
  3.  unsigned short addr; //低7位为芯片地址    
  4.  char name[I2C_NAME_SIZE];//设备名称  
  5.  struct i2c_adapter *adapter;//依附的i2c_adapter  
  6.  struct i2c_driver *driver;//依附的i2c_driver   
  7.  struct device dev;//设备结构体    
  8.  int irq;//设备所使用的结构体    
  9.  struct list_head detected;//链表头  
  10.  };  

 

1、i2c_adapter和i2c_algorithm

一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器访问特定的访问周期。

i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的信号,以 i2c_msg为单位。

2、i2c_driver和i2c_client

i2c_driver对应一套驱动方法,i2c_client对应真实的物理设备,每个i2c设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结构体中。i2c_driver 与 i2c_client 发生关联的时刻在 i2c_driver 的 attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个 client 存在时,把该 client 使用的 i2c_client 数据结构的 adapter 指针指向对应的 i2c_adapter, driver 指针指向该 i2c_driver,并会调用 i2c_adapter 的 client_register()函数。

3、i2c_adapter和i2c_client

一个i2c_adapter可以被多个i2c_client依附,i2c_adapter中包含依附于它的i2c_client的链表。

 

 

梳理图

 

 

 

驱动工程师的主要工作:

1、提供 I2C 适配器的硬件驱动,探测、初始化 I2C 适配器(如申请 I2C 的 I/O 地址和中断号)、驱动CPU 控制的 I2C 适配器从硬件上产生各种信号以及处理 I2C 中断等;

2、提供 I2C 适配器的 algorithm,用具体适配器的 xxx_xfer()函数填充 i2c_algorithm 的 master_xfer 指针,并把 i2c_algorithm 指针赋值给 i2c_adapter 的 algo 指针;

3、 实现 I2C 设备驱动与 i2c_driver 接口,用具体设备 yyy 的 yyy_attach_adapter()函数指针、yyy_detach_client()函数指针和 yyy_command()函数指针的赋值给 i2c_driver 的 attach_ adapter、

detach_adapter 和 detach_client 指针;

4、 实现 I2C 设备驱动的文件操作接口,即实现具体设备 yyy 的 yyy_read()、 yyy_write()和 yyy_ioctl()函数等。

 

 

I2C驱动难点释疑:

1、关于匹配

i2c_client->dev和i2c_driver->driver的总线类型为i2c_bus_type, 而i2c_adapter->dev的总线类型并不是i2c_bus_type,在注册后设备和驱动匹配,是i2c_client->dev和i2c_driver->driver的匹配。i2c_adapter->dev和i2c_driver->driver并没有匹配关系,而是i2c_adapter->class与i2c_driver->class的匹配,它们支持的设备类型的匹配。

2、两个重要函数的区别

当有一个新的驱动加入内核时,会在类型为i2c_adapter_class的适配器中找与之相匹配的,并将驱动支持的设备插入适配器。

class_for_each_device(&i2c_adapter_class,NULL,driver,__attach_adapter);

当一个新的适配器加入内核时,会在总线类型为i2c_bus_type的驱动中找与之相匹配的,并将驱动支持的设备插入新加入的这个适配器。

bus_for_each_drv(&i2c_bus_type,NULL,adap,i2c_do_add_adapter);

 

__attach_adapter()函数和i2c_do_add_adapter()函数对比:

前者是在新的驱动加入内核时调用函数class_for_each_device时调用的函 数。函数class_for_each_device是在类i2c_adapter_class中找一个适配器设备

与新驱动匹配。该函数变化的是设备不变的是加入的新的驱动(void *data);

 后者是在新的适配器加入内核时调用函数 bus_for_each_drv时调用的函数。函数bus_for_each_drv是在总线类型为i2c_bus_type的驱动中找到一个驱动

与新加入的适配器匹配。该函数变化的是驱动,不变的是新加入的适配器(void *data)。

相同点都是执行函数i2c_detect和函数driver->attach_adapter。在class_for_each_device和i2c_do_add_adapter并不执行匹配的操作,只是简单的取出所有的驱动和适配器,执行函数i2c_detect和driver->attach_adapter。真正的匹配是在i2c_detect和driver->attach_adapter中实现的。

函数i2c_detect中会去判断驱动支持的设备类和适配器所支持的设备类是否匹配。函数driver->attach_adapter中会判断adapter->nr是不是自己需要处理的适配器,或者是是不是该适配器已被处理过了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值