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宏。

temperature_hal.h 接口定义(结构体+函数指针) -DUSE_STUB temperature_stub.c 返回固定值 25.5°C 无硬件依赖 HAL_MODULE_INFO_SYM temperature_real.c 读取sysfs真实温度 /sys/class/thermal/... HAL_MODULE_INFO_SYM main.c 调用 open → get_temperature 输出温度值 USE_STUB 定义时 USE_STUB 未定义时