实现i2c通道切换并且写数据,通道不被切走问题咨询

问题描述

读取访问9555芯片数据,中间经过9545通道切换芯片,通过lua代码实现,发现读取数据时候,通道会被切走;

环境信息

  • 操作系统:[如 Ubuntu 24.04]

  • 软件版本:[如 OpenUBMC2509]

  • 硬件配置:[如 CPU、内存等]

重现步骤

  1. [第一步]
    使用/bmc/kepler/Bus/I2c/I2c_10的bmc.kepler.Bus.BlockIO接口调用.Write方法进行切换通道到CH3

  2. [第二步]
    使用/bmc/kepler/Bus/I2c/I2c_10的bmc.kepler.Bus.BlockIO接口调用.WriteRead方法进行读取9555芯片的数据

期望结果

正常读取到数据

实际结果

数据数据失败

求助

是否有加锁的机制,防止通道被切走

答案内容由AI生成,仅供参考:

:white_check_mark: 解决方案

:white_check_mark: 推荐方案:使用 原子化连续命令调用

为避免通道被中途切走,必须确保通道切换和数据读写操作连续、无中断地完成

:white_check_mark: 正确调用方式(使用 busctl 连续发送)

# 示例:切换到CH3(地址0x70,写入0x08表示CH3),然后读取PCA9555(地址0x20)的2个字节
busctl --user call bmc.kepler.hwproxy /bmc/kepler/Bus/I2c/I2c_10 bmc.kepler.Bus.BlockIO Write a{ss}yay 0 0x70 1 0x08; \
busctl --user call bmc.kepler.hwproxy /bmc/kepler/Bus/I2c/I2c_10 bmc.kepler.Bus.BlockIO WriteRead a{ss}yayy 0 0x20 0 2

:warning: 注意:建议在同一Shell命令行中使用分号 ; 连接两条命令,尽量减少时间间隔。


:white_check_mark: 替代方案:使用支持 ComboWriteRead 的BlockIO接口(推荐)

若底层支持,应优先使用 ComboWriteRead 方法,它在一个事务中完成“写地址 + 读数据”,更适用于带通道切换的场景。

# 使用ComboWriteRead实现原子操作
busctl --user call bmc.kepler.hwproxy /bmc/kepler/Bus/I2c/I2c_10 bmc.kepler.Bus.BlockIO ComboWriteRead ...

:pushpin: 说明ComboWriteReadBMC.Kepler.Chip.BlockIO 接口的一部分,设计用于优化写后读的操作流程,减少中间状态被干扰的风险。


:white_check_mark: 软件层加锁机制(预防性建议)

目前 未发现明确的软件锁机制 用于锁定I2C通道防止被切走。但可采取以下措施增强稳定性:

措施 实现方式 说明
Lua脚本锁通道 在操作前后加标志位或互斥信号量 自行实现逻辑锁,但不能阻止 hwproxy 轮询
禁用相关轮询服务(测试用) 临时停止影响通道的 hwproxy 模块 仅用于调试,生产环境不推荐
重试机制 失败后自动重试3~5次 实践中有效,结合错误码判断
点击此处查看详细分析解答

I2C通道切换与数据读写问题分析及解决方案

在使用I2C总线进行多路复用器(如PCA9545)通道切换并读取目标设备(如PCA9555芯片)数据的过程中,遇到了通道被意外切换导致读取失败的问题。以下是对该问题的综合分析与解决方案。


:magnifying_glass_tilted_left: 问题定位

现象描述

  • 使用 bmc.kepler.Bus.BlockIO.Write 方法通过PCA9545切换I2C通道至CH3;
  • 随后调用 WriteRead 方法读取挂载在该通道下的PCA9555芯片数据;
  • 实际执行过程中,在两次操作之间通道被其他线程或服务意外切走,导致后续读取失败。

根本原因

根据知识库信息:

“如果要对需要切通道的chip进行读写,需要保证两条命令没有间隔,连续输入,不然可能会被代理自身的轮询打断,将通道切到错误的地方,导致读写失败。”

此问题本质是 并发访问竞争 导致:

  • BMC系统中 hwproxy 服务存在周期性轮询任务,会主动管理I2C通道状态;
  • 若两次操作(切通道 + 读写)之间有延迟,hwproxy 轮询可能插入围,将通道切换回默认状态;
  • Lua脚本执行不具备原子性,无法保证两个D-Bus调用间不被中断。

:white_check_mark: 解决方案

:white_check_mark: 推荐方案:使用 原子化连续命令调用

为避免通道被中途切走,必须确保通道切换和数据读写操作连续、无中断地完成

:white_check_mark: 正确调用方式(使用 busctl 连续发送)

