硬件访问服务(Hardware Access Service)设计文档

1. 概述

硬件访问服务(Hardware Access Service, HAS)是设备监控系统(devmon)中的底层组件,提供了对硬件总线和芯片的统一访问抽象。HAS库设计为轻量级、灵活、可扩展的架构,支持各种总线协议和芯片类型,为上层应用提供了一致的硬件交互接口。

通过HAS库,上层应用可以:

  • 通过标准接口访问不同类型的硬件总线(I2C, SMBus等)
  • 与各种芯片交互,无需关心底层通信细节
  • 统一错误处理和资源管理
  • 实现硬件控制和状态监控

2. 架构设计

2.1 整体架构

HAS库采用分层设计,自底向上分为总线层、芯片层和应用接口层:

flowchart TD
    subgraph ApplicationLayer["应用层"]
        DeviceMonitor["设备监控应用"]
        ConfigTool["配置工具"]
    end

    subgraph HasLibrary["硬件访问服务库(libhas)"]
        direction TB

        subgraph ChipLayer["芯片层"]
            ChipBase["Chip基类"]
            Hi182x["Hi182x芯片"]
            FutureChips["其他芯片..."]
        end

        subgraph BusLayer["总线层"]
            I2C["I2C总线"]
            SMBus["SMBus总线"]
            FutureBus["其他总线..."]
        end

        ChipLayer --> BusLayer
    end

    subgraph HardwareLayer["硬件层"]
        PhysicalBus["物理总线"]
        PhysicalChips["物理芯片"]
    end

    ApplicationLayer --> HasLibrary
    HasLibrary --> HardwareLayer

2.2 目录结构

HAS库的代码组织反映了其模块化设计:

libhas/
├── commdef.h             # 通用定义和数据结构
├── bus/                  # 总线实现目录
│   ├── i2c/              # I2C总线实现
│   │   ├── i2c.h
│   │   └── i2c.cpp
│   └── smbus/            # SMBus总线实现
│       ├── smbus_huawei.h
│       └── smbus_huawei.cpp
└── chip/                 # 芯片实现目录
    ├── chip.h            # 芯片基类定义
    └── Hi182x/           # Hi182x芯片实现
        ├── Hi182x.h
        └── Hi182x.cpp

3. 组件设计

3.1 通用定义(commdef.h)

通用定义文件提供了整个库使用的基础数据结构和常量:

#define ERROR -1
#define OK 0

using ACCESS_DATA_S = struct access_data_s {
    uint8_t addr;          // 设备地址
    uint32_t offset;       // 访问偏移量
    uint8_t offset_width;  // 偏移量宽度
    uint8_t *in_data;      // 输入数据
    uint32_t len;          // 数据长度
    uint32_t write_timeout; // 写超时时间
};

ACCESS_DATA_S结构体是硬件访问操作的核心数据结构,封装了总线通信所需的所有参数。

3.2 总线层设计

总线层提供了对各种物理总线的抽象,目前支持I2C和SMBus总线。

3.2.1 I2C总线

I2C总线实现提供了标准的I2C通信功能:

class I2c {
    public:
        I2c();
        ~I2c();
        int32_t read(uint8_t addr, uint8_t reg, uint8_t *data, uint32_t len);
        int32_t write(uint8_t addr, uint8_t reg, uint8_t *data, uint32_t len);

    private:
        uint8_t m_bus_id;   // 总线ID
        uint8_t m_bus_type; // 总线类型
        uint8_t m_speed;    // 总线速度
        uint8_t m_mode;     // 总线模式
};

I2C总线类提供了基本的读写接口,支持指定设备地址、寄存器和数据进行通信。

3.2.2 SMBus总线

SMBus是System Management Bus的缩写,是I2C的一个子集,添加了一些特定功能。HAS库中实现了华为定制的SMBus协议:

class SMBusHuawei : public I2c {
    public:
        SMBusHuawei();
        ~SMBusHuawei();
        int32_t send_request(smbus_access_data_t &access_data);
        int32_t write_read(smbus_request_t *request, smbus_access_data_t &access_data, std::string &recv_data);
    private:
        std::mutex m_mutex;
};

SMBus实现扩展了I2C基类,添加了特定的请求响应模式和数据结构,并通过互斥锁确保线程安全。

3.3 芯片层设计

芯片层基于总线层,提供了特定芯片的访问抽象。

3.3.1 芯片基类

芯片基类定义了所有芯片实现的通用接口:

class Chip : public mc::db::object<Chip> {
    public:
        Chip();
        ~Chip();

    public:
        virtual void lock();
        virtual void unlock();

    protected:
        uint8_t m_addr_width;
        uint8_t m_offset_width;
};

芯片基类继承自mc::db::object,提供了数据库集成能力,同时定义了锁定机制以支持线程安全操作。

