RK Camera HAL核心模块解析:入口函数与设备管理

大家好,我是你们的嵌入式Camera架构讲师。今天我们来聊聊RK Camera HAL里最核心的几个模块——说白了,就是整个HAL层怎么启动、怎么创建Camera设备、又是怎么管理起来的。

我个人习惯把Camera HAL比作一个酒店的前台。客人(Framework)来了,先找前台(HAL_MODULE_INFO_SYM),前台安排房间(CameraDevice),然后客人才能入住使用。嗯,这个比喻虽然简单,但核心逻辑就是这么回事。

一、HAL_MODULE_INFO_SYM:一切从这里开始

在Android Camera HAL架构里,HAL_MODULE_INFO_SYM是Framework找到HAL模块的入口点。它本质上是一个结构体变量,名字是固定的,不能改。

我记得第一次看RK的HAL代码时,找了半天入口在哪。后来发现,其实就是这个结构体:

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               = "Rockchip Camera HAL",
        .author             = "Rockchip Corporation",
        .methods            = &camera_module_methods,
        .dso                = NULL,
        .reserved           = {0},
    },
    .get_number_of_cameras      = rk_get_number_of_cameras,
    .get_camera_info            = rk_get_camera_info,
    .set_callbacks              = rk_set_callbacks,
    .get_vendor_tag_ops         = rk_get_vendor_tag_ops,
    .open_legacy                = NULL,
    .set_torch_mode             = rk_set_torch_mode,
    .init                       = rk_init,
    .reserved                   = {0},
};

这里有个坑,我曾经踩过——HAL_MODULE_INFO_SYM这个变量名必须一字不差,否则Framework根本加载不到你的HAL。你想想看,编译通过了,但运行时log里死活找不到camera模块,多尴尬。

注意:HAL_MODULE_INFO_SYM 是强制命名规则,不能修改。如果同时存在多个camera HAL实现,需要通过id字段区分。

二、open/initialize流程:从模块到设备

Framework调用hw_get_module()拿到模块后,下一步就是调用open()方法。这个open()可不是Linux文件系统的open,它是HAL层创建Camera设备实例的入口。

我们来看看RK的实现逻辑:

static int camera_device_open(const struct hw_module_t* module,
                              const char* id,
                              struct hw_device_t** device)
{
    int camera_id = atoi(id);
    RKCameraDevice* camera_dev = NULL;

    // 1. 参数检查
    if (camera_id < 0 || camera_id >= g_camera_info_count) {
        ALOGE("Invalid camera id: %d", camera_id);
        return -EINVAL;
    }

    // 2. 分配设备结构体
    camera_dev = (RKCameraDevice*)malloc(sizeof(RKCameraDevice));
    if (!camera_dev) {
        ALOGE("Failed to allocate camera device");
        return -ENOMEM;
    }
    memset(camera_dev, 0, sizeof(*camera_dev));

    // 3. 填充hw_device_t公共部分
    camera_dev->device.common.tag     = HARDWARE_DEVICE_TAG;
    camera_dev->device.common.version = CAMERA_DEVICE_API_VERSION_2_4;
    camera_dev->device.common.module = (hw_module_t*)module;
    camera_dev->device.common.close  = camera_device_close;

    // 4. 填充camera_device_ops操作函数
    camera_dev->device.ops = &camera_device_ops;

    // 5. 初始化内部状态
    camera_dev->camera_id = camera_id;
    camera_dev->ops = &g_rk_camera_ops;

    *device = &camera_dev->device.common;
    return 0;
}

这里有个细节——open()只是创建了设备句柄,真正的初始化工作是在initialize()里完成的。为什么这么设计?我个人理解是:open负责"造房子",initialize负责"装修"。Framework拿到设备后,会调用initialize()传入Camera3Device的回调接口。

核心要点:open() 创建设备实例,initialize() 建立与Framework的通信通道。两者缺一不可。

三、CameraModule与CameraDevice的创建与管理

在RK的实现中,CameraModule是一个全局单例,负责管理所有Camera设备的信息。而CameraDevice是每个Camera实例的抽象。

我画了一张图,帮你理清它们的关系:

RK Camera HAL 核心模块关系图 Android Framework hw_get_module() HAL_MODULE_INFO_SYM camera_module 结构体(入口点) open() CameraModule 管理Camera设备列表 open() CameraDevice 单个Camera实例 管理 initialize() 底层驱动层(V4L2 / ISP / Sensor) 硬件抽象接口 注:CameraModule为全局单例,CameraDevice为每个Camera独立创建

从图中你可以看到,CameraModule和CameraDevice是1对N的关系。一个CameraModule管理多个CameraDevice,每个CameraDevice对应一个物理或虚拟Camera。

在RK的实现中,CameraModule的创建是在模块加载时完成的:

static int rk_init(const struct camera_module *module)
{
    // 初始化Camera列表
    g_camera_info_count = 0;
    memset(g_camera_info, 0, sizeof(g_camera_info));

    // 扫描系统中可用的Camera设备
    // 这里会读取设备树或配置文件
    int ret = rk_camera_hal_init();
    if (ret != 0) {
        ALOGE("rk_camera_hal_init failed: %d", ret);
        return ret;
    }

    // 注册Camera信息
    for (int i = 0; i < g_camera_info_count; i++) {
        ALOGI("Camera[%d]: id=%d, facing=%s, orientation=%d",
              i, g_camera_info[i].device_id,
              g_camera_info[i].facing == CAMERA_FACING_BACK ? "BACK" : "FRONT",
              g_camera_info[i].orientation);
    }

    return 0;
}
经验之谈:我曾经在一个项目里,因为设备树配置错误,导致CameraModule初始化时只扫描到1个Camera,但实际硬件有3个。排查了半天,最后发现是I2C地址冲突。所以,初始化阶段的log一定要仔细看。

四、设备管理的关键数据结构

RK Camera HAL里,设备管理主要依赖两个核心结构体:

结构体 作用 关键字段
camera_module HAL模块入口,提供查询Camera数量、获取Camera信息等接口 get_number_of_cameras, get_camera_info, init
camera3_device_t 单个Camera设备实例,包含所有操作函数指针 ops, priv, common
RKCameraDevice RK自定义的设备结构体,封装了底层驱动接口 camera_id, ops, device

嗯,这里要特别说一下RKCameraDevice。它是RK在标准camera3_device_t基础上做的扩展,里面包含了V4L2设备节点、ISP处理句柄、Sensor配置等底层信息。说白了,它就是连接HAL层和驱动层的桥梁。

五、避坑指南

最后,分享几个我在项目中踩过的坑:

  • 我曾经在open()里做了太多初始化工作,导致Framework超时。记住,open()要轻量,重活留给initialize()。
  • 我曾经忘记在close()里释放RKCameraDevice的内存,造成内存泄漏。每次创建都要对应释放,这个习惯要养成。
  • 我曾经在get_camera_info()里返回了错误的Camera朝向信息,导致预览画面是倒的。检查Sensor安装方向时,一定要和硬件工程师确认。
总结一下:HAL_MODULE_INFO_SYM是入口,open/initialize是流程,CameraModule和CameraDevice是管理核心。理解了这几个模块,你就掌握了RK Camera HAL的骨架。

好了,这一章的内容就到这里。记住,代码是死的,思路是活的。多读代码,多动手调试,你也能成为Camera HAL的高手。

可可精品资料库