Linux I2C 故障注入

基于 GPIO 的 I2C 总线主控驱动可以配置为提供故障注入功能。然后,它旨在连接到另一个 I2C 总线,该总线由正在测试的 I2C 总线主控驱动。GPIO 故障注入驱动可以在总线上创建特殊状态,其他 I2C 总线主控驱动应优雅地处理这些状态。

一旦 Kconfig 选项 I2C_GPIO_FAULT_INJECTOR 被启用,在内核 debugfs 文件系统中将有一个名为 'i2c-fault-injector' 的子目录,通常挂载在 /sys/kernel/debug。每个 GPIO 驱动的 I2C 总线都会有一个单独的子目录。每个子目录将包含用于触发故障注入的文件。下面将描述这些文件及其预期用例。

线状态

“scl”

读取此文件,可以获取 SCL 的当前状态。写入此文件,可以改变其状态,使其强制为低电平或再次释放。因此,通过使用“echo 0 > scl”命令,您将 SCL 强制置为低电平,从而无法进行通信,因为正在测试的总线主控将无法进行时钟操作。它应该检测到 SCL 无响应的状况,并向更高层报告错误。

“sda”

读取此文件,可以获取 SDA 的当前状态。写入此文件,可以改变其状态,使其强制为低电平或再次释放。因此,通过使用“echo 0 > sda”命令,您将 SDA 强制置为低电平,从而无法传输数据。正在测试的总线主控应该检测到此状况并触发总线恢复(参见 I2C 规范第 4 版,第 3.1.16 节),使用 Linux I2C 核心的辅助函数(参见 'struct bus_recovery_info')。然而,总线恢复不会成功,因为 SDA 将一直被固定为低电平,直到您使用“echo 1 > sda”再次手动释放它。可以利用“不完整传输”类故障注入器进行自动释放测试。

不完整传输

以下故障注入器会创建 SDA 被设备拉低的情况。总线恢复应该能够解决这些情况。但请注意:有些 I2C 客户端设备会检测到其 SDA 被卡住,并在几毫秒后自行释放。此外,可能存在一个外部设备对 I2C 总线进行去毛刺和监控。它也可能检测到卡住的 SDA 并自行启动总线恢复。如果您想在总线主控驱动中实现总线恢复,请务必提前检查您的硬件设置中是否存在此类设备。并且务必使用示波器或逻辑分析仪进行验证!

“incomplete_address_phase”

此文件仅供写入,您需要向其写入一个现有 I2C 客户端设备的地址。然后,将启动对此设备的读取传输,但它会在客户端地址传输后的 ACK 阶段停止。由于设备会 ACK 其存在,这导致 SDA 在 SCL 为高电平时被设备拉低。因此,与上述“sda”文件类似,正在测试的总线主控应该检测到此状况并尝试总线恢复。然而,这一次它应该会成功,设备在 SCL 切换后应该会释放 SDA。

“incomplete_write_byte”

与上述类似,此文件仅供写入,您需要向其写入一个现有 I2C 客户端设备的地址。

注入器将再次在某个 ACK 阶段停止,因此设备将保持 SDA 为低电平,因为它确认了数据。然而,与‘incomplete_address_phase’相比,存在两个区别

  1. 发送的消息将是写入消息

  2. 在地址字节之后,将传输一个 0x00 字节。然后,在 ACK 处停止。

这是一个非常微妙的状态,当 SCL 上发生更多时钟脉冲时,设备会设置为将任何数据写入寄存器 0x00(如果它有寄存器)。这就是为什么总线恢复(最多 9 个时钟脉冲)必须检查 SDA 或发送额外的 STOP 条件以确保总线已释放的原因。否则,随机数据将被写入设备!

仲裁丢失

在这里,我们希望模拟正在测试的主控在多主控设置中与另一个主控失去总线仲裁的情况。

“lose_arbitration”

此文件仅供写入,您需要写入仲裁干扰的持续时间(单位为微秒,最大为 100 毫秒)。调用进程将休眠并等待下一个总线时钟。然而,该进程是可中断的。

仲裁丢失是通过等待正在测试的主控将 SCL 拉低,然后将 SDA 拉低一段时间来实现的。因此,发送的 I2C 地址应该被破坏,并且应该被正确检测到。这意味着发送的地址应该包含大量‘1’位,以便能够检测到损坏。此地址不需要有设备,因为仲裁丢失应该提前检测到。另请注意,SCL 的下降是通过中断进行监控的,因此中断延迟可能会导致前几个位未被破坏。在其他情况下空闲的总线上使用此故障注入器的一个良好起点是

# echo 200 > lose_arbitration &
# i2cget -y <bus_to_test> 0x3f

传输期间崩溃

一旦正在测试的主控开始传输,此故障注入器将引发内核崩溃。这通常意味着总线主控驱动的状态机将被非正常中断,总线可能陷入异常状态。使用此功能检查您的关机/重启/启动代码是否能处理此场景。

“inject_panic”

此文件仅供写入,您需要写入检测到传输开始与诱导内核崩溃之间的延迟(单位为微秒,最大为 100 毫秒)。调用进程将休眠并等待下一个总线时钟。然而,该进程是可中断的。

传输的开始是通过等待正在测试的主控将 SCL 拉低来检测的。使用此故障注入器的一个良好起点是

# echo 0 > inject_panic &
# i2cget -y <bus_to_test> <some_address>

请注意,不需要有设备侦听您正在使用的地址。然而,结果可能会因此而异。