3. HAL模块加载机制

HAL(Hardware Abstraction Layer)模块加载机制是Camera HAL层初始化的核心入口。Android系统通过标准化的结构体定义与回调函数接口,实现HAL模块的动态发现、加载与生命周期管理。理解这一机制,是掌握Camera HAL层开发的基础。

3.1 HAL module结构体定义

每个HAL模块都必须以hw_module_t结构体作为起始成员。该结构体定义在hardware/libhardware/include/hardware/hardware.h中,是HAL模块与Android框架之间的契约。

成员 类型 说明
tag uint32_t 必须初始化为HARDWARE_MODULE_TAG,用于版本校验
module_api_version uint16_t 模块API版本号,如HAL_MODULE_API_VERSION(1, 0)
hal_api_version uint16_t HAL框架层API版本号
id const char* 模块唯一标识符,如"camera"
name const char* 模块可读名称,如"Camera HAL Module"
author const char* 模块作者或厂商信息
methods hw_module_methods_t* 指向回调函数结构体的指针,核心接口
dso void* 模块动态库句柄,由加载器填充
reserved uint32_t[32] 保留字段,用于未来扩展
关键点:Camera HAL模块的hw_module_t实例必须使用HAL_MODULE_INFO_SYM(即HMI)作为符号名导出,这是Android HAL加载器查找模块入口的约定。

3.2 hw_module_methods_t回调函数

hw_module_methods_t是HAL模块暴露给框架层的唯一回调结构体,目前只包含一个函数指针——open。该结构体定义如下:

typedef struct hw_module_methods_t {
    int (*open)(const struct hw_module_t* module, 
                const char* id,
                struct hw_device_t** device);
} hw_module_methods_t;

open回调的三个参数含义:

  • module:指向当前HAL模块实例的指针
  • id:要打开的设备标识符,如"0"表示Camera ID 0
  • device:输出参数,指向新创建的hw_device_t结构体指针

框架层通过hw_get_module()加载HAL模块后,立即调用methods->open()来获取具体设备实例。这意味着open函数是HAL模块从“静态描述”进入“运行时设备”的桥梁。

3.3 open/close接口实现

open接口的实现通常包含以下步骤:

  1. 参数校验:检查module和device指针非空,id合法
  2. 内存分配:为Camera设备结构体(如camera_device_t)分配内存
  3. 初始化:填充设备结构体的ops函数表、私有数据等
  4. 硬件准备:打开驱动节点、初始化传感器、分配内存池
  5. 返回设备:将设备结构体指针写入*device
static int camera_device_open(const hw_module_t* module, 
                              const char* id, 
                              hw_device_t** device) {
    if (module == NULL || device == NULL) {
        return -EINVAL;
    }

    camera_device_t* cam_dev = (camera_device_t*)malloc(sizeof(camera_device_t));
    if (cam_dev == NULL) {
        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 = camera_device_close;

    // 填充ops函数表
    cam_dev->ops = &camera_ops;

    // 初始化私有数据(如驱动句柄、流状态等)
    cam_dev->priv = init_camera_private_data(atoi(id));

    *device = &cam_dev->common;
    return 0;
}

close接口负责资源释放:

static int camera_device_close(hw_device_t* device) {
    camera_device_t* cam_dev = (camera_device_t*)device;
    if (cam_dev == NULL) {
        return -EINVAL;
    }

    // 释放私有数据资源
    if (cam_dev->priv) {
        deinit_camera_private_data(cam_dev->priv);
    }

    free(cam_dev);
    return 0;
}
注意:open和close必须保证线程安全。在多Camera场景下,多个客户端可能同时调用open打开不同Camera ID,需要合理使用互斥锁保护共享资源。

3.4 HAL module ID注册

HAL模块的ID注册通过hw_module_t结构体的id字段实现。Camera HAL模块的ID固定为"camera"。完整的模块定义示例如下:

static struct hw_module_methods_t camera_module_methods = {
    .open = camera_device_open
};

camera_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = CAMERA_MODULE_API_VERSION_1_0,
        .hal_api_version = HARDWARE_HAL_API_VERSION,
        .id = "camera",
        .name = "Camera HAL Module",
        .author = "Your Company",
        .methods = &camera_module_methods,
        .dso = NULL,
        .reserved = {0}
    },
    .get_number_of_cameras = get_number_of_cameras,
    .get_camera_info = get_camera_info
};

Android HAL加载器通过以下流程定位模块:

  • 根据id(如"camera")在系统库路径中搜索camera.<variant>.so
  • 使用dlopen()加载动态库
  • 通过dlsym()查找HAL_MODULE_INFO_SYM符号
  • 验证tag和版本号,返回hw_module_t指针
最佳实践:在camera_module_t中扩展的get_number_of_cameras和get_camera_info是Camera HAL特有的接口,用于向框架层报告设备数量和能力。这些接口在open之前被调用,因此实现时不应依赖设备级别的资源。
HAL模块加载与设备打开流程 框架层调用hw_get_module() 加载器搜索camera.*.so dlopen + dlsym 返回hw_module_t指针 调用methods->open() 参数校验+内存分配 填充ops + 硬件初始化 返回hw_device_t指针 说明: 整个流程从框架层发起,经过动态库加载、符号查找、模块验证,最终通过open回调创建设备实例。 close接口在设备销毁时释放资源,形成完整的生命周期管理。

3.5 模块加载机制总结

组件 职责 关键约定
hw_module_t 模块元数据描述,包含ID、版本、方法表 必须使用HAL_MODULE_INFO_SYM导出
hw_module_methods_t 提供open回调,创建设备实例 目前仅包含一个函数指针
open接口 分配并初始化设备结构体,返回hw_device_t 必须校验参数、保证线程安全
close接口 释放设备资源,清理内存 与open成对出现,防止内存泄漏
ID注册 通过id字段标识模块类型 Camera模块固定为"camera"
核心要点:HAL模块加载机制的本质是“约定优于配置”——通过固定的结构体布局、固定的符号名、固定的回调接口,让框架层无需了解具体实现细节即可加载和操作任何HAL模块。这种设计模式在Android系统中被广泛使用,是HAL层可插拔架构的基石。