一、i2c驱动核心层分析
i2c驱动核心层主要为设备驱动层提供设备注册、通信、管理等接口,同时连接i2c控制器驱动。这里首先分析i2c子系统中比较重要的几个结构体:
1、driver
struct i2c_driver {
unsigned int class; // 驱动支持的设备类型,如传感器、eeprom等
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this if you can, it will probably
* be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *); // 旧版探测函数
int (*detach_adapter)(struct 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 *);
/* 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; // 驱动支持的设备地址,传递给detect函数
struct list_head clients; // 驱动节点
};
这个结构体代表了一个i2c设备驱动,其中class代表i2c器件的类型,attach_adapter和detach_adapter代表从总线上添加或者删除设备,probe和remove用于绑定和解除绑定,alert用于SMBus,driver是设备的驱动模型,id_tabel代表驱动所支持设备的信息,detect用于设备探测,address_list传递给detect,clients代表我们创建的客户端。
2、device
struct i2c_client {
unsigned short flags; // 设备标志
unsigned short addr; // 设备地址-7位
char name[I2C_NAME_SIZE]; // 设备名称
struct i2c_adapter *adapter; // 设备所属的适配器
struct i2c_driver *driver; // 设备所用的驱动
struct device dev; // 设备模型
int irq; // 设备可能会用到的中断号
struct list_head detected;
};
该结构体代表一个i2c从设备,flags指定需要的特殊处理方式,addr是i2c设备的7位地址值,name是i2c设备名称,adapter为设备所属的适配器,driver为此设备的驱动程序,dev为设备的设备模型,irq为设备可能会用到的中断号。
struct i2c_board_info {
char type[I2C_NAME_SIZE]; // 设备名称
unsigned short flags; // 设备标志,如读取标志、十位地址位等
unsigned short addr; // 设备地址
void *platform_data; // 设备私有数据
struct dev_archdata *archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
int irq; // 设备采用的中断号
};
这个结构体通常用于创建i2c设备,type为设备名称,platform_data为platform总线信息。通常采用如下方式注册i2c设备:
static struct i2c_board_info __initdata i2c_devices0[]= {
#ifdefined(CONFIG_REGULATOR_AXP192)
{
I2C_BOARD_INFO("axp192",0x34),
.irq = PMIC_IRQ,
.platform_data= &axp192_info,
},
#endif
};
该数组包含一个通道上所有设备的信息,然后通过i2c_register_board_info函数注册该通道上所有设备。
int __init i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
{
int status;
down_write(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
}
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); // 将设备依次添加到全局设备链表中
}
up_write(&__i2c_board_lock);
return status;
}
其中busnum指定通道,info指定设备信息,len指定设备数量,该函数将设备依次添加到全局设备链表__i2c_board_list中。链表节点devinfo中有设备信息以及设备所连接的通道。
struct i2c_devinfo {
struct list_head list; // 设备节点
int busnum; // i2c通道
struct i2c_board_info board_info; // 设备信息
};
函数的调用方式如下:
i2c_register_board_info(0, i2c_devices0, ARRAY_SIZE(i2c_devices0));
3、adapter
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; // 适配器支持的类型
const struct i2c_algorithm *algo; // 适配器通信的方法
void *algo_data; // 适配器私有数据
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; // 超时时间
int retries; // i2c超时重试次数
struct device dev; // 设备模型
int nr; // 适配器为i2c的第几通道
char name[48]; // 适配器名称
struct completion dev_released;
struct list_head userspace_clients;
};
i2c_adapter代表一个适配器,一个i2c通道就是一个适配器,algo指定该适配器的方法。
4、algorithm
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
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 *);
};
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = rt_mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
rt_mutex_lock(&adap->bus_lock