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映射表。