筆記六:linux3.0以後自己實現i2c驅動probe探測函數不執行原因

先不上直接結果,隨我娓娓道來。。

一、回顧:

       在筆記五: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實現!!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章