Camera HAL3开发调试与图像参数适配 - 第4节
4.1 HAL 模块加载机制
Camera HAL 的初始化始于 Android 系统通过 hw_get_module() 或 HAL_MODULE_INFO_SYM 符号查找并加载 HAL 动态库。该过程由 cameraserver 进程触发,最终定位到 camera.<variant>.so 共享库。
关键入口: 每个 HAL 库必须导出
HAL_MODULE_INFO_SYM 符号,类型为 hw_module_t,其中包含模块 ID、版本号及 open() 函数指针。
4.1.1 模块加载流程
- 服务启动:
cameraserver调用CameraService::instantiate()。 - HAL 查找:
hw_get_module(CAMERA_HARDWARE_MODULE_ID, &module)根据系统属性ro.hardware.camera或ro.board.platform定位 .so 文件。 - 符号解析: 加载库后,通过
dlsym()获取HAL_MODULE_INFO_SYM结构体地址。 - 模块验证: 检查
module->tag是否为HARDWARE_MODULE_TAG,版本号是否匹配。 - 回调注册: 将
camera_module_callbacks_t注册到 HAL,用于热插拔通知。
调试技巧: 使用
adb shell lsof | grep camera 可查看已加载的 HAL 库路径;若加载失败,检查 logcat -s CameraService 中的 E 级别日志。
4.2 open() 函数调用链
模块加载成功后,CameraService 调用 module->common.open() 打开指定 camera ID 的设备。该函数在 HAL 侧对应 camera_device_open(),负责初始化硬件并返回 camera_device_t 接口。
4.2.1 调用链分解
| 调用层 | 函数/方法 | 职责 |
|---|---|---|
| Framework | CameraService::connect() | 验证客户端权限,分配 CameraClient |
| HAL 适配层 | camera_module_t::open() | 根据 camera_id 查找内部设备列表 |
| HAL 实现层 | camera_device_open() | 初始化传感器、ISP、电源管理 |
| Kernel 接口 | open("/dev/video*") | 建立 V4L2 或自定义驱动通道 |
常见失败原因: open() 返回
-ENODEV 通常表示 camera_id 越界或对应硬件未上电;返回 -EBUSY 表示设备已被占用,需检查并发访问控制。
// HAL 侧 open() 典型实现骨架
static int camera_device_open(const hw_module_t* module, const char* id,
hw_device_t** device) {
int camera_id = atoi(id);
if (camera_id < 0 || camera_id >= MAX_CAMERAS) {
ALOGE("Invalid camera id: %s", id);
return -ENODEV;
}
camera_device_t* camera_dev = (camera_device_t*)malloc(sizeof(camera_device_t));
memset(camera_dev, 0, sizeof(*camera_dev));
camera_dev->common.tag = HARDWARE_DEVICE_TAG;
camera_dev->common.version = CAMERA_DEVICE_API_VERSION_3_5;
camera_dev->common.module = (hw_module_t*)module;
camera_dev->common.close = camera_device_close;
camera_dev->ops = &camera_device_ops;
// 初始化硬件:GPIO、时钟、I2C 配置
if (init_hardware(camera_id) != 0) {
free(camera_dev);
return -EIO;
}
*device = &camera_dev->common;
return 0;
}
4.3 camera_info 初始化
在 open() 之后,CameraService 调用 camera_module_t::get_camera_info() 获取每个 camera 的静态能力信息。该信息填充到 camera_info_t 结构体中,包含设备方向、闪光灯支持、传感器朝向等。
4.3.1 camera_info_t 关键字段
| 字段 | 类型 | 说明 |
|---|---|---|
facing | int | CAMERA_FACING_BACK=0 / CAMERA_FACING_FRONT=1 |
orientation | int | 传感器安装角度,0/90/180/270 |
device_version | uint32_t | HAL 设备版本,如 CAMERA_DEVICE_API_VERSION_3_5 |
resource_cost | int | 资源开销评分,用于多摄像头策略 |
conflicting_devices | const char** | 互斥设备 ID 列表,NULL 结尾 |
4.4.2 初始化流程
- 静态配置读取: 从设备树、板级配置文件或硬编码表中读取 sensor 物理属性。
- 动态校准加载: 从
/vendor/etc/camera/或/data/misc/camera/加载镜头校准参数。 - 能力合成: 将静态配置与校准参数合并,填充
camera_info_t。 - 注册到服务:
CameraService将 info 存入全局列表,供后续getCameraInfo()查询。
调试方法: 在 HAL 的
get_camera_info() 中添加 ALOGD 打印 facing 和 orientation;使用 adb shell dumpsys media.camera 可查看服务端已注册的 camera_info 列表。
// camera_info 初始化示例
static int camera_get_info(const struct camera_module* module,
int camera_id, struct camera_info* info) {
if (camera_id == 0) {
info->facing = CAMERA_FACING_BACK;
info->orientation = 90;
info->device_version = CAMERA_DEVICE_API_VERSION_3_5;
info->resource_cost = 100;
info->conflicting_devices = (const char*[]){ "1", NULL };
} else if (camera_id == 1) {
info->facing = CAMERA_FACING_FRONT;
info->orientation = 270;
info->device_version = CAMERA_DEVICE_API_VERSION_3_5;
info->resource_cost = 50;
info->conflicting_devices = NULL;
} else {
return -EINVAL;
}
ALOGD("camera_info[%d]: facing=%d, orientation=%d", camera_id,
info->facing, info->orientation);
return 0;
}
4.4 初始化流程总结
Camera HAL 初始化可归纳为三个阶段:模块加载、设备打开、信息注册。每个阶段都有明确的错误码和调试入口,掌握这些流程是进行参数配置与调试的基础。
| 阶段 | 关键函数 | 输出产物 | 典型调试点 |
|---|---|---|---|
| 模块加载 | hw_get_module() | hw_module_t | 检查 .so 路径和符号表 |
| 设备打开 | camera_device_open() | camera_device_t | 验证 device_version 和 ops 指针 |
| 信息注册 | get_camera_info() | camera_info_t | 确认 facing/orientation 值 |
核心原则: 初始化阶段的任何失败都会导致相机服务无法正常启动。建议在 HAL 实现中为每个阶段添加详细的
ALOGE 日志,并配合 dumpsys 进行问题定位。