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

南向网卡驱动适配指南

实践案例

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] Copyright © 2026 openUBMC Community. This article was first published by the openUBMC Community. Reproduction is welcomed under CC-BY-SA 4.0. When reproducing, please prominently note the source in the text and retain the original article link and author information.

[Disclaimer] The views expressed in this article are solely those of the author and do not represent the stance of this website. This website remains neutral regarding the statements and opinions presented and provides no express or implied warranty as to the accuracy, reliability, or completeness of the content. This article is intended for reference only, and all legal responsibilities arising therefrom shall be borne by the reader.

About the Author

常德兴

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