硬件访问服务(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
错误处理流程:
- 函数通过返回整数值表明操作是否成功
- 对于更详细的错误信息,使用特定的错误码
- 在必要时,函数可以通过输出参数提供额外的错误细节
7. 线程安全
HAS库设计考虑了多线程环境下的并发访问:
- 芯片级锁机制:芯片基类提供了
lock()和unlock()方法,子类可以实现这些方法来保护关键操作 - 互斥锁:关键类如
SMBusHuawei和Hi182x使用std::mutex实现互斥访问 - 资源保护:共享资源如总线和设备访问通过锁机制保护,防止并发访问导致的问题
// 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:
- 在
bus/目录下创建新的子目录,如bus/spi/ - 实现总线接口,提供统一的读写方法
- 保持接口一致性,使芯片层能够无缝使用新总线
9.2 添加新的芯片支持
要添加对新芯片的支持:
- 在
chip/目录下创建新的子目录,如chip/NewChip/ - 继承
Chip基类,实现特定功能 - 根据芯片通信方式,选择合适的总线进行通信
- 实现线程安全的读写操作
9.3 最佳实践
- 接口分离原则:保持总线和芯片层的清晰分离
- 依赖注入:通过构造函数传递总线对象,降低耦合
- 错误处理一致性:遵循统一的错误报告机制
- 文档完善:为新添加的组件提供详细文档
- 线程安全:实现适当的锁机制,确保并发安全
10. 总结
HAS库提供了一个灵活、可扩展的硬件访问抽象层,支持各种总线和芯片。通过分层设计和统一接口,上层应用可以轻松访问各种硬件设备,而无需关心底层通信细节。随着新总线和芯片的添加,HAS库可以不断扩展,满足不断变化的硬件需求。