3.3.2 Hi182x芯片实现

Hi182x芯片是一个具体的芯片实现示例:

class Hi182x : public Chip {
    public:
        Hi182x(std::shared_ptr<SMBusHuawei> smbus);
        ~Hi182x();
        bool set_delay_time(uint32_t delay_time);
        int32_t get_capability();

    private:
        uint32_t get_max_recv_len(uint16_t opcode, uint32_t *max_recv_len);
        std::vector<uint16_t> m_opcodes;
        uint32_t m_delay_time = 1;
        uint32_t m_max_frame_length = 64;
        std::mutex m_mutex;
        std::shared_ptr<SMBusHuawei> m_smbus;
        uint8_t m_addr;
        uint8_t m_offset_width = 0;
};

Hi182x芯片类通过SMBus进行通信,实现了芯片特定的功能如设置延迟时间和获取能力信息。

4. 接口设计

4.1 总线接口

总线接口设计遵循简单、一致的原则,主要提供读写操作:

classDiagram
    class I2c {
        +read(addr, reg, data, len)
        +write(addr, reg, data, len)
    }

    class SMBusHuawei {
        +send_request(access_data)
        +write_read(request, access_data, recv_data)
    }

    I2c <|-- SMBusHuawei

4.2 芯片接口

芯片接口提供了更高级的抽象,根据具体芯片功能提供相应的方法:

classDiagram
    class Chip {
        +lock()
        +unlock()
        #m_addr_width
        #m_offset_width
    }

    class Hi182x {
        +set_delay_time(delay_time)
        +get_capability()
        -get_max_recv_len(opcode, max_recv_len)
        -m_opcodes
        -m_delay_time
        -m_max_frame_length
        -m_mutex
        -m_smbus
        -m_addr
        -m_offset_width
    }

    Chip <|-- Hi182x

5. 通信流程

5.1 I2C通信流程

sequenceDiagram
    participant App as 应用层
    participant I2C as I2C总线类
    participant Dev as I2C设备

    App->>I2C: 创建I2C对象
    App->>I2C: 调用read(addr, reg, data, len)
    I2C->>Dev: 发送设备地址+寄存器地址
    Dev-->>I2C: 确认接收
    I2C->>Dev: 读取数据请求
    Dev-->>I2C: 返回请求的数据
    I2C-->>App: 返回读取结果

    App->>I2C: 调用write(addr, reg, data, len)
    I2C->>Dev: 发送设备地址+寄存器地址+数据
    Dev-->>I2C: 确认接收
    I2C-->>App: 返回写入结果

5.2 SMBus通信流程

sequenceDiagram
    participant App as 应用层
    participant SMBus as SMBus类
    participant I2C as I2C总线层
    participant Dev as SMBus设备

    App->>SMBus: 创建SMBus对象
    App->>SMBus: 准备access_data数据结构
    App->>SMBus: 调用send_request(access_data)
    SMBus->>SMBus: 构造请求包(opcode, offset, length等)
    SMBus->>I2C: 调用I2C写操作
    I2C->>Dev: 发送请求数据
    Dev-->>I2C: 确认接收
    SMBus->>SMBus: 等待指定的延迟时间
    SMBus->>I2C: 调用I2C读操作
    I2C->>Dev: 读取请求
    Dev-->>I2C: 返回响应数据
    I2C-->>SMBus: 返回设备响应
    SMBus->>SMBus: 解析响应数据和错误码
    SMBus-->>App: 返回处理结果

5.3 芯片访问流程

sequenceDiagram
    participant App as 应用层
    participant Chip as 芯片类(Hi182x)
    participant SMBus as SMBus总线
    participant Dev as 物理芯片设备

    App->>Chip: 创建芯片对象(传入SMBus实例)
    App->>Chip: 调用芯片特定方法(如get_capability())
    Chip->>Chip: lock()加锁确保线程安全
    Chip->>Chip: 准备访问数据结构
    Chip->>SMBus: 发送请求
    SMBus->>Dev: 通过总线发送命令
    Dev-->>SMBus: 返回响应
    SMBus-->>Chip: 解析响应结果
    Chip->>Chip: 处理数据
    Chip->>Chip: unlock()释放锁
    Chip-->>App: 返回处理结果

6. 错误处理

HAS库采用返回值和错误码相结合的错误处理机制:

6.1 通用错误码

#define ERROR -1   // 通用错误
#define OK 0       // 操作成功

6.2 SMBus特定错误码

#define ERROR_CODE_SUCCESS 0
#define ERROR_CODE_FAILED -1
#define ERROR_CODE_I2C_READ_FAILED 0x3001
#define ERROR_CODE_I2C_WRITE_FAILED 0x3002
#define ERROR_CODE_REQUEST_LEN_ERROR 0x3003
#define ERROR_CODE_IDENTIFY_CODE_ERROR 0x3004
#define ERROR_CODE_CAP_UNSUPPORTED 0x3005

