4. Camera HAL模块初始化
Camera HAL模块的初始化是上层Framework与底层硬件驱动建立通信的第一道关卡。本章将深入剖析HAL模块的四个核心入口点:HAL_MODULE_INFO_SYM结构体定义、open()、close()以及get_number_of_cameras()的实现细节。理解这些基础构件,是后续开发稳定、高性能Camera HAL的前提。
HAL_MODULE_INFO_SYM找到你的模块,然后通过open()创建相机设备实例。
4.1 HAL_MODULE_INFO_SYM:模块的身份证
在Android HAL架构中,HAL_MODULE_INFO_SYM是一个强制性的全局符号。Framework的硬件服务(如cameraserver)通过dlopen()加载HAL共享库后,会立即查找这个符号,以获取模块的元数据和操作函数表。
该结构体是hw_module_t类型的实例,其定义位于hardware/libhardware/include/hardware/hardware.h。开发者必须使用HAL_MODULE_INFO_SYM宏来声明,以确保符号名称与Framework的查找逻辑一致。
// 标准定义方式:使用HAL_MODULE_INFO_SYM宏
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "MyCamera HAL Module",
.author = "Your Company",
.methods = &hal_module_methods,
.dso = NULL,
.reserved = {0},
};
- tag:必须为
HARDWARE_MODULE_TAG,用于校验结构体有效性。 - id:模块标识符,Camera HAL必须设置为
CAMERA_HARDWARE_MODULE_ID(即"camera")。 - methods:指向
hw_module_methods_t结构体的指针,其中包含open()函数。 - dso:动态共享对象句柄,通常由加载器填充,开发者初始化为NULL。
tag字段未正确初始化,Framework在加载模块时会直接返回-ENOSYS,导致整个相机服务无法启动。务必在定义时显式赋值。
4.2 open()函数:设备实例的工厂
open()函数是hw_module_methods_t结构体中唯一的回调,其原型为:
typedef int (*open)(const struct hw_module_t* module,
const char* id,
struct hw_device_t** device);
当Framework调用camera_device_open()时,最终会路由到HAL层的open()实现。该函数的核心职责是:
- 参数校验:检查
module和id的有效性。 - 设备实例分配:为
camera_device_t结构体分配内存。 - 操作函数表填充:将
camera_device_ops_t中的函数指针指向具体的实现。 - 硬件初始化:打开驱动节点、配置GPIO、初始化传感器等。
- 返回设备句柄:通过
**device参数返回。
static int hal_open(const struct hw_module_t* module,
const char* id,
struct hw_device_t** device) {
if (id == NULL || device == NULL) {
ALOGE("Invalid parameters: id=%p, device=%p", id, device);
return -EINVAL;
}
// 解析相机ID,例如 "0" 或 "1"
int camera_id = atoi(id);
if (camera_id < 0 || camera_id >= MAX_CAMERAS) {
ALOGE("Invalid camera id: %d", camera_id);
return -ENODEV;
}
// 分配并初始化 camera_device_t
camera_device_t* cam_dev = (camera_device_t*)malloc(sizeof(camera_device_t));
if (cam_dev == NULL) {
ALOGE("Failed to allocate camera device");
return -ENOMEM;
}
memset(cam_dev, 0, sizeof(camera_device_t));
// 填充通用设备头
cam_dev->common.tag = HARDWARE_DEVICE_TAG;
cam_dev->common.version = CAMERA_DEVICE_API_VERSION_1_0;
cam_dev->common.module = (hw_module_t*)module;
cam_dev->common.close = hal_close;
// 填充相机操作函数表
cam_dev->ops = &camera_device_ops;
// 执行硬件初始化(伪代码)
if (init_hardware(camera_id) != 0) {
ALOGE("Hardware init failed for camera %d", camera_id);
free(cam_dev);
return -EIO;
}
*device = &cam_dev->common;
ALOGI("Camera %d opened successfully", camera_id);
return 0;
}
open()函数实际上是一个工厂方法。它根据传入的id(如"0"、"1")创建并返回不同的相机设备实例。在多摄像头系统中,每个摄像头对应一个独立的camera_device_t实例。
4.3 close()函数:资源的清洁工
close()函数负责释放open()分配的所有资源。它被注册在hw_device_t.common.close字段中,当Framework不再使用相机设备时调用。
static int hal_close(struct hw_device_t* device) {
if (device == NULL) {
ALOGE("Close called with NULL device");
return -EINVAL;
}
camera_device_t* cam_dev = (camera_device_t*)device;
// 1. 停止所有正在进行的流
stop_streaming(cam_dev);
// 2. 释放硬件资源(关闭驱动节点、释放DMA缓冲区等)
deinit_hardware(cam_dev);
// 3. 释放内部数据结构
free(cam_dev->settings);
free(cam_dev->frame_queue);
// 4. 释放设备本身
free(cam_dev);
ALOGI("Camera device closed");
return 0;
}
close()没有正确释放所有资源(如未关闭文件描述符、未释放内核缓冲区),会导致内存泄漏或驱动状态异常。在长时间运行的相机服务中,反复打开/关闭设备后,泄漏会逐渐累积,最终导致系统崩溃。
4.4 get_number_of_cameras():能力宣告
这个函数是Framework了解设备上物理摄像头数量的唯一途径。它通常在HAL模块加载后立即被调用,用于决定后续的枚举和打开操作。
// 标准实现:返回静态定义的摄像头数量
static int get_number_of_cameras() {
// 方式1:编译时常量
// return 2; // 前置+后置
// 方式2:运行时检测(推荐)
int count = 0;
// 遍历 /dev/video* 或读取设备树
for (int i = 0; i < MAX_CAMERAS; i++) {
if (check_camera_present(i)) {
count++;
}
}
return count;
}
| 策略 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 编译时常量 | 简单、零运行时开销 | 硬件变更时需要重新编译 | 固定硬件平台(如手机) |
| 运行时检测 | 灵活、自适应硬件变化 | 增加启动延迟 | 模块化平台(如车载、IoT) |
| 配置文件驱动 | 无需重新编译,易于维护 | 依赖文件系统,可能被篡改 | 需要动态配置的系统 |
在实现get_number_of_cameras()时,还需要配合get_camera_info()函数,为每个摄像头提供camera_info_t结构体,包含朝向(前置/后置)、闪光灯支持等静态信息。
// 配套函数:提供每个摄像头的详细信息
static int get_camera_info(int camera_id, struct camera_info* info) {
if (camera_id < 0 || camera_id >= get_number_of_cameras()) {
return -EINVAL;
}
memset(info, 0, sizeof(*info));
switch (camera_id) {
case 0: // 后置摄像头
info->facing = CAMERA_FACING_BACK;
info->orientation = 90;
info->device_version = CAMERA_DEVICE_API_VERSION_1_0;
break;
case 1: // 前置摄像头
info->facing = CAMERA_FACING_FRONT;
info->orientation = 270;
info->device_version = CAMERA_DEVICE_API_VERSION_1_0;
break;
default:
return -EINVAL;
}
// 设置静态特征(flash、zoom等)
info->static_camera_characteristics = get_static_metadata(camera_id);
return 0;
}
4.5 模块方法表与整体注册流程
将上述所有函数组织起来,形成完整的模块方法表:
// 模块方法表:包含open回调
static struct hw_module_methods_t hal_module_methods = {
.open = hal_open,
};
// 模块操作函数表(camera_device_ops_t)
static camera_device_ops_t camera_device_ops = {
.set_preview_window = hal_set_preview_window,
.start_preview = hal_start_preview,
.stop_preview = hal_stop_preview,
.take_picture = hal_take_picture,
.cancel_picture = hal_cancel_picture,
// ... 其他操作函数
};
// HAL_MODULE_INFO_SYM 最终定义
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "Example Camera HAL",
.author = "Embedded Dev Team",
.methods = &hal_module_methods,
.dso = NULL,
.reserved = {0},
};
- Framework
dlopen()加载camera.xxx.so。 - Framework
dlsym()查找HAL_MODULE_INFO_SYM符号。 - Framework 调用
get_number_of_cameras()获取摄像头数量。 - Framework 循环调用
get_camera_info()获取每个摄像头的静态信息。 - 当需要操作相机时,Framework 调用
open()创建设备实例。 - 使用完毕后,Framework 调用
close()释放资源。
掌握这四个核心入口点的实现,就掌握了Camera HAL模块初始化的精髓。它们是整个HAL层的基石,后续所有功能(预览、拍照、录像)都建立在这个初始化框架之上。