runtime_accessor
版本信息
| 项目 | 内容 |
|---|---|
| 组件版本 | 1.130.9 |
| 首发版本 | openUBMC 26.09 |
| 文档作者 | openUBMC 社区 |
| 最后更新 | 2026-09-15 |
| 许可证 | Mulan PSL v2 |
1. 组件概述
1.1 组件简介
runtime_accessor 是 openUBMC 的 Lua 器件访问库,将外围器件(如 PCA9555、EEPROM、Lm75)和总线(如 I2C、Hisport、LocalBus)的读写操作封装为可按 CSR class_name 选择的驱动模块,供 hwproxy 在进程内调用。
组件在 mds/service.json 中声明为 library。它不启动独立服务进程、不提供 mds/model.json 资源协作接口,也不提供 IPMI 命令。Lua 代码安装到 /opt/bmc/apps/hwproxy/lualib,由 hwproxy 通过 require('driver.driver')、require('stream.stream')、require('mux.mux')、require('protocol.protocol') 加载。底层用户态驱动来自 libsoc_adapter。
1.2 解决什么问题
BMC 需要通过多种总线访问温度传感器、GPIO 扩展器、EEPROM、SMC、CPLD、SPI Flash 等外围器件。若各业务组件各自封装总线读写、Mux 切通道和协议组包,会出现重复实现、器件差异难以复用、无法随 hwproxy 组件化交付等问题。
runtime_accessor 把器件驱动、总线 Stream、Mux 切通道和协议包装拆成四类 Lua 模块,hwproxy 按 CSR 对象的 class_name 实例化对应实现,从而:
- 将器件/总线访问从业务组件中剥离,支持独立仓交付。
- 对
read/write/bit_read/bit_write以及 Mux 的open_channel/close_channel给出统一调用面。 - 通过 Protocol 的
wrap/unwrap支持 SMC 转发、MCTP over SMC 等组包,而不改动上层 Chip 调度。
1.3 核心功能
- 器件驱动(driver):按芯片类型封装寄存器读写、分页、自检和复位等能力。
- 总线 Stream(stream):封装 I2C、Hisport、GPIO、LocalBus、JTAG、CAN、ADC 等总线的
init/read/write,内部通过stream.open_drive加载libsoc_adapter.*。 - Mux:封装 PCA9545、PCA9544、JtagSwitch、McuSwitch 的通道打开/关闭。
- Protocol:封装 SMC 读写转发与 CRC 校验;无协议时使用
None透传。 - 字节序辅助:
drvlib_common提供htons/ntohs/htonl/ntohl。
1.4 关键术语表
| 术语 | 解释 |
|---|---|
| driver | 器件驱动类,封装芯片级 read / write,基类位于 driver.base。 |
| stream | 总线访问类,封装一次总线事务的 read / write,基类位于 stream.base。 |
| mux | 多路复用器封装,提供 open_channel / close_channel。 |
| protocol | 协议包装器,在下发总线前 wrap、在回读后 unwrap。 |
class_name | CSR/对象模型中的类型名,hwproxy 用它在 driver / stream / mux / protocol 导出表中选实现。 |
input | 读写请求表,常见字段包括 addr、offset、offsetWidth、len、mask、type、buffer。 |
| libsoc_adapter | 用户态 SoC 驱动适配库,Stream 通过 require('libsoc_adapter.i2c') 等打开。 |
| hwproxy | 硬件代理组件,加载本库并按资源树调度 Chip / Bus 访问。 |
| SMC | Server Management Channel,本库同时提供 driver.Smc 与 protocol.Smc。 |
| CSR | Component Self-Description Record,描述芯片地址、总线、Mux 等访问参数。 |
1.5 外部交互边界图
构建测试依赖见 mds/service.json:libmc4lua、persistence(均仅出现在 dependencies.test)。运行时代码随 hwproxy 部署;组件自身没有常驻进程、监听端口或 D-Bus 服务名。
2. API 使用说明与示例
以下接口均为 Lua 本地接口,不是资源协作接口,不能通过 busctl / mdbctl 调用。仓库无 mds/model.json、无 mds/ipmi.json。
2.1 模块加载与调用约定
功能说明
hwproxy 在构造 Chip / Bus 时按对象 class_name 选择实现;未知类型回退到对应导出表中的 None。
local driver = require('driver.driver')
local mux = require('mux.mux')
local protocol = require('protocol.protocol')
local stream = require('stream.stream')
-- hwproxy/src/lualib/chip.lua
obj.protocol = (protocol[object.class_name] or protocol.None).new()
obj.driver = (driver[object.class_name] or driver.None).new(obj, object)
obj.mux = (mux[object.class_name] or mux.None).new(obj)
-- hwproxy/src/lualib/hwproxy_objects/work_bus.lua
self.stream = (stream[object.class_name] or stream.None).new(object, object.name)安装后 Lua 搜索路径需包含 /opt/bmc/apps/hwproxy/lualib(由 hwproxy 进程配置,本库不单独设置 package.path)。
参数说明
四个聚合模块及其导出键如下。键名必须与 CSR class_name 一致(大小写敏感)。
| 模块 | 文件 | 导出键 |
|---|---|---|
driver.driver | src/lualib/driver/driver.lua | Eeprom、Lm75、Pca9555、Pca9545、Pca9544、Smc、Vrd、Ads78、CpldRegister、Cpld、Chip、CanbusChip、JtagSwitch、Rtc、CpldChip、Ina、None、SPIFlash |
stream.stream | src/lualib/stream/stream.lua | I2c、Hisport、Gpio、LocalBus、Jtag、JtagOverLocalBus、JtagOverGpio、Can、Adc、I3cOverLocalBus、I2cOverHisport、SPIOverHisport、JtagOverHisport、I2cOverLocalBus、InnerBus、None |
mux.mux | src/lualib/mux/mux.lua | Pca9545、Pca9544、None、JtagSwitch、McuSwitch |
protocol.protocol | src/lualib/protocol/protocol.lua | Smc、None |
driver.Rtc 实际加载 driver.rtc_chip。stream 目录另有 hisport2.lua,但 未 加入 stream.stream 导出表;按 class_name = Hisport2 构造时 hwproxy 会落到 stream.None。
返回值与异常
require 失败时由 Lua 抛出 module not found。driver.None:read / write 会 error('request error, Invalid chip type')。stream.None 的 read / write 为空实现。
应用场景
hwproxy 根据资源树加载 Chip 和 Bus 后,业务通过 Chip 的访问队列间接调用本库,而不是在其他组件中直接 require 器件驱动。
限制条件
- 本库不是独立 Skynet 服务;
dist/config.cfg中的runtime_accessor/service/main启动项在当前仓库无对应src/service实现。 gen/runtime_accessor/service.lua为 bingo 脚手架生成的DemoService,不是对外 API。- 未知
class_name不会自动报错,而是静默回退到None。
调试示例
local driver = require('driver.driver')
local lm75 = driver.Lm75.new(chip_obj, {
Address = 0x90,
AddrWidth = 1,
OffsetWidth = 1,
HighTempLimitation = 0xffff,
LowTempLimitation = 0xffff,
})
lm75:add_bus(host_bus)
local data = lm75:read({
addr = 0x90,
addrWidth = 1,
offsetWidth = 1,
offset = 0,
len = 2,
mask = 0xffff,
})单元测试入口为 test/unit/test.lua,覆盖 SMC 转发、RTC、INA 等用例。
2.2 通用访问入参 input
功能说明
Driver 与 Stream 的 read / write 共用一张 Lua 表作为请求上下文。基类 driver.base 会写入 rw_type(写 0、读 1),再调用 own_chip:wrap(input),然后把请求交给 host_bus:read / write。
参数说明
| 字段 | 类型 | 说明 |
|---|---|---|
addr | integer | 器件地址(I2C 等总线使用)。 |
addrWidth | integer | 地址宽度,构造 Driver 时来自对象属性 AddrWidth。 |
offset | integer | 寄存器或存储偏移;0xFFFFFFFF 表示扩展芯片读模式(drvlib_common.HAS_EXTEND_CHIP_READ_MODE)。 |
offsetWidth | integer | 偏移宽度,单位字节;I2C/Hisport 要求 0~4。 |
len | integer | 本次读写长度。I2C write 超过 1100 会报 request error, input len too long。 |
mask | integer | 位访问掩码;type == 0 时 Stream 可能按掩码裁剪返回值。 |
type | integer | 访问类型。hwproxy 中 0 为 bit、1 为 block;SPI Flash 另用 2 擦除、3 raw_write、4 raw_write_read。 |
buffer | string | 写数据;部分接口(如 PCA9555)写时不可为 nil。 |
name | string | 芯片名,用于日志。 |
protocol_flag | integer | 协议标志。I2C SMBUS 为 0x02;MCTP over SMC 为 0x03。 |
drv_write_delay | integer | 写延迟,默认 0。 |
append_write | table | SMC 转发读前需要先下发的写请求。 |
smc_target | any | Protocol SMC 用来区分 I2C 转发与 SMC 转发。 |
is_trace | boolean | 为真时触发 set_tracechip 注册的回调。 |
Driver 构造时从对象属性读取的公共字段(driver.base:ctor):
| 对象属性 | Driver 字段 | 默认值 |
|---|---|---|
Address | address | 无 |
AddrWidth | addr_width | 无 |
OffsetWidth | offset_width | 无 |
WriteTmout | write_tmout | 0 |
ReadTmout | read_tmout | 0 |
HealthStatus | health_status | nil |
ChipReset | ref_accessor | false |
返回值与异常
read 成功返回二进制字符串。write 无返回值。失败时通过 Lua error 抛出字符串,或抛出表 { err = <string>, cc = <number> }(SMC Completion Code)。
应用场景
所有器件/总线读写、SMC 组包、Mux 切通道后的访问都填充这张表。
限制条件
调用方必须先 driver:add_bus(bus),否则 read / write 访问 self.host_bus 会失败。wrap / unwrap 由 Chip 对象提供,单元测试需自行实现链式 wrap(见 test/unit/test_smc.lua)。
调试示例
local input = {
addr = 0x40,
addrWidth = 1,
offsetWidth = 1,
offset = 0x1f,
len = 10,
mask = 0xffffffff,
type = 1,
}
local ok, ret = pcall(chip.read, chip, input)
if not ok then
-- SMC 失败时 ret.err / ret.cc 可用
end2.3 Driver 器件驱动接口
功能说明
基类 driver.base 提供统一读写路径:设置 rw_type → own_chip:wrap → host_bus:read/write → own_chip:unwrap。具体器件类覆盖 read / write / bit_read / bit_write / chip_test 等。
参数说明
基类方法
| 方法 | 入参 | 出参 | 说明 |
|---|---|---|---|
ctor(chip, object) | Chip 对象、CSR 属性表 | 无 | 保存地址、位宽、超时等。 |
add_bus(bus) | 带 read / write 的 Bus | 无 | 绑定前级总线。 |
read(input) | input 表 | string | 读;若存在 append_write 则先写再循环读。 |
write(input) | input 表 | 无 | 写。 |
bit_read(input) | input 表 | string | 默认转发 read。 |
bit_write(input) | input 表 | 无 | 默认转发 write。 |
chip_test(test_para) | 可选 {offset, len} | boolean | 连续读两次并比较;未设置 chip_type 时记 error 并返回 false。 |
set_tracechip(func) | 回调 | 无 | 在 input.is_trace 时调用。 |
reset_chip() | 无 | 无 | 基类空实现;Pca9545 / Pca9544 可按 ref_accessor 复位。 |
offset == 0x34(SMC_READ_BUFFER_FORWARD_MCTP)且存在 append_write 时,基类走 read_forward_mctp:按 SMC Completion Code 0 成功、2 未就绪、6 需继续读进行重试,最多 5 次未就绪则结束。
导出器件与差异行为
| 导出键 | 源文件 | 相对基类的主要差异 |
|---|---|---|
Lm75 | driver/lm75.lua | 首次访问按 HighTempLimitation / LowTempLimitation 配置阈值寄存器;read/bit_read 要求 len 为 1 或 2;自检写 0x55 0x00 到寄存器 2。 |
Eeprom | driver/eeprom.lua | 按 RwBlockSize(默认 32,最大 1024)分页读写;写间隔 WriteInterval(默认 50 ms,最大 500 ms)。 |
Pca9555 | driver/pca9555.lua | bit_write 先改配置寄存器再改输出寄存器;len 必须为 1,offset 只能为 0 或 1,mask 不能为 0。 |
Pca9545 / Pca9544 | driver/pca9545.lua、driver/pca9544.lua | read/write 要求 len == 1;提供 reset_chip。 |
Smc | driver/smc.lua | 按 opcode 0x20/0x21/0x22 组包并做 CRC8;读侧轮询 Length/CC;DrvWriteDelay 可配。 |
Chip | driver/chip.lua | 通用芯片;init_cfg / chip_init 按寄存器表初始化;BusType、DrvWriteDelay 可配。 |
Ina | driver/ina.lua | 按 ChipModel(Ina220/Ina226/Ina238)写配置与校准寄存器;未配置寄存器时 error('request error, calibration_reg or config_reg is nil')。 |
SPIFlash | driver/spi_flash.lua | 除 read/write 外提供 erase、raw_write、raw_write_read、init;ChipSelect、BytesPerPage、CommandSet 可配。擦除模式由 input.buffer 取值 0~3 对应 Sector / 32K / 64K / ChipErase。 |
Vrd | driver/vrd.lua | 按 page/寄存器操作 VRD;提供 init、delay、operate_vrd。 |
Rtc | driver/rtc_chip.lua | RTC 寄存器读写。 |
Cpld / CpldRegister / CpldChip | 对应 lua | CPLD 访问;Cpld 另有 get_cpld_id、check_bypass_channel、set_num、set_bypass_mode。 |
CanbusChip | driver/canbus_chip.lua | CAN 芯片读写;另有 multi_read、reset、set_speed、set_id_mask。 |
JtagSwitch | driver/jtag_switch.lua | JTAG 切换芯片的读写。 |
Ads78 | driver/ads78.lua | ADC 芯片 bit_read / read / write。 |
None | driver/none.lua | read/write 抛 Invalid chip type。 |
返回值与异常
成功:read 返回原始字节串。失败:error 字符串(前缀多为 request error / response error)或 SMC 表 {err, cc}。chip_test 返回 true/false,不抛错。
SMC Completion Code(driver.base / driver.smc / protocol.smc 一致):
| cc | 含义 |
|---|---|
0 | 成功 |
1 | request error, opcode not supported |
2 | 未就绪(读侧重试) |
3 | request error, parameter error |
4 | response error, chip internal error |
5 | request error, CRC error |
6 | 需继续读(MCTP forward) |
应用场景
hwproxy 将 CSR 中的芯片对象映射为 Driver,通过访问器/扫描器发起周期或按需 IO。
限制条件
- 器件类覆盖了
read后,仍依赖host_bus与 Chip 的wrap/unwrap。 Lm75在阈值均为默认0xffff时跳过配置;温度寄存器bit_read固定按 2 字节读取后再按请求长度截取。SPIFlash:erase在器件仍处于写过程中会error('the spi flash is in writing process, unable to erase')。
调试示例
-- 摘自 README 的新增器件方式,实现见 src/lualib/driver/lm75.lua
local class = require('mc.class')
local driver = require('driver.base')
local lm75 = class(driver)
function lm75:read(input)
if input.len ~= 1 and input.len ~= 2 then
error(string.format('request error, invalid input length: %d', input.len))
end
return driver.read(self, input)
end2.4 Stream 总线接口
功能说明
Stream 负责打开 libsoc_adapter 驱动并执行总线事务。基类 stream.base 提供 open_drive、bus_lock / bus_unlock。hwproxy 的 work_bus 将 bus:read / write 直接转给 self.stream。
参数说明
基类
| 方法 | 入参 | 出参 | 说明 |
|---|---|---|---|
ctor(property, bus_name) | 总线属性表、总线名 | 无 | 保存 property.Id(默认 0)和 bus_name。 |
open_drive(drv_file_name, ...) | 模块名,如 'libsoc_adapter.i2c' | 驱动对象或 nil | pcall(require) 后 pcall(drv_lib.new, ...);失败只记录一次 failed to open drive。 |
bus_lock() / bus_unlock() | 无 | 无 | 若底层驱动提供 lock/unlock 则调用。 |
get_value_with_mask(...) | type、value、length、mask | value | 基类原样返回;I2C/Hisport/LocalBus 等会覆盖。 |
已导出 Stream
| 导出键 | 源文件 | 主要方法 | 初始化要点 |
|---|---|---|---|
I2c | stream/i2c.lua | init、read、write | Speed/Mode/SlaveAddr/UseSmbus/SdaHold/MultiIO;UseSmbus==1 走 deploy,否则 drv:init。读路径:扩展模式 / SMBUS 0x02 / 普通读。 |
Hisport | stream/hisport.lua | read、write | 构造时 open_drive('libsoc_adapter.hisport') 并 init;BusNum、RegNum、MaxDataLen 等可配。 |
Gpio | stream/gpio.lua | init、read、write、gpio_group_read、gpio_group_write | Direction、ReverseBit、Bit0GpioNum~Bit7GpioNum。 |
LocalBus | stream/localbus.lua | init、read、write | 校验 id 0~3、地址偏移与位宽配置。 |
Can | stream/can.lua | init、read、write、reset、set_speed、set_id_mask | 打开 libsoc_adapter CAN 驱动。 |
Adc | stream/adc.lua | init、read | ADC 采样读。 |
I2cOverHisport / SPIOverHisport / JtagOverHisport | 对应 lua | read/write 等 | 在 Hisport 上承载 I2C/SPI/JTAG。 |
I2cOverLocalBus / I3cOverLocalBus / JtagOverLocalBus / JtagOverGpio | 对应 lua | init、read/write | 在 LocalBus 或 GPIO 上承载对应协议。 |
Jtag | stream/jtag.lua | init、write、get_cpld_id、check_bypass_channel | 具体升级/校验/收集在 jtag_base.lua。 |
InnerBus | stream/innerbus.lua | init、read、write | 进程内总线。 |
None | stream/none.lua | 空 read/write | 未知总线类型回退。 |
jtag_base.lua 另提供 start_upgrade_work、upgrade、verify、collect、set_num、set_bypass_mode,由 JTAG 系列 Stream 使用。
返回值与异常
I2C:底层 ret ~= 0 时调用 reinit_i2c_and_throw_error;若 protocol_flag == 0x02 或 ret == 19(ENODEV)会尝试重新 init,然后 error('response error, i2c <read|write|deploy> fail, ...')。offsetWidth 非法时 error('request error, offsetWidth is invalid: ...')。Hisport 对 offsetWidth > 4 同样报错。
应用场景
CSR 中的 Bus 对象(如 I2c、Hisport)由 hwproxy 创建 Stream,Chip Driver 通过 host_bus 访问。
限制条件
- I2C 在
speed == 2(代码中的I2C_HIGH_SPEED)且走 SMBus deploy 时直接error('request error, i2c smbus init failed')。 - I2C 普通读的
offsetWidth只能是0~4。 open_drive失败返回nil,后续对self.drv的调用会在业务路径上失败。hisport2.lua未从stream.stream导出。
调试示例
local stream = require('stream.stream')
local i2c = stream.I2c.new({
Id = 1,
Speed = 0, -- 100k
Mode = 0, -- master
UseSmbus = 0,
}, 'I2c1')
i2c:init()
local data = i2c:read({
addr = 0x90,
offset = 0,
offsetWidth = 1,
len = 2,
mask = 0xffff,
type = 1,
})hwproxy 单测 test/unit/test_hwproxy.lua 通过 require('stream.i2c') 直接加载该类。
2.5 Mux 多路复用接口
功能说明
Mux 在访问挂在多路复用器后级的芯片前打开通道,访问结束后关闭。基类 mux.base:ctor(chip) 只保存 own_chip。hwproxy 在 SwitchSupported 未开启(或未提供函数形态的 ComboWriteRead)时,按 class_name 创建 Mux。
参数说明
| 导出键 | 源文件 | open_channel(id) | close_channel |
|---|---|---|---|
Pca9545 | mux/pca9545.lua | 向 offset 0 写入 1 << id | 写入 0 |
Pca9544 | mux/pca9544.lua | id >= 4 时报 invalid PCA9544 channel id;写入 0x04 | id | 写入 0 |
JtagSwitch | mux/jtag_switch.lua | own_chip:write({}, id) | own_chip:write({}, 0),无 id 参数 |
McuSwitch | mux/mcu_switch.lua | 依赖 libmgmt_protocol 与 config.ieu_mcu;成功则 SetMcuSwitch('\x01') | SetMcuSwitch('\x00') |
None | mux/none.lua | 空实现 | 空实现 |
McuSwitch 额外方法:init()、switch(data)、reboot_action()。libmgmt_protocol 加载失败时 init 打 warn 并跳过功能。
返回值与异常
PCA9544 通道号越界抛 request error, invalid PCA9544 channel id。McuSwitch 在依赖缺失或不支持 opcode 0x0201 时直接返回,不抛错。
应用场景
I2C Mux 芯片(PCA954x)后挂 LM75/EEPROM 等器件时,hwproxy 先 open_channel 再访问后级 Chip。
限制条件
Mux 通过 own_chip:write 下发,因此该 Chip 必须已绑定 Driver 与 Bus。McuSwitch 不是纯寄存器 Mux,行为取决于 Riser MCU Capabilities。
调试示例
local mux = require('mux.mux')
local pca9545 = mux.Pca9545.new(own_chip)
pca9545:open_channel(0) -- 写 0x01 到 offset 0
pca9545:close_channel(0) -- 写 0x002.6 Protocol 协议包装接口
功能说明
Protocol 在 Driver 真正访问总线前改写 input(wrap),并在读回后剥协议头/校验(unwrap)。hwproxy 按芯片 class_name 创建;仅 Smc 与 None 两种实现。
参数说明
| 导出键 | 方法 | 说明 |
|---|---|---|
None | wrap(data, _, _) 空实现;unwrap(data, _) 原样返回 data | 无协议包装。 |
Smc | wrap(input, self_chip, r_chip)、unwrap(data, input, self_chip) | 按 protocol_flag 与 rw_type 选择 I2C/SMC 转发或 MCTP over SMC 转发。 |
SMC wrap 行为摘要:
protocol_flag | rw_type | 行为 |
|---|---|---|
0x03 | 写 0 | smc_write_forward_mctp:opcode 0x33,payload 超 250 报错 |
0x03 | 读 1 | smc_read_forward_mctp:读缓冲 opcode 0x34 |
| 其他 | 写 0 | smc_write_forward:opcode 0x30,I2C 或 SMC 目标 |
| 其他 | 读 1 | smc_read_forward:读缓冲 opcode 0x31,I2C 读会填充 append_write |
unwrap 校验 Length、Completion Code 和 CRC8;MCTP 路径截取 data:sub(4, #data - 1)。
返回值与异常
payload 超过 MAX_SMC_WRITE_LENGTH(250)时 error('request error, smc wrap exceed maximum payload length')。CC 非成功且非未就绪时抛 {err, cc}。MCTP CRC 不匹配抛 request error, get crc: ...。普通 CRC 不匹配当前仅 log:debug,不抛错。
应用场景
后级芯片挂在 SMC 后面时,前级 protocol.Smc 把 I2C/MCTP 访问转换成 SMC Forward 命令。单元测试 test/unit/test_smc.lua、test_mctp_over_smc.lua 覆盖该路径。
限制条件
wrap 会就地修改 input 的 addr/offset/buffer/len。多级 SMC 需要 Chip 上的 l_chip 链表由调用方(hwproxy 或测试夹具)维护。
调试示例
local smc_protocol = require('protocol.smc')
local proto = smc_protocol.new()
-- 由 Chip.wrap 沿 l_chip 链路调用:
-- tmp_chip.protocol:wrap(input, tmp_chip, r_chip)2.7 drvlib_common 辅助函数
功能说明
src/lualib/drvlib_common.lua 提供网络字节序转换,以及扩展读模式常量。
参数说明
| 符号 | 说明 |
|---|---|
HAS_EXTEND_CHIP_READ_MODE | 0xFFFFFFFF,与 Stream/Protocol 中的扩展芯片读偏移相同。 |
htons(value) / ntohs(value) | 16 位主机序与网络序互转,返回 2 字节串或整数。 |
htonl(value) / ntohl(value) | 32 位主机序与网络序互转。 |
返回值与异常
转换函数按位运算实现,无额外错误码。
应用场景
Hisport 扩展读、SMC 组包等需要固定字节序的场合。
限制条件
ntohs/ntohl 假定入参已是二进制字符串;长度不足时 string.byte 行为未在本库额外防护。
调试示例
local drvlib_common = require('drvlib_common')
assert(drvlib_common.HAS_EXTEND_CHIP_READ_MODE == 0xFFFFFFFF)3. 组件扩展案例
3.1 拓展能力概述
本库没有插件加载机制。扩展方式是在对应目录新增 Lua 类,并登记到聚合导出表,使 hwproxy 能按新的 CSR class_name 实例化。
3.2 扩展点说明
| 扩展点 | 文件 | 说明 |
|---|---|---|
| 器件驱动 | src/lualib/driver/<name>.lua 与 driver.lua | 继承 driver.base,实现 read/write 等,并在 driver.lua 增加导出键。 |
| 总线 Stream | src/lualib/stream/<name>.lua 与 stream.lua | 继承 stream.base,实现 init/read/write,通过 open_drive 绑定 libsoc_adapter。 |
| Mux | src/lualib/mux/<name>.lua 与 mux.lua | 实现 open_channel / close_channel。 |
| Protocol | src/lualib/protocol/<name>.lua 与 protocol.lua | 实现 wrap / unwrap。 |
| MCU 切换配置 | src/lualib/config/ieu_mcu.lua | McuSwitch 使用的 SMBus opcode 与 Capabilities 解析。 |
同步修改 hwproxy 的 object_defs.lua(芯片/总线类型声明)不在本仓库范围内,但新 class_name 必须能被 hwproxy 识别,否则对象不会走到本库。
3.3 二次开发指导
以新增器件驱动为例(与仓库 README 及 driver/lm75.lua 一致):
-- 1. 在 src/lualib/driver 新增 lua 文件
local class = require('mc.class')
local driver = require('driver.base')
local my_chip = class(driver)
function my_chip:ctor(chip, object)
self.chip_type = 'MyChip'
end
function my_chip:read(input)
return driver.read(self, input)
end
function my_chip:write(input)
driver.write(self, input)
end
return my_chip-- 2. 在 src/lualib/driver/driver.lua 登记
local driver = {
-- ...
MyChip = require('driver.my_chip'),
}验证步骤:
- 按仓库既有流程构建并安装到 hwproxy 的
lualib。 - 在单元测试中
require('driver.driver'),构造假host_bus后调用read/write。 - 需要总线时再为 Stream 增加用例;SMC 转发可参考
test/unit/test_smc.lua。
注意事项:导出键必须与 CSR class_name 完全一致;read/write 最终应回到 driver.base,以便走协议包装和前级总线。
4. 日志说明
4.1 一键日志收集
runtime_accessor 是库,不创建独立日志文件,仓库未注册 on_dump 回调。一键收集是否包含本库日志,取决于宿主 hwproxy 的 dump 配置。dist/config.cfg 写有 logger = "/data/var/log/bmc/runtime_accessor.log",但当前仓库无独立 src/service 进程消费该配置。
实际日志走宿主进程的 mc.logging,常见路径为 hwproxy 日志(如 /data/var/log/bmc 下的 app 日志)。
4.2 关键日志信息
| 级别 | 典型日志 | 含义 |
|---|---|---|
| error | failed to open drive: %s, err: %s | stream.open_drive 打开 libsoc_adapter 失败。 |
| error | SelfTest failed: This type of ChipTest is not supported temporarily | 该 Driver 未设置 chip_type,无法自检。 |
| error | %s selfTest failed: The first reading operation failed | 基类自检第一次读失败。 |
| error | lm75-selfTest failed: The writing operation failed | LM75 自检写失败。 |
| error | response error, i2c %s fail, ret: %s, input:%s | I2C 读写失败(抛错前)。 |
| error | localbus invalid id:%s, expect 0~3 | LocalBus Id 非法。 |
| notice | Config %s high limitation register ... successfully | LM75 高温阈值配置并回读成功。 |
| notice | localbus set id:%s config success | LocalBus 位宽/偏移配置成功。 |
| warn | require libmgmt failed, the environment does not support the MCU Switch. | 无法加载 libmgmt_protocol,McuSwitch 不可用。 |
| debug | Chip %s fails to write data through the SMBUS protocol | SMBus 写失败,随后会尝试重初始化。 |
5. 问题定界指南
5.1 典型问题定界
| 问题描述 | 是否为本组件问题 | 判断依据 | 关键证据收集方法 |
|---|---|---|---|
require('driver.driver') 找不到模块 | 可能是 | 安装路径或 hwproxy package.path 未包含本库 lualib。 | 检查 /opt/bmc/apps/hwproxy/lualib/driver/driver.lua 是否存在。 |
request error, Invalid chip type | 是 | class_name 未在 driver.driver 导出表中,落到 driver.None。 | 核对 CSR class_name 与 driver.lua 导出键。 |
| 总线读写无效果或空返回 | 可能是 | class_name 未在 stream.stream 中,落到 stream.None(空 read/write)。 | 核对 Bus 类型是否在 2.1 导出表中;注意 Hisport2 未导出。 |
response error, i2c read/write fail | 可能是 | libsoc_adapter 返回非 0,或 SMBus 切换失败。 | 记录 ret、input 的 JSON、总线 Id、是否 protocol_flag=0x02。 |
SMC cc=4 / chip internal error | 可能是 | 后级芯片或 SMC 应答失败。 | 参考 test_smc.lua 中 ret.err / ret.cc;抓 I2C 与 SMC opcode。 |
PCA9544 invalid channel id | 是 | 通道号 >= 4。 | 检查 Mux id 与硬件通道数。 |
| McuSwitch 无切通道动作 | 可能是 | libmgmt_protocol 未加载,或 Capabilities 不含 0x0201。 | 检索 require libmgmt failed / get %s smbus Capabilities failed。 |
| 打开驱动失败 | 可能是 | libsoc_adapter.<bus> 不存在或 new 失败。 | 检索 failed to open drive。 |
5.2 错误码速查表
| 错误 | 来源 | 含义 | 排查建议 |
|---|---|---|---|
request error, Invalid chip type | driver.None | 未知器件类型。 | 补齐导出或修正 class_name。 |
request error, invalid input length | Lm75/Pca954x/Pca9555 等 | len 不符合器件约束。 | 按器件文档核对 len。 |
request error, invalid input mask: 0 | Pca9555 | 位写 mask 为 0。 | 传入非 0 mask。 |
request error, input len too long | I2c | 写长度大于 1100。 | 拆分写入。 |
request error, offsetWidth is invalid | I2c/Hisport | 偏移宽度大于 4。 | 修正 CSR OffsetWidth。 |
request error, i2c smbus init failed | I2c | 高速档不允许该 SMBus deploy。 | 检查 Speed。 |
request error, smc wrap exceed maximum payload length | protocol.Smc | Forward payload > 250。 | 减小一次转发长度。 |
response error, reinit i2c fail | I2c | 失败后重新 init 仍失败。 | 检查控制器与 libsoc_adapter.i2c。 |
SMC cc 1/3/4/5 | Driver/Protocol SMC | opcode/参数/内部/CRC 错误。 | 对照 2.3 节 CC 表。 |
I2C ret == 19 | I2c | ENODEV,会尝试重初始化。 | 确认总线与从设备在位。 |
5.3 最小化复现与证据收集
- 记录 CSR
class_name、总线类型、器件地址、offset/len/mask/type、protocol_flag。 - 在 hwproxy 日志中检索
failed to open drive、i2c ... fail、selfTest failed、SMCcompletion code。 - 用假
host_bus在test/unit下复现 Driver/Protocol(无需真实硬件)。 - 确认
/opt/bmc/apps/hwproxy/lualib/{driver,stream,mux,protocol}已按dist/permissions.ini部署。
5.4 调试方法
- 运行
test/unit/test.lua(含test_smc、test_mctp_over_smc、test_rtc、test_ina)。 - 在 hwproxy 侧打开 debug 日志,观察 SMBus 切换与 SMC CRC debug。
- 对 Mux 问题先确认
open_channel写入值(PCA9545 为1<<id,PCA9544 为0x04|id)。
6. 常见问题解答
Q1:require('driver.driver') 失败怎么办?
- 问题描述:Lua 报 module not found。
- 一句话答案:本库安装在 hwproxy 的
lualib下,必须在 hwproxy 进程的 Lua 路径中加载。 - 根因说明:未随 hwproxy 部署,或在其他进程中直接 require 且未设置搜索路径。
- 解决方案:确认
/opt/bmc/apps/hwproxy/lualib/driver/driver.lua存在,并在 hwproxy 环境中调用。 - 规避方案:不要把本库当作独立服务启动;不要只拷贝单个 lua 文件。
- 适用版本:1.130.9。
Q2:读写落到空实现或 Invalid chip type?
- 问题描述:访问无数据,或抛
request error, Invalid chip type。 - 一句话答案:
class_name与四个导出表的键不一致,分别回退到stream.None或driver.None。 - 根因说明:hwproxy 使用
table[class_name] or None,不会因未知类型立即失败。 - 解决方案:对照 2.1 节导出键修正 CSR;新增类型必须同时改导出表。
- 规避方案:变更 CSR 类型名时同步改
driver.lua/stream.lua/mux.lua/protocol.lua。 - 适用版本:1.130.9。
Q3:如何确认问题在器件驱动还是总线上?
- 问题描述:Chip 读失败,不确定是 Driver、Protocol 还是 Stream。
- 一句话答案:先看错误字符串前缀和是否含 SMC
cc;再看是否已open_drive成功。 - 根因说明:Driver 负责组包与器件语义,Stream 负责 libsoc_adapter 返回码,Protocol 只在 SMC 链路上改写
input。 - 解决方案:
Invalid chip type/长度/mask 错误偏 Driver;i2c fail/failed to open drive偏 Stream;smc wrap/completion code偏 Protocol/SMC。 - 规避方案:用
test_smc.lua风格的假host_bus隔离 Driver/Protocol。 - 适用版本:1.130.9。
Q4:本组件能否用 busctl 调试?
- 问题描述:希望像其他业务组件一样 introspect D-Bus 接口。
- 一句话答案:不能。本组件是 library,没有资源协作接口。
- 根因说明:仓库仅有
mds/service.json,无mds/model.json,也无 IPMI 命令定义。 - 解决方案:在 hwproxy 侧查看 Chip 对象与访问日志,或跑本仓库单元测试。
- 规避方案:不要为 runtime_accessor 查找
bmc.kepler.runtime_accessor服务名。 - 适用版本:1.130.9。