2. HAL模块的加载与调用

任务目标

本案例实现一个自定义HAL模块(vendor demo),并编写用户态测试程序,通过hw_get_module加载该模块,调用其open函数获取设备句柄,最终完成一次简单的读写操作验证。

模块接口定义

首先定义HAL模块的公共接口头文件,该头文件同时被HAL实现和测试程序引用:

```c // demo_hw.h #ifndef ANDROID_DEMO_HW_H #define ANDROID_DEMO_HW_H #include #include #include __BEGIN_DECLS #define DEMO_HW_MODULE_ID "demo" #define DEMO_HW_DEVICE_ID "demo_device" struct demo_device_t { struct hw_device_t common; int (*set_value)(struct demo_device_t *dev, int value); int (*get_value)(struct demo_device_t *dev, int *value); }; __END_DECLS #endif // ANDROID_DEMO_HW_H ```

HAL模块实现

创建HAL模块的共享库源码,实现模块定义、open函数及设备操作:

```c // demo_hw.c #define LOG_TAG "demo_hw" #include #include #include #include #include "demo_hw.h" static int demo_device_close(struct hw_device_t *device) { if (device) { free(device); } return 0; } static int demo_set_value(struct demo_device_t *dev, int value) { if (!dev) return -EINVAL; // 实际工程中此处可写入寄存器或共享内存 ALOGI("demo_set_value: %d", value); return 0; } static int demo_get_value(struct demo_device_t *dev, int *value) { if (!dev || !value) return -EINVAL; *value = 42; // 模拟读取到的硬件值 ALOGI("demo_get_value: %d", *value); return 0; } static int demo_device_open(const struct hw_module_t *module, const char *name, struct hw_device_t **device) { if (strcmp(name, DEMO_HW_DEVICE_ID) != 0) { return -EINVAL; } struct demo_device_t *dev = calloc(1, sizeof(struct demo_device_t)); if (!dev) { ALOGE("Failed to alloc demo_device_t"); return -ENOMEM; } dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = HARDWARE_DEVICE_API_VERSION(1, 0); dev->common.module = (struct hw_module_t *)module; dev->common.close = demo_device_close; dev->set_value = demo_set_value; dev->get_value = demo_get_value; *device = &dev->common; ALOGI("demo_device_open: success"); return 0; } static struct hw_module_methods_t demo_module_methods = { .open = demo_device_open, }; struct hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 1, .version_minor = 0, .id = DEMO_HW_MODULE_ID, .name = "Demo HAL Module", .author = "Android HAL Course", .methods = &demo_module_methods, }; ```

测试程序

编写用户态测试程序,通过hw_get_module加载模块并调用open函数:

```c // demo_test.c #include #include #include "demo_hw.h" int main(void) { const struct hw_module_t *module = NULL; struct demo_device_t *dev = NULL; int ret, value; // 1. 加载HAL模块 ret = hw_get_module(DEMO_HW_MODULE_ID, &module); if (ret != 0) { fprintf(stderr, "hw_get_module failed: %d\n", ret); return -1; } printf("Module loaded: %s (id=%s)\n", module->name, module->id); // 2. 调用模块的open函数获取设备句柄 ret = module->methods->open(module, DEMO_HW_DEVICE_ID, (struct hw_device_t **)&dev); if (ret != 0) { fprintf(stderr, "device open failed: %d\n", ret); return -1; } printf("Device opened, tag=0x%x\n", dev->common.tag); // 3. 调用设备操作函数 ret = dev->set_value(dev, 100); if (ret != 0) { fprintf(stderr, "set_value failed: %d\n", ret); } ret = dev->get_value(dev, &value); if (ret != 0) { fprintf(stderr, "get_value failed: %d\n", ret); } else { printf("get_value returned: %d\n", value); } // 4. 关闭设备 dev->common.close(&dev->common); printf("Test completed\n"); return 0; } ```

Android.bp构建配置

在AOSP源码树中创建Android.bp文件,分别构建HAL模块和测试程序:

```bp // Android.bp cc_library_shared { name: "demo.hw", vendor: true, relative_install_path: "hw", srcs: ["demo_hw.c"], shared_libs: ["liblog"], header_libs: ["libhardware_headers"], } cc_binary { name: "demo_test", srcs: ["demo_test.c"], shared_libs: ["libhardware"], header_libs: ["libhardware_headers"], } ```

编译与运行

  1. 将上述三个文件(demo_hw.h、demo_hw.c、demo_test.c、Android.bp)放入AOSP源码树同一目录,如device/vendor/demo_hal/。
  2. 在AOSP根目录执行模块编译:mmm device/vendor/demo_hal/,或使用make demo_test。
  3. 编译产物:demo.hw.so位于/vendor/lib64/hw/,demo_test位于/system/bin/。
  4. 将编译产物push到设备或模拟器:adb push demo_test /data/local/tmp/,adb push demo.hw.so /vendor/lib64/hw/(需root权限)。
  5. 运行测试:adb shell /data/local/tmp/demo_test。

验证方法

测试程序正常执行时,预期输出如下(与代码逻辑严格对应):


Module loaded: Demo HAL Module (id=demo)
Device opened, tag=0x100
demo_set_value: 100
demo_get_value: 42
get_value returned: 42
Test completed

通过条件:程序退出码为0,且日志中依次出现demo_device_open: success、demo_set_value: 100、demo_get_value: 42。若hw_get_module返回非零值,请检查demo.hw.so是否已安装到/vendor/lib64/hw/目录,且模块ID与头文件定义一致。

HAL模块加载与调用流程 测试程序 demo_test hw_get_module() libhardware demo.hw.so HAL_MODULE_INFO_SYM ① ② 返回 hw_module_t* methods->open() ③ 调用模块open demo_device_open() 分配并初始化设备 返回 demo_device_t* set_value() / get_value() ④ 设备操作 demo_set_value() demo_get_value() common.close() ⑤ 释放资源 测试程序调用 libhardware加载 HAL模块响应 资源释放

常见问题

  • hw_get_module返回-ENOENT:模块未安装到正确路径,确认demo.hw.so位于/vendor/lib64/hw/且权限为644。
  • 编译报错找不到hardware.h:确认header_libs包含libhardware_headers,且源码位于AOSP树内。
  • open返回-ENODEV:检查传入的device name是否与DEMO_HW_DEVICE_ID一致。