南向网卡驱动适配指南
南向驱动是基于硬件组件驱动程序框架,采用分层架构设计,支持多种设备类型和通信协议。本文档面向网卡适配的设备驱动开发者,提供从设计到实现的完整开发流程,并基于现有代码实例分析,提供一篇快速适配的开发指南。
1. 概述
1.1 什么是南向网卡驱动适配?
南向网卡驱动适配是指在BMC系统中,为不同厂商的PCIe网卡提供统一的驱动程序接入方案,使BMC能够管理和监控网卡设备。
1.2 适配后可以实现什么功能?
- 获取网卡基本信息(厂商ID、设备ID、固件版本等)
- 监控网卡状态(温度、链路状态、带宽等)
- 管理网口(链路信息、MAC地址、LLDP等)
- 监控光模块(温度、收发功率、序列号等)
- 故障诊断和日志收集
1.3 需要了解的关键概念
设备对象
| 对象类型 | 说明 | 示例 |
|---|---|---|
| 网卡主对象 | 代表整个PCIe网卡 | PCIeNicCard |
| 网口对象 | 代表网卡上的物理网口 | NicPort |
| 光模块对象 | 代表插在网口上的光模块 | OpticalTransceiver |
接口
- gen命名空间:自动生成的接口基类,定义属性
- dev命名空间:厂商具体实现类,实现功能
- 接口组合:一个设备可以实现多个接口,如
PCIeDevice、NetworkAdapter等
协议库
- NCSI over MCTP:网卡控制协议,通过PCIe与网卡通信
- SMBUS:系统管理总线协议,用于I2C通信
- 不同厂商可能使用不同的协议扩展
1.4 驱动分层架构
- 分层架构:构建了一个从接口定义到硬件交互的完整体系,接口定义层→设备接口层→设备对象层→设备驱动层,每层职责明确,协同工作;
- 设计理念:接口与实现分离,先是“接口定义层”建立接口规范,并在“设备接口层” 自动生成C++接口代码,再由各厂商提供“设备驱动层”的具体部件驱动实现;
2. 前置准备
2.1 明确网卡信息
必需信息
| 信息项 | 说明 | 示例 |
|---|---|---|
| VID | Vendor ID(厂商ID) | 0x19e5 |
| DID | Device ID(设备ID) | 0x0222 |
| SVID | Subsystem Vendor ID | 0x19e5 |
| SSID | Subsystem Device ID | 0x0052 |
协议信息
- 支持的通信协议(NCSI标准协议、厂商OEM扩展等)
- 协议命令集(获取温度、链路状态等命令的定义)
- 数据格式(响应数据的字节序、字段定义等)
硬件信息
- 网口数量
- 是否支持光模块
- 特殊功能(如DCB、LLDP等)
2.2 驱动代码结构熟悉
Component Driver采用分层架构设计,代码组织清晰,各个目录职责明确。理解这些目录的结构和作用是快速适配驱动的基础。
2.2.1 整体目录结构
component_drivers/
├── drivers/ # 设备驱动实现目录(各类硬件设备驱动)
├── gen/ # 自动生成的代码目录(接口基类)
├── libraries/ # 协议库目录(通信协议封装)
├── tests/ # 测试代码目录
├── docs/ # 文档目录
├── include/ # 公共头文件目录
└── meson.build # 构建配置文件
2.2.2 drivers目录 - 设备驱动实现
drivers/目录包含所有硬件设备的具体驱动实现,按设备类型组织。
目录结构
drivers/
├── pcie_nic_card/ # PCIe网卡驱动(本指南重点)
│ ├── hisi/ # 华为海思网卡驱动
│ │ ├── csr/ # 设备配置文件(DDS/SR)
│ │ │ ├── 14140130_19e50222_19e50052.dds # Hi1822设备描述
│ │ │ ├── 14140130_19e50222_19e50052.sr # Hi1822硬件自描述信息
│ │ │ ├── 14140130_19e50222_19e500a1.dds # Hi1822另一配置
│ │ │ └── 14140130_19e50222_19e500a1.sr
│ │ ├── hi182x/ # Hi182x系列网卡实现
│ │ │ ├── hi182x_card.h # 网卡主对象头文件
│ │ │ ├── hi182x_card.cpp # 网卡主对象实现
│ │ │ ├── hi182x_port.h # 网口对象头文件
│ │ │ ├── hi182x_port.cpp # 网口对象实现
│ │ │ ├── hi182x_om.h # 光模块对象头文件
│ │ │ ├── hi182x_om.cpp # 光模块对象实现
│ │ │ ├── hi182x_abi.cpp # ABI导出接口
│ │ │ └── meson.build # 构建配置
│ │ ├── hi183x/ # Hi183x系列网卡实现
│ │ ├── interface/ # 接口实现目录(继承gen基类)
│ │ │ ├── pcie_device.h/cpp # PCIe设备接口实现
│ │ │ ├── network_adapter.h/cpp # 网络适配器接口实现
│ │ │ ├── network_adapter/ # 网络适配器子接口
│ │ │ │ ├── cooling.h/cpp # 温度监控接口
│ │ │ │ ├── fault_status.h/cpp # 故障状态接口
│ │ │ │ └── log_collection.h/cpp # 日志收集接口
│ │ │ ├── network_port.h/cpp # 网口接口实现
│ │ │ ├── network_port/ # 网口子接口
│ │ │ │ ├── link_info.h/cpp # 链路信息接口
│ │ │ │ ├── lldp_receive.h/cpp # LLDP接收接口
│ │ │ │ ├── data_center_bridging.h/cpp # DCB接口
│ │ │ │ └── metrics.h/cpp # 流量统计接口
│ │ │ ├── optical_module/ # 光模块子接口
│ │ │ │ ├── cooling.h/cpp # 光模块温度接口
│ │ │ │ ├── status.h/cpp # 光模块状态接口
│ │ │ │ ├── power.h/cpp # 光模块功率接口
│ │ │ │ ├── voltage.h/cpp # 光模块电压接口
│ │ │ │ └── current.h/cpp # 光模块电流接口
│ │ │ ├── board.h/cpp # 板卡信息接口
│ │ │ ├── pcie_card.h/cpp # PCIe卡接口
│ │ │ └── pcie_device/
│ │ │ └── bandwidth.h/cpp # PCIe带宽接口
│ │ └── meson.build
│ ├── wx/ # 网迅网卡驱动
│ │ ├── csr/ # 设备配置文件
│ │ ├── interface/ # 接口实现目录
│ │ ├── wx_card.h/cpp # 网卡主对象
│ │ ├── wx_port.h/cpp # 网口对象
│ │ ├── wx_abi.cpp # ABI导出接口
│ │ └── meson.build
│ └── meson.build
│
├── pcie_gpu_card/ # PCIe GPU卡驱动
│ └── innosilicon/ # 芯动GPU驱动
│ ├── csr/ # 设备配置文件
│ ├── awm_m11p/ # AWM-M11P GPU实现
│ ├── interface/ # 接口实现目录
│ └── meson.build
│
├── bus/ # 总线驱动
│ ├── i2c/ # I2C总线驱动
│ ├── i2c_mux/ # I2C多路复用器驱动
│ ├── hisport/ # Hisport总线驱动
│ └── meson.build
│
├── chip/ # 芯片驱动
│ ├── lm75/ # LM75温度传感器驱动
│ ├── ads78/ # ADS78 ADC驱动
│ ├── eeprom/ # EEPROM驱动
│ ├── pca9545/ # PCA9545 I2C多路复用器驱动
│ ├── pca9555/ # PCA9555 GPIO扩展器驱动
│ ├── complex/ # 复杂芯片驱动
│ ├── interface/ # 芯片接口定义
│ └── meson.build
│
├── accessor/ # 访问器驱动(提供统一的属性访问接口)
├── scanner/ # 扫描器驱动(自动发现硬件设备)
├── debounce/ # 防抖驱动(信号滤波算法)
│ ├── average/ # 平均值滤波
│ ├── median/ # 中位数滤波
│ ├── continue/ # 连续性滤波
│ └── binary_continue/ # 二值连续性滤波
│
├── internal/ # 内部实现(驱动框架基础类)
│ ├── bus/ # 总线基类实现
│ ├── chip/ # 芯片基类实现
│ └── manager.h/cpp # 驱动管理器
│
└── meson.build
关键目录说明
| 目录 | 说明 | 用途 |
|---|---|---|
| pcie_nic_card/ | PCIe网卡驱动根目录 | 按厂商组织网卡驱动 |
| hisi/csr/ | 设备配置文件目录 | 存放DDS和SR配置文件,定义设备接口和硬件自描述信息 |
| hisi/hi182x/ | Hi182x网卡实现 | 包含网卡、网口、光模块的实现代码 |
| hisi/interface/ | 接口实现目录 | 继承gen基类,实现具体协议逻辑和数据更新 |
| internal/ | 内部基础框架 | 提供驱动框架的基础类 |
2.2.3 gen目录 - 自动生成的接口基类
gen/目录包含自动生成的接口基类定义,驱动代码继承这些基类实现功能。注意:此目录的代码由工具自动生成,不要手动修改。
目录结构
gen/
├── include/ # 头文件目录
│ └── device_tree/
│ ├── base.h # 基础类定义
│ ├── interface/ # 接口基类定义
│ │ ├── NetworkAdapter.h # 网络适配器接口基类
│ │ ├── NetworkAdapter/ # 网络适配器子接口
│ │ │ ├── Cooling.h # 温度监控接口基类
│ │ │ ├── FaultStatus.h # 故障状态接口基类
│ │ │ └── LogCollection.h # 日志收集接口基类
│ │ ├── NetworkPort.h # 网口接口基类
│ │ ├── NetworkPort/ # 网口子接口
│ │ │ ├── LinkInfo.h # 链路信息接口基类
│ │ │ ├── LLDPReceive.h # LLDP接收接口基类
│ │ │ ├── DataCenterBridging.h # DCB接口基类
│ │ │ ├── Metrics.h # 流量统计接口基类
│ │ │ └── FibreChannel.h # 光纤通道接口基类
│ │ ├── OpticalModule.h # 光模块接口基类
│ │ ├── OpticalModule/ # 光模块子接口
│ │ │ ├── Status.h # 光模块状态接口基类
│ │ │ ├── Cooling.h # 光模块温度接口基类
│ │ │ ├── Power.h # 光模块功率接口基类
│ │ │ ├── Voltage.h # 光模块电压接口基类
│ │ │ ├── Current.h # 光模块电流接口基类
│ │ │ ├── Channel.h # 光模块通道接口基类
│ │ │ └── Diagnose.h # 光模块诊断接口基类
│ │ ├── PCIeDevice.h # PCIe设备接口基类
│ │ ├── PCIeDevice/
│ │ │ └── Bandwidth.h # PCIe带宽接口基类
│ │ ├── PCIeCard.h # PCIe卡接口基类
│ │ ├── Board.h # 板卡信息接口基类
│ │ ├── Gpu.h # GPU接口基类
│ │ ├── Gpu/
│ │ │ ├── Power.h # GPU功率接口基类
│ │ │ └── Status.h # GPU状态接口基类
│ │ ├── Memory.h # 内存接口基类
│ │ ├── Processor.h # 处理器接口基类
│ │ ├── Chip.h # 芯片接口基类
│ │ ├── Chip/
│ │ │ ├── BlockIO.h # 块IO接口基类
│ │ │ └── Pca9545.h # PCA9545接口基类
│ │ ├── Bus/ # 总线接口基类
│ │ │ ├── I2c.h # I2C总线接口基类
│ │ │ ├── I2cMux.h # I2C多路复用器接口基类
│ │ │ └── Hisport.h # Hisport总线接口基类
│ │ ├── Debounce/ # 防抖接口基类
│ │ │ ├── Average.h # 平均值滤波接口基类
│ │ │ ├── Median.h # 中位数滤波接口基类
│ │ │ ├── Continue.h # 连续性滤波接口基类
│ │ │ └── BinaryContinue.h # 二值连续性滤波接口基类
│ │ ├── Accessor.h # 访问器接口基类
│ │ └── Scanner.h # 扫描器接口基类
│ │
│ └── object/ # 对象组合定义
│ ├── PCIeNicCard/ # 网卡对象组合
│ │ ├── pcie_nic_card.h # 网卡对象组合基类
│ │ ├── network_port.h # 网口对象组合基类
│ │ └── optical_module.h # 光模块对象组合基类
│ └── PCIeGpuCard/ # GPU卡对象组合
│ ├── pcie_gpu_card.h # GPU卡对象组合基类
│ ├── gpu.h # GPU对象组合基类
│ └── memory.h # 内存对象组合基类
│
├── src/ # 源文件目录(与include结构对应)
│ └── device_tree/
│ ├── interface/ # 接口基类实现(.cpp文件)
│ └── object/ # 对象组合实现(.cpp文件)
│
└── meson.build
关键概念
| 概念 | 说明 | 示例 |
|---|---|---|
| 接口基类 | 定义接口的属性和方法签名 | gen::NetworkAdapter定义了FirmwareVersion、ChipModel等属性 |
| 对象组合 | 将多个接口组合成完整对象 | 网卡对象组合了PCIeDevice、NetworkAdapter、Board等接口 |
| 命名空间 | 所有生成的代码在gen命名空间下 | 驱动代码继承gen::NetworkAdapter实现dev::NetworkAdapter |
| 自动生成 | 基于模型文件自动生成,不手动修改 | 通过修改模型文件(JSON)重新生成 |
使用方式
驱动开发时,需要继承对应的gen基类并实现功能:
// 继承gen命名空间的基类
class NetworkAdapter : public gen::NetworkAdapter {
public:
// 实现具体功能
void update_firmware_version();
void start_ncsi_update_task(...);
};
// 注册时指定基类
MC_REFLECT(dev::NetworkAdapter, (gen::NetworkAdapter), ())
2.2.4 libraries目录 - 通信协议库
libraries/目录包含各种通信协议的封装实现,为驱动提供标准化的协议接口。
目录结构
libraries/
├── mctp/ # MCTP协议库(管理组件传输协议)
│ ├── mctp.h # MCTP协议接口定义
│ ├── mctp.cpp # MCTP协议实现
│ ├── pcie_transport.h # PCIe传输层实现
│ ├── pcie_transport.cpp
│ └── meson.build
│
├── ncsi_over_mctp/ # NCSI over MCTP协议库
│ ├── ncsi_over_mctp.h # NCSI over MCTP基类
│ ├── ncsi_over_mctp.cpp
│ ├── ncsi_over_mctp_standard.h # 标准NCSI协议实现
│ ├── ncsi_over_mctp_standard.cpp
│ ├── ncsi_over_mctp_huawei.h # 华为NCSI扩展协议
│ ├── ncsi_over_mctp_huawei.cpp
│ ├── ncsi_over_mctp_wx.h # 网迅NCSI扩展协议
│ ├── ncsi_over_mctp_wx.cpp
│ └── meson.build
│
├── ncsi/ # NCSI协议定义(头文件库)
│ ├── ncsi_protocol.h # NCSI标准协议定义
│ ├── ncsi_huawei.h # 华为NCSI扩展定义
│ ├── ncsi_wx.h # 网迅NCSI扩展定义
│ ├── ncsi_socket.h # NCSI Socket接口
│ ├── adapter.h # NCSI适配器接口
│ └── meson.build
│
├── smbus/ # SMBus协议库
│ ├── smbus.h # SMBus协议接口定义
│ ├── smbus.cpp # SMBus协议实现
│ ├── std_smbus.h # 标准SMBus实现
│ ├── std_smbus.cpp
│ └── meson.build
│
├── ipmb/ # IPMB协议库(IPMI消息块协议)
│ ├── ipmb.h # IPMB协议接口定义
│ ├── ipmb.cpp # IPMB协议实现
│ └── meson.build
│
├── imu/ # IMU协议库(智能管理单元协议)
│ ├── imu.h # IMU协议接口定义
│ ├── imu.cpp # IMU协议实现
│ └── meson.build
│
├── protocol/ # 通用协议基类
│ ├── protocol.h # 协议基类定义
│ ├── protocol.cpp # 协议基类实现
│ └── meson.build
│
└── meson.build
协议库说明
| 协议库 | 说明 | 传输方式 | 应用场景 |
|---|---|---|---|
| mctp | 管理组件传输协议(DMTF标准) | PCIe、SMBus | 作为底层传输协议,封装上层协议消息 |
| ncsi_over_mctp | 基于MCTP的NCSI协议 | MCTP over PCIe/SMBus | 网卡管理的主要协议,获取网卡状态、配置信息 |
| ncsi | NCSI协议定义 | - | 定义NCSI命令格式、数据结构(仅头文件) |
| smbus | 系统管理总线协议 | I2C/SMBus | 直接读取芯片寄存器,获取温度等数据 |
| ipmb | IPMI消息块协议 | I2C | IPMI命令传输 |
| imu | 智能管理单元协议 | I2C/IMU | 获取PCIe配置空间信息、BDF地址 |
| protocol | 协议基类 | - | 所有协议的通用基类,定义标准接口 |
协议分层关系
应用层: 网卡驱动代码
↓
协议层: NCSI over MCTP (ncsi_over_mctp_huawei/wx)
↓
传输层: MCTP (mctp + pcie_transport)
↓
物理层: PCIe硬件 / SMBus硬件
使用示例
// 1. 创建MCTP对象
mctp* mctp_object = new mctp(this, phy_addr,
MCTP_MESSAGE_TYPE::MCTP_MESSAGE_TYPE_NCSI, "");
// 2. 创建NCSI协议对象
auto ncsi_huawei = std::make_shared<ncsi_over_mctp_huawei>(*mctp_object);
// 3. 发送NCSI请求
mc::dict request = {
{"CommandId", 0x50}, // OEM命令
{"Data", data_vec}
};
auto response = ncsi_huawei->request(request);
// 4. 创建SMBus对象
auto smbus_obj = mc::make_shared<smbus>(this);
smbus_obj->init(params);
// 5. 读取SMBus数据
std::vector<uint8_t> data = smbus_obj->read(offset, length);
2.2.5 目录之间的关系
工作流程
- 代码生成:根据模型文件生成
gen/目录下的接口基类 - 接口实现:在
drivers/*/interface/下继承gen基类,实现具体功能 - 驱动组装:在
drivers/*/具体型号/下组合接口,创建完整驱动对象 - 协议调用:驱动通过
libraries/中的协议库与硬件通信 - 框架支持:
drivers/internal/提供基础设施支持
2.3 驱动协议分层
2.4 底层协议确定
在适配网卡驱动前,首先要确定网卡支持的底层通信协议。不同厂商的网卡可能支持不同的协议组合。
协议类型
| 协议 | 说明 | 适用场景 |
|---|---|---|
| NCSI over MCTP over PCIe | 通过PCIe总线进行MCTP通信 | 网卡直接连接到PCIe总线 |
| NCSI over MCTP over SMBus | 通过SMBus/I2C进行MCTP通信 | 网卡通过SMBus桥接芯片管理 |
| SMBus直接访问 | 通过I2C直接读取芯片数据 | 获取MAC、温度等信息 |
厂商协议支持对比
| 厂商 | NCSI over MCTP over PCIe | NCSI over MCTP over SMBus | SMBus直接访问 | 说明 |
|---|---|---|---|---|
| 华为海思 Hi182x | ✓ | - | ✓ | 主要通过PCIe,SMBus用于带外信息获取 |
| 网迅 WX | - | ✓ | - | 通过SMBus桥接MCTP通信 |
协议配置方式
方式一:NCSI over MCTP over PCIe + SMBus(华为海思Hi182x)
这种方式同时使用PCIe和SMBus两种协议:
- PCIe通道:用于NCSI协议通信,获取网卡主要信息(MAC地址、链路状态等)
- SMBus通道:用于直接读取芯片温度等数据
SR文件配置示例(参考14140130_19e50222_19e500a1.sr):
{
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2cMux_9545Chan3"
]
},
"I2cMux_9545Chan3": {
"Chips": [
"Chip_Hi1822", // 网卡芯片(SMBus访问)
]
}
},
"Objects": {
// 配置芯片对象(SMBus访问参数)
"Chip_Hi1822": {
"OffsetWidth": 0,
"AddrWidth": 1,
"Address": 232, // I2C地址:0xE8
"WriteTmout": 100,
"ReadTmout": 100,
"HealthStatus": 0,
"WriteRetryTimes": 2,
"ReadRetryTimes": 0
},
"PCIeNicCard_1": {
"bmc.dev.NetworkAdapter": {
"Manufacturer": "Huawei",
"ChipModel": "Hi1822",
"NetworkPortCount": 2,
"SupportedMctp": true,
// 关键:通过RefChip关联Chip_Hi1822,支持SMBus访问
"RefChip": "#/Chip_Hi1822"
}
}
}
}
关键配置说明:
- ManagementTopology定义I2C拓扑
- 指定网卡芯片挂在哪条I2C总线上
Chip_Hi1822定义芯片的I2C访问参数
- RefChip关联
bmc.dev.NetworkAdapter接口通过RefChip字段引用Chip_Hi1822- 这样
NetworkAdapter接口可以通过SMBus读取芯片数据(如温度)
- 双协议工作
- PCIe协议:代码中通过MCTP over PCIe获取网卡功能信息
- SMBus协议:代码中通过
RefChip访问芯片寄存器获取温度
代码实现要点(hi182x_card.cpp):
bool hi182x_card::start_protocol() {
// 1. 启动NCSI over MCTP over PCIe协议
if (!start_ncsi_protocol()) {
// BDF未就绪,注册回调等待
}
// 2. 启动SMBus协议
start_smbus_protocol();
return true;
}
bool hi182x_card::start_smbus_protocol() {
// 获取RefChip引用
register_RefChip();
// 初始化SMBus对象
m_smbus_obj = mc::make_shared<smbus>(this);
m_smbus_obj->init(params);
// 注册读写方法,通过RefChip访问
m_smbus_obj->register_WriteRead_method([&](const std::vector<uint8_t>& data, uint32_t len) {
return m_ref_chip->invoke("bmc.dev.Chip", "BlockIOWriteRead", {data, len})
.as<std::vector<uint8_t>>();
});
// 启动各接口的SMBus更新任务
m_network_adapter.start_smbus_update_task(m_smbus_obj, m_smbus_interval);
m_network_adapter_cooling.start_smbus_update_task(m_smbus_obj, m_smbus_interval);
return true;
}
方式二:NCSI over MCTP over SMBus(网迅WX)
这种方式通过SMBus桥接实现MCTP通信,所有数据都通过SMBus传输。
SR文件配置示例(参考14140130_80881001_80880300.sr):
{
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2cMux_9545Chan"
]
},
"I2cMux_9545Chan": {
"Chips": [
"Chip_SmbusChip" // SMBus桥接芯片
]
}
},
"Objects": {
// SMBus芯片配置
"Chip_SmbusChip": {
"Address": 146, // I2C地址:0x92
"AddrWidth": 1,
"OffsetWidth": 1,
"WriteTmout": 100,
"ReadTmout": 100,
"HealthStatus": 0
},
// MCTP绑定配置
"MctpBinding_1": {
"BmcSMBusEid": 8, // BMC的MCTP端点ID
"BmcSMBusPhyAddr": 16 // BMC的物理地址
},
// MCTP端点配置
"Endpoint_1": {
"TargetEid": "${Slot} |> expr($1 + 8)", // 目标端点ID(动态计算)
"TargetPhyAddr": 73, // 目标物理地址
"MessageType": 2, // 消息类型:NCSI
"MediumType": 128, // 介质类型:SMBus
"RefChip": "#/Chip_SmbusChip" // 关联SMBus芯片
},
"PCIeNicCard_1": {
"bmc.dev.NetworkAdapter": {
"Manufacturer": "Beijing Wangxun Technology Co., Ltd.",
"ChipModel": "SP1000A",
"NetworkPortCount": 2,
"SupportedMctp": true,
"SupportedLldp": true
// 注意:没有RefChip字段,因为不需要直接SMBus访问
}
}
}
}
关键配置说明:
- Chip_SmbusChip
- 定义SMBus桥接芯片的I2C访问参数
- 这个芯片负责将MCTP消息转换为SMBus传输
- MctpBinding_1
- 配置BMC侧的MCTP绑定信息
- 指定BMC的EID和物理地址
- Endpoint_1
- 配置网卡侧的MCTP端点
MessageType: 2表示NCSI消息类型MediumType: 128表示通过SMBus传输RefChip关联到SMBus芯片
- 无RefChip
NetworkAdapter接口不需要RefChip- 所有数据通过NCSI over MCTP over SMBus获取
代码实现要点(wx_card.cpp):
bool wx_card::start_protocol() {
// 只需启动NCSI over MCTP协议
// 框架会根据SR配置自动使用SMBus传输
if (!start_ncsi_protocol()) {
// BDF未就绪,注册回调等待
}
return true;
}
bool wx_card::start_ncsi_protocol() {
// 创建MCTP对象(框架会根据SR配置使用SMBus传输)
mctp* mctp_object = new mctp(this, phy_addr,
MCTP_MESSAGE_TYPE::MCTP_MESSAGE_TYPE_NCSI, 0);
auto ncsi_update_task = [this, mctp_object]() {
// 创建网迅专用的NCSI协议实例
m_ncsi_over_mctp_wx = std::make_shared<ncsi_over_mctp_wx>(*mctp_object);
// 启动更新任务(温度等数据也通过NCSI获取)
m_pcie_device.start_ncsi_update_task(m_ncsi_over_mctp_wx, m_interval);
m_network_adapter_cooling.start_ncsi_update_task(m_ncsi_over_mctp_wx, m_interval);
};
mctp_object->create_transport_and_endpoint(
std::string(get_object_name()), ncsi_update_task);
return true;
}
配置检查清单
方式一(NCSI over MCTP over PCIe + SMBus):
- SR文件中定义了网卡芯片对象(如
Chip_Hi1822) -
ManagementTopology中配置了I2C拓扑 -
NetworkAdapter接口中配置了RefChip字段 - 代码中实现了
start_smbus_protocol()方法 - 代码中实现了SMBus更新任务
方式二(NCSI over MCTP over SMBus):
- SR文件中定义了SMBus芯片对象(如
Chip_SmbusChip) - 配置了
MctpBinding_1对象 - 配置了
Endpoint_1对象,并关联SMBus芯片 -
Endpoint_1的MessageType设置为2(NCSI) -
Endpoint_1的MediumType设置为128(SMBus) - 代码中只需实现NCSI协议,无需SMBus
2.5 网卡支持的能力
本节以Hi182x网卡为例,详细列举网卡驱动可以支持的各种能力,帮助开发者了解可以实现哪些功能接口。
网卡级能力
Hi182x网卡主对象实现了以下8个接口:
| 能力类别 | 接口名称 | 说明 | 可获取的信息 |
|---|---|---|---|
| PCIe设备信息 | PCIeDevice | PCIe设备基础信息 | VID/DID、BDF地址、设备名称、设备类型 |
| PCIe卡信息 | PCIeCard | 板卡物理信息 | 卡槽位置、序列号、插槽类型 |
| 网络适配器 | NetworkAdapter | 网卡功能信息 | 厂商、型号、固件版本、MAC地址数量、支持的协议 |
| 温度监控 | NetworkAdapter_Cooling | 网卡温度信息 | 芯片温度、最高温度、温度阈值、散热状态 |
| 故障诊断 | NetworkAdapter_FaultStatus | 故障状态信息 | 故障代码、健康状态、错误计数、告警信息 |
| 带宽监控 | PCIeDevice_Bandwidth | PCIe带宽信息 | 链路速度、链路宽度、实际吞吐量、协商状态 |
| 日志收集 | NetworkAdapter_LogCollection | 日志导出功能 | 网卡日志文件、故障诊断信息、事件记录 |
| 板卡信息 | Board | 板卡制造信息 | 板卡序列号、部件号、制造商、生产日期 |
网口级能力
Hi182x的每个网口对象实现了以下5个接口:
| 能力类别 | 接口名称 | 说明 | 可获取的信息 |
|---|---|---|---|
| 网口基础信息 | NetworkPort | 网口基本属性 | 端口ID、MAC地址、最大速率、端口类型、启用状态 |
| 链路信息 | NetworkPort_LinkInfo | 链路状态信息 | 链路Up/Down、当前速度、双工模式、自协商状态 |
| LLDP接收 | NetworkPort_LLDPReceive | LLDP协议信息 | 邻居设备信息、网络拓扑、设备能力、VLAN信息 |
| DCB支持 | NetworkPort_DataCenterBridging | 数据中心桥接 | QoS配置、流量控制、优先级映射、带宽分配 |
| 网口指标 | NetworkPort_Metrics | 流量统计信息 | 收发包数、字节数、错误包、丢包率 |
光模块能力
Hi182x支持光模块,每个光模块对象实现了以下6个接口:
| 能力类别 | 接口名称 | 说明 | 可获取的信息 |
|---|---|---|---|
| 光模块基础信息 | OpticalModule | 光模块识别信息 | 型号、序列号、厂商、波长、传输距离、接口类型 |
| 状态信息 | OpticalModule_Status | 光模块状态 | 在位状态、工作状态、故障状态、使能状态 |
| 温度监控 | OpticalModule_Cooling | 光模块温度 | 当前温度、最高/最低温度、温度告警阈值 |
| 电压监控 | OpticalModule_Voltage | 工作电压 | 当前电压、电压范围、电压告警阈值 |
| 功率监控 | OpticalModule_Power | 光功率信息 | 发射功率、接收功率、功率告警阈值、光衰减 |
| 电流监控 | OpticalModule_Current | 工作电流 | 偏置电流、电流范围、电流告警阈值 |
能力获取方式
Hi182x网卡通过三种协议获取不同的信息:
| 协议类型 | 获取的信息 | 更新频率 | 数据来源 |
|---|---|---|---|
| NCSI over MCTP over PCIe | 固件版本、MAC地址、链路状态、LLDP信息、DCB配置、网口流量统计、光模块信息、故障状态、带宽统计 | 5秒~180秒 | 网卡固件 |
| SMBus直接访问 | 芯片温度、故障寄存器状态 | 5秒 | 网卡芯片寄存器 |
| IMU协议 | PCIe配置空间信息、BDF地址、四元组信息 | 按需 | PCIe配置空间 |
更新频率策略:
- 快速变化数据(5秒):温度、链路状态、故障状态、流量统计
- 慢速变化数据(120秒):固件版本、设备信息、网卡配置
- 极慢变化数据(180秒):带宽统计、历史数据
3. 适配流程详解
假设我们要适配一个新厂商"XYZ"的网卡"xyz1000"
3.1 创建目录结构
在 drivers/pcie_nic_card/ 下创建:
pcie_nic_card/
└── xyz/ # 厂商目录
├── xyz1000/ # 型号目录
│ ├── xyz1000_card.h # 主卡头文件
│ ├── xyz1000_card.cpp # 主卡实现
│ ├── xyz1000_port.h # 网口头文件
│ ├── xyz1000_port.cpp # 网口实现
│ ├── xyz1000_om.h # 光模块头文件(可选)
│ ├── xyz1000_om.cpp # 光模块实现(可选)
│ ├── xyz1000_abi.cpp # ABI导出
│ └── meson.build # 构建配置
├── interface/ # 接口实现目录
│ ├── pcie_device.h
│ ├── pcie_device.cpp
│ ├── network_adapter.h
│ ├── network_adapter.cpp
│ └── meson.build
├── csr/ # 设备配置目录
│ ├── [VID_DID_SVID_SSID].dds # DDS文件(设备接口定义)
│ └── [VID_DID_SVID_SSID].sr # SR文件(硬件自描述信息)
└── meson.build
3.2 配置设备描述文件(DDS)
DDS文件定义设备的接口和路径,是框架识别和加载驱动的关键配置。
文件命名规则
格式:14140130_[VID][DID]_[SVID][SSID].dds
示例:14140130_19e50222_19e50052.dds
说明:VID=0x19e5, DID=0x0222, SVID=0x19e5, SSID=0x0052
DDS文件内容
{
"Schema": "dds-v1",
"Type": "Component",
"DeviceCategory": "PCIeNicCard",
"ID": "N/A",
"Objects": {
"PCIeNicCard": {
"Path": "/bmc/dev/Systems/:SystemId/PCIeNicCard/:Id",
"Interfaces": [
"bmc.dev.PCIeDevice",
"bmc.dev.PCIeCard",
"bmc.dev.Board",
"bmc.dev.NetworkAdapter",
"bmc.dev.NetworkAdapter.Cooling",
"bmc.dev.NetworkAdapter.FaultStatus",
"bmc.dev.PCIeDevice.Bandwidth"
]
},
"NicPort": {
"Path": ":Parent/NicPort/:Id",
"Interfaces": [
"bmc.dev.NetworkPort",
"bmc.dev.NetworkPort.LinkInfo",
"bmc.dev.NetworkPort.DataCenterBridging",
"bmc.dev.NetworkPort.Metrics"
]
},
"OpticalTransceiver": {
"Path": ":Parent/OpticalTransceiver",
"Interfaces": [
"bmc.dev.OpticalModule",
"bmc.dev.OpticalModule.Status",
"bmc.dev.OpticalModule.Cooling",
"bmc.dev.OpticalModule.Voltage",
"bmc.dev.OpticalModule.Power",
"bmc.dev.OpticalModule.Current"
]
}
}
}
关键字段说明
| 字段 | 说明 | 注意事项 |
|---|---|---|
DeviceCategory | 设备类别 | 必须是"PCIeNicCard" |
Objects | 对象定义 | 键名必须与ABI中的device_name一致 |
Path | 对象路径 | :SystemId、:Id是动态参数,:Parent表示父对象 |
Interfaces | 接口列表 | 必须与MC_OBJECT宏中声明的接口一致 |
配置要点
- 接口列表要完整:DDS中的
Interfaces必须包含代码中MC_OBJECT声明的所有接口 - 对象名称要匹配:
Objects的键名(如PCIeNicCard、NicPort)必须与ABI注册时的device_name一致 - 路径格式固定:主对象路径和子对象路径格式不要修改
- 可选对象:如果不支持光模块,可以删除
OpticalTransceiver对象定义
3.3 配置硬件自描述信息(SR)
SR文件定义硬件拓扑、芯片配置和对象初始值,用于复杂的硬件管理场景。
文件命名规则
格式:14140130_[VID][DID]_[SVID][SSID].sr
示例:14140130_19e50222_19e50052.sr
说明:文件名必须与DDS文件一致
SR文件结构
{
"FormatVersion": "5.00",
"DataVersion": "5.00",
"Unit": {
"Type": "PCIeNicCard",
"Name": "PCIeNicCard_1",
"Compatible": ["xyz_vendor", "xyz_model"]
},
"ManagementTopology": {
// 硬件拓扑配置(I2C总线、芯片连接关系)
},
"Objects": {
// 对象初始配置和属性值
}
}
ManagementTopology配置(I2C拓扑)
如果网卡需要通过SMBus访问,需要配置I2C拓扑:
"ManagementTopology": {
"Anchor": {
"Buses": ["I2cMux_9545Chan3"]
},
"I2cMux_9545Chan3": {
"Chips": [
"Chip_XYZ1000", // 网卡芯片
]
}
},
"Objects": {
"Chip_XYZ1000": {
"OffsetWidth": 0,
"AddrWidth": 1,
"Address": 232, // I2C地址:0xE8
"WriteTmout": 100,
"ReadTmout": 100,
"HealthStatus": 0,
"WriteRetryTimes": 2,
"ReadRetryTimes": 0
}
}
网卡对象配置(PCIeNicCard_1)
定义网卡的初始属性值:
"PCIeNicCard_1": {
"bmc.dev.PCIeDevice": {
"DeviceName": "PCIe Card ${Slot} (XYZ1000)",
"FunctionClass": 2,
"VendorId": "0x1234",
"DeviceId": "0x0001",
"SubSystemVendorId": "0x1234",
"SubSystemDeviceId": "0x0002",
"Slot": "${Slot}",
"Segment": 0,
"SocketId": 0,
"DeviceType": "MultiFunction",
"SlotType": "FullLength",
"FunctionProtocol": "PCIe",
"FunctionType": "Physical",
"ComponentType": 8,
"Container": "${Container}"
},
"bmc.dev.NetworkAdapter": {
"Manufacturer": "XYZ Vendor",
"ChipModel": "XYZ1000",
"Description": "4*25GE",
"ChipVendor": "XYZ",
"NetworkPortCount": 4,
"SupportedMctp": true,
"SupportedLldp": true,
"Type": 3,
"RefChip": "#/Chip_XYZ1000", // 关联SMBus芯片(如果需要)
"OSPowerState": 0
},
"bmc.dev.Board": {
"Slot": "${Slot}",
"Description": "4*25GE",
"Id": 255,
"Type": "PCIeCard",
"PartNumber": "PART1234"
},
"bmc.dev.NetworkAdapter.Cooling": {
"TemperatureStatus": 3,
"TemperatureCelsius": 0
}
}
网口对象配置(NicPort_N)
为每个网口配置初始值:
"NicPort_0": {
"@Parent": "PCIeNicCard_1",
"bmc.dev.NetworkPort": {
"PortId": 0,
"NetDevFuncType": 1,
"MediumType": "FiberOptic",
"MaxSpeedSupported": "25GE",
"PermanentMACAddress": "00:00:00:00:00:00",
"MACAddress": "00:00:00:00:00:00",
"BDF": ""
},
"bmc.dev.NetworkPort.LinkInfo": {
"AutoSpeedNegotiationEnabled": false,
"SpeedMbps": 4294967295,
"WorkloadType": 0,
"LinkStatus": 255
}
}
说明:@Parent指定父对象,表示这个网口属于PCIeNicCard_1。
光模块对象配置(OpticalTransceiver_N)
如果支持光模块,为每个端口配置光模块对象:
"OpticalTransceiver_0": {
"@Parent": "NicPort_0",
"bmc.dev.OpticalModule": {
"ChannelNum": 1,
"Presence": 0,
"MediumType": ""
},
"bmc.dev.OpticalModule.Status": {
"PowerState": 0,
"FaultState": 0,
"SpeedMatch": true,
"TypeMatch": true
},
"bmc.dev.OpticalModule.Cooling": {
"TemperatureCelsius": 65535,
"TemperatureLowerThresholdCritical": 65535,
"TemperatureUpperThresholdCritical": 65535
}
}
SR文件配置要点
- RefChip关联:如果需要通过SMBus直接访问网卡芯片,必须在
NetworkAdapter接口中配置RefChip - 父子关系:使用
@Parent字段指定对象的层次关系 - 动态参数:
${Slot}等参数会在运行时替换 - 初始值:65535通常表示"无效值"或"未初始化"
- 接口完整性:SR中定义的接口必须与DDS文件中声明的接口一致
3.4 创建网卡主对象
代码示例:xyz1000_card.h
参考 hi182x_card.h:
#ifndef XYZ1000_CARD_H
#define XYZ1000_CARD_H
#include <mc/engine.h>
#include <ncsi_over_mctp/ncsi_over_mctp.h>
#include "xyz1000_port.h"
#include "interface/board.h"
#include "interface/network_adapter.h"
#include "interface/pcie_card.h"
#include "interface/pcie_device.h"
namespace dev {
using ncsi_over_mctp_xyz_ptr = std::shared_ptr<ncsi_over_mctp>;
class xyz1000_card : public mc::engine::object<xyz1000_card> {
public:
// MC_OBJECT宏定义:类名、对象类型、路径模式、接口列表
MC_OBJECT(
xyz1000_card,
"PCIeNicCard",
"/bmc/dev/Systems/1/PCIeNicCard/${object_name}",
(PCIeDevice)(PCIeCard)(NetworkAdapter)(Board)
)
xyz1000_card(); // 构造函数
~xyz1000_card(); // 析构函数
// 生命周期方法
bool init(mc::mutable_dict& csr_object, const mc::dict& connector);
bool start();
bool stop();
// 子设备管理
void init_network_ports();
// 接口成员变量
PCIeDevice m_pcie_device;
PCIeCard m_pcie_card;
NetworkAdapter m_network_adapter;
Board m_board;
uint8_t m_system_id;
private:
std::vector<xyz1000_port*> m_network_ports;
// 协议支持
bool start_ncsi_protocol();
bool start_protocol();
ncsi_over_mctp_xyz_ptr m_ncsi_over_mctp;
mc::milliseconds m_interval = mc::milliseconds(5000);
mctp* m_mctp_object; // 管理MCTP对象生命周期
};
} // namespace dev
// MC_REFLECT宏:反射信息注册
MC_REFLECT(dev::xyz1000_card,
((m_system_id, "SystemId"))
((m_pcie_device, "bmc.dev.PCIeDevice"))
((m_pcie_card, "bmc.dev.PCIeCard"))
((m_network_adapter, "bmc.dev.NetworkAdapter"))
((m_board, "bmc.dev.Board")))
#endif // XYZ1000_CARD_H
关键说明
1. MC_OBJECT宏的参数:
- 第1个:类名
- 第2个:设备类型(固定为
"PCIeNicCard") - 第3个:对象路径模板(固定格式)
- 第4个:实现的接口列表(按需选择)
2. 接口选择原则:
| 接口 | 必需性 | 说明 |
|---|---|---|
PCIeDevice、PCIeCard、Board | 必需 | 基础接口 |
NetworkAdapter | 必需 | 网卡功能接口 |
NetworkAdapter_Cooling | 可选 | 支持温度监控时添加 |
NetworkAdapter_FaultStatus | 可选 | 支持故障诊断时添加 |
PCIeDevice_Bandwidth | 可选 | 支持带宽监控时添加 |
3.5 实现网卡主对象
代码示例:xyz1000_card.cpp
#include "xyz1000_card.h"
#include <mc/log.h>
namespace dev {
// 构造函数:初始化MCTP对象指针为nullptr
xyz1000_card::xyz1000_card() : m_mctp_object(nullptr) {
}
// 析构函数:释放MCTP对象,防止内存泄漏
xyz1000_card::~xyz1000_card() {
// 释放MCTP对象内存
// 取消注册OS重置回调
}
// 初始化方法:从配置文件加载网卡属性
bool xyz1000_card::init(mc::mutable_dict& csr_object, const mc::dict& connector) {
// 使用from_variant自动加载配置到成员变量
// 异常处理
}
// 启动方法:启动网卡和所有子设备
bool xyz1000_card::start() {
// 初始化子网口对象
// 启动每个网口
// 启动NCSI协议
// 启动SMBus协议(如果支持)
}
// 停止方法:停止网卡并清理资源
bool xyz1000_card::stop() {
// 停止所有网口
// 清理定时器等资源
}
// 初始化网口对象:从子对象列表中提取所有网口
void xyz1000_card::init_network_ports() {
// 获取子对象列表
// 筛选出NicPort类型的对象
// 加入m_network_ports向量
}
// 启动NCSI协议:创建MCTP通信和NCSI协议实例
bool xyz1000_card::start_ncsi_protocol() {
// 检查BDF地址是否就绪
// 创建MCTP对象(赋值给成员变量)
// 定义NCSI更新任务(lambda表达式)
// 为各接口启动更新任务
// 创建传输层并启动
}
// 启动协议:启动NCSI协议,如果失败则注册回调等待BDF就绪
bool xyz1000_card::start_protocol() {
// 尝试启动NCSI协议
// 如果失败,注册BDF变化回调
}
} // namespace dev
关键说明
| 方法 | 职责 |
|---|---|
init() | 加载配置文件中的属性值,使用from_variant自动映射 |
start() | 初始化子设备、启动协议通信、注册回调和定时器 |
stop() | 清理资源 |
协议启动流程:
- 获取BDF(Bus/Device/Function)
- 创建MCTP对象
- 创建NCSI协议实例
- 为接口绑定更新任务
3.6 实现网口对象
代码示例:xyz1000_port.h
#ifndef XYZ1000_PORT_H
#define XYZ1000_PORT_H
#include <mc/engine.h>
#include <ncsi_over_mctp/ncsi_over_mctp.h>
#include "interface/network_port.h"
#include "interface/network_port/link_info.h"
namespace dev {
using ncsi_over_mctp_xyz_ptr = std::shared_ptr<ncsi_over_mctp>;
class xyz1000_port : public mc::engine::object<xyz1000_port> {
public:
MC_OBJECT(
xyz1000_port,
"NicPort",
":Parent/NicPort/:Id",
(NetworkPort)(NetworkPort_LinkInfo)
)
xyz1000_port() = default;
~xyz1000_port() = default;
bool init(mc::mutable_dict& csr_object, const mc::dict& connector);
bool start();
bool stop();
void start_ncsi_protocol(ncsi_over_mctp_xyz_ptr ncsi_over_mctp,
mc::milliseconds interval);
NetworkPort m_network_port;
NetworkPort_LinkInfo m_network_port_link_info;
private:
ncsi_over_mctp_xyz_ptr m_ncsi_over_mctp;
mc::core::timer* m_ncsi_timer = nullptr;
};
} // namespace dev
MC_REFLECT(dev::xyz1000_port,
((m_network_port, "bmc.dev.NetworkPort"))
((m_network_port_link_info, "bmc.dev.NetworkPort.LinkInfo")))
#endif // XYZ1000_PORT_H
代码示例:xyz1000_port.cpp
#include "xyz1000_port.h"
#include <mc/log.h>
namespace dev {
// 初始化网口:从配置加载网口属性
bool xyz1000_port::init(mc::mutable_dict& csr_object, const mc::dict& connector) {
// 使用from_variant加载配置
// 加载PortId、MAC地址等属性
}
// 启动网口:执行网口启动逻辑
bool xyz1000_port::start() {
// 执行网口特定的启动操作
// 打印启动日志
}
// 停止网口:停止定时器并清理资源
bool xyz1000_port::stop() {
// 停止NCSI定时器
// 释放定时器内存
}
// 启动NCSI协议:创建定时器周期性更新网口状态
void xyz1000_port::start_ncsi_protocol(ncsi_over_mctp_xyz_ptr ncsi_over_mctp,
mc::milliseconds interval) {
// 保存NCSI协议对象
// 创建定时器
// 设置定时器回调(获取链路状态、流量统计等)
// 启动定时器
}
} // namespace dev
关键说明
- 网口对象的路径使用
:Parent,表示在父对象(网卡)路径下 - 定时器用于周期性更新网口状态
- 从NCSI协议获取数据并更新到接口属性
3.7 实现接口类
核心要点
接口类继承自gen命名空间的基类,负责具体的协议实现和数据更新。
1. 接口类定义
// 继承自生成的基类
class NetworkAdapter : public gen::NetworkAdapter {
public:
// 启动NCSI更新任务:创建定时器周期性更新属性
void start_ncsi_update_task(ncsi_over_mctp_xyz_ptr ncsi_over_mctp,
mc::milliseconds interval);
// 停止更新任务:停止并释放定时器
void stop_ncsi_update_task();
// 更新方法:通过NCSI协议获取数据并更新属性
void update_firmware_version();
void update_chip_info();
private:
mc::core::timer* m_ncsi_timer = nullptr;
ncsi_over_mctp_xyz_ptr m_ncsi_over_mctp;
};
// MC_REFLECT注册:指定基类
MC_REFLECT(dev::NetworkAdapter, (gen::NetworkAdapter), ())
2. 关键实现方法
// 启动更新任务:创建定时器周期性更新属性
void NetworkAdapter::start_ncsi_update_task(ncsi_over_mctp_xyz_ptr ncsi_over_mctp,
mc::milliseconds interval) {
// 保存协议对象
// 创建定时器
// 设置定时器回调(调用各个update方法)
// 启动定时器
// 立即执行一次更新
}
// 更新固件版本:通过NCSI协议获取并更新FirmwareVersion属性
void NetworkAdapter::update_firmware_version() {
// 构造NCSI请求数据
// 调用m_ncsi_over_mctp->request()发送请求
// 解析响应数据
// 更新属性:FirmwareVersion = parsed_version
}
关键要点:
- 接口类必须继承对应的
gen基类 - 使用定时器周期性调用update方法
- update方法负责:获取数据 → 解析 → 更新属性
- 属性更新后会自动触发信号通知上层
3.8 实现ABI导出
ABI(Application Binary Interface)是驱动程序与框架交互的标准接口。
代码示例:xyz1000_abi.cpp
#include <devmon/driver_abi.h>
#include <mc/log.h>
#include "xyz1000_card.h"
#include "xyz1000_port.h"
using namespace dev;
extern "C" {
// ============================================
// 网卡主对象ABI函数
// ============================================
// 创建网卡对象:分配内存并设置基本属性
driver_handle_t create_xyz1000_card(void* service, const char* name) {
// 参数检查
// 创建xyz1000_card对象
// 设置service和对象名称
// 返回对象指针
}
// 初始化网卡对象:加载配置
status_t init_xyz1000_card(driver_handle_t device, void* csr_object, void* connector) {
// 参数检查
// 类型转换
// 调用对象的init方法
// 返回状态码
}
// 启动网卡对象:启动设备和协议
status_t start_xyz1000_card(driver_handle_t device) {
// 参数检查
// 调用对象的start方法
// 返回状态码
}
// 停止网卡对象:停止设备和清理资源
status_t stop_xyz1000_card(driver_handle_t device) {
// 参数检查
// 调用对象的stop方法
// 返回状态码
}
// ============================================
// 网口对象ABI函数
// ============================================
// 创建网口对象:分配内存并设置基本属性
driver_handle_t create_xyz1000_port(void* service, const char* name) {
// 参数检查
// 创建xyz1000_port对象
// 设置service和对象名称
// 返回对象指针
}
// 初始化网口对象:加载配置
status_t init_xyz1000_port(driver_handle_t device, void* csr_object, void* connector) {
// 参数检查
// 类型转换
// 调用对象的init方法
// 返回状态码
}
// 启动网口对象:启动网口
status_t start_xyz1000_port(driver_handle_t device) {
// 参数检查
// 调用对象的start方法
// 返回状态码
}
// 停止网口对象:停止网口
status_t stop_xyz1000_port(driver_handle_t device) {
// 参数检查
// 调用对象的stop方法
// 返回状态码
}
// ============================================
// 驱动注册
// ============================================
// 网卡主对象驱动结构体:定义设备名和生命周期函数
device_driver_t xyz1000_card_device_driver = {
.device_name = "PCIeNicCard", // 设备类型名(必须与DDS文件一致)
.ctor = create_xyz1000_card, // 创建函数
.init = init_xyz1000_card, // 初始化函数
.start = start_xyz1000_card, // 启动函数
.stop = stop_xyz1000_card // 停止函数
};
// 网口对象驱动结构体:定义设备名和生命周期函数
device_driver_t xyz1000_port_device_driver = {
.device_name = "NicPort", // 设备类型名(必须与DDS文件一致)
.ctor = create_xyz1000_port, // 创建函数
.init = init_xyz1000_port, // 初始化函数
.start = start_xyz1000_port, // 启动函数
.stop = stop_xyz1000_port // 停止函数
};
// 驱动数组:包含所有设备类型的驱动
device_driver_t xyz1000_device_driver[] = {
xyz1000_card_device_driver,
xyz1000_port_device_driver
};
// 驱动注册函数:框架调用此函数获取驱动列表
status_t register_device_driver(device_driver_t** device_driver, uint8_t* count) {
// 返回驱动数组指针
*device_driver = xyz1000_device_driver;
// 返回驱动数量
*count = sizeof(xyz1000_device_driver) / sizeof(xyz1000_device_driver[0]);
ilog("Registered xyz1000 device driver: ${count} devices", ("count", *count));
return STATUS_OK;
}
} // extern "C"
关键说明
- 所有ABI函数必须用
extern "C"包装,确保C语言兼容 - 每个设备类型需要4个函数:
create、init、start、stop device_name必须与DDS文件中的对象类型匹配register_device_driver是框架调用的入口函数
3.9 配置构建编译
代码示例:meson.build
# 源文件列表
xyz1000_sources = files(
'xyz1000_card.cpp',
'xyz1000_port.cpp',
'xyz1000_abi.cpp',
)
# 接口实现源文件
interface_sources = files(
'interface/pcie_device.cpp',
'interface/network_adapter.cpp',
'interface/network_port.cpp',
'interface/network_port/link_info.cpp',
)
# 合并源文件
all_sources = xyz1000_sources + interface_sources
# 包含目录
include_dirs = [
include_directories('.'),
include_directories('interface'),
gen_inc,
internal_inc
]
# 静态库(用于测试)
libxyz1000_static = static_library(
'xyz1000',
all_sources,
include_directories: include_dirs,
dependencies: [
dev_deps,
libncsi_over_mctp_dep,
libmctp_dep
],
install: false
)
# 动态库(用于部署)
libxyz1000 = shared_library(
'xyz1000',
include_directories: include_dirs,
name_prefix: 'lib',
name_suffix: 'so',
install: true,
install_dir: drivers_install_dir,
link_whole: [libxyz1000_static]
)
关键说明
- 库名会成为驱动SO文件名:
libxyz1000.so install_dir使用drivers_install_dir变量(框架定义)- 依赖关系要明确列出,避免链接错误
4. 最佳实践建议
4.1 代码规范
命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 类名 | 厂商_型号_类型 | xyz1000_card |
| 文件名 | 与类名一致 | xyz1000_card.h |
| 成员变量 | m_前缀 | m_network_ports |
| 常量 | 大写+下划线 | DEFAULT_TIMEOUT |
日志规范
- 启动/停止:使用
ilog - 错误:使用
elog - 调试信息:使用
dlog - 日志要包含上下文信息
ilog("Starting xyz1000_card: ${name} with ${count} ports",
("name", card_name)("count", port_count));
错误处理
- 所有可能失败的操作都要检查返回值
- 使用
try-catch捕获异常 - 记录详细的错误信息
try {
bool ret = operation();
if (!ret) {
elog("Operation failed: ${reason}", ("reason", error_msg));
return false;
}
} catch (const std::exception& e) {
elog("Exception caught: ${error}", ("error", e.what()));
return false;
}
4.2 性能优化
定时任务间隔
- 根据数据变化频率设置不同的更新间隔,避免不必要的通信开销
- 快速变化数据:5秒(5000ms) - 温度、链路状态、故障状态
- 慢速变化数据:120秒(120000ms) - 固件版本、设备信息
- 极慢变化数据:180秒(180000ms) - 带宽统计、配置信息
实际应用示例:
// 不同接口使用不同的更新间隔
m_network_adapter_cooling.start_ncsi_update_task(ncsi, mc::milliseconds(5000)); // 温度:5秒
m_network_adapter.start_ncsi_update_task(ncsi, mc::milliseconds(120000)); // 基础信息:120秒
m_pcie_device_bandwidth.start_ncsi_update_task(ncsi, mc::milliseconds(180000)); // 带宽:180秒
内存管理
- 优先使用智能指针(
shared_ptr) - 及时清理定时器等资源
- 重要:MCTP对象必须使用成员变量管理,防止严重内存泄漏
MCTP对象管理:
// 头文件声明
class xyz1000_card : public mc::engine::object<xyz1000_card> {
private:
mctp* m_mctp_object; // 使用成员变量管理
};
// 实现文件
xyz1000_card::xyz1000_card() : m_mctp_object(nullptr) {}
xyz1000_card::~xyz1000_card() {
// 在析构函数中释放,防止内存泄漏
if (m_mctp_object) {
delete m_mctp_object;
m_mctp_object = nullptr;
}
}
bool xyz1000_card::start_ncsi_protocol() {
// 赋值给成员变量,而不是局部变量
m_mctp_object = new mctp(this, phy_addr, ...);
}
定时器资源管理:
// 清理定时器
if (m_timer != nullptr) {
m_timer->stop();
delete m_timer;
m_timer = nullptr;
}
协议优化
- 批量请求减少通信次数
- 缓存不变的数据
- 失败时使用指数退避
4.3 测试建议
单元测试
- 测试每个接口的更新方法
- 测试异常情况处理
- 使用mock对象模拟协议
TEST(xyz1000_card, test_init) {
xyz1000_card card;
mc::mutable_dict csr_object = {...};
mc::dict connector = {...};
EXPECT_TRUE(card.init(csr_object, connector));
}
集成测试
- 测试完整的生命周期:
create→init→start→stop - 测试与实际硬件的通信
- 测试长时间运行的稳定性
回归测试
- 每次修改后重新测试基本功能
- 检查是否影响其他网卡驱动
- 验证Redfish接口数据正确
结语
南向网卡驱动适配是一项系统性工作,需要对硬件特性、通信协议、软件架构都有深入理解。本文档提供了一套完整的适配流程和代码示例,但实际适配中还会遇到各种具体问题,欢迎大家积极在社区沟通交流和贡献。
【版权声明】Copyright © 2026 openUBMC Community。本文由openUBMC社区首发,欢迎遵照CC-BY-SA 4.0协议规定转载。转载时敬请在正文注明并保留原文链接和作者信息。
【免责声明】本文仅代表作者本人观点,与本网站无关。本网站对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文仅供读者参考,由此产生的所有法律责任均由读者本人承担。
