2. 传统HAL (Legacy HAL) 架构详解:libhardware架构、模块加载机制、hw_module_t与hw_device_t结构体
在Android系统演进过程中,传统HAL(Legacy HAL)是早期(Android 4.x及之前)定义的硬件抽象层标准,其核心实现位于 libhardware 库中。理解这一架构是掌握后续HAL演进(如Camera HAL3)的基础。本章将深入剖析libhardware的模块化设计、动态加载机制以及核心数据结构。
2.1 libhardware 架构概述
libhardware 是Android系统中最底层的硬件抽象库,它定义了一套统一的接口规范,使得上层Framework(如Camera Service、Audio Service)无需关心具体硬件实现细节。其核心设计思想是:模块化与动态加载。
核心目标: 将硬件驱动细节封装在共享库(.so)中,通过标准接口进行加载与调用,实现Framework与硬件解耦。
下图展示了libhardware在Android系统架构中的位置及其与上下层的关系:
2.2 模块加载机制:hw_get_module()
传统HAL的核心加载函数是 hw_get_module(),它负责根据模块ID和厂商实现,动态加载对应的共享库(.so)。其工作流程如下:
- 确定模块ID: 上层服务传入预定义的模块ID(如
CAMERA_HARDWARE_MODULE_ID)。 - 搜索路径: 系统按照优先级顺序搜索以下路径:
/vendor/lib/hw/(厂商定制路径,优先)/system/lib/hw/(系统默认路径)
- 文件名匹配: 搜索形如
<MODULE_ID>.<variant>.so的文件,其中variant由硬件属性(如ro.product.board)决定。 - 动态加载: 使用
dlopen()加载共享库,并通过dlsym()获取HAL_MODULE_INFO_SYM符号(即hw_module_t结构体)。 - 返回模块: 将加载的
hw_module_t指针返回给调用者。
关键点: 文件名中的
variant 机制允许同一模块ID对应不同硬件平台的实现,例如 camera.msm8998.so 和 camera.exynos8890.so 可以共存。
以下是 hw_get_module() 的核心逻辑伪代码:
// 位于 hardware/libhardware/hardware.c
int hw_get_module(const char *id, const hw_module_t **module) {
// 1. 获取硬件 variant(如 board 名)
char variant[PROPERTY_VALUE_MAX];
property_get("ro.product.board", variant, "");
// 2. 构造搜索路径列表
const char *paths[] = {
"/vendor/lib/hw",
"/system/lib/hw"
};
// 3. 遍历路径,尝试加载
for (int i = 0; i < 2; i++) {
char filename[PATH_MAX];
// 构造文件名:<id>.<variant>.so
snprintf(filename, sizeof(filename), "%s/%s.%s.so",
paths[i], id, variant);
// 4. 使用 dlopen 加载
void *handle = dlopen(filename, RTLD_NOW);
if (handle) {
// 5. 获取 HAL_MODULE_INFO_SYM 符号
hw_module_t *hmi = (hw_module_t *)dlsym(handle, HAL_MODULE_INFO_SYM_AS_STR);
if (hmi) {
*module = hmi;
return 0;
}
}
}
return -ENOENT; // 未找到模块
}
2.3 核心数据结构:hw_module_t 与 hw_device_t
传统HAL定义了两个核心结构体,它们是所有硬件模块交互的基础:
2.3.1 hw_module_t:模块描述符
hw_module_t 描述一个硬件模块的元信息,每个共享库必须导出一个该类型的实例,并命名为 HAL_MODULE_INFO_SYM(通常为 HMI)。
// 位于 hardware/libhardware/include/hardware/hardware.h
typedef struct hw_module_t {
/** 标签,必须设置为 HARDWARE_MODULE_TAG */
uint32_t tag;
/** 模块主版本号 */
uint16_t version_major;
/** 模块次版本号 */
uint16_t version_minor;
/** 模块 ID,如 "camera"、"audio" */
const char *id;
/** 模块名称(可读字符串) */
const char *name;
/** 模块作者 */
const char *author;
/** 模块自定义方法,用于打开设备 */
struct hw_module_methods_t *methods;
/** 保留字段,用于扩展 */
void *dso;
/** 保留字段,用于未来使用 */
uint32_t reserved[32-7];
} hw_module_t;
| 字段 | 说明 | 典型值/作用 |
|---|---|---|
tag |
魔数标记 | 必须为 HARDWARE_MODULE_TAG,用于校验结构体有效性 |
version_major/minor |
版本号 | 标识模块接口版本,如 Camera HAL 1.0 对应 major=1, minor=0 |
id |
模块标识 | 与 hw_get_module() 的 id 参数匹配 |
methods |
方法表指针 | 指向 hw_module_methods_t,其中包含 open() 函数 |
2.3.2 hw_device_t:设备实例描述符
hw_device_t 代表一个已打开的硬件设备实例。通过 hw_module_methods_t::open() 创建,上层服务通过该结构体与硬件交互。
// 位于 hardware/libhardware/include/hardware/hardware.h
typedef struct hw_device_t {
/** 标签,必须设置为 HARDWARE_DEVICE_TAG */
uint32_t tag;
/** 设备版本号 */
uint32_t version;
/** 指向所属模块的指针 */
struct hw_module_t *module;
/** 保留字段,用于扩展 */
uint32_t reserved[12];
/** 关闭设备的方法 */
int (*close)(struct hw_device_t *device);
} hw_device_t;
注意:
hw_device_t 本身是一个“基类”结构体。具体的硬件类型(如 Camera、Audio)会在此基础上扩展,添加自己的操作函数指针。例如,Camera HAL1 的 camera_device_t 在 hw_device_t 之后追加了 ops 字段。
2.4 模块与设备的关系:open/close 生命周期
下图展示了从模块加载到设备打开、使用的完整生命周期:
2.5 传统HAL的局限性
尽管libhardware架构设计简洁,但随着硬件复杂度提升,其局限性逐渐暴露:
| 局限性 | 具体表现 | 影响 |
|---|---|---|
| 接口僵化 | hw_device_t 的扩展依赖厂商自定义,缺乏标准化 |
不同厂商的同一类型设备(如Camera)接口不统一,Framework适配困难 |
| 同步阻塞模型 | 所有操作默认同步,不支持异步回调或Buffer管理 | 高吞吐场景(如Camera预览/录像)性能瓶颈明显 |
| 无生命周期管理 | 设备打开/关闭完全由上层控制,缺乏引用计数或状态机 | 多客户端并发访问时容易产生资源冲突 |
| 版本演进困难 | 修改 hw_module_t 或 hw_device_t 需要所有厂商同步更新 |
引入新特性(如Camera HAL3)必须设计全新的接口体系 |
总结: 传统HAL(Legacy HAL)通过
libhardware 实现了基础的硬件抽象,其模块加载机制和 hw_module_t/hw_device_t 结构体奠定了Android HAL的基石。然而,其同步、静态的设计已无法满足现代多媒体硬件的需求,这也直接催生了后续更先进的HAL接口定义(如Camera HAL3、Audio HAL 4.0等)。