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

 

<think>好的,我现在需要帮助用户寻找关于BK1198 I2C驱动程序开发的资源。首先,我得理解用户的需求。他们可能正在开发嵌入式系统,需要与BK1198芯片通过I2C接口通信。BK1198的具体功能是什么?可能是一个无线芯片或者某种传感器?了解芯片的功能有助于确定驱动开发的方向。 接下来,我应该考虑如何获取BK1198的技术文档。通常,芯片厂商会提供数据手册(datasheet)和应用笔记(application notes)。用户需要这些文档来了解寄存器的配置、通信协议等关键信息。比如,用户可能需要设置I2C地址、数据传输速率,以及如何通过寄存器配置芯片的工作模式。 然后是I2C协议的基础知识。用户可能需要复习I2C的基本操作,比如起始信号、停止信号、地址传输、读写操作等。Linux内核中已经提供了I2C子系统,包括核心驱动框架和常用的函数接口,如i2c_transfer、i2c_smbus_write_byte等。这部分内容对于开发驱动程序至关重要,用户需要熟悉这些接口的使用方法。 接下来,参考现有的I2C驱动程序示例会很有帮助。Linux内核中的其他I2C设备驱动可以作为模板,用户可以根据BK1198的特定需求进行修改。例如,编写探测函数probe)、初始化函数,以及实现文件操作结构体中的读写方法。用户需要注意处理中断、电源管理等功能,如果芯片支持的话。 另外,用户可能需要配置设备树(Device Tree)来声明I2C设备。这包括指定I2C总线地址、时钟频率和其他相关参数。设备树的配置是否正确直接影响到驱动是否能正确识别硬件。 调试工具也是开发过程中可或缺的部分。i2c-tools是一个实用的工具集,用户可以使用i2cdetect检测设备是否在线,i2cget和i2cset进行寄存器读写测试。这些工具能帮助快速验证硬件连接和基本通信是否正常。 社区和论坛资源方面,Linux内核邮件列表和Stack Overflow是解决问题的好地方。用户可能会遇到硬件兼容性问题或内核版本差异,这时候社区的经验会非常有用。 此外,用户可能需要关注芯片厂商是否提供SDK或参考代码,这可以大大加速开发进程。如果BK1198有常见的应用场景,比如音频处理或无线通信,相关的开源项目或论文也可能提供有价值的实现参考。 最后,测试与验证阶段需要确保驱动程序的稳定性和性能。用户应该编写单元测试,模拟各种使用场景,并记录调试日志,便于排查问题。 总结下来,我需要为用户提供从技术文档获取、I2C协议基础、驱动开发步骤、调试工具使用到社区资源的一整套解决方案,确保他们能够系统地进行BK1198的I2C驱动开发。</think>### BK1198 I2C驱动程序开发资源指南 #### 1. **技术文档与数据手册** - **BK1198芯片手册**:需联系芯片厂商获取官方数据手册,重点关注I2C接口章节,包含寄存器地址、控制命令、时序要求等。例如,I2C地址通常为$7\text{bit}$格式(如$0x50$),通信速率可能支持标准模式($100\text{kHz}$)或快速模式($400\text{kHz}$)[^1]。 - **应用笔记**:厂商可能提供参考设计或配置示例,如初始化流程: ```c // 示例:设置BK1198工作模式 i2c_smbus_write_byte_data(client, REG_MODE, 0x01); // 向模式寄存器写入0x01 ``` #### 2. **I2C协议基础** - **Linux I2C子系统**:内核提供`i2c_driver`结构体框架,需实现以下关键函数: ```c static struct i2c_driver bk1198_driver = { .probe = bk1198_probe, .remove = bk1198_remove, .id_table = bk1198_id, .driver = { .name = "bk1198" }, }; ``` - **数据传输函数**:使用`i2c_transfer()`或封装函数(如`i2c_smbus_read_word_data()`)[^2]。 #### 3. **驱动开发步骤** - **设备树配置**:在`.dts`文件中声明I2C设备: ```dts &i2c1 { bk1198@50 { compatible = "vendor,bk1198"; reg = <0x50>; clock-frequency = <400000>; }; }; ``` - **驱动代码结构**: ```c static int bk1198_probe(struct i2c_client *client) { // 初始化硬件、注册字符设备等 return 0; } static const struct of_device_id bk1198_of_match[] = { { .compatible = "vendor,bk1198" }, {} }; ``` #### 4. **调试工具** - **i2c-tools**: ```bash i2cdetect -y 1 # 扫描I2C总线上的设备 i2cset -y 1 0x50 0x00 0xFF # 向地址0x50写入数据 ``` - **内核日志**:通过`dmesg`查看驱动加载状态和错误信息。 #### 5. **社区资源** - **Linux内核邮件列表**:提交问题时需附上代码片段和日志。 - **GitHub仓库**:搜索类似驱动实现(如`bmp280_i2c.c`)作为参考。 #### 6. **测试与验证** - **单元测试**:编写内核模块测试读写操作: ```c static void test_read_reg(void) { u8 val = i2c_smbus_read_byte_data(client, REG_STATUS); printk(KERN_INFO "Status: 0x%02X\n", val); } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值