Linux中IIC总线驱动分析

来源:互联网 发布:ubuntu和centos 编辑:程序博客网 时间:2024/05/17 19:20

   1.1 Linux的I2C驱动框架

Linux内核的I2C总线驱动程序框架如图1所示:

图1.I2C总线驱动程序框架

Linux的I2C体系结构分为3个组成部分:

I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“al2gorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。这部分是与平台无关的。与其对应的是Linux内核源代码中的drivers目录下的i2c2core.c。

I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现。I2C总线驱动主要包含了I2C适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。不同的CPU平台对应着不同的I2C总线驱动,比如下文要提到的S3C2410的总线驱动i2c2s3c2410.c,它位于Linux内核源代码中的drivers目录下busses文件夹

I2C设备驱动:I2C设备驱动是对I2C硬件体系结构中设备端的实现。设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。I2C设备驱动主要包含了数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。在Linux内核源代码中的drivers目录下的i2c2dev.c文件,实现了I2C适配器设备文件的功能,应用程序通过“i2c2%d”文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器并控制I2C设备的工作方式。

1.2 Linux的I2C驱动框架中的主要数据结构及其关系

        Linux的I2C驱动框架中的主要数据结构包括:i2c_driver、i2c_client、i2c_adapter和i2c_algo2rithm。它们的定义在内核中的i2c.h头文件中。

        i2c_adapter对应于物理上的一个适配器,这个适配器是基于不同的平台的,一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器,因此i2c_adapter中包含其使用的i2c_algorithm的指针。

        i2c_algorithm中的关键函数master_xfer()以i2c_msg为单位产生I2C访问需要的信号。不同的平台所对应的master_xfer()是不同的,开发人员需要根据所用平台的硬件特性实现自己的xxx_xfer()方法以填充i2c_algorithm的master_xfer指针。

        i2c_driver对应一套驱动方法,不对应于任何的物理实体。

        i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client依附于i2c_adpater,这与I2C硬件体系中适配器和设备的关系一致。i2c_driver提供了i2c2cli2ent与i2c2adapter产生联系的函数。

       当attach_a2dapter()函数探测物理设备时,如果确定存在一个client,则把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter,driver指针指向该i2c_driver,并调用i2c_adapter的client_register()函数来注册此设备。相反的过程发生在i2c_driver的detach_client()函数被调用的时候。

1.3 Linux的I2C体系结构中三个组成部分的作用

        I2C核心提供了一组不依赖于硬件平台的接口函数,I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带。I2C核心提供了i2c_adapter的增加和删除函数、i2c_driver的增加和删除函数、i2c_client的依附和脱离函数以及i2c传输、发送和接收函数。i2c传输函数i2c_transfer()用于进行I2C适配器和I2C设备之间的一组消息交互i2c_mas2ter_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息。

        I2C总线驱动包括I2C适配器驱动加载与卸载以及I2C总线通信方法。其中I2C适配器驱动加载(与卸载)要完成初始化(释放)I2C适配器所使用的硬件资源,申请I/O地址、中断号、通过i2c_add_adapter()添加i2c_adapter的数据结构(通过i2c_del_adapter()删除i2c_adapter的数据结构)的工作。I2C总线通信方法主要对特定的I2C适配器实现i2c_algorithm的master_xfer()方法来实现i2c_msg的传输。不同的适配器对应的master_xfer()方法由其处理器的硬件特性决定。

        I2C设备驱动主要用于I2C设备驱动模块加载与卸载以及提供I2C设备驱动文件操作接口。I2C设备驱动的模块加载通用的方法遵循以下流程:首先通过register_chrdev()将I2C设备注册为一个字符设备,然后利用I2C核心中的i2c_add_a2dapter()添加i2c_driver。调用i2c_add_adapter()过程中会引发i2c_driver结构体中的yyy_attach_adapter()的执行,它通过调用I2C核心的i2c_probe()实现物理设备的探测。i2c_probe()会引发yyy_detect()的调用。yyy_detect()中会初始化i2c_client,然后调用内核的i2c_attach_client()通知I2C核心此时系统中包含了一个新的I2C设备。之后会引发I2C设备驱动中yyy_init_client()来初始化设备。卸载过程执行相反的操作。

