6.2 驱动架构设计:分层思想的现实应用
一个问题:SDK的驱动层是怎么组织的?
假设你理解了SDK的整体架构,现在打开传输层的代码目录。
你发现代码分散在多个文件:
driver_i2c.c、driver_spi.chal_gpio.c、hal_i2c.cdevice_hsm.c
你可能会困惑:
- 这些文件是什么关系?
- 为什么需要这么多层级?
- 调用顺序是什么?
更深层的问题是:
SDK如何用分层设计实现硬件抽象?这与hsm-lite有什么不同?
与hsm-lite的对比
hsm-lite没有驱动层——密钥和Session都在内存中,不需要与硬件通信。
真实SDK必须解决硬件通信问题:
对比:
hsm-lite(教学简化)
├── C_GenerateKey → 直接生成随机数 → 存到内存数组
├── C_Encrypt → 直接调用xor_encrypt
└── 无硬件通信
真实SDK(生产实现)
├── hsm_generate_key → 组APDU → 封帧 → 发SPI → HSM处理 → 收响应 → 解帧 → 返回结果
└── 每一步都要处理硬件细节
真实SDK需要在应用层和硬件之间插入多层驱动,每层处理一类问题。
三层驱动架构
典型的SDK采用三层驱动架构:
三层驱动架构
┌─────────────────────────────────────────────────┐
│ 第一层:总线驱动(bus_driver) │
│ 职责:管理特定总线类型(I2C/SPI/UART)的通用操作 │
│ 存储:全局数组 g_bus_drivers[] │
│ 特点:一个总线类型对应一个驱动实例 │
└─────────────────────────────────────────────────┘
↓ 挂载设备
┌─────────────────────────────────────────────────┐
│ 第二层:设备驱动(device_driver) │
│ 职责:管理连接到总线上的具体设备 │
│ 存储:总线驱动的 devices[] 数组 │
│ 特点:同一总线上的不同设备有不同驱动实例 │
└─────────────────────────────────────────────────┘
↓ 硬件操作
┌─────────────────────────────────────────────────┐
│ 第三层:硬件抽象层(HAL) │
│ 职责:封装GPIO/I2C/SPI的具体操作 │
│ 存储:设备驱动的函数指针 │
│ 特点:平台相关代码,需要适配 │
└─────────────────────────────────────────────────┘
数据结构设计
总线驱动结构
typedef struct {
bus_type_t type; // 总线类型:SPI/I2C/UART
device_driver *devices[MAX_DEVICES]; // 挂载的设备数组
int (*init)(device_driver *); // 总线初始化
int (*open)(device_driver *, uint8_t *, size_t *); // 打开设备(含ATR)
int (*close)(device_driver *); // 关闭设备
int (*transmit)(device_driver *, uint8_t *, size_t); // 发送
int (*receive)(device_driver *, uint8_t *, size_t *); // 接收
void *extra; // 扩展数据
} bus_driver;
设备驱动结构
typedef struct {
bus_type_t type; // 设备类型(与总线类型一致)
uint32_t id; // 设备标识
int (*init)(void *); // 设备特定初始化
int (*transmit)(void *, uint8_t *, size_t); // 设备特定发送
int (*receive)(void *, uint8_t *, size_t *); // 设备特定接收
void *config; // 设备配置参数
} device_driver;
全局管理
bus_driver *g_bus_drivers[MAX_BUS_TYPES]; // 全局驱动数组
struct {
bus_driver *bus;
device_driver *device;
} g_current_driver; // 当前选择的驱动
注册/选择/连接流程
注册流程:hsm_register_driver
int hsm_register_driver(bus_type_t type, uint32_t device_id) {
bus_driver *bus = find_bus_driver(type);
device_driver *device = find_device_driver(type, device_id);
if (bus == NULL || device == NULL) {
return HSM_ERR_INVALID_PARAM;
}
return add_driver_to_global(bus, device);
}
注册后的结构:
全局驱动管理器 g_bus_drivers[4]
├── g_bus_drivers[0] → I2C总线驱动
│ ├── devices[0] → HSM设备
│ ├── devices[1] → NULL
│ └── ...
├── g_bus_drivers[1] → NULL
├── g_bus_drivers[2] → NULL
└── g_bus_drivers[3] → NULL
选择流程:hsm_select_device
int hsm_select_device(bus_type_t type, uint32_t device_id) {
bus_driver *bus = find_bus_driver(type);
if (bus == NULL) return HSM_ERR_NOT_FOUND;
device_driver *device = find_device(bus, device_id);
if (device == NULL) return HSM_ERR_NOT_FOUND;
g_current_driver.bus = bus;
g_current_driver.device = device;
return HSM_OK;
}
后续操作通过g_current_driver获取当前驱动。
连接流程:hsm_connect
int hsm_connect(uint8_t *atr, size_t *atr_len) {
bus_driver *bus = g_current_driver.bus;
device_driver *device = g_current_driver.device;
// 初始化总线
int ret = bus->init(device);
if (ret != HSM_OK) return ret;
// 打开设备,获取ATR
ret = bus->open(device, atr, atr_len);
if (ret != HSM_OK) return ret;
return HSM_OK;
}
初始化的层层调用
bus->init()会层层向下:
调用链:
hsm_connect
↓
bus->init(device) ← 总线驱动层:driver_i2c_init
↓
device->init(device) ← 设备驱动层:hal_i2c_device_init
↓
hal_gpio_init() ← 硬件抽象层:GPIO初始化
hal_i2c_config() ← 硬件抽象层:I2C配置
↓
open("/dev/i2c-1", O_RDWR) ← 打开设备文件
ioctl(fd, I2C_SLAVE, addr) ← 设置从设备地址
ioctl(fd, I2C_TIMEOUT, timeout) ← 设置超时
完整调用示例
// 步骤1:注册驱动
int ret = hsm_register_driver(BUS_I2C, DEVICE_HSM_0);
if (ret != HSM_OK) {
return ret;
}
// 步骤2:选择设备
ret = hsm_select_device(BUS_I2C, DEVICE_HSM_0);
if (ret != HSM_OK) {
return ret;
}
// 步骤3:连接设备
uint8_t atr[32];
size_t atr_len;
ret = hsm_connect(atr, &atr_len);
if (ret != HSM_OK) {
return ret;
}
设计优势总结
三层架构的分层思想与PKCS#11的设计理念一致:
| PKCS#11理念 | SDK驱动设计 | 共通原理 |
|---|---|---|
| Slot抽象硬件差异 | 总线驱动抽象总线差异 | 概念抽象 |
| Session管理状态 | 设备驱动管理设备状态 | 状态管理 |
| Object统一接口 | HAL统一硬件接口 | 接口统一 |
| Mechanism支持扩展 | 多总线类型可扩展 | 扩展性 |
理解了PKCS#11的设计哲学,再看SDK的驱动架构,会发现它们遵循相同的分层抽象原则。 下一节,我们将深入通信协议——看数据如何从代码变成波形。
【下集预告】
APDU是什么?帧格式是什么?
I2C和SPI有什么区别?
长帧传输会遇到什么问题?
下一节,通信协议实战。