3. HAL模块初始化流程:HAL_MODULE_INFO_SYM、open()函数实现、设备初始化与能力上报
好,咱们今天聊聊HAL模块的初始化流程。这部分是Camera HAL架构的基石,说白了就是系统怎么找到你的HAL、怎么打开它、怎么让Camera跑起来。我当年第一次接触这个流程时,也被那一堆宏定义和回调函数搞得晕头转向。今天咱们把它拆开揉碎了讲清楚。
3.1 HAL_MODULE_INFO_SYM:一切的起点
每个HAL模块都必须导出一个名为 HAL_MODULE_INFO_SYM 的符号。这个符号是系统加载HAL时的入口点。你想想看,Android的Camera Service怎么知道你的HAL在哪儿?就是靠这个符号。
核心要点:HAL_MODULE_INFO_SYM 是一个 hw_module_t 结构体实例,必须用 __attribute__((visibility("default"))) 导出。
来看一个典型的定义:
static struct hw_module_methods_t camera_module_methods = {
.open = camera_device_open,
};
struct camera_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_2_4,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "My Camera HAL Module",
.author = "My Company",
.methods = &camera_module_methods,
.dso = NULL,
.reserved = {0},
},
.get_number_of_cameras = get_number_of_cameras,
.get_camera_info = get_camera_info,
};
这里有个坑,我踩过。 .tag 字段必须赋值为 HARDWARE_MODULE_TAG,否则系统会直接拒绝加载你的HAL。我记得有一次调试,死活加载不上,查了半天发现是tag写错了。嗯,这种低级错误,犯过一次就记住了。
3.2 open()函数实现:从模块到设备实例
系统调用 hw_module_t->methods->open() 来打开一个Camera设备。这个函数要做的事情很明确:分配一个 camera_device_t 结构体,填充它的操作函数表,然后返回给上层。
我个人习惯把open函数分成三步走:
- 参数校验:检查name是否合法,比如"0"代表后置Camera,"1"代表前置Camera
- 设备分配:malloc一个camera_device_t,并初始化内部数据结构
- 函数表绑定:把ops结构体里的函数指针指向你的实现
代码示例:
static int camera_device_open(const struct hw_module_t* module,
const char* name,
struct hw_device_t** device) {
if (module == NULL || name == NULL || device == NULL) {
ALOGE("Invalid parameters");
return -EINVAL;
}
int camera_id = atoi(name);
if (camera_id < 0 || camera_id >= MAX_CAMERAS) {
ALOGE("Invalid camera id: %d", camera_id);
return -ENODEV;
}
camera_device_t* camera_dev = (camera_device_t*)malloc(sizeof(camera_device_t));
if (camera_dev == NULL) {
ALOGE("Failed to allocate camera device");
return -ENOMEM;
}
memset(camera_dev, 0, sizeof(camera_device_t));
camera_dev->common.tag = HARDWARE_DEVICE_TAG;
camera_dev->common.version = CAMERA_DEVICE_API_VERSION_2_4;
camera_dev->common.module = (hw_module_t*)module;
camera_dev->common.close = camera_device_close;
camera_dev->ops = &camera_device_ops;
// 初始化内部状态
camera_dev->priv = new CameraDevice(camera_id);
if (camera_dev->priv == NULL) {
free(camera_dev);
return -ENOMEM;
}
*device = &camera_dev->common;
return 0;
}
注意:open函数可能会被多个线程同时调用。我曾经在项目中遇到过竞态条件,两个线程同时open同一个Camera设备,导致内部状态混乱。解决方案是加一个互斥锁保护设备分配过程。
3.3 设备初始化:让Camera真正跑起来
open函数只是创建了设备实例,真正的初始化工作是在 initialize() 回调中完成的。这个回调由Camera Service在open之后调用。
初始化流程通常包括:
- 硬件上电:通过GPIO或I2C给Sensor供电
- 驱动加载:打开V4L2设备节点,设置格式、分辨率
- ISP初始化:配置ISP的时钟、PLL、黑电平校正等
- 内存分配:申请用于preview/capture的buffer
- 创建处理线程:比如request处理线程、frame回调线程
我建议把初始化过程做成状态机,方便调试:
typedef enum {
CAMERA_STATE_UNINITIALIZED = 0,
CAMERA_STATE_HW_POWERED,
CAMERA_STATE_DRIVER_OPENED,
CAMERA_STATE_ISP_CONFIGURED,
CAMERA_STATE_BUFFERS_ALLOCATED,
CAMERA_STATE_READY,
} camera_state_t;
为什么要这么做?因为初始化过程中任何一步失败,你都能清楚地知道卡在哪儿了。我在项目中就用这个状态机快速定位过几次硬件时序问题。
3.4 能力上报:告诉系统你能做什么
初始化完成后,HAL需要上报自己的能力。这个能力信息通过 get_camera_info() 返回的 camera_info_t 结构体传递。
关键字段包括:
| 字段 | 说明 | 常见值 |
|---|---|---|
| facing | 摄像头朝向 | CAMERA_FACING_BACK / FRONT |
| orientation | 图像旋转角度 | 0, 90, 180, 270 |
| resource_cost | 资源消耗等级 | 0-100 |
| conflicting_devices | 冲突设备列表 | 如前后摄不能同时用 |
| static_camera_characteristics | 静态元数据 | 包含所有支持的能力 |
其中 static_camera_characteristics 是最复杂的部分。它是一个ACameraMetadata对象,包含了Sensor尺寸、支持的帧率、AE/AWB范围、ISP能力等几百个字段。
我的经验:能力上报不要贪多。只上报你真正支持的特性,否则上层会尝试调用你未实现的功能,导致crash。我曾经为了好看,上报了HDR+支持,结果底层根本没实现,每次拍照都崩。后来老老实实只上报基础能力。
上报能力的代码片段:
static int get_camera_info(int camera_id, struct camera_info* info) {
if (camera_id == 0) {
info->facing = CAMERA_FACING_BACK;
info->orientation = 90;
} else {
info->facing = CAMERA_FACING_FRONT;
info->orientation = 270;
}
// 构建静态元数据
CameraMetadata metadata;
metadata.update(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
{4096, 3072});
metadata.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
{0, 0, 4096, 3072});
metadata.update(ANDROID_SENSOR_INFO_SENSITIVITY_RANGE,
{100, 3200});
// ... 更多字段
info->static_camera_characteristics = metadata.release();
return 0;
}
这里要注意, static_camera_characteristics 的生命周期管理。它由HAL分配,由Camera Service释放。如果你用 allocate_camera_metadata() 分配,记得设置正确的free函数。
3.5 整体流程回顾
咱们把整个初始化流程串起来看:
- 系统加载HAL so库,找到
HAL_MODULE_INFO_SYM - 调用
hw_get_module()获取模块句柄 - 调用
module->methods->open()打开Camera设备 - Camera Service调用
device->ops->initialize()完成硬件初始化 - 通过
get_camera_info()上报能力 - 一切就绪,等待上层下发capture request
这个流程看似简单,但每个环节都有坑。我建议你在实现时,每一步都加上详细的log,包括时间戳。这样出了问题,看log就能定位到是哪个环节耗时过长或者失败了。
好了,HAL模块初始化就讲到这里。下一章咱们聊聊request处理流程,那才是真正考验性能的地方。