博客详情页
  • 下载
  • 开发
  • 文档
  • 学习
  • 支持
  • 社区
  • 动态
Repositories
EN
Repositories
EN
南向网卡驱动适配指南

南向网卡驱动适配指南

实践案例

2025/11/06
常德兴

南向网卡驱动适配指南

南向驱动是基于硬件组件驱动程序框架,采用分层架构设计,支持多种设备类型和通信协议。本文档面向网卡适配的设备驱动开发者,提供从设计到实现的完整开发流程,并基于现有代码实例分析,提供一篇快速适配的开发指南。

1. 概述

1.1 什么是南向网卡驱动适配?

南向网卡驱动适配是指在BMC系统中,为不同厂商的PCIe网卡提供统一的驱动程序接入方案,使BMC能够管理和监控网卡设备。

1.2 适配后可以实现什么功能?

  • 获取网卡基本信息(厂商ID、设备ID、固件版本等)
  • 监控网卡状态(温度、链路状态、带宽等)
  • 管理网口(链路信息、MAC地址、LLDP等)
  • 监控光模块(温度、收发功率、序列号等)
  • 故障诊断和日志收集

1.3 需要了解的关键概念

设备对象

对象类型说明示例
网卡主对象代表整个PCIe网卡PCIeNicCard
网口对象代表网卡上的物理网口NicPort
光模块对象代表插在网口上的光模块OpticalTransceiver

接口

  • gen命名空间:自动生成的接口基类,定义属性
  • dev命名空间:厂商具体实现类,实现功能
  • 接口组合:一个设备可以实现多个接口,如PCIeDeviceNetworkAdapter

协议库

  • NCSI over MCTP:网卡控制协议,通过PCIe与网卡通信
  • SMBUS:系统管理总线协议,用于I2C通信
  • 不同厂商可能使用不同的协议扩展

1.4 驱动分层架构

  1. 分层架构:构建了一个从接口定义到硬件交互的完整体系,接口定义层→设备接口层→设备对象层→设备驱动层,每层职责明确,协同工作;
  2. 设计理念:接口与实现分离,先是“接口定义层”建立接口规范,并在“设备接口层” 自动生成C++接口代码,再由各厂商提供“设备驱动层”的具体部件驱动实现;

驱动分层架构

2. 前置准备

2.1 明确网卡信息

必需信息

信息项说明示例
VIDVendor ID(厂商ID)0x19e5
DIDDevice ID(设备ID)0x0222
SVIDSubsystem Vendor ID0x19e5
SSIDSubsystem Device ID0x0052

协议信息

  • 支持的通信协议(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定义了FirmwareVersionChipModel等属性
对象组合将多个接口组合成完整对象网卡对象组合了PCIeDeviceNetworkAdapterBoard等接口
命名空间所有生成的代码在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网卡管理的主要协议,获取网卡状态、配置信息
ncsiNCSI协议定义-定义NCSI命令格式、数据结构(仅头文件)
smbus系统管理总线协议I2C/SMBus直接读取芯片寄存器,获取温度等数据
ipmbIPMI消息块协议I2CIPMI命令传输
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 目录之间的关系

目录之间的关系

工作流程
  1. 代码生成:根据模型文件生成gen/目录下的接口基类
  2. 接口实现:在drivers/*/interface/下继承gen基类,实现具体功能
  3. 驱动组装:在drivers/*/具体型号/下组合接口,创建完整驱动对象
  4. 协议调用:驱动通过libraries/中的协议库与硬件通信
  5. 框架支持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 PCIeNCSI over MCTP over SMBusSMBus直接访问说明
华为海思 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"
            }
        }
    }
}

关键配置说明:

  1. ManagementTopology定义I2C拓扑
    • 指定网卡芯片挂在哪条I2C总线上
    • Chip_Hi1822定义芯片的I2C访问参数
  2. RefChip关联
    • bmc.dev.NetworkAdapter接口通过RefChip字段引用Chip_Hi1822
    • 这样NetworkAdapter接口可以通过SMBus读取芯片数据(如温度)
  3. 双协议工作
    • 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访问
            }
        }
    }
}