// 设备返回的错误码
#define ERROR_CODE_FOR_SUCCESS 0
#define ERROR_CODE_FOR_OPCODE_UNSUPPORTED 1
#define ERROR_CODE_FOR_PARAMETER_ERROR 2
#define ERROR_CODE_FOR_INTERNAL_ERROR 3
#define ERROR_CODE_FOR_CRC_ERROR 4

错误处理流程:

  1. 函数通过返回整数值表明操作是否成功
  2. 对于更详细的错误信息,使用特定的错误码
  3. 在必要时,函数可以通过输出参数提供额外的错误细节

7. 线程安全

HAS库设计考虑了多线程环境下的并发访问:

  1. 芯片级锁机制:芯片基类提供了lock()和unlock()方法,子类可以实现这些方法来保护关键操作
  2. 互斥锁:关键类如SMBusHuawei和Hi182x使用std::mutex实现互斥访问
  3. 资源保护:共享资源如总线和设备访问通过锁机制保护,防止并发访问导致的问题
// Hi182x类中的互斥锁使用示例
void Hi182x::some_method() {
    std::lock_guard<std::mutex> lock(m_mutex);
    // 执行需要保护的操作...
}

8. 使用示例

8.1 I2C总线使用示例

// 创建I2C对象
I2c i2c;

// 读取I2C设备
uint8_t addr = 0x50;  // 设备地址
uint8_t reg = 0x10;   // 寄存器地址
uint8_t data[4];      // 数据缓冲区
uint32_t len = 4;     // 数据长度

if (i2c.read(addr, reg, data, len) == OK) {
    // 读取成功,处理数据
    printf("Read data: %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3]);
} else {
    // 读取失败,处理错误
    printf("Failed to read I2C device\n");
}

// 写入I2C设备
uint8_t write_data[2] = {0x55, 0xAA};
if (i2c.write(addr, reg, write_data, 2) == OK) {
    printf("Write successful\n");
} else {
    printf("Write failed\n");
}

8.2 SMBus使用示例

// 创建SMBus对象
SMBusHuawei smbus;

// 准备访问数据
smbus_access_data_t access_data;
access_data.comm_access_data.addr = 0x60;
access_data.comm_access_data.offset = 0;
access_data.comm_access_data.offset_width = 0;
access_data.comm_access_data.in_data = nullptr;
access_data.comm_access_data.len = 0;
access_data.comm_access_data.write_timeout = 1000;
access_data.opcode = OPCODE_KEY_CAPABILITY;
access_data.offset = 0;
access_data.rlen = 0;
access_data.max_recv_len = 0xFFFFFFFF;
access_data.delay_time = 10;

// 发送请求
if (smbus.send_request(access_data) == OK) {
    // 请求成功,处理接收到的数据
    printf("Received data: %s\n", access_data.out_data.c_str());
} else {
    printf("Failed to send SMBus request\n");
}

8.3 Hi182x芯片使用示例

// 创建SMBus对象并传递给芯片类
auto smbus = std::make_shared<SMBusHuawei>();
Hi182x chip(smbus);

// 设置延迟时间
chip.set_delay_time(20);

// 获取芯片能力信息
if (chip.get_capability() == OK) {
    printf("Got chip capabilities successfully\n");
    // 进一步处理芯片信息...
} else {
    printf("Failed to get chip capabilities\n");
}

9. 扩展指南

9.1 添加新的总线类型

要添加新的总线类型,如SPI或UART:

  1. 在bus/目录下创建新的子目录,如bus/spi/
  2. 实现总线接口,提供统一的读写方法
  3. 保持接口一致性,使芯片层能够无缝使用新总线

9.2 添加新的芯片支持

要添加对新芯片的支持:

  1. 在chip/目录下创建新的子目录,如chip/NewChip/
  2. 继承Chip基类,实现特定功能
  3. 根据芯片通信方式,选择合适的总线进行通信
  4. 实现线程安全的读写操作

9.3 最佳实践

  • 接口分离原则:保持总线和芯片层的清晰分离
  • 依赖注入:通过构造函数传递总线对象,降低耦合
  • 错误处理一致性:遵循统一的错误报告机制
  • 文档完善:为新添加的组件提供详细文档
  • 线程安全:实现适当的锁机制,确保并发安全

10. 总结

HAS库提供了一个灵活、可扩展的硬件访问抽象层,支持各种总线和芯片。通过分层设计和统一接口,上层应用可以轻松访问各种硬件设备,而无需关心底层通信细节。随着新总线和芯片的添加,HAS库可以不断扩展,满足不断变化的硬件需求。