目录
Linux的I2C体系结构分为3个组成部份,下边对这三个部份进行详尽解释。
I2C核心。
I2C核心提供了I2C总线驱动和设备驱动的注册、注销方式,I2C通讯方式(即“algorithm”,笔者觉得译音为“运算方式”并不合适,为免造成误会linux i2c驱动架构,下文将直接使用“algorithm”)下层的、与具体适配器无关的代码以及侦测设备、检测设备地址的下层代码等。如上图中的蓝色部份
I2C总线驱动。
I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
I2C总线驱动主要包含了I2C适配器数据结构i2c_adapter、I2C适配器的algorithm数据结构i2c_algorithm和控制I2C适配器形成通讯讯号的函数。
经由I2C总线驱动的代码,我们可以控制I2C适配器以主控形式形成开始位、停止位、读写周期,以及以从设备形式被读写、产生ACK等。
如上图中的红色部份
I2C设备驱动。
I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备通常挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。如上图中的红色部份
I2C设备驱动主要包含了数据结构i2c_driver和i2c_client,我们须要依照具体设备实现其中的成员函数。
在Linux2.6内核中linux系统装win7,所有的I2C设备都在sysfs文件系统中显示,存于/sys/bus/i2c/目录,以适配器地址和芯片地址的方式列举,比如:
$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
| |-- 0-0048 -> ../../../devices/legacy/i2c-0/0-0048
| |-- 0-0049 -> ../../../devices/legacy/i2c-0/0-0049
| |-- 0-004a -> ../../../devices/legacy/i2c-0/0-004a
| |-- 0-004b -> ../../../devices/legacy/i2c-0/0-004b
| |-- 0-004c -> ../../../devices/legacy/i2c-0/0-004c
| |-- 0-004d -> ../../../devices/legacy/i2c-0/0-004d
| |-- 0-004e -> ../../../devices/legacy/i2c-0/0-004e
| '-- 0-004f -> ../../../devices/legacy/i2c-0/0-004f
'-- drivers
|-- i2c_adapter
'-- lm75
|-- 0-0048 -> ../../../../devices/legacy/i2c-0/0-0048
|-- 0-0049 -> ../../../../devices/legacy/i2c-0/0-0049
|-- 0-004a -> ../../../../devices/legacy/i2c-0/0-004a
|-- 0-004b -> ../../../../devices/legacy/i2c-0/0-004b
|-- 0-004c -> ../../../../devices/legacy/i2c-0/0-004c
|-- 0-004d -> ../../../../devices/legacy/i2c-0/0-004d
|-- 0-004e -> ../../../../devices/legacy/i2c-0/0-004e
'-- 0-004f -> ../../../../devices/legacy/i2c-0/0-004f
linuxI2C代码
在Linux内核源代码中的drivers目录下包含一个i2c目录,而在i2c目录下又包含如下文件和文件夹。
这个文件实现了I2C核心的功能以及/proc/bus/i2c*插口。
实现了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设备的工作形式。直接只用此函数尽心I2C通讯,通过过程不须要I2C设备驱动的支持
这个目录中包含了一些特定的I2C设备驱动,如Dallas公司的DS1337实时钟芯片、EPSON公司的RTC8564实时钟芯片和I2C插口的EEPROM驱动等。
这个文件中包含了一些I2C主机控制器驱动(总线驱动),如S3C2410的I2C控制器驱动为i2c-s3c2410.c。
实现了一些I2C总线适配器的algorithm通讯方式。
I2C结构体
内核中的i2c.h这个头文件i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构进行了定义。理解这4个结构体的作用非常关键。
struct i2c_adapter {
struct module *owner;/*所属模块*/
unsigned int class;
struct i2c_algorithm *algo;/*总线通信方法结构体指针 */
void *algo_data; /* algorithm 数据 */
struct semaphore bus_lock; /*控制并发访问的自旋锁*/
int timeout;
int retries; /*重试次数*/
struct device dev; /* 适配器设备 */
int nr;
char name[48]; /*适配器名称*/
struct completion dev_released; /*用于同步*/
struct mutex userspace_clients_lock;
struct list_head userspace_clients; /* client 链表头*/
Struct i2c_bus_recovery_info *bus_recovery_info;
};
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/*i2c 传输函数指针,对应于普通的 i2c 传输协议*/
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
/*smbus 传输函数指针,对应于i2c协议子集 smbus ,有些设备只支持这个协议*/
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data);
u32 (*functionality) (struct i2c_adapter *);
/*返回适配器支持的功能,用来描述,adapter 所具有的功能,比如是否支持 smbus*/
};
上述代码的master_xfer对应为I2C传输函数表针,I2C的主机驱动部份的大部份工作也聚焦在这儿linux软件,smbus_xfer对应SMBus传输函数表针,SMBus大部份基于I2C总线规范,SMBus不需降低额外引脚。与I2C总线相比,SMBus降低了一些新的功能性,在访问时序也有一定的差别。
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this, it will be removed in a
* near future.
*/
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
/* 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 *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* 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);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
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 */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
I2C结构体解析
下边剖析i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构的作用及其盘根错节的关系。
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; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
i2c_driver对应一套驱动方式,是纯粹的用于辅助作用的数据结构,它不对应于任何的数学实体。其主要成员函数是probe、remove、suspend、resume等,另外。Structi2c_device_id方式的id_table是该驱动所支持的I2C设备的ID表。i2c_client对应于真正的化学设备,每位i2c设备都须要一个i2c_client来描述,i2c_driver于i2c_client的关系是一对多,一个i2c_driver可以支持多个类型的i2c_client。
i2c_client的信息一般在bsp的版文件中通过i2c_board_info填充,如下边的代码就定义了一个I2C设备的id为“ad7142_joystick”、地址为0X2C,中断号为39的i2c_client。
static struct i2c_board_info __initdata bfin_i2c_board_info = {
{
I2C_BOARD_INFO("ad7142_joystick", 0x2C),
.irq = 39,
}
i2c_adpater与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client屈从于i2c_adpater。因为一个适配器上可以联接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client屈从,i2c_adpater中包括屈从于它的i2c_client的数组。
驱动工程师工作
尽管I2C硬件体系结构比较简单,而且I2C体系结构在Linux中的实现却相当复杂。当工程师领到实际的电路板,面对复杂的LinuxI2C子系统,应当怎样下手写驱动呢?到底有什么是须要亲自做的,什么是内核早已提供的呢?理清这个问题十分有意义,可以使我们面对具体问题时迅速地抓牢重点。
一方面,适配器驱动可能是Linux内核本身还不包含的;另一方面,挂接在适配器上的具体设备驱动可能也是Linux内核还不包含的。虽然上述设备驱动都存在于Linux内核中linux i2c驱动架构,其基于的平台也可能与我们的电路板不一样。为此,工程师要实现的主要工作如下。