4. Camera HAL模块初始化

Camera HAL模块的初始化是上层Framework与底层硬件驱动建立通信的第一道关卡。本章将深入剖析HAL模块的四个核心入口点:HAL_MODULE_INFO_SYM结构体定义、open()、close()以及get_number_of_cameras()的实现细节。理解这些基础构件,是后续开发稳定、高性能Camera HAL的前提。

核心要点:HAL模块的初始化并非简单的“启动”,而是一个结构化的注册与能力声明过程。Framework通过固定的符号名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,导致整个相机服务无法启动。务必在定义时显式赋值。
HAL_MODULE_INFO_SYM 结构体与Framework加载流程 cameraserver (Framework进程) dlopen() camera.xxx.so (HAL共享库) dlsym() HAL_MODULE_INFO_SYM (hw_module_t实例) 内部字段 tag = HARDWARE_MODULE_TAG id = "camera" version_major = 1 methods → open() dso = NULL

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()实现。该函数的核心职责是:

  1. 参数校验:检查module和id的有效性。
  2. 设备实例分配:为camera_device_t结构体分配内存。
  3. 操作函数表填充:将camera_device_ops_t中的函数指针指向具体的实现。
  4. 硬件初始化:打开驱动节点、配置GPIO、初始化传感器等。
  5. 返回设备句柄:通过**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()没有正确释放所有资源(如未关闭文件描述符、未释放内核缓冲区),会导致内存泄漏或驱动状态异常。在长时间运行的相机服务中,反复打开/关闭设备后,泄漏会逐渐累积,最终导致系统崩溃。
Camera HAL 设备生命周期状态机 CLOSED open() OPENED STREAMING start() stop() close() ⚠ 关键约束: • close() 只能在非STREAMING状态下调用,否则应先调用stop()。 • 多次调用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;
}
摄像头枚举与信息获取流程 1. get_number_of_cameras() 返回摄像头总数 N 2. 循环 i = 0 到 N-1 遍历每个摄像头 3. get_camera_info(i) 填充 camera_info i++ 直到 i == N 4. Framework 根据 camera_info 构建 Camera 对象列表 包含 facing、orientation、device_version 等关键信息 所有摄像头信息收集完毕

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},
};
初始化顺序总结:
  1. Framework dlopen() 加载 camera.xxx.so。
  2. Framework dlsym() 查找 HAL_MODULE_INFO_SYM 符号。
  3. Framework 调用 get_number_of_cameras() 获取摄像头数量。
  4. Framework 循环调用 get_camera_info() 获取每个摄像头的静态信息。
  5. 当需要操作相机时,Framework 调用 open() 创建设备实例。
  6. 使用完毕后,Framework 调用 close() 释放资源。

掌握这四个核心入口点的实现,就掌握了Camera HAL模块初始化的精髓。它们是整个HAL层的基石,后续所有功能(预览、拍照、录像)都建立在这个初始化框架之上。