3. HAL stub与真实实现分离
任务说明
本案例要求设计一个HAL stub接口层,并分别提供stub实现与真实设备驱动实现,通过条件编译在两者之间切换。核心目标是验证HAL架构中“接口与实现分离”的设计模式,以及编译期切换机制的正确性。
环境与依赖
- Android NDK(提供
hardware.h头文件)或Linux系统(使用模拟的HAL头文件) - 交叉编译工具链(如aarch64-linux-android-clang)或本机gcc
- 本案例为演示HAL stub分离机制,使用标准C语言实现,不依赖具体硬件
实现方案
设计一个虚拟的“温度传感器”HAL模块。接口层定义通用的temperature_device_t结构体与操作函数指针;stub实现返回模拟温度值(25.5°C),真实实现通过读取系统文件(/sys/class/thermal/thermal_zone0/temp)获取真实温度。通过USE_STUB宏在编译期选择实现。
完整代码
// temperature_hal.h - HAL接口定义
#ifndef TEMPERATURE_HAL_H
#define TEMPERATURE_HAL_H
#include <stdint.h>
#define TEMPERATURE_MODULE_ID "temperature"
typedef struct temperature_device_t {
int (*get_temperature)(struct temperature_device_t *dev, float *value);
int (*close)(struct temperature_device_t *dev);
} temperature_device_t;
typedef struct hw_module_t {
uint32_t tag;
const char *id;
int (*open)(const struct hw_module_t *module, const char *name,
struct temperature_device_t **device);
} hw_module_t;
// 模块入口点(由实现方提供)
extern const hw_module_t HAL_MODULE_INFO_SYM;
#endif
// temperature_stub.c - Stub实现(模拟数据)
#include "temperature_hal.h"
#include <stdio.h>
static int stub_get_temperature(temperature_device_t *dev, float *value) {
*value = 25.5f; // 固定模拟值
return 0;
}
static int stub_close(temperature_device_t *dev) {
(void)dev;
return 0;
}
static int stub_open(const hw_module_t *module, const char *name,
temperature_device_t **device) {
(void)module; (void)name;
static temperature_device_t dev = {
.get_temperature = stub_get_temperature,
.close = stub_close
};
*device = &dev;
return 0;
}
const hw_module_t HAL_MODULE_INFO_SYM = {
.tag = 0x534f4d45, // 'SOME'
.id = TEMPERATURE_MODULE_ID,
.open = stub_open
};
// temperature_real.c - 真实设备实现(读取sysfs)
#include "temperature_hal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SYSFS_PATH "/sys/class/thermal/thermal_zone0/temp"
static int real_get_temperature(temperature_device_t *dev, float *value) {
FILE *fp = fopen(SYSFS_PATH, "r");
if (!fp) return -1;
int millidegree = 0;
if (fscanf(fp, "%d", &millidegree) != 1) {
fclose(fp);
return -1;
}
fclose(fp);
*value = millidegree / 1000.0f;
return 0;
}
static int real_close(temperature_device_t *dev) {
(void)dev;
return 0;
}
static int real_open(const hw_module_t *module, const char *name,
temperature_device_t **device) {
(void)module; (void)name;
static temperature_device_t dev = {
.get_temperature = real_get_temperature,
.close = real_close
};
*device = &dev;
return 0;
}
const hw_module_t HAL_MODULE_INFO_SYM = {
.tag = 0x534f4d45,
.id = TEMPERATURE_MODULE_ID,
.open = real_open
};
// main.c - 调用入口与验证
#include "temperature_hal.h"
#include <stdio.h>
int main(void) {
temperature_device_t *dev = NULL;
int ret = HAL_MODULE_INFO_SYM.open(&HAL_MODULE_INFO_SYM, "default", &dev);
if (ret != 0 || !dev) {
printf("FAIL: open device failed\n");
return 1;
}
float temp = 0.0f;
ret = dev->get_temperature(dev, &temp);
if (ret != 0) {
printf("FAIL: read temperature failed\n");
return 1;
}
printf("Temperature: %.2f°C\n", temp);
dev->close(dev);
return 0;
}
编译与切换
# 编译stub版本
gcc -DUSE_STUB -c temperature_stub.c -o temp_stub.o
gcc -DUSE_STUB -c main.c -o main_stub.o
gcc temp_stub.o main_stub.o -o test_stub
# 编译真实版本(需Linux系统且存在thermal_zone0)
gcc -c temperature_real.c -o temp_real.o
gcc -c main.c -o main_real.o
gcc temp_real.o main_real.o -o test_real
# 运行
./test_stub # 预期输出: Temperature: 25.50°C
./test_real # 预期输出: 实际系统温度(如 45.00°C)
验证方法
- stub版本验证:运行
./test_stub,输出必须为Temperature: 25.50°C,证明stub路径生效。 - 真实版本验证:在具有thermal_zone0的Linux设备上运行
./test_real,输出应与cat /sys/class/thermal/thermal_zone0/temp除以1000的结果一致。 - 切换正确性:两个可执行文件分别链接不同实现,但调用方代码(main.c)完全一致,证明接口层隔离有效。
关键说明
本案例使用条件编译(-DUSE_STUB)控制编译单元选择,而非在代码内使用#ifdef包裹实现。实际Android HAL中,通常通过hw_get_module()按模块ID动态加载,本案例简化为直接链接以聚焦stub分离的核心机制。真实设备路径/sys/class/thermal/thermal_zone0/temp为Linux标准接口,若系统路径不同,需相应调整SYSFS_PATH宏。