2. 实现Camera设备打开流程

任务说明

本案例要求实现Camera设备打开流程的核心代码:通过HAL层接口打开相机设备,处理设备不存在、权限不足、打开失败等异常情况,并在成功后正确返回设备句柄。代码基于高通Camera HAL层标准接口框架,聚焦于camera_device_open函数的完整实现。

核心实现

// camera_device_open.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <cutils/log.h>
#include <hardware/camera_common.h>
#include <hardware/camera.h>
#include <system/camera_metadata.h>

#define CAMERA_ID_MAX 8
#define CAMERA_HAL_DEVICE_NAME "camera"

// 模拟设备状态表(实际项目中由底层驱动维护)
static int g_device_open_count[CAMERA_ID_MAX] = {0};
static int g_device_error_flags[CAMERA_ID_MAX] = {0};

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

/**
 * 打开指定ID的Camera设备
 * @param id: 相机ID(0为主摄,1为前摄等)
 * @param device: 输出参数,成功时返回设备句柄
 * @return: 0成功;负值对应错误码
 */
static int camera_device_open(const struct hw_module_t* module,
                              const char* id,
                              struct hw_device_t** device)
{
    int camera_id;
    char *endptr = NULL;
    camera_device_t *camera_device = NULL;
    camera_device_ops_t *camera_ops = NULL;

    // 1. 参数校验
    if (module == NULL || id == NULL || device == NULL) {
        ALOGE("%s: invalid argument (module=%p id=%p device=%p)",
              __func__, module, id, device);
        return -EINVAL;
    }

    // 2. 解析相机ID
    errno = 0;
    camera_id = (int)strtol(id, &endptr, 10);
    if (errno != 0 || endptr == id || *endptr != '\0') {
        ALOGE("%s: invalid camera id '%s'", __func__, id);
        return -EINVAL;
    }

    // 3. 检查ID范围
    if (camera_id < 0 || camera_id >= CAMERA_ID_MAX) {
        ALOGE("%s: camera id %d out of range [0, %d)",
              __func__, camera_id, CAMERA_ID_MAX);
        return -ENODEV;
    }

    // 4. 检查设备是否已被占用
    if (g_device_open_count[camera_id] > 0) {
        ALOGE("%s: camera %d already opened (count=%d)",
              __func__, camera_id, g_device_open_count[camera_id]);
        return -EBUSY;
    }

    // 5. 检查设备错误状态(模拟硬件不可用场景)
    if (g_device_error_flags[camera_id]) {
        ALOGE("%s: camera %d hardware error flag set", __func__, camera_id);
        return -EIO;
    }

    // 6. 分配设备结构体内存
    camera_device = (camera_device_t*)calloc(1, sizeof(camera_device_t));
    if (camera_device == NULL) {
        ALOGE("%s: failed to allocate camera_device_t", __func__);
        return -ENOMEM;
    }

    // 7. 分配操作函数表内存
    camera_ops = (camera_device_ops_t*)calloc(1, sizeof(camera_device_ops_t));
    if (camera_ops == NULL) {
        ALOGE("%s: failed to allocate camera_device_ops_t", __func__);
        free(camera_device);
        return -ENOMEM;
    }

    // 8. 初始化设备结构体
    camera_device->common.tag     = HARDWARE_DEVICE_TAG;
    camera_device->common.version = CAMERA_DEVICE_API_VERSION_1_0;
    camera_device->common.module  = (struct hw_module_t*)module;
    camera_device->common.close   = camera_device_close;
    camera_device->ops            = camera_ops;

    // 9. 填充操作函数(此处仅填充close,其余按需注册)
    // 实际项目中需注册set_preview_window、set_callbacks等全部接口
    camera_ops->close = camera_device_close;

    // 10. 模拟硬件初始化成功(实际项目中调用ISP/传感器驱动)
    g_device_open_count[camera_id] = 1;

    // 11. 返回设备句柄
    *device = &camera_device->common;
    ALOGI("%s: camera %d opened successfully, handle=%p",
          __func__, camera_id, *device);

    return 0;
}

/**
 * 关闭Camera设备
 */
