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 */
複製代碼