I2C设备驱动模块加载与卸载的流程如图2所示:

图2.I2C设备驱动模块加载与卸载流程

        综上所述,对于特定的嵌入式Linux操作系统,由于I2C核心是不依赖硬件平台的,所以开发的主要工作在于特定平台的总线驱动的开发以及特定设备的驱动开发。

        下面以S3C2410I2C设备的读过程中的函数调用过程进行图示(如图3)来进一步表明在实际的嵌入式Linux系统中I2C驱动体系三个组成部分即I2C核心、I2C总线驱动以及I2C设备驱动之间的关系(写过程类似)。

        嵌入式Linux中I2C2core框架提供了统一的、不需要修改的接口。而驱动程序开发者只需要实现特定的I2C总线适配器驱动和I2C设备驱动便可以进行用户空间的应用程序设计。本文阐述了Linux系统中I2C总线体系结构,并基于项目要求通过具体例子给出了基于S3C2410处理器利用I2C总线与I2C设备通信的用户空间的实现。

本文转自:道客巴巴《嵌入式Linux系统中I2C总线驱动的研究与应用》篇

1.4重要的结构体

1.各个结构体

   在内核中的i2c.h这个头文件中对i2c_driver,i2c_client,i2c_adapter和i2c_algorithm这个四个结构体进行了定义。理解这4个结构体的作用十分关键。
 i2c_adapter结构体
 struct i2c_adapter {
 struct module *owner;//所属模块
 unsigned int id;//algorithm的类型,定义于i2c-id.h,
 unsigned int class;   
 const struct i2c_algorithm *algo; //总线通信方法结构体指针
 void *algo_data;//algorithm数据
 struct rt_mutex bus_lock;//控制并发访问的自旋锁
 int timeout;   
 int retries;//重试次数
 struct device dev; //适配器设备 
 int nr;
 char name[48];//适配器名称
 struct completion dev_released;//用于同步
 struct list_head userspace_clients;//client链表头
};
 
 I2c_algorithm结构体
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//I2C传输函数指针
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union
i2c_smbus_data *data);//smbus传输函数指针
u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能
};
SMbus大部分基于I2C总线规范,SMbus不需要增加额外引脚。与I2C总线相比,SMbus增加了一些新的功能特性,在访问时序也有
一定的差异。
 i2c_driver结构体
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针
int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针

int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
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;//该驱动所支持的设备ID表
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
 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;//链表头
 };


2:各结构体的作用与它们之间的关系

   i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指针。i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg也很重要,代码清单如下:
struct i2c_msg {
 __u16 addr;//设备地址  
 __u16 flags;//标志
 __u16 len;//消息长度     
 __u8 *buf;//消息数据    
};
   i2c_driver与i2c_client

i2c_driver对应一套驱动方法,其主要成员函数是probe(),remove(),suspend(),resume()等,另外id_table是该驱动所支持的I2C设备的ID表。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_driver与i2c_client的关系是一对多,一个i2c_driver上可以支持多个同等类型的i2c_client。i2c_client信息通常在BSP的板文件中通过i2c_board_info填充。一般在arch/arm目录下的板文件中。在I2C总线驱动i2c_bus_type的match()函数i2c_device_match()中,会调用i2c_match_id()函数匹配板文件中的ID和i2c_driver所支持的ID表。

   i2c_adpater与i2c_client

 i2c_adpater与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附与i2c_adpater.由于一个适配器上可以连接多个I2C设备,所以就一个i2c_adpter也可以被多个i2c_client依附,i2c_adpter中包括依附与它的i2c_client的链表。

3.编写驱动需要完成的工作

   编写具体的I2C驱动时,工程师需要处理的主要工作如下:

1).提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。

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

3).实现I2C设备驱动中的i2c_driver接口,用具体yyy的yyy_probe(),
yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。

4)实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接。
上面的工作中前两个属于I2C总线驱动,后面两个属于I2C设备驱动。

 

原创粉丝点击