static int camera_device_close(struct hw_device_t* device)
{
    camera_device_t *camera_device = (camera_device_t*)device;
    int camera_id = -1;

    if (device == NULL) {
        ALOGE("%s: device is NULL", __func__);
        return -EINVAL;
    }

    // 根据设备句柄反查ID(实际项目中通过内部映射表实现)
    for (int i = 0; i < CAMERA_ID_MAX; i++) {
        // 此处简化处理:通过设备指针匹配(实际项目需维护句柄-ID映射表)
        if (g_device_open_count[i] > 0) {
            camera_id = i;
            break;
        }
    }

    if (camera_id < 0) {
        ALOGE("%s: cannot find camera id for device %p", __func__, device);
        return -ENODEV;
    }

    // 释放操作函数表
    if (camera_device->ops != NULL) {
        free(camera_device->ops);
        camera_device->ops = NULL;
    }

    // 释放设备结构体
    free(camera_device);

    // 更新设备状态
    g_device_open_count[camera_id] = 0;
    ALOGI("%s: camera %d closed", __func__, camera_id);

    return 0;
}

/**
 * HAL模块入口(简化版,仅用于演示打开流程)
 */
static struct hw_module_methods_t camera_module_methods = {
    .open = camera_device_open
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag           = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id            = CAMERA_HAL_DEVICE_NAME,
    .name          = "Camera HAL Module",
    .author        = "Qualcomm",
    .methods       = &camera_module_methods,
};

测试代码

// test_camera_open.c
#include <stdio.h>
#include <stdlib.h>
#include <hardware/hardware.h>
#include <hardware/camera.h>

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

    // 1. 加载Camera HAL模块
    ret = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
                        (const hw_module_t**)&module);
    if (ret != 0) {
        printf("FAIL: hw_get_module failed, ret=%d\n", ret);
        return -1;
    }
    printf("PASS: module loaded, id=%s\n", module->id);

    // 2. 正常打开相机0
    ret = module->methods->open(module, "0", &device);
    if (ret != 0) {
        printf("FAIL: open camera 0 failed, ret=%d\n", ret);
        return -1;
    }
    printf("PASS: camera 0 opened, handle=%p\n", device);

    // 3. 重复打开同一设备(应返回-EBUSY)
    hw_device_t *dup_device = NULL;
    ret = module->methods->open(module, "0", &dup_device);
    if (ret == -EBUSY) {
        printf("PASS: duplicate open rejected with -EBUSY\n");
    } else {
        printf("FAIL: duplicate open expected -EBUSY, got %d\n", ret);
    }

    // 4. 打开不存在的设备ID(应返回-ENODEV)
    ret = module->methods->open(module, "99", &device);
    if (ret == -ENODEV) {
        printf("PASS: invalid id rejected with -ENODEV\n");
    } else {
        printf("FAIL: invalid id expected -ENODEV, got %d\n", ret);
    }

    // 5. 关闭设备
    camera_device_t *cam_dev = (camera_device_t*)device;
    ret = cam_dev->ops->close(device);
    if (ret == 0) {
        printf("PASS: camera closed\n");
    } else {
        printf("FAIL: close failed, ret=%d\n", ret);
    }

    printf("ALL TESTS DONE\n");
    return 0;
}

编译与运行

# 编译(Android源码环境或NDK交叉编译)
# 在Android源码树中执行:
mmm hardware/qcom/camera/  # 或使用mm编译当前模块

# 独立测试编译(需链接libhardware)
gcc -o test_camera_open test_camera_open.c \
    -I /path/to/android/system/core/include \
    -I /path/to/android/hardware/libhardware/include \
    -L /path/to/out/target/product/generic/system/lib \
    -lhardware -lcamera_metadata -llog

# 运行测试
./test_camera_open

验证方法与判定标准

运行测试程序后,按以下标准判定实现是否正确:

  • 正常打开:输出包含PASS: camera 0 opened,且返回的设备句柄非NULL;
  • 重复打开保护:第二次打开同一设备时返回-EBUSY,测试输出显示PASS: duplicate open rejected;
  • 非法ID处理:打开ID为99的设备返回-ENODEV,测试输出显示PASS: invalid id rejected;
  • 关闭流程:调用close后返回0,且设备状态被正确清除(可再次打开同一设备验证);
  • 日志检查:通过logcat观察HAL层日志,确认camera_device_open和camera_device_close的ALOGI/ALOGE输出与代码路径一致。

若测试程序全部输出PASS且无FAIL,则判定当前案例实现通过。注意:本实现为教学演示,实际项目中需根据具体平台驱动注册全部camera_device_ops_t接口,并维护完整的句柄-ID映射表。