3. 初始化流程:open()、initialize()、configure_streams() 接口调用链与状态机
Camera HAL3 的初始化是上层框架与底层硬件驱动建立通信管道、协商数据通路的关键阶段。整个流程由三个核心接口串联:open()、initialize()、configure_streams()。这三个接口不仅完成了设备句柄的获取、回调机制的注册以及数据流的配置,还驱动着 HAL 设备内部状态机的精确跳转。理解这一调用链及其状态约束,是后续处理 CaptureRequest 的基础。
3.1 初始化调用链全景
下图展示了从 CameraService 发起请求到 HAL 完成流配置的完整调用链与状态变迁路径:
3.2 接口详解与实现要点
3.2.1 open():设备句柄获取
open() 是 HAL 模块的入口函数,由 hw_module_t 的 open 方法指针定义。调用时传入设备 ID 和指向 hw_device_t 的指针,HAL 实现需要在此函数中完成硬件设备的初始化,并将 camera3_device_t 结构体填充完整。
关键实现要点:
- 分配并初始化
camera3_device_t结构体,设置ops函数指针表。 - 打开底层驱动设备节点(如
/dev/video*),进行硬件复位。 - 分配内部状态机结构体,初始状态设置为
CLOSED。 - 返回 0 表示成功,返回负值表示失败(如设备忙、权限不足)。
// HAL3 open() 典型实现骨架
static int camera_device_open(const struct hw_module_t* module,
const char* id,
struct hw_device_t** device) {
camera3_device_t* camera_dev = (camera3_device_t*)malloc(sizeof(camera3_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;
// 设置 ops 函数表
camera_dev->ops = &g_camera3_ops;
// 初始化内部状态
camera_dev->priv = malloc(sizeof(hal_private_t));
hal_private_t* priv = (hal_private_t*)camera_dev->priv;
priv->state = HAL_DEVICE_STATE_CLOSED;
*device = &camera_dev->common;
return 0;
}
3.2.2 initialize():回调注册与框架握手
initialize() 是 HAL 设备从 OPEN 状态进入 INITIALIZED 状态的关键步骤。CameraService 在此接口中传入 camera3_callback_ops_t 结构体,HAL 必须保存该回调句柄,用于后续向框架上报事件、返回结果和发送通知。
| 回调函数 | 触发时机 | HAL 实现责任 |
|---|---|---|
process_capture_result | CaptureRequest 处理完成 | 填充结果元数据、输出 buffer、部分结果 |
notify | 快门事件、错误事件 | 填充 camera3_notify_msg_t 并调用 |
signal_stream_flush | flush() 调用时 | 通知 HAL 内部线程停止等待 |
最佳实践:在 initialize() 中完成以下操作:
- 保存
camera3_callback_ops_t指针到私有数据区。 - 创建内部工作线程池(如 request 处理线程、result 返回线程)。
- 初始化同步锁和条件变量,确保线程安全。
- 将状态机从
OPEN切换为INITIALIZED。
// initialize() 实现骨架
static int camera_device_initialize(const struct camera3_device* device,
const camera3_callback_ops_t* callback_ops) {
hal_private_t* priv = (hal_private_t*)device->priv;
// 保存回调句柄
priv->callback_ops = callback_ops;
// 创建工作线程
priv->request_thread = new RequestThread(priv);
priv->result_thread = new ResultThread(priv);
// 状态切换
priv->state = HAL_DEVICE_STATE_INITIALIZED;
ALOGI("Camera HAL3 initialized successfully");
return 0;
}
3.2.3 configure_streams():数据通路协商
configure_streams() 是初始化流程中最为复杂的接口。CameraService 在此传入 camera3_stream_configuration_t 结构体,包含所有需要配置的 stream 列表。HAL 需要根据硬件能力决定是否接受该配置,并为每个 stream 分配 buffer 队列和内部资源。
注意:configure_streams() 可能被多次调用(如切换分辨率、添加新 stream)。每次调用都会重置所有 stream 的配置,HAL 必须释放旧资源并重新分配。
// configure_streams() 实现骨架
static int camera_device_configure_streams(
const struct camera3_device* device,
camera3_stream_configuration_t* stream_list) {
hal_private_t* priv = (hal_private_t*)device->priv;
// 验证状态机:必须在 INITIALIZED 或 CONFIGURED 状态
if (priv->state != HAL_DEVICE_STATE_INITIALIZED &&
priv->state != HAL_DEVICE_STATE_CONFIGURED) {
ALOGE("Invalid state for configure_streams: %d", priv->state);
return -ENODEV;
}
// 释放旧配置(如果有)
if (priv->state == HAL_DEVICE_STATE_CONFIGURED) {
free_stream_resources(priv);
}
// 遍历所有 stream,验证并配置
for (uint32_t i = 0; i < stream_list->num_streams; i++) {
camera3_stream_t* stream = stream_list->streams[i];
if (!validate_stream_format(stream)) {
return -EINVAL;
}
allocate_stream_buffers(priv, stream);
}
// 配置 ISP 和 sensor 参数
configure_hardware(priv, stream_list);
// 状态切换
priv->state = HAL_DEVICE_STATE_CONFIGURED;
ALOGI("Streams configured: %u streams", stream_list->num_streams);
return 0;
}
3.3 状态机约束与非法转换
HAL3 规范对状态机转换有严格约束,违反这些约束将导致框架报错或设备异常。下表总结了所有合法的状态转换路径:
| 当前状态 | 调用接口 | 目标状态 | 说明 |
|---|---|---|---|
| CLOSED | open() | OPEN | 设备打开,尚未初始化 |
| OPEN | initialize() | INITIALIZED | 回调注册完成,可配置流 |
| INITIALIZED | configure_streams() | CONFIGURED | 首次流配置 |
| CONFIGURED | configure_streams() | CONFIGURED | 重新配置流(切换分辨率等) |
| CONFIGURED | close() | CLOSED | 关闭设备,释放所有资源 |
| INITIALIZED | close() | CLOSED | 未配置流时直接关闭 |
| OPEN | close() | CLOSED | 打开后未初始化直接关闭 |
常见实现错误:
- 在
OPEN状态下直接调用configure_streams(),跳过initialize()。 - 在
CLOSED状态下调用initialize(),未先执行open()。 - 多次调用
initialize()导致回调句柄被覆盖,未做幂等处理。 configure_streams()失败后未回退到INITIALIZED状态。
3.4 初始化流程中的资源管理
在初始化过程中,HAL 需要管理多种资源,包括内存、文件描述符、硬件寄存器等。下表总结了各阶段需要分配和释放的资源:
| 阶段 | 分配资源 | 释放时机 |
|---|---|---|
| open() | camera3_device_t 结构体、私有数据区、驱动 fd | close() |
| initialize() | 工作线程、同步锁、条件变量、回调句柄 | close() 或重新 initialize() |
| configure_streams() | gralloc buffer 句柄、ISP 配置块、sensor 参数 | 下一次 configure_streams() 或 close() |
调试建议:在 HAL 实现中增加状态机日志输出,每次状态转换时打印当前状态和目标状态。例如:
ALOGI("HAL state transition: %s -> %s",
state_to_string(priv->state),
state_to_string(new_state));
这有助于在开发阶段快速定位状态机异常。
3.5 初始化流程的时序约束
除了状态机约束,HAL3 规范还对初始化流程的时序有明确要求:
- open() 超时:HAL 应在 500ms 内完成设备打开和初始化,超过此时间框架可能认为设备无响应。
- initialize() 超时:回调注册和线程创建应在 200ms 内完成。
- configure_streams() 超时:流配置应在 1s 内完成,复杂场景(如多 camera 同步)可适当放宽。
- 幂等性:多次调用
configure_streams()时,HAL 必须正确处理资源释放和重新分配,不能出现内存泄漏。