在非general-hardware组件内如何调用smc命令来获取信息

场景1: 当前想新创建个组件,在组件内部给扩展板cpld发送smc命令获取其他板卡的在位信息,根据板卡的在位情况得出机型,请问要怎样在代码中实现呢?

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

:white_check_mark: 总结:实现流程图

graph TD
    A[创建组件] --> B[确认目标SMC芯片: Smc_ExpBoardSMC]
    B --> C[通过D-Bus或mdbctl调用BlockIO]
    C --> D[发送SMC命令获取Presence数据]
    D --> E[解析位图或寄存器值]
    E --> F[根据板卡组合判断机型]
点击此处查看详细分析解答

在非 general-hardware 组件内调用 SMC 命令获取信息

在非 general_hardware 组件中调用 SMC(System Management Controller)命令获取扩展板 CPLD 的信息(如板卡在位状态、机型识别等),可以通过以下方式实现。以下内容基于上下文中的知识图谱和文档内容,提供实现方案和技术要点。


:small_blue_diamond: 一、基本原理:SMC 命令与通信机制

1. SMC 是什么?

  • SMC(System Management Controller)是系统管理控制器,负责低层硬件控制,包括:
    • 读取传感器数据
    • 管理板卡在位状态(Presence)
    • 控制电源、风扇、LED
    • 提供标准化命令接口(SMC 命令字)

在天池架构中,SMC 协议是连接 CPU-BIOS、BMC 和 CPLD 的通用标准接口。

2. SMC 命令通信方式

  • 使用 I2C/SMBus 接口进行通信
  • 通过 Accessor 扫描器(Scanner)或 BlockIO/BitIO 接口发起读写操作
  • 每个命令由 Function + Command + M/S + R/W + Param 构成(共5字节)

:small_blue_diamond: 二、实现路径:如何在自定义组件中调用 SMC 命令

:white_check_mark: 步骤 1:确定目标 SMC 芯片对象

你需要明确要与哪个 SMC 芯片通信,通常是:

  • Smc_ExpBoardSMC:扩展板上的 SMC
  • Smc_CpuBrdSMC:CPU 板上的 SMC
  • Smc_EnclSMC:背板(Enclosure)上的 SMC

这些芯片通过 Chip.Smc 模块暴露为 D-Bus 对象。

示例:获取扩展板 SMC 对象

"Chip": "#/Smc_ExpBoardSMC"

:white_check_mark: 步骤 2:使用 mdbctl 或 D-Bus 调用 BlockIO 接口

方法一:使用 mdbctl call 命令(适用于调试或脚本)

你可以使用 mdbctl 命令直接对 SMC 芯片进行 BlockIO 读写操作。

示例:向扩展板 SMC 发起写操作(发送 SMC 命令)
mdbctl call Smc_ExpBoardSMC_0101 bmc.kepler.Chip.BlockIO Write 0 0x8008a01 1 0x01
  • Smc_ExpBoardSMC_0101:目标 SMC 芯片对象名
  • BlockIO Write:写一个字节
  • 0x8008a01:命令偏移地址(需根据硬件定义确定)
  • 0x01:写入值(可为 SMC 命令参数)
示例:读取传感器或状态信息
mdbctl call Smc_ExpBoardSMC_0101 bmc.kepler.Chip.BlockIO Read 0 0x1234 2
  • 从地址 0x1234 读取 2 字节数据

:white_check_mark: 提示:BlockIO 是推荐方式,优于 BitIO,支持多字节读写。


方法二:在组件代码中调用 D-Bus 接口(正式实现方式)

如果你是在 C/C++ 或 Lua 组件中开发,可通过 D-Bus 调用 bmc.kepler.Chip.BlockIO 接口。

接口路径示例:
/bmc/kepler/Chip/Smc/Smc_ExpBoardSMC_0101
接口方法:
  • Read(a{sv} config, uint32 offset, uint32 size)
  • Write(a{sv} config, uint32 offset, uint32 size, ay data)
示例 Lua 调用(伪代码):
local dbus = require("luci.dbus")
local conn = dbus.session()

local smc_path = "/bmc/kepler/Chip/Smc/Smc_ExpBoardSMC_0101"
local proxy = conn:object("bmc.kepler.hwproxy", smc_path)

-- 读取 2 字节数据,从 offset 0x1234
local result = proxy:invoke("bmc.kepler.Chip.BlockIO", "Read", {
    config = {},
    offset = 0x1234,
    size = 2
})

:light_bulb: 结果 result 将返回传感器或状态值。


:white_check_mark: 步骤 3:解析 SMC 命令返回的数据

根据硬件设计文档,SMC 返回的数据格式如下:

