Linux I2C子系统分析之(二) ----- 概述

Linux定义了系统的I2C驱动体系结构,在Linux系统中,I2C驱动由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。这3部分相互协作,形成了非常通用、可适应性很强的I2C框架。

Linux的I2C体系结构分为3个组成部分:I2C核心、I2C总线驱动、I2C设备驱动,如下图所示。I2C核心提供总线驱动和设备驱动的注册、注销方法,algorithm;I2C总线驱动对硬件体系结构中适配器的实现,主要包括适配器i2c_adapter、适配器通信算法i2c_algorithm,如果CPU集成了I2C控制器并且linux内核支持这个CPU,那么总线驱动就不用管,比如S3C2440就属于这类情况,在后文中我们将分析它的总线驱动;I2C设备驱动是具体的一个设备(如AT24C02),挂接在CPU控制的I2C适配器的设备驱动,有了这部分驱动才能使用控制器操作该设备,设备驱动主要包括i2c_driver 和i2c_client数据结构。

一: Linux I2C体系结构

LinuxI2C体系结构分为3个组成部分:
•  I2C
核心
I2C
核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”,笔者认为直译为运算方法并不合适,为免引起误解,下文将直接使用“algorithm”)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
•  I2C
总线驱动
I2C
总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至直接集成在CPU内部。
I2C
总线驱动主要包含了I2C适配器数据结构i2c_adapterI2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器产生通信信号的函数。
经由I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。
•  I2C
设备驱动
I2C
设备驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。
I2C
设备驱动主要包含了数据结构i2c_driveri2c_client,我们需要根据具体设备实现其中的成员函数。




Linux 内核源代码中的 drivers 目录下包含一个 i2c 目录,而在 i2c 目录下又包含如下文件和文件夹:
•  i2c-core.c
这个文件实现了 I2C 核心的功能以及 /proc/bus/i2c* 接口。
•  i2c-dev.c
实现了 I2C 适配器设备文件的功能,每一个 I2C 适配器都被分配一个设备。通过适配器访问设备时的主设备号都为 89 ,次设备号为 0 255 。应用程序通过 “i2c-%d” (i2c-0, i2c-1, ..., i2c-10, ...) 文件名并使用文件操作接口 open() write() read() ioctl() close() 等来访问这个设备。
i2c-dev.c
并没有针对特定的设备而设计,只是提供了通用的 read() write() ioctl() 等接口,应用层可以借用这些接口访问挂接在适配器上的 I2C 设备的存储空间或寄存器并控制 I2C 设备的工作方式。
•  chips
文件夹
这个目录中包含了一些特定的 I2C 设备驱动,如 Dallas 公司的 DS1337 实时钟芯片、 EPSON 公司的 RTC8564 实时钟芯片和 I2C 接口的 EEPROM 驱动等。
•  busses
文件夹
这个文件中包含了一些 I2C 总线的驱动,如 S3C2410 I2C 控制器驱动为 i2c-s3c2410.c
•  algos
文件夹

实现了一些I2C总线适配器的algorithm


此外,内核中的 i2c.h 这个头文件对 i2c_driver i2c_client i2c_adapter i2c_algorithm 4 个数据结构进行了定义。理解这 4 个结构体的作用十分关键, 分别给出了它们的定义。
   
i2c_adapter结构体
# struct i2c_adapter {  
#     struct module *owner;  
#     unsigned int id;  
#     unsigned int class;       /* classes to allow probing for */  
#     const struct i2c_algorithm *algo; /* the algorithm to access the bus总线通信方法结构体指针 */  
#     void *algo_data;                  /* algorithm数据 */  
#   
#     /* data fields that are valid for all devices   */  
#     u8 level;           /* nesting level for lockdep */  
#     struct mutex bus_lock;  
#   
#     int timeout;            /* in jiffies */  
#     int retries;                    /* 重试次数 */  
#     struct device dev;      /* the adapter device */  
#   
#     int nr;  
#     char name[48];  
#     struct completion dev_released;  
# };  

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传输函数指针*/  
#   
#     /* To determine what the adapter supports */  
#     u32 (*functionality) (struct i2c_adapter *);            /*返回适配器支持的功能*/  
# };  


i2c_driver结构体:
# struct i2c_driver {  
#     unsigned int class;  
#   
#     int (*attach_adapter)(struct i2c_adapter *);       /*依附i2c_adapter函数指针*/  
#     int (*detach_adapter)(struct i2c_adapter *);       /*脱离i2c_adapter函数指针*/  
#   
#     /* 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 *);        /* probe,remove,suspend,resume驱动方法重要成员函数 */  
#   
#     /* a ioctl like command that can be used to perform specific functions 
#      * with the device. 
#      */  
#     int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);   /*类似ioctl*/  
#    
#     struct device_driver driver;  
#     const struct i2c_device_id *id_table;   /* 驱动支持多个设备,这里面就要包含这些设备的ID */  
#   
#     /* Device detection callback for automatic device creation */  
#     int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);   /*i2c client脱离函数指针*/  
#     const struct i2c_client_address_data *address_data;  
#     struct list_head clients;  
# };  


i2c_client结构体:

