先不上直接結果,隨我娓娓道來。。
一、回顧:
在筆記五:linux下i2c子系統學習中,是基於linux2.6.內核。在linux3.0一下,i2c的設備表示是使用的板級程序實現,及i2c設備使用i2c_client表示,將設備信息用struct i2c_board_info加載,用i2c_new_device函數將設備加載到i2c總線。
exp:device.c
static struct i2c_board_info xxx_info = {
I2C_BOARD_INFO("xxx", 0x48), //xxx爲匹配名字!!!
};
driver.c
const struct i2c_device_id xxx_id[] = {
{ "xxx", 0 }, //i2c_device_id結構體中的xxx纔是匹配的名字!!!
{ }
};
static struct i2c_driver it6801_drv = {
.driver = {
.name = "xxx",
.owner = THIS_MODULE,
},
.probe = xxx_drv_probe,
.remove = xxx_drv_remove,
.id_table = xxx_id,
};
二、新的內核(linux3.0以後的內核),引入DTS設備樹。設備的表述全部用DTS設備樹表示。
引出我實現的i2c驅動而且probe函數不執行的原因-->問題代碼:
exp:dts
&i2cN { //N代表是第N路i2c總線
status = "okay";
xxx@48 {
status = "okay";
compatible = "xxx"; //compatible變量爲匹配的名字
reg = <0x48>;
};
}
driver:
static const struct of_device_id of_xxx_match[] = {
{ .compatible = "xxx" }, //可以看出,這個爲匹配dts中的設備
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_xxx_match);
static struct i2c_driver xxx_drv = {
.driver = {
.name = "xxx",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_xxx_match),
},
.probe = xxx_drv_probe,
.remove = xxx_drv_remove,
};
飛速寫完代碼,發現我的xxx_drv_probe函數並沒有執行,我寫的另外兩個i2c驅動,也是掛載到一路總線,而且另外兩個驅動跑的尚好,emmm...爲啥這個就不行呢?分析原因:首先懷疑dts沒有寫對,compatible 變量名字不一致;其次,查看/sys/bus/i2c/devices/下,設備存在,查看/sys/bus/i2c/drivers/下,我的xxx_i2c_driver也存在!這就奇怪了;然後我用arm-linux-nm 命令查看我的驅動,顯示xxx_drv_probe也編譯進去了。。。。還是無果,最後這個驅動和好使的兩個驅動比較,發現少了一個struct i2c_driver中的id_table沒有實現。
driver:
static const struct of_device_id of_xxx_match[] = {
{ .compatible = "xxx" }, //可以看出,這個爲匹配dts中的設備
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_xxx_match);
const struct i2c_device_id xxx_id[] = {
{ "xxx", 0 },
{ }
};
static struct i2c_driver xxx_drv = {
.driver = {
.name = "xxx",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_xxx_match),
},
.probe = xxx_drv_probe,
.remove = xxx_drv_remove,
.id_table = xxx_id, //雖然沒有使用到,但是必須實現,否則probe函數不會調用!!!
};
加上struct i2c_driver中的id_table之後,果然probe函數調用成功,,,我的天!!!帶着問題查看源代碼,如下。
三、i2c總線匹配流程
調用流程:
i2c_add_driver
-->i2c_register_driver
-->driver->driver.bus = &i2c_bus_type;
-->driver_register
-->bus_add_driver
-->driver_attach
-->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); // 輪詢查找i2c總線上的設備
-->__driver_attach
-->driver_match_device
-->driver_probe_device
-->really_probe
-->dev->bus->probe(dev);
-->i2c_device_probe<i2c_bus_type;結構體中實現>
-->if (!driver->probe || !driver->id_table)
-->driver->probe(client, i2c_match_id(driver->id_table, client));
-->xxx_drv_probe<i2c_driver結構體中我們的驅動實現的probe>
最終在really_probe函數中:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
... ...//省略無關代碼
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
... ...
}
在註冊i2c總線時, bus->probe函數已經實現,及 i2c_device_probe函數;
在 i2c_device_probe函數中:
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
... ...
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)//判斷可知,如果driver->id_table爲空,則直接return,不會再往下執行。所以id_table必須實現!!!,纔會調用probe函數
return -ENODEV;
... ...
status = driver->probe(client, i2c_match_id(driver->id_table, client));
... ...
}
四、綜上代碼流程總結自己寫的i2c驅動probe不執行的原因:
如果在自己的驅動中:i2c_driver的id_table不實現,最終會在i2c_device_probe函數中的
if (!driver->probe || !driver->id_table) return -ENODEV;
語句中直接返回,從而調不到自己實現的xxx_drv_probe函數!
所以,在新內核的dts設備樹表述設備時,註冊i2c_driver時,一定要將i2c_driver中的id_table實現!!!