字节 含义
0 命令响应头(如 0x40 表示成功)
1~n 实际数据(如 Presence 位图)

例如,读取板卡在位状态的响应可能为一个 位图(bitmap)

  • Bit0:Riser Card 1 Presence → 1 表示在位
  • Bit1:NVMe Card 1 Presence → 1 表示在位

示例:判断某板卡是否在位

uint8_t presence_map = response[1];
bool is_riser_present = (presence_map >> 0) & 0x1;
bool is_nvme_present = (presence_map >> 1) & 0x1;

:white_check_mark: 步骤 4:根据 Presence 信息推断机型

你可以通过多个板卡的在位组合来判断整机型号。

示例逻辑:

if is_riser_present and is_nvme_present and not is_gpu_present then
    model = "Server_Model_A"
elseif is_riser_present and is_gpu_present then
    model = "Server_Model_B"
else
    model = "Unknown"
end

:small_blue_diamond: 三、前提条件与配置要求

:gear: 1. CSR 文件中正确配置 SMC 芯片对象

在 CSR 文件(如 exp_board.sr)中必须定义 SMC 芯片:

"Smc_ExpBoardSMC_0101": {
    "Type": "Smc",
    "ChipType": "SMC",
    "Bus": "I2c_5",
    "Address": "0x3e",
    "Offset": 0
}

:gear: 2. 配置 Scanner 或 Accessor(可选)

如果你需要周期性扫描状态,可配置 Scanner

"Scanner_BoardPresence": {
    "Chip": "#/Smc_ExpBoardSMC_0101",
    "Offset": 12544,
    "Size": 2,
    "Period": 2000,
    "Value": 0
}

然后通过 Scanner_BoardPresence.Value 获取实时值。


:small_blue_diamond: 四、调试建议

  1. 查看 SMC 是否存在

    ipmcget -d v
    

    检查是否有 Smc_ExpBoardSMC_... 类似对象。

  2. 查看日志确认通信是否成功

    grep "Smc" /var/log/app.log
    
  3. 使用 mdbctl call 先手动测试读写


:white_check_mark: 总结:实现流程图

graph TD
    A[创建组件] --> B[确认目标SMC芯片: Smc_ExpBoardSMC]
    B --> C[通过D-Bus或mdbctl调用BlockIO]
    C --> D[发送SMC命令获取Presence数据]
    D --> E[解析位图或寄存器值]
    E --> F[根据板卡组合判断机型]

:books: 参考资料

如遇 Device not present 错误,请检查:

  • I2C 总线连接是否正常
  • CSR 是否正确定义 SMC 对象
  • 地址和偏移是否与硬件一致

:white_check_mark: 完整实现建议:优先使用 BlockIO 接口 + mdbctl 调试 + CSR 正确定义对象,即可在非 general_hardware 组件中成功调用 SMC 命令获取板卡信息并识别机型。

一般是用个对象,引用smc器件,对象发给组件后通过引用的对象去调用Write/Read方法获取数据。

1.当前代码里面有具体的例子么?

2.下面这样先获取smc,然后再调用smc的方法去读写是否可行?
ok, smc = pcall(mdb.get_object, self.bus, ‘/bmc/kepler/Chip/Smc/Smc_ExpBoardSMC_0101’,
‘bmc.kepler.Chip.BlockIO’)

参考14140224_PROTOCOL_0.sr文件里NVMe对象的VPDChip属性

"Nvme_1": {
            "Slot": "${Slot}",
            "TemperatureCelsius": "<=/Scanner_Temp.Value",
            "MediaType": 1,
            "Protocol": 6,
            "PredictedMediaLifeLeftPercent": "<=/Scanner_Remtime.Value",
            "RefComponent": "#/Component_PCIeCard1",
            "Failure": "<=/Scanner_SSD_Fault.Value",
            "PredictiveFailure": "<=/Scanner_SSD_Pre_Fault.Value",
            "VPDChip": "#/Chip_Virtual_SSD",
            "SSDChip": "#/Chip_SSD"
        },
        "Chip_Virtual_SSD": {
            "Address": 166,
            "AddrWidth": 1,
            "OffsetWidth": 1,
            "WriteTmout": 100,
            "ReadTmout": 100,
            "HealthStatus": 0
        },

代码里

        ok, ret = pcall(function ()
            return string.unpack('B', self.VPDChip:Read(ctx.get_context_or_default(), next_item_offset, 1))
        end)

当前可通过如下方式获取扩展板的SMC对象,已完成验证可行;需在server.json中required中添加相关的path和interface即可;

ok, smc = pcall(mdb.get_object, self.bus, ‘/bmc/kepler/Chip/Smc/Smc_ExpBoardSMC_0101’,
‘bmc.kepler.Chip.BlockIO’)