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"],
}
```
编译与运行
- 将上述三个文件(
demo_hw.h、demo_hw.c、demo_test.c、Android.bp)放入AOSP源码树同一目录,如device/vendor/demo_hal/。
- 在AOSP根目录执行模块编译:
mmm device/vendor/demo_hal/,或使用make demo_test。
- 编译产物:
demo.hw.so位于/vendor/lib64/hw/,demo_test位于/system/bin/。
- 将编译产物push到设备或模拟器:
adb push demo_test /data/local/tmp/,adb push demo.hw.so /vendor/lib64/hw/(需root权限)。
- 运行测试:
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与头文件定义一致。
常见问题
- 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一致。