关键配置说明:

  1. Chip_SmbusChip
    • 定义SMBus桥接芯片的I2C访问参数
    • 这个芯片负责将MCTP消息转换为SMBus传输
  2. MctpBinding_1
    • 配置BMC侧的MCTP绑定信息
    • 指定BMC的EID和物理地址
  3. Endpoint_1
    • 配置网卡侧的MCTP端点
    • MessageType: 2表示NCSI消息类型
    • MediumType: 128表示通过SMBus传输
    • RefChip关联到SMBus芯片
  4. 无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_1MessageType设置为2(NCSI)
  • Endpoint_1MediumType设置为128(SMBus)
  • 代码中只需实现NCSI协议,无需SMBus

2.5 网卡支持的能力

本节以Hi182x网卡为例,详细列举网卡驱动可以支持的各种能力,帮助开发者了解可以实现哪些功能接口。

网卡级能力

Hi182x网卡主对象实现了以下8个接口:

能力类别接口名称说明可获取的信息
PCIe设备信息PCIeDevicePCIe设备基础信息VID/DID、BDF地址、设备名称、设备类型
PCIe卡信息PCIeCard板卡物理信息卡槽位置、序列号、插槽类型
网络适配器NetworkAdapter网卡功能信息厂商、型号、固件版本、MAC地址数量、支持的协议
温度监控NetworkAdapter_Cooling网卡温度信息芯片温度、最高温度、温度阈值、散热状态
故障诊断NetworkAdapter_FaultStatus故障状态信息故障代码、健康状态、错误计数、告警信息
带宽监控PCIeDevice_BandwidthPCIe带宽信息链路速度、链路宽度、实际吞吐量、协商状态
日志收集NetworkAdapter_LogCollection日志导出功能网卡日志文件、故障诊断信息、事件记录
板卡信息Board板卡制造信息板卡序列号、部件号、制造商、生产日期

网口级能力

Hi182x的每个网口对象实现了以下5个接口:

能力类别接口名称说明可获取的信息
网口基础信息NetworkPort网口基本属性端口ID、MAC地址、最大速率、端口类型、启用状态
链路信息NetworkPort_LinkInfo链路状态信息链路Up/Down、当前速度、双工模式、自协商状态
LLDP接收NetworkPort_LLDPReceiveLLDP协议信息邻居设备信息、网络拓扑、设备能力、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宏中声明的接口一致

配置要点

  1. 接口列表要完整:DDS中的Interfaces必须包含代码中MC_OBJECT声明的所有接口
  2. 对象名称要匹配Objects的键名(如PCIeNicCardNicPort)必须与ABI注册时的device_name一致
  3. 路径格式固定:主对象路径和子对象路径格式不要修改
  4. 可选对象:如果不支持光模块,可以删除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文件配置要点

  1. RefChip关联:如果需要通过SMBus直接访问网卡芯片,必须在NetworkAdapter接口中配置RefChip
  2. 父子关系:使用@Parent字段指定对象的层次关系
  3. 动态参数${Slot}等参数会在运行时替换
  4. 初始值:65535通常表示"无效值"或"未初始化"
  5. 接口完整性: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. 接口选择原则:

接口必需性说明
PCIeDevicePCIeCardBoard必需基础接口
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()清理资源

协议启动流程:

  1. 获取BDF(Bus/Device/Function)
  2. 创建MCTP对象
  3. 创建NCSI协议实例
  4. 为接口绑定更新任务

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个函数:createinitstartstop
  • 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));
}

集成测试

  • 测试完整的生命周期:createinitstartstop
  • 测试与实际硬件的通信
  • 测试长时间运行的稳定性

回归测试

  • 每次修改后重新测试基本功能
  • 检查是否影响其他网卡驱动
  • 验证Redfish接口数据正确

结语

南向网卡驱动适配是一项系统性工作,需要对硬件特性、通信协议、软件架构都有深入理解。本文档提供了一套完整的适配流程和代码示例,但实际适配中还会遇到各种具体问题,欢迎大家积极在社区沟通交流和贡献。

【版权声明】Copyright © 2026 openUBMC Community。本文由openUBMC社区首发,欢迎遵照CC-BY-SA 4.0协议规定转载。转载时敬请在正文注明并保留原文链接和作者信息。

【免责声明】本文仅代表作者本人观点,与本网站无关。本网站对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文仅供读者参考,由此产生的所有法律责任均由读者本人承担。

关于作者

常德兴

openUBMC component-drivers SIG Committer,致力于深耕部件驱动标准化,建设部件生态社区。