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函数分成三步走:

  1. 参数校验:检查name是否合法,比如"0"代表后置Camera,"1"代表前置Camera
  2. 设备分配:malloc一个camera_device_t,并初始化内部数据结构
  3. 函数表绑定:把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 整体流程回顾

咱们把整个初始化流程串起来看:

  1. 系统加载HAL so库,找到 HAL_MODULE_INFO_SYM
  2. 调用 hw_get_module() 获取模块句柄
  3. 调用 module->methods->open() 打开Camera设备
  4. Camera Service调用 device->ops->initialize() 完成硬件初始化
  5. 通过 get_camera_info() 上报能力
  6. 一切就绪,等待上层下发capture request

这个流程看似简单,但每个环节都有坑。我建议你在实现时,每一步都加上详细的log,包括时间戳。这样出了问题,看log就能定位到是哪个环节耗时过长或者失败了。

好了,HAL模块初始化就讲到这里。下一章咱们聊聊request处理流程,那才是真正考验性能的地方。