2. HAL模块的动态加载与注册
任务目标
在Android HAL框架中,实现一个自定义硬件模块的 hw_module_t 结构体,通过标准宏完成模块注册,并利用 hw_get_module() 在运行时动态加载该模块,最终获取模块句柄并验证加载成功。
环境与前置依赖
- Android源码环境(AOSP),已编译
libhardware库。 - 本案例代码放置于
hardware/libhardware/modules/demo/目录下。 - 模块ID定义为
HARDWARE_MODULE_ID_DEMO,值为"demo"。
核心实现
以下代码完整实现了HAL模块的定义、注册与动态加载验证。模块仅暴露一个返回版本信息的简单操作,用于确认加载链路正确。
// demo_hal.c
#include <hardware/hardware.h>
#include <stdint.h>
#define DEMO_MODULE_ID "demo"
#define DEMO_API_VERSION HARDWARE_MODULE_API_VERSION(1, 0)
// 模块自定义方法:返回模块版本字符串
static int demo_get_version(const struct hw_module_t* module, char* buf, int size) {
if (buf == NULL || size < 8) return -1;
snprintf(buf, size, "v1.0");
return 0;
}
// 模块实例结构(可扩展自定义字段)
struct demo_module_t {
struct hw_module_t common;
int (*get_version)(const struct hw_module_t*, char*, int);
};
// 打开设备时回调(本案例仅验证加载,不实现具体设备)
static int demo_device_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device) {
(void)module; (void)name; (void)device;
return -ENOSYS; // 无设备操作,仅验证模块加载
}
// 模块方法表
static struct hw_module_methods_t demo_methods = {
.open = demo_device_open,
};
// 模块实例定义
struct demo_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = DEMO_API_VERSION,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = DEMO_MODULE_ID,
.name = "Demo HAL Module",
.author = "AOSP Course",
.methods = &demo_methods,
.dso = NULL, // 由hw_get_module()加载后填充
},
.get_version = demo_get_version,
};
// 使用标准宏注册模块(展开后声明HAL_MODULE_INFO_SYM)
HAL_MODULE_INFO_SYM = HAL_MODULE_INFO_SYM; // 确保符号不被优化掉
// Android.bp
cc_library_shared {
name: "demo.default",
relative_install_path: "hw",
srcs: ["demo_hal.c"],
shared_libs: ["libhardware"],
vendor: true,
proprietary: true,
}
// 验证程序 test_demo_load.c
#include <hardware/hardware.h>
#include <stdio.h>
#define DEMO_MODULE_ID "demo"
int main() {
const struct hw_module_t* hw_module = NULL;
int ret = hw_get_module(DEMO_MODULE_ID, &hw_module);
if (ret != 0) {
fprintf(stderr, "hw_get_module failed: %d\n", ret);
return -1;
}
// 验证模块基本信息
if (hw_module == NULL || hw_module->id == NULL ||
strcmp(hw_module->id, DEMO_MODULE_ID) != 0) {
fprintf(stderr, "Invalid module loaded\n");
return -2;
}
// 调用自定义方法
struct demo_module_t* demo_mod = (struct demo_module_t*)hw_module;
char version[16] = {0};
if (demo_mod->get_version(hw_module, version, sizeof(version)) != 0) {
fprintf(stderr, "get_version failed\n");
return -3;
}
printf("Module loaded: id=%s, version=%s\n", hw_module->id, version);
return 0;
}
编译与运行
- 编译模块:在AOSP根目录执行
mmm hardware/libhardware/modules/demo/,生成demo.default.so。 - 推送模块:
adb push demo.default.so /vendor/lib64/hw/(64位系统)或/vendor/lib/hw/(32位系统)。 - 编译验证程序:将
test_demo_load.c加入Android.bp或使用NDK直接编译,推送至设备执行。
验证方法与判定标准
- 运行
test_demo_load,观察输出。 - 通过条件:程序返回0,且打印
Module loaded: id=demo, version=v1.0。 - 若返回
-1,检查模块路径是否正确、模块ID是否匹配;若返回-2,检查模块结构体初始化是否正确。
关键说明
HAL_MODULE_INFO_SYM是Android HAL框架约定的模块入口符号,hw_get_module()通过dlsym()查找该符号获取模块结构体。- 模块ID必须与
hw_get_module()传入的ID严格一致,且.so文件名需以.default结尾(如demo.default.so),系统才能正确识别。 - 本案例仅验证模块加载链路,
device_open返回-ENOSYS表示未实现具体设备操作,不影响模块加载验证。