先不上直接结果,随我娓娓道来。。

一、回顾:

       在笔记五: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实现!!!

 

Logo

更多推荐