Linux i2c驱动框架分析 (一)
Linux i2c驱动框架分析 (二)
Linux i2c驱动框架分析 (三)
通用i2c设备驱动分析
Linux的i2c体系结构
Linux的i2c体系结构分为3个组成部分。
(1) i2c核心
i2c核心提供了i2c总线驱动和设备驱动的注册、注销方法,i2c通信方法上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
(2) i2c总线驱动
i2c总线驱动是对i2c硬件体系结构中适配器端的实现。
i2c总线驱动主要包含了i2c适配器数据结构i2c_adapter、i2c适配器的i2c_algorithm数据结构和控制i2c适配器产生通信信号的函数。
经由i2c总线驱动的代码,我们可以控制i2c适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ack等。
(3) i2c设备驱动
i2c设备驱动是对i2c硬件体系结构中设备端的实现,设备挂接在i2c适配器上,通过i2c适配器与cpu交换数据。i2c设备驱动主要包含了数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。
i2c驱动框架如下图所示。
在Linux内核源代码中的drivers目录下包含一个i2c目录,而在i2c目录下又包含如下文件和文件夹:
i2c-core.c,这个文件实现了i2c核心的功能。
i2c-dev.c,实现了i2c适配器设备文件的功能,每一个i2c适配器都被分配一个设备。
busses文件夹包含了一些I2C总线的驱动,如针对S3C2410、S3C2440和S3C6410等处理器的i2c控制器驱动为i2c-s3c2410.c。
algos文件夹,实现了一些i2c总线适配器的algorithm。
主要的数据结构
i2c_adapter结构体,描述一个i2c适配器(控制器),定义如下:
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
//总线通信方法
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
//总线号
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
i2c_algorithm结构体,描述一个适配器的总线通信方法:
struct i2c_algorithm {
//传输数据函数
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
//SMBus传输函数
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* 这个函数返回适配器支持的功能 */
u32 (*functionality) (struct i2c_adapter *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
SMBus大部分基于i2c总线规范,SMBus不需增加额外引脚。与i2c总线相比,SMBus增加了一些新的功能特性,在访问时序也有一定的差异。
i2c_driver结构体,描述一个i2c设备驱动,定义如下:
struct i2c_driver {
unsigned int class;
//老式的探测函数,已不推荐使用
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* 驱动与适配时,会调用probe函数 */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
......
struct device_driver driver;
//该驱动所支持的设备ID
const struct i2c_device_id *id_table;
/* 探测函数 */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
//探测设备地址列表
const unsigned short *address_list;
//该驱动支持的设备会通过这个链表头链在一起
struct list_head clients;
}
i2c_client结构体,描述一个i2c设备,定义如下:
struct i2c_client {
unsigned short flags; /* div., see below */
//设备地址
unsigned short addr;
char name[I2C_NAME_SIZE];
//设备所挂在的控制器
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
//通过这个链表节点,挂接在驱动的clients链表
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
下面梳理一下i2c_driver、i2c_client、i2c_adapter和i2c_algorithm这4个数据结构的作用及其盘根错节的关系。
- i2c_adapter与i2c_algorithm
i2c_adapter对应于物理上的一个i2c适配器,而i2c_algorithm对应一套通信方法。一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少 i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的 i2c_algorithm 的指针。i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的信号,以 i2c_msg为单位。i2c_msg结构体也非常关键,代码清单如下给出了它的定义:
struct i2c_msg {
__u16 addr; /* 设备地址 */
__u16 flags;
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
__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_adpater与i2c_client
i2c_adpater与i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client 依附,i2c_adpater中包括依附于它的i2c_client的链表。