# struct i2c_client {  
#     unsigned short flags;       /* div., see below      */  
#     unsigned short addr;        /* chip address - NOTE: 7bit  芯片地址  */  
#                     /* addresses are stored in the  */  
#                     /* _LOWER_ 7 bits       */  
#     char name[I2C_NAME_SIZE];  
#     struct i2c_adapter *adapter;    /* the adapter we sit on  依附的i2c_adapter    */  
#     struct i2c_driver *driver;  /* and our access routines 依附的i2c_driver    */  
#     struct device dev;      /* the device structure     */  
#     int irq;            /* irq issued by device     */  
#     struct list_head detected;  
# };  


下面分析一下i2c_driveri2c_clienti2c_adapteri2c_algorithm4个数据结构的作用及其盘根错节的关系。• 
 i2c_adapteri2c_algorithmi2c_adapter
对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithmi2c_adapter什么也做不了,因此i2c_adapter中包含其使用的
 i2c_algorithm的指针。i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg结构体也非常关键,代码清单15.5给出了它的定义。代码清单15.5
 i2c_msg结构体1 struct i2c_msg {2  __u16 addr; /*
设备地址*/3   __u16 flags; /*
标志 */ 4   __u16 len;  /*
消息长度*/

5   __u8 *buf;  /* 消息数据*/

#define I2C_M_TEN        0x0010    /* this is a ten bit chip address */#define I2C_M_RD        0x0001    /* read data, from slave to master */#define I2C_M_NOSTART        0x4000    /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR    0x2000    /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK    0x1000    /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NO_RD_ACK        0x0800    /* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_RECV_LEN        0x0400    /* length will be first received byte */

6 };•  i2c_driveri2c_clienti2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client一般被包含在i2c字符设备的私有信息结构体中。i2c_driver i2c_client发生关联的时刻在i2c_driverattach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapterdriver指针指向该i2c_driver,并会调用i2c_adapterclient_register()函数。相反的过程发生在 i2c_driver detach_client()函数被调用的时候。•  i2c_adpateri2c_client

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

从上面的分析可知,虽然I2C硬件体系结构比较简单,但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板,面对复杂的 Linux I2C子系统,应该如何下手写驱动呢?究竟有哪些是需要亲自做的,哪些是内核已经提供的呢?理清这个问题非常有意义,可以使我们面对具体问题时迅速地抓住重点。    一方面,适配器驱动可能是Linux内核本身还不包含的。另一方面,挂接在适配器上的具体设备驱动可能也是Linux不存在的。即便上述设备驱动都存在于Linux内核中,其基于的平台也可能与我们的电路板不一样。因此,工程师要实现的主要工作将包括:•  提供I2C适配器的硬件驱动,探测、初始化I2C适配器(如申请I2CI/O地址和中断号)、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。•  提供I2C适配器的algorithm,用具体适配器的xxx_xfer()函数填充i2c_algorithmmaster_xfer指针,并把i2c_algorithm指针赋值给i2c_adapteralgo指针。•  实现I2C设备驱动与i2c_driver接口,用具体设备yyyyyy_attach_adapter()函数指针、 yyy_detach_client()函数指针和yyy_command()函数指针的赋值给i2c_driverattach_adapter detach_adapterdetach_client指针。•  实现I2C设备驱动的文件操作接口,即实现具体设备yyyyyy_read()yyy_write()yyy_ioctl()函数等。

I2C核心

    I2C核心是总线驱动和设备驱动的纽带,源码位于drivers/i2c/i2c-core.c,它并不依赖于硬件平台的接口函数,I2C核心中一些重要函数如下:

增加/删除i2c_adapter

int i2c_add_adapter(struct i2c_adapter *adapter)

i2c_add_numbered_adapter

int i2c_del_adapter(struct i2c_adapter *adapter)

增加/删除i2c_driver

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

int i2c_add_driver(struct i2c_driver *driver)         //调用i2c_register_driver

void i2c_del_driver(struct i2c_driver *driver)

增加/删除i2c_client

struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

void i2c_unregister_device(struct i2c_client *client)

      注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函数。之后attach被merge到了i2c_new_device中,而detach直接被unresister取代。实际上这两个函数内部都是调用了device_register()和device_unregister()

I2C传输、发送接收

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

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)

      i2c_transfer()函数用于进行I2C 适配器和I2C 设备之间的一组消息交互,i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息,i2c_transfer()本身不能喝硬件完成消息交互,它寻找i2c_adapter对应的i2c_algorithm,要实现数据传送就要实现i2c_algorithm的master_xfer(),在总线驱动中就是重点。

I2C总线通信方法我们需要为特定的I2C适配器实现其通信方法,主要实现i2c_algorithm的master_xfer()函数和functionality()函数。functionality ()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、 I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息,代码清单15.11给出了xxx设备的master_xfer()函数模板。

小结::

Linux I2C驱动体系结构有相当的复杂度,它主要由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。I2C核心是I2C总线驱动和I2C设备驱动的 中间枢纽,它以通用的、与平台无关的接口实现了I2C中设备与适配器的沟通。I2C总线驱动填充i2c_adapter和i2c_algorithm结构 体,I2C设备驱动填充i2c_driver和i2c_client结构体。另外,系统中i2c-dev.c文件定义的主设备号为89的设备可以方便地给应用程序提供读写I2C设备寄存器的能力,使得工程师大多数时候并不需要为具体的I2C设备驱动定义文件操作接口。最后,工程师在设计I2C设备驱动的程序,并不是一定要遵守Linux I2C驱动的体系结构,完全可以把它当作一个普通的字符设备处理,如果不考虑融入Linux现有的I2C框架的话。

参考: http://blog.csdn.net/liaozc/article/details/6655082




  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值