struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match, //匹配规则
.probe = i2c_device_probe, //匹配成功后的行为
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
复制代码
I2C总线对应着/bus下的一条总线,这个i2c总线结构体管理着i2c设备与I2C驱动的匹配,删除等操做,I2C总线会调用i2c_device_match函数看I2C设备和I2C驱动是否匹配,若是匹配就调用i2c_device_prob函数,进而调用I2C驱动的probe函数。
特别提示:i2c_device_match会管理I2C设备和I2C总线匹配规则,这将和如何编写I2C驱动程序息息相关。node
struct i2c_driver {
int (*probe)(struct i2c_client *, const struct i2c_device_id *); //probe函数
struct device_driver driver; //代表这是一个驱动
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; //设备链表
};
复制代码
struct i2c_client {
unsigned short addr; //设备地址
char name[I2C_NAME_SIZE]; //设备名称
struct i2c_adapter *adapter; //适配器,I2C控制器。
struct i2c_driver *driver; //设备对应的驱动
struct device dev; //代表这是一个设备
int irq; //中断号
struct list_head detected; //节点
};
复制代码
I2C适配器是什么?算法
通过上面的介绍,知道有I2C驱动和I2C设备,咱们须要经过I2C驱动去和I2C设备通信,这其中就须要一个I2C适配器,I2C适配器对应的就是SOC上的I2C控制器。数组
struct i2c_adapter { //适配器
unsigned int id; //适配器的编号
const struct i2c_algorithm *algo; //算法,发送时序
struct device dev; //代表这是一个设备
};
复制代码
I2C算法对应的就是如何发送I2C时序数据结构
struct i2c_algorithm {
/* 做为主设备时的发送函数 */
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);
};
复制代码
I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C适配器框架
I2C总线的运行机制 函数
注册I2C设备能够经过i2c_new_device,此函数会生成一个i2c_client,指定对应的总线为I2C总线,而后向总线注册设备。源码分析
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) {
struct i2c_client *client;
client = kzalloc(sizeof *client, GFP_KERNEL);
client->dev.bus = &i2c_bus_type; //指定I2C总线
device_register(&client->dev); //向总线注册设备
return client;
}
复制代码
device_register首先会将设备添加到总线的设备链表中,而后遍历总线的驱动链表,判断设备和驱动是否匹配,若是匹配就调用驱动的probe函数:ui
//第一层
int device_register(struct device *dev) {
device_add(dev);
}
//第二层
int device_add(struct device *dev) {
/* 添加设备到总线的设备链表中 */
bus_add_device(dev);
/* 遍历总线的驱动进行操做 */
bus_probe_device(dev);
}
//第三层
int bus_add_device(struct device *dev) {
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
}
void bus_probe_device(struct device *dev) {
device_attach(dev);
}
//第四层
int device_attach(struct device *dev) {
/* 遍历总线的驱动链表每一项,而后调用__device_attach */
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
//第五层
static int __device_attach(struct device_driver *drv, void *data)
{
/* 判断设备和驱动是否匹配 */
if (!driver_match_device(drv, dev))
return 0;
/* 匹配成功 */
return driver_probe_device(drv, dev);
}
//第六层
static inline int driver_match_device(struct device_driver *drv, struct device *dev) {
/* 调用了总线的match函数,这里的总线在注册i2c设备时以及设置为I2C总线了 */
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
int driver_probe_device(struct device_driver *drv, struct device *dev) {
really_probe(dev, drv);
}
//第七层
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match, //匹配规则
.probe = i2c_device_probe, //匹配后的行为
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
/* 这里调用了i2c_device_match函数 * i2c_device_match会经过I2C驱动的id_table中每一的name和I2C设备的name进行匹配 */
static int i2c_device_match(struct device *dev, struct device_driver *drv) {
i2c_match_id(driver->id_table, client);
}
static int really_probe(struct device *dev, struct device_driver *drv) {
i2c_device_probe(dev)
}
//第八层
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) {
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0) //字符串匹配
return id;
id++;
}
return NULL;
}
static int i2c_device_probe(struct device *dev) {
/* 调用驱动的probe函数 */
driver->probe(client, i2c_match_id(driver->id_table, client));
}
复制代码
与注册设备驱动过程基本一致spa
//第一层
int i2c_register_driver(struct module *owner, struct i2c_driver *driver) {
driver->driver.bus = &i2c_bus_type; //指定I2C总线
driver_register(&driver->driver); //向总线注册驱动
}
//第二层
int driver_register(struct device_driver *drv) {
bus_add_driver(drv);
}
//第三层
int bus_add_driver(struct device_driver *drv) {
driver_attach(drv); //此函数会遍历总线设备链表进行操做
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); // 添加进bus的driver链表中
}
//第四层
int driver_attach(struct device_driver *drv) {
/* 遍历总线的设备链表,调用__driver_attach */
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
//第五层
static int __driver_attach(struct device *dev, void *data)
{
if (!driver_match_device(drv, dev))
return 0;
driver_probe_device(drv, dev);
}
复制代码
I2C适配器驱动就是SOC的I2C控制器驱动,主要是由SOC厂商去编写,咱们不用过度注意细节。内部两个重要的数据结构i2c_adapter和 i2c_algorithmcode
//第一层
struct i2c_adapter{
const struct i2c_algorithm *algo; /* 总线访问算法 */
}
/* i2c_algorithm 就是I2C适配器与IIC设备进行通讯的方法。*/
//第二层
struct i2c_algorithm {
......
/* I2C适配器的传输函数,此函数完成与IIC设备的通讯 */
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);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
......
};
/* 实例-构建适配器 */
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static int s3c24xx_i2c_probe(struct platform_device *pdev) {
i2c->adap.algo = &s3c24xx_i2c_algorithm; //构建了算法
i2c_add_numbered_adapter(&i2c->adap); //注册了适配器
}
复制代码
上面介绍I2C数据传输是经过I2C适配器完成的,下面来分析一下源码在I2C驱动中,使用i2c_transfer来传输I2C数据,此函数确定是经过I2C适配器的算法进行操做的,以下
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) {
adap->algo->master_xfer(adap, msgs, num); //调用适配器的算法
}
复制代码
i2c设备驱动重点关注两个数据结构i2c_client 和 i2c_driver,前者是描述设备信息的,后者是描述驱动的。
static const struct i2c_device_id my_i2c_dev_id[] = {
{ "my_i2c_dev", 0}, /* 设备名字 */
{ }
};
static struct i2c_driver my_i2c_drv = {
.driver = {
.name = "no", /* 这个名字不重要 */
.owner = THIS_MODULE,
},
.probe = my_i2c_drv_probe, /* 当匹配到i2c设备时调用 */
.remove = my_i2c_drv_remove, /* 当卸载i2c设备或驱动时调用 */
.id_table = my_i2c_dev_id, /* 这个结构体中的名字很重要 */
};
复制代码
其中my_i2c_dev很是的重要,由于这个名字就是用来和设备进行匹配的名字。
/* 设备树匹配列表 */
static const struct of_device_id my_i2c_dev_of_match[] = {
{ .compatible = "my_i2c_dev, 0" },
{ /* Sentinel */ }
};
/* i2c驱动结构体 */
static struct i2c_driver my_i2c_drv = {
.probe = ap3216c_probe,
.remove = ap3216c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "no",
.of_match_table = my_i2c_dev_of_match,
},
};
复制代码
static int __init my_i2c_drv_init(void) {
i2c_add_driver(&my_i2c_drv);
return 0;
}
复制代码
static struct i2c_board_info my_i2c_dev_info = {
I2C_BOARD_INFO("my_i2c_dev", 0x20), //名字,设备地址
};
复制代码
/* * busnum:哪一条总线,也就是选择哪个i2c控制器 * info:i2c设备信息数组 * n:数组有几项 */
i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned n);
i2c_register_board_info(0, my_i2c_dev_info, ARRAY_SIZE(my_i2c_dev_info));
复制代码
/* 动态注册能够在内核运行期间注册,也就是能够应用 在加载驱动模块中 */
static struct i2c_board_info my_i2c_dev_info = {
I2C_BOARD_INFO("my_i2c_dev", 0x20), //名字,设备地址
};
复制代码
/* * adap:指定i2c设备器,之后访问设备的时候,使用过哪个设备器(i2c主机控制器)去访问 * info:指定i2c设备信息 */
struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);
复制代码
/* 在i2c节点下添加设备信息 */
&i2c1 {
my_i2c_dev@20 {
compatible = "my_i2c_dev,0"
}
}
复制代码
/* * adap:i2c适配器 * msgs:消息数据 * num:数组的个数 */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 复制代码
struct i2c_msg {
__u16 addr; //从设备地址
__u16 flags; //读或写
__u16 len; //消息的长度
__u8 *buf; //消息
};
复制代码
/* 定义 i2c_msg 结构体 */
struct i2c_msg msg[2];
char val[10]
/* 填充msg */
msg[0].addr = my_i2c_client->addr; /* 这个client在probe函数中获得的 */
msg[0].flags = 0; /* 0表示写,1表示读 */
msg[0].buf = 0x80; /* 写:要发送的数据地址,读:读取到的数据存放的地址 */
msg[0].len = 1; /* 想要传输的字节数 */
/* 填充msg */
msg[1].addr = my_i2c_client->addr; /* 这个client在probe函数中获得的 */
msg[1].flags = 1; /* 1表示读 */
msg[1].buf = val; /* 读到的数据存在这里 */
msg[1].len = 4; /* 想要读取的字节数 */
/* 传输数据 */
i2c_transfer(my_i2c_client->adapter, msg, 2); /* 有两个msg */
复制代码