# 示例:切换到CH3(地址0x70,写入0x08表示CH3),然后读取PCA9555(地址0x20)的2个字节
busctl --user call bmc.kepler.hwproxy /bmc/kepler/Bus/I2c/I2c_10 bmc.kepler.Bus.BlockIO Write a{ss}yay 0 0x70 1 0x08; \
busctl --user call bmc.kepler.hwproxy /bmc/kepler/Bus/I2c/I2c_10 bmc.kepler.Bus.BlockIO WriteRead a{ss}yayy 0 0x20 0 2

:warning: 注意:建议在同一Shell命令行中使用分号 ; 连接两条命令,尽量减少时间间隔。


:white_check_mark: 替代方案:使用支持 ComboWriteRead 的BlockIO接口(推荐)

若底层支持,应优先使用 ComboWriteRead 方法,它在一个事务中完成“写地址 + 读数据”,更适用于带通道切换的场景。

# 使用ComboWriteRead实现原子操作
busctl --user call bmc.kepler.hwproxy /bmc/kepler/Bus/I2c/I2c_10 bmc.kepler.Bus.BlockIO ComboWriteRead ...

:pushpin: 说明ComboWriteReadBMC.Kepler.Chip.BlockIO 接口的一部分,设计用于优化写后读的操作流程,减少中间状态被干扰的风险。


:white_check_mark: 软件层加锁机制(预防性建议)

目前 未发现明确的软件锁机制 用于锁定I2C通道防止被切走。但可采取以下措施增强稳定性:

措施 实现方式 说明
Lua脚本锁通道 在操作前后加标志位或互斥信号量 自行实现逻辑锁,但不能阻止 hwproxy 轮询
禁用相关轮询服务(测试用) 临时停止影响通道的 hwproxy 模块 仅用于调试,生产环境不推荐
重试机制 失败后自动重试3~5次 实践中有效,结合错误码判断

:puzzle_piece: 相关配置与注意事项

:wrench: ChannelId 配置原则

  • 每个 ChannelId 必须唯一;
  • ChannelId=3 对应 PCA9545 的 CH3;
  • 示例配置(CSR中定义):
    "I2cMux_Pca9545_i2c10_chan3": {
      "ChannelId": 3
    }
    

:hammer_and_wrench: 工具建议:使用 i2ctransfer 验证

可用 i2ctransfer 工具验证通信是否正常:

# 切换通道到CH3 (0x70, 写0x08)
i2ctransfer -w 1 -r 0 10 0x70@0x08

# 读取PCA9555地址0x20的2字节数据
i2ctransfer -w 0 -r 2 10 0x20

:white_check_mark: i2ctransfer 已验证可用于与MCU通信,也适配于 hwproxy 组件。


:cross_mark: 不推荐做法:使用 mdbctl

mdbctl call I2c_10 bmc.kepler.Bus.BlockIO Write ...

:cross_mark: 原因mdbctl 暂不支持一行集成多条命令,难以保证连续性,容易被轮询打断。


:white_check_mark: 最佳实践总结

项目 推荐做法
调用方式 使用 busctl 连续调用 Write + WriteRead(或 ComboWriteRead
命令格式 同一行使用 ; 分隔,缩短间隔
工具选择 首选 busctl,次选 i2ctransfer 调试
加锁机制 无内置锁,依赖操作原子性或重试机制
失败处理 添加重试逻辑(3~5次),判断返回码
通道验证 操作前后可读取多路复用器状态确认当前通道

:books: 参考资料


如仍有问题,建议开启 hwproxy 日志跟踪 RPC count 和通道切换记录,进一步定位是否由轮询抢占导致。

硬件代理维护有所有CSR配置的硬件器件的拓扑信息,访问器件时不需要手动去切通道

只要CSR配置正确,硬件代理会自动处理前级器件的通道切换,不要手动去切通道,没有实际作用。

例如,用户手动切通道到CH1 ,组件业务访问了位域CH2上的某个器件,硬件代理在处理这个访问请求时会自动切通道到CH2,导致用户操作失效

对于反馈的场景,只要器件在CSR配置了,可以直接通过资源树方法访问对应的9555器件就行了,不要考虑切通道问题。

如果还是访问失败,请提供失败提示日志,提供下一键日志

我们目前需求需要用lua实现切换通道,并且访问比较多的芯片偏移地址数据;
由于拓扑里面的设备比较多,就没有都配置到SR里面;
还需需要提供一个锁操作,可以在lua里面实现不被切走的机制

目前通道切换加锁这些动作是根据一次rpc请求走的,两次bus的BlockIO属于两次rpc,没法保证通道不被切走。硬件代理没有对外提供通道加锁的能力。通过sr配置的话可以由硬件代理处理前级器件的通道切换。