2. 打开相机设备

任务说明

本案例实现 Camera HAL 模块中的设备打开流程:编写 camera_module_t.open 回调,根据传入的相机 ID 创建设备实例,并正确填充 camera_device_t 接口结构体,最终通过 hw_module_methods_t 暴露给 Android HAL 框架层调用。

核心实现

// camera_hal.c
#include <hardware/hardware.h>
#include <hardware/camera_common.h>
#include <stdlib.h>
#include <string.h>

#define CAMERA_DEVICE_API_VERSION HARDWARE_DEVICE_API_VERSION(1, 0)
#define CAMERA_ID_BACK   0
#define CAMERA_ID_FRONT  1
#define MAX_CAMERAS      2

/* 前置声明 */
static int camera_device_close(struct hw_device_t* device);

/* 设备实例结构 */
typedef struct {
    camera_device_t base;
    int camera_id;
    int ref_count;
} camera_device_impl_t;

/* 打开相机设备 */
static int camera_device_open(
    const struct hw_module_t* module,
    const char* id,
    struct hw_device_t** device)
{
    if (module == NULL || id == NULL || device == NULL) {
        return -EINVAL;
    }

    /* 解析相机 ID */
    char* endptr;
    long camera_id = strtol(id, &endptr, 10);
    if (*endptr != '\0' || camera_id < 0 || camera_id >= MAX_CAMERAS) {
        return -ENODEV;
    }

    /* 分配设备实例 */
    camera_device_impl_t* dev_impl = calloc(1, sizeof(camera_device_impl_t));
    if (dev_impl == NULL) {
        return -ENOMEM;
    }

    /* 填充 hw_device_t 公共字段 */
    dev_impl->base.common.tag     = HARDWARE_DEVICE_TAG;
    dev_impl->base.common.version = CAMERA_DEVICE_API_VERSION;
    dev_impl->base.common.module  = (struct hw_module_t*)module;
    dev_impl->base.common.close   = camera_device_close;

    /* 填充 camera_device_t 特有字段 */
    dev_impl->base.ops = NULL;  /* 后续案例实现具体操作集 */

    /* 保存设备私有数据 */
    dev_impl->camera_id  = (int)camera_id;
    dev_impl->ref_count  = 1;

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

/* 关闭相机设备 */
static int camera_device_close(struct hw_device_t* device)
{
    if (device == NULL) {
        return -EINVAL;
    }

    camera_device_impl_t* dev_impl =
        (camera_device_impl_t*)((camera_device_t*)device);

    if (dev_impl->ref_count <= 0) {
        return -EBUSY;
    }

    dev_impl->ref_count--;
    if (dev_impl->ref_count == 0) {
        free(dev_impl);
    }
    return 0;
}

/* HAL 模块方法表 */
static struct hw_module_methods_t camera_module_methods = {
    .open = camera_device_open,
};

/* 模块定义 */
struct 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_HARDWARE_MODULE_ID,
        .name          = "Camera HAL Module",
        .author        = "Android HAL Course",
        .methods       = &camera_module_methods,
    },
    .get_number_of_cameras = NULL,  /* 后续案例实现 */
    .get_camera_info       = NULL,  /* 后续案例实现 */
};

关键说明

camera_device_open 是模块打开设备的核心入口。它首先校验传入参数,然后通过 strtol 将字符串形式的相机 ID(如 "0"、"1")转换为整数,并检查是否在有效范围内。设备实例使用 calloc 动态分配,确保所有字段初始为零。填充 hw_device_t 公共字段时,tag 必须为 HARDWARE_DEVICE_TAG,version 使用 HARDWARE_DEVICE_API_VERSION(1, 0) 表示支持 Camera Device API 1.0,module 指回所属模块,close 指向关闭函数。当前案例仅完成设备实例创建与接口返回,ops 操作集暂置 NULL,将在后续案例中填充具体相机操作。

编译与验证

# 编译(需 Android 源码环境或 NDK 头文件)
gcc -c camera_hal.c \
    -I /path/to/android/system/core/include \
    -I /path/to/android/hardware/libhardware/include \
    -I /path/to/android/hardware/libhardware/modules/camera

# 验证:编写测试程序调用 open
cat > test_open.c << 'EOF'
#include <hardware/camera_common.h>
#include <hardware/hardware.h>
#include <stdio.h>

int main(void) {
    hw_module_t* module = NULL;
    hw_device_t* device = NULL;

    /* 加载 HAL 模块 */
    int ret = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
                            (const hw_module_t**)&module);
    if (ret != 0) {
        printf("hw_get_module failed: %d\n", ret);
        return -1;
    }

    /* 打开相机 0 */
    ret = module->methods->open(module, "0", &device);
    if (ret != 0) {
        printf("open camera 0 failed: %d\n", ret);
        return -1;
    }
    printf("open camera 0 success, tag=0x%x\n", device->tag);

    /* 关闭设备 */
    device->close(device);
    return 0;
}
EOF

gcc -o test_open test_open.c camera_hal.c \
    -I /path/to/android/system/core/include \
    -I /path/to/android/hardware/libhardware/include \
    -L /path/to/android/out/target/product/generic/system/lib/hw \
    -lhardware

./test_open

判定标准

  • 编译无错误、无警告(-Wall 下)
  • 测试程序输出 open camera 0 success, tag=0x... ,且 tag 值为 HARDWARE_DEVICE_TAG 的宏定义值
  • 使用无效 ID(如 "2" 或 "abc")调用 open 时返回 -ENODEV
  • 传入 NULL 参数时返回 -EINVAL
  • close 后再次 close 返回 -EINVAL(设备已释放)