一、前言

本文主要研究展讯平台Camera驱动和HAL层代码架构,熟悉展讯Camera的控制流程。
平台:Sprd-展讯平台
Hal版本:【HAL3】
知识点如下:
从HAL层到deiver层
1.Camera的打开(open)、初始化(init)和供电(power on)调用流程
2.预览(preview)调用流程
3.拍照(snapshot)调用流程

Camera软件架构

camera软件架构

二、Camera的打开(open)和初始化(init)调用流程

2.1 framework层的入口

经过App->framework层->jni层->cameraservice这个过程,接着调用到:
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

status_t Camera3Device::initialize(camera_module_t *module)
{.../** Open HAL device */status_t res; String8 deviceName = String8::format("%d", mId);camera3_device_t *device;//这里调用modules->open函数打开摄像头res = module->common.methods->open(&module->common, deviceName.string(),reinterpret_cast<hw_device_t**>(&device));if (res != OK) {SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);return res; }
...
}

这里调用module->common.methods->open开始操作HAL层,我们继续往下看

2.2 HAL层

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3Factory.cpp

struct hw_module_methods_t SprdCamera3Factory::mModuleMethods = { .open = SprdCamera3Factory::camera_device_open,
};

实际上是调用的SprdCamera3Factory::camera_device_open方法。

int SprdCamera3Factory::camera_device_open(const struct hw_module_t *module,const char *id,struct hw_device_t **hw_device) {
···if (isSingleIdExposeOnMultiCameraMode(atoi(id))) {return gSprdCamera3Wrapper->cameraDeviceOpen(module, id, hw_device);} else {return gSprdCamera3Factory.cameraDeviceOpen(atoi(id), hw_device);}
···
}

这里open a camera device by its ID,通过ID来打开摄像头(后主摄:0 后副摄:2 前主摄:1 前副摄:3)

ID

接着继续调用gSprdCamera3Factory.cameraDeviceOpen();

int SprdCamera3Factory::cameraDeviceOpen(int camera_id,struct hw_device_t **hw_device) {
···SprdCamera3HWI *hw =new SprdCamera3HWI(multiCameraModeIdToPhyId(camera_id));rc = hw->openCamera(hw_device);
···return rc;
}

这里new了一个SprdCamera3HWI的实例,然后调用openCamera(hw_device)方法。

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3HWI.cpp

int SprdCamera3HWI::openCamera(struct hw_device_t **hw_device) {
···ret = openCamera();if (ret == 0) { *hw_device = &mCameraDevice.common;mCameraSessionActive++;} else *hw_device = NULL;
···return ret;
}

接着继续调用空构造方法openCamera();

int SprdCamera3HWI::openCamera() {
···//new SprdCamera3OEMIf的实例mOEMIf = new SprdCamera3OEMIf(mCameraId, mSetting);mOEMIf->camera_ioctrl(CAMERA_IOCTRL_SET_MULTI_CAMERAMODE, &mMultiCameraMode,NULL);//打开mOEMIf->openCamera()方法ret = mOEMIf->openCamera();mCameraOpened = true;
···if (mOEMIf->isIspToolMode()) {mOEMIf->ispToolModeInit();//初始化ispToolModestartispserver(mCameraId);ispvideo_RegCameraFunc(1, ispVideoStartPreview);//注册ispVideoStartPreview函数ispvideo_RegCameraFunc(2, ispVideoStopPreview);//注册ispVideoStopPreview 函数ispvideo_RegCameraFunc(3, ispVideoTakePicture);//注册 ispVideoTakePicture函数ispvideo_RegCameraFunc(4, ispVideoSetParam);//注册 ispVideoSetParam函数}
···return NO_ERROR;
}

这里new SprdCamera3OEMIf的实例,继续调用mOEMIf->openCamera()方法。

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

int SprdCamera3OEMIf::openCamera() {
···//设置宽和高mSetting->getLargestPictureSize(mCameraId, &picW, &picH);mSetting->getLargestSensorSize(mCameraId, &snsW, &snsH);if (picW * picH > snsW * snsH) {mLargestPictureWidth = picW;mLargestPictureHeight = picH;} else {mLargestPictureWidth = snsW;mLargestPictureHeight = snsH;}//设置最大尺寸mHalOem->ops->camera_set_largest_picture_size(mCameraId, mLargestPictureWidth, mLargestPictureHeight);//调用startCameraIfNecessary继续启动摄像头if (!startCameraIfNecessary()) {ret = UNKNOWN_ERROR;HAL_LOGE("start failed");goto exit;}//零延时模式线程初始化ZSLMode_monitor_thread_init((void *)this);#ifdef CONFIG_CAMERA_GYROgyro_monitor_thread_init((void *)this);
#endifproperty_get("persist.sys.camera.raw.mode", value, "jpeg");if (!strcmp(value, "raw") || !strcmp(value, "bin")) {is_raw_capture = 1;}property_get("persist.sys.isptool.mode.enable", value, "false");if (!strcmp(value, "true") || is_raw_capture) {mIsIspToolMode = 1;}
···
}

SprdCamera3OEMIf::openCamera主要做了以下事情:
1.设置图像的最大尺寸
2.调用startCameraIfNecessary继续启动摄像头
3.零延时模式线程初始化
4.根据persist.sys.camera.raw.mode和persist.sys.isptool.mode.enable设置属性

继续根据startCameraIfNecessary方法,这个方法做了很多事情,有些复杂。

bool SprdCamera3OEMIf::startCameraIfNecessary() {
···//如果camera没有初始化,进行初始化if (!isCameraInit()) {HAL_LOGI("wait for camera_init");if (CMR_CAMERA_SUCCESS !=mHalOem->ops->camera_init(mCameraId, camera_cb, this, 0,&mCameraHandle, (void *)Callback_Malloc,(void *)Callback_Free)) {setCameraState(SPRD_INIT);HAL_LOGE("CameraIfNecessary: fail to camera_init().");return false;} else {setCameraState(SPRD_IDLE);}
···//获得零延时快拍的相关参数mHalOem->ops->camera_get_zsl_capability(mCameraHandle, &is_support_zsl,&max_width, &max_height);//判断是否支持零延时if (!is_support_zsl) {mParameters.setZSLSupport("false");}// 获取抓取能力,包含3dnr能力mHalOem->ops->camera_ioctrl(mCameraHandle, CAMERA_IOCTRL_GET_GRAB_CAPABILITY, &grab_capability);/*从oem层获取传感器和镜头信息*/mHalOem->ops->camera_get_sensor_exif_info(mCameraHandle, &exif_info);mSetting->getLENSTag(&lensInfo);lensInfo.aperture = exif_info.aperture;mSetting->setLENSTag(lensInfo);/*从oem层获取传感器otp*//*开始读取refoucs模式*/if (MODE_SINGLE_CAMERA != mMultiCameraMode &&MODE_3D_CAPTURE != mMultiCameraMode &&MODE_BLUR != mMultiCameraMode && MODE_BOKEH != mMultiCameraMode) {mSprdRefocusEnabled = true;CMR_LOGI("mSprdRefocusEnabled %d", mSprdRefocusEnabled);}/*结束读取refoucs模式*//*从oem层 获取OPT信息 开始 */if ((MODE_BOKEH == mMultiCameraMode || mSprdRefocusEnabled == true) &&mCameraId == 0) {OTP_Tag otpInfo;memset(&otpInfo, 0, sizeof(OTP_Tag));mSetting->getOTPTag(&otpInfo);···struct sensor_otp_cust_info otp_info;memset(&otp_info, 0, sizeof(struct sensor_otp_cust_info));mHalOem->ops->camera_get_sensor_otp_info(mCameraHandle, &otp_info);···}/*从oem 层获取OTP信息 结束*//**添加3d校准,获取最大传感器尺寸*/mSetting->getSPRDDEFTag(&sprddefInfo);mHalOem->ops->camera_get_sensor_info_for_raw(mCameraHandle, mode_info);for (i = SENSOR_MODE_PREVIEW_ONE; i < SENSOR_MODE_MAX; i++) {HAL_LOGD("trim w=%d, h=%d", mode_info[i].trim_width,mode_info[i].trim_height);if (mode_info[i].trim_width * mode_info[i].trim_height >=sprddefInfo.sprd_3dcalibration_cap_size[0] *sprddefInfo.sprd_3dcalibration_cap_size[1]) {sprddefInfo.sprd_3dcalibration_cap_size[0] =mode_info[i].trim_width;sprddefInfo.sprd_3dcalibration_cap_size[1] =mode_info[i].trim_height;}}
···return true;
}

该函数已经在相应位置添加代码注释,我们继续关注调用流程,通过mHalOem->ops->camera_init最终会调用到SprdOEMCamera.c代码的camera_init进行初始化

vendor/sprd/modules/libcamera/oem2v1/src/SprdOEMCamera.c

cmr_int camera_init(cmr_u32 camera_id, camera_cb_of_type callback,void *client_data, cmr_uint is_autotest,cmr_handle *camera_handle, void *cb_of_malloc,void *cb_of_free) {
···//初始化OEM的log   oem_init_log_level();//调用camera_local_int继续进行初始化ret = camera_local_int(camera_id, callback, client_data, is_autotest,camera_handle, cb_of_malloc, cb_of_free);
···//其他的一些初始化camera_lls_enable(*camera_handle, 0);camera_set_lls_shot_mode(*camera_handle, 0);camera_vendor_hdr_enable(*camera_handle, 0);
···return ret;
}

vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_int(cmr_u32 camera_id, camera_cb_of_type callback,void *client_data, cmr_uint is_autotest,cmr_handle *oem_handle, void *cb_of_malloc,void *cb_of_free) {
···//内存申请struct camera_context *cxt = NULL;*oem_handle = (cmr_handle)0;cxt = (struct camera_context *)malloc(sizeof(struct camera_context));//参数赋值cmr_bzero(cxt, sizeof(*cxt));cxt->camera_id = camera_id;cxt->camera_cb = callback;cxt->client_data = client_data;cxt->hal_malloc = cb_of_malloc;cxt->hal_free = cb_of_free;cxt->hal_gpu_malloc = NULL; cxt->is_multi_mode = is_multi_camera_mode_oem;cxt->blur_facebeauty_flag = 0;//调用camera_init_internal进行下一步初始化ret = camera_init_internal((cmr_handle)cxt, is_autotest);
···return ret;
}

调用camera_init_internal进行下一步初始化

cmr_int camera_init_internal(cmr_handle oem_handle, cmr_uint is_autotest) {
···//sensor初始化ret = camera_sensor_init(oem_handle, is_autotest);if (ret) {CMR_LOGE("failed to init sensor %ld", ret);goto exit;}//grab初始化ret = camera_grab_init(oem_handle);if (ret) {CMR_LOGE("failed to init grab %ld", ret);goto sensor_deinit;}//res初始化ret = camera_res_init(oem_handle);if (ret) {CMR_LOGE("failed to init res %ld", ret);goto grab_deinit;}//isp初始化ret = camera_isp_init(oem_handle);if (ret) {CMR_LOGE("failed to init isp %ld", ret);goto res_deinit;}//初始化完成ret = camera_res_init_done(oem_handle);···return ret;
}

该函数主要做了以下事情:
1.sensor初始化
2.grab初始化
3.res初始化
4.isp初始化
我们继续关注camera_sensor_init这个函数

cmr_int camera_sensor_init(cmr_handle oem_handle, cmr_uint is_autotest) {
···ret = cmr_sensor_init(&init_param, &sensor_handle);ret = cmr_sensor_open(sensor_handle, camera_id_bits);
···
}

该函数分别调用了cmr_sensor_init初始化和cmr_sensor_open打开Camera

vendor/sprd/modules/libcamera/oem2v1/src/cmr_sensor.c

cmr_int cmr_sensor_init(struct sensor_init_param *init_param_ptr,cmr_handle *sensor_handle) {
···/*save init param*/handle->oem_handle = init_param_ptr->oem_handle;handle->sensor_bits = init_param_ptr->sensor_bits;handle->private_data = init_param_ptr->private_data;handle->is_autotest = init_param_ptr->is_autotest;/*create thread*/ret = cmr_sns_create_thread(handle);
···return ret;
}

该函数 对一些参数进行赋值,然后调用cmr_sns_create_thread方法创建cmr_sns_thread_proc线程。如下:
ret = cmr_thread_create(&handle->thread_cxt.thread_handle,
SENSOR_MSG_QUEUE_SIZE,cmr_sns_thread_proc,
(void *)handle)

cmr_int cmr_sensor_open(cmr_handle sensor_handle, cmr_u32 sensor_id_bits) {
···struct cmr_sensor_handle *handle =(struct cmr_sensor_handle *)sensor_handle;/*the open&close function should be sync*/message.msg_type = CMR_SENSOR_EVT_OPEN;message.sync_flag = CMR_MSG_SYNC_PROCESSED;message.data = (void *)((unsigned long)sensor_id_bits);//这里发送msg消息,去启动在cmr_sensor_init创建的ret = cmr_thread_msg_send(handle->thread_cxt.thread_handle, &message);
···return ret;
}

在cmr_sensor_open中,发送了msg消息,去启动在cmr_sensor_init创建的线程cmr_sns_thread_proc。
这里的消息类型是message.msg_type = CMR_SENSOR_EVT_OPEN;

cmr_int cmr_sns_thread_proc(struct cmr_msg *message, void *p_data) {
···switch (evt) {case CMR_SENSOR_EVT_INIT:/*common control info config*/CMR_LOGI("INIT DONE!");break;case CMR_SENSOR_EVT_OPEN:/*camera sensor open for every bits*/ops_param = (cmr_u32)((unsigned long)message->data);ret = cmr_sns_open(handle, ops_param);if (ret) {/* notify oem through fd_sensor */CMR_LOGE("cmr_sns_open failed!");}    return CMR_CAMERA_INVALID_PARAM;}···
}

因此,接下来会走case CMR_SENSOR_EVT_OPEN这个分支,调用cmr_sns_open方法。

cmr_int cmr_sns_open(struct cmr_sensor_handle *handle, cmr_u32 sensor_id_bits) {
···/*open all signed camera sensor*/for (cameraId = 0; cameraId < CAMERA_ID_MAX; cameraId++) {if (0 != (sensor_id_bits & (1 << cameraId))) {ret = sensor_open_common(&handle->sensor_cxt[cameraId], cameraId,handle->is_autotest);if (ret) {CMR_LOGE("camera %u open failed!", cameraId);} else {handle->sensor_bits |= (1 << cameraId);}}}
···
}

cmr_sns_open方法又继续调用sensor_open_common函数,这个函数比较复杂,主要工作如下:
1.初始化ctx(context)这个结构体
2.初始化exif信息(拍照信息)
3.加载sensor file文件,里面保存了camera的id
4.根据sensor file里保存的camera 的id打开摄像头
我们来看代码:
vendor/sprd/modules/libcamera/sensor/sensor_drv_u.c

cmr_int sensor_open_common(struct sensor_drv_context *sensor_cxt,cmr_u32 sensor_id, cmr_uint is_autotest) {
···/* 调用sensor_context_init 初始化ctx(context)这个结构体*/ret_val = sensor_context_init(sensor_cxt, sensor_id, is_autotest);/* 创建sensor_ctrl_thread_proc线程. */ret_val = sensor_create_ctrl_thread(sensor_cxt);/* 初始化内核驱动程序的结构体hw_drv_init_para . */struct hw_drv_init_para input_ptr;cmr_int fd_sensor = SENSOR_FD_INIT;//SENSOR_FD_INIT =-1cmr_handle hw_drv_handle = NULL;input_ptr.sensor_id = sensor_id;input_ptr.caller_handle = sensor_cxt;fd_sensor = hw_sensor_drv_create(&input_ptr, &hw_drv_handle);if ( (SENSOR_FD_INIT == fd_sensor) || (NULL == hw_drv_handle) ) {SENSOR_LOGE("sns_device_init %d error, return", sensor_id);ret_val = SENSOR_FAIL;goto init_exit;}//初始化sensor_cxtsensor_cxt->fd_sensor = fd_sensor;sensor_cxt->hw_drv_handle = hw_drv_handle;sensor_cxt->sensor_hw_handler = hw_drv_handle;/* 根据存储在传感器idx文件中的索引加载所有传感器ic信息*/sensor_load_idx_inf_file(sensor_cxt);if (sensor_cxt->sensor_identified) {if (SENSOR_SUCCESS == sns_load_drv(sensor_cxt, SENSOR_MAIN)){sensor_num++;}
···SENSOR_LOGI("1 is identify, register OK");/*读到id信息,就去open*/ret_val = sensor_open(sensor_cxt, sensor_id);if (ret_val != SENSOR_SUCCESS) {SENSOR_LOGI("first open sensor failed,start identify");}}/* 扫描cfg表中的设备,找出正确的传感器驱动程序 */if ((!sensor_cxt->sensor_identified) || (ret_val != SENSOR_SUCCESS)) {sensor_num = 0;SENSOR_LOGI("register sensor fail, start identify");//遍历的核心函数是sensor_identifyif (sensor_identify(sensor_cxt, SENSOR_MAIN))sensor_num++;
···//遍历成功后,继续执行sensor_open动作ret_val = sensor_open(sensor_cxt, sensor_id);}sensor_cxt->sensor_identified = SCI_TRUE;//设置sensor_id的状态为TRUEsensor_save_idx_inf_file(sensor_cxt);//把识别到的id信息保存到/data/misc/cameraserver/sensor.file//把节点信息保存到/sys/devices/virtual/misc/sprd_sensor/camera_sensor_namesensor_rid_save_sensor_info(sensor_cxt);
···return ret_val;
}

分析:首先进行一些必要的初始化,然后调用sensor_load_idx_inf_file函数去加载/data/misc/cameraserver/路径下的sensor.file文件,

1.如果读到了sensor_id, sensor_cxt->sensor_identified 设置为SCI_TRUE(这个值是1),走sns_load_drv(sensor_cxt, SENSOR_MAIN)函数去注册驱动程序,接着直接执行sensor_open动作。

2.否则,调用sensor_identify(sensor_cxt, SENSOR_MAIN)遍历sensor list,扫描cfg表中的设备,找出正确的传感器驱动程序。

以上成功后,调用sensor_open函数进行:
1.AF的初始化 sensor_af_init()
2.OTP的读取 otp_module_init()
3.拍照信息的设置 sensor_set_export_Info()

流程图如下:

sensor_open_common流程

sensor_identify扫描流程

LOCAL cmr_int sensor_identify(struct sensor_drv_context *sensor_cxt,SENSOR_ID_E sensor_id) {
···ret = sensor_get_match_info(sensor_cxt, sensor_id);ret = sensor_ic_identify(sensor_cxt, sensor_id);retValue = sensor_identify_search(sensor_cxt, sensor_id);return retValue;
}

分析:

1.首先调用sensor_get_match_info去获取我们自己配置的camera驱动,流程是:

sensor_get_match_info -> sensor_get_module_tab -> back_sensor_infor_tab(如下所示)

vendor/sprd/modules/libcamera/sensor/sensor_cfg.c

这就是为啥我们驱动工程师添加新的Camea时,都要在这个cfg列表里添加我们的驱动

const SENSOR_MATCH_T back_sensor_infor_tab[] = {
// gc area
#ifdef GC5005{MODULE_SUNNY, "gc5005", &g_gc5005_mipi_raw_info, {&dw9714_drv_entry, 0}, NULL},
#endif
#ifdef GC8024{MODULE_SUNNY, "gc8024", &g_gc8024_mipi_raw_info, {&dw9714_drv_entry, 0}, NULL},
#endif
#ifdef GC030A{MODULE_SUNNY, "gc030a", &g_gc030a_mipi_raw_info, {NULL, 0}, NULL},
#endif
#ifdef GC2385{MODULE_SUNNY, "gc2385", &g_gc2385_mipi_raw_info, {NULL, 0}, NULL},
#endif
···
}

2.然后调用sensor_ic_identify去识别ic信息。

识别步骤如下:
1.建立sensor IC驱动结构体
2.配置I2C总线,传感器ID, I2C时钟,从addr, reg addr lenth,数据长度
3.给sensor IC 上电
4.识别sensor IC 的PID和VID
5.删除sensor IC驱动结构体

sensor_ic_identify遍历的流程图如下,

通过 sns_ops->power(sensor_cxt->sns_ic_drv_handle, power_on);调用到sensor驱动的power_on接口,
如ov8856_drv_power_on()

sns_ops->identify(sensor_cxt->sns_ic_drv_handle,SENSOR_ZERO_I2C);调用到sensor驱动的identify接口,
如ov8856_drv_identify()

identify流程

static cmr_int sensor_ic_identify(struct sensor_drv_context *sensor_cxt,cmr_u32 sensor_id) {···//1.建立sensor IC驱动结构体struct sensor_ic_ops *sns_ops = PNULL;struct sensor_ic_drv_init_para sns_init_para;register_info = &sensor_cxt->sensor_register_info;sns_ops = sensor_cxt->sensor_info_ptr->sns_ops;sensor_cxt->i2c_addr = mod_cfg_info->major_i2c_addr;/* 创建 sensor ic handle */ret = sensor_ic_create(sensor_cxt, sensor_id);try: /*sensor has backup addr*/if (sns_ops && sns_ops->identify) {/*2.初始化 i2c配置*/hw_drv_cfg.i2c_bus_config = mod_cfg_info->reg_addr_value_bits;hw_sensor_drv_cfg(sensor_cxt->hw_drv_handle, &hw_drv_cfg);sensor_i2c_init(sensor_cxt, sensor_id);//设置i2c地址hw_sensor_i2c_set_addr(sensor_cxt->hw_drv_handle,sensor_cxt->i2c_addr);//设置i2c时钟hw_sensor_i2c_set_clk(sensor_cxt->hw_drv_handle);
···//3.给sensor 上电sensor_power_on(sensor_cxt, SCI_TRUE); /*power on*///调用具体的驱动进行identifyret = sns_ops->identify(sensor_cxt->sns_ic_drv_handle,SENSOR_ZERO_I2C);if (SENSOR_SUCCESS == ret) {/**if the following is SCI_FALSE,that is,now is in identify*process* should delete sensor ic handle**/if (register_info->is_register[sensor_id] != SCI_TRUE) {sensor_power_on(sensor_cxt, SCI_FALSE);sensor_i2c_deinit(sensor_cxt, sensor_id);sensor_ic_delete(sensor_cxt);}sensor_cxt->sensor_list_ptr[sensor_id] =sensor_cxt->sensor_info_ptr;register_info->is_register[sensor_id] = SCI_TRUE;register_info->img_sensor_num++;} else {// register_info->is_register[sensor_id] = SCI_FALSE;sensor_power_on(sensor_cxt, SCI_FALSE);if ((sensor_cxt->i2c_addr != mod_cfg_info->minor_i2c_addr) &&mod_cfg_info->minor_i2c_addr != 0x00) {sensor_cxt->i2c_addr = mod_cfg_info->minor_i2c_addr;SENSOR_LOGI("use backup i2c address,try again!");goto try;}SENSOR_LOGI("identify failed!");//如果identify failed就删除sensor IC信息sensor_ic_delete(sensor_cxt);return SENSOR_FAIL;}}return ret;
}

PS: power on 流程 也是我们驱动工程师经常修改的地方,这里啰嗦几句,以ov8856的上电为例子

这的主要是三路电压,avdd,dvdd,iovdd设置供电,具体参考我之前写的文章: 你应该了解的Camera HW-硬件知识

1.供电部分
camera包含的三路电压为模拟电压(VCAMA),数字电压(VCAMD),IO口电压(VCAMIO)
a) VCAMD 就是 DVDD 数字供电,主要给 ISP 供电
b) VCAM_IO 就是 VDDIO 数字 IO 电源主要给 I2C 部分供电;
c) VCAMA 就是 AVDD 模拟供电,主要给感光区和 ADC 部分供电;
d) VCAM_AF 是对 Camera 自动对焦马达的供电

/*==============================================================================* Description:* sensor power on* please modify this function acording your spec*============================================================================*/
static cmr_int ov8856_drv_power_on(cmr_handle handle, cmr_u32 power_on) {SENSOR_IC_CHECK_HANDLE(handle);
···if (SENSOR_TRUE == power_on) {//上电流程//先拉低pnd脚hw_sensor_power_down(sns_drv_cxt->hw_handle, power_down);//拉低reset脚hw_sensor_set_reset_level(sns_drv_cxt->hw_handle, reset_level);usleep(500);//延迟500微秒,ps这里的延迟要根据规格书来//设置av电压,主要给感官区和adc部分供电hw_sensor_set_avdd_val(sns_drv_cxt->hw_handle, avdd_val);//设置DVDD 电压,主要给ISP供电hw_sensor_set_dvdd_val(sns_drv_cxt->hw_handle, dvdd_val);//设置IO电压,IO 电源主要给 I2C 部分供电hw_sensor_set_iovdd_val(sns_drv_cxt->hw_handle, iovdd_val);usleep(500);//延迟500微秒//拉高PND脚hw_sensor_power_down(sns_drv_cxt->hw_handle, !power_down);//拉高rst脚hw_sensor_set_reset_level(sns_drv_cxt->hw_handle, !reset_level);usleep(500);//延迟500微秒//设置mclk时钟hw_sensor_set_mclk(sns_drv_cxt->hw_handle, EX_MCLK);} else {//下电流程,和上电相反hw_sensor_set_mclk(sns_drv_cxt->hw_handle, SENSOR_DISABLE_MCLK);usleep(500);hw_sensor_set_reset_level(sns_drv_cxt->hw_handle, reset_level);hw_sensor_power_down(sns_drv_cxt->hw_handle, power_down);usleep(200);hw_sensor_set_avdd_val(sns_drv_cxt->hw_handle, SENSOR_AVDD_CLOSED);hw_sensor_set_dvdd_val(sns_drv_cxt->hw_handle, SENSOR_AVDD_CLOSED);hw_sensor_set_iovdd_val(sns_drv_cxt->hw_handle, SENSOR_AVDD_CLOSED);}SENSOR_LOGI("(1:on, 0:off): %d", power_on);return SENSOR_SUCCESS;
}

PS2:identify的实现也贴出来,继续啰嗦几句,以ov8856的上电为例子
添加了关键代码注释,很容易理解!

/*==============================================================================* Description:* identify sensor id* please modify this function acording your spec*============================================================================*/
static cmr_int ov8856_drv_identify(cmr_handle handle, cmr_uint param) {
···//hw_sensor_read_reg 读取寄存器信息pid_value = hw_sensor_read_reg(sns_drv_cxt->hw_handle, ov8856_PID_ADDR);//识别到具体的sendor idif (ov8856_PID_VALUE == pid_value) {ver_value = hw_sensor_read_reg(sns_drv_cxt->hw_handle, ov8856_VER_ADDR);SENSOR_LOGI("Identify: PID = %x, VER = %x", pid_value, ver_value);if (ov8856_VER_VALUE == ver_value) {SENSOR_LOGI("this is ov8856 sensor");//把id信息保存起来ov8856_drv_init_fps_info(handle);ret_value = SENSOR_SUCCESS;} else {SENSOR_LOGI("Identify this is %x%x sensor", pid_value, ver_value);}} else {SENSOR_LOGE("sensor identify fail, pid_value = %x", pid_value);}return ret_value;
}

3.最后如果identify失败,则重新执行上面2个步骤,重新遍历

sensor_identify_search函数实现如下:

LOCAL cmr_u32 sensor_identify_search(struct sensor_drv_context *sensor_cxt,SENSOR_ID_E sensor_id) {
···//调用sensor_get_match_info去获取我们自己配置的camera驱动module_tab = sensor_get_module_tab(sensor_cxt->is_autotest, sensor_id);
···//调用sensor_ic_identify去识别ic信息retValue = sensor_ic_identify(sensor_cxt, sensor_id);
···return retValue;
}

到此文章的第一部分就写完了,松口气,喝口水,继续写第二部分内容!

三、预览(preview)调用流程

【Hal层】

vendor/sprd/modules/libcamera/hal3_2v1a/SprdCamera3HWI.cpp

int SprdCamera3HWI::openCamera() {
···//注册ispVideoStartPreview函数ispvideo_RegCameraFunc(1, ispVideoStartPreview);
···
}

在openCamera函数中,通过这个ispvideo_RegCameraFunc(1, ispVideoStartPreview);注册ispVideoStartPreview

static int ispVideoStartPreview(uint32_t param1, uint32_t param2) {
···
rtn = regularChannel->start(dev->mFrameNum);
···
}

接下来调用regularChannel->start(dev->mFrameNum)往下走

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3Channel.cpp

int SprdCamera3RegularChannel::start(uint32_t frame_number) {int ret = NO_ERROR;size_t i = 0; ret = mOEMIf->start(mChannelType, frame_number);return ret;
}

这里的type :
typedef enum {
CAMERA_CHANNEL_TYPE_DEFAULT, / default /
CAMERA_CHANNEL_TYPE_REGULAR, / regular channel /
CAMERA_CHANNEL_TYPE_PICTURE, / picture channel/
CAMERA_CHANNEL_TYPE_RAW_CALLBACK, /YUV888 callback/
CAMERA_CHANNEL_TYPE_MAX,
} camera_channel_type_t;

接着调用 ret = mOEMIf->start(mChannelType, frame_number);

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

int SprdCamera3OEMIf::start(camera_channel_type_t channel_type,uint32_t frame_number) {
···switch (channel_type) {case CAMERA_CHANNEL_TYPE_REGULAR: {
···ret = startPreviewInternal();//这里继续跟进去break;}//以下是拍照部分,我们下个部分在进行分析case CAMERA_CHANNEL_TYPE_PICTURE: {if (mTakePictureMode == SNAPSHOT_NO_ZSL_MODE ||ret = takePicture();}else if (mTakePictureMode == SNAPSHOT_ZSL_MODE) {ret = zslTakePicture();} else if (mTakePictureMode == SNAPSHOT_VIDEO_MODE) {ret = VideoTakePicture();}break;}default:break;}
···return ret;
}

如果类型为CAMERA_CHANNEL_TYPE_REGULAR,则调用:
ret = startPreviewInternal();//这里继续跟进去
如果类型为CAMERA_CHANNEL_TYPE_PICTURE,则调用拍照相关:
ret = takePicture();
ret = zslTakePicture();
ret = VideoTakePicture();

int SprdCamera3OEMIf::startPreviewInternal() {
···//preview的时候,设置照片的thumbnail size(压缩后的大小)和camera app的大小一致chooseDefaultThumbnailSize(&jpeg_thumb_size.width, &jpeg_thumb_size.height);
···ret = mHalOem->ops->camera_start_preview(mCameraHandle, mCaptureMode);
···
}

mHalOem->ops->camera_start_preview(mCameraHandle, mCaptureMode);方法的实现在SprdOEMCamera.c里
vendor/sprd/modules/libcamera/oem2v1/src/SprdOEMCamera.c

cmr_int camera_start_preview(cmr_handle camera_handle,enum takepicture_mode mode) {
···ret = camera_local_start_preview(camera_handle, mode, CAMERA_PREVIEW);
···return ret;
}

【OEM层】

该函数很简单,就继续调用camera_local_start_preview函数
vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_start_preview(cmr_handle oem_handle,enum takepicture_mode mode, cmr_uint is_snapshot) {//设置preview的参数ret = camera_set_preview_param(oem_handle, mode, is_snapshot);//继续cmr_preview_startret = cmr_preview_start(prev_cxt->preview_handle, cxt->camera_id);···return ret;
}

该函数设置preview的参数信息,然后继续调用cmr_preview_start方法
vendor/sprd/modules/libcamera/oem2v1/src/cmr_preview.c

cmr_int cmr_preview_start(cmr_handle preview_handle, cmr_u32 camera_id) {
···message.msg_type = PREV_EVT_ASSIST_START;//设置msg的type类型PREV_EVT_ASSIST_STARTmessage.sync_flag = CMR_MSG_SYNC_PROCESSED;//设置msg的flag//发送msg消息ret = cmr_thread_msg_send(handle->thread_cxt.assist_thread_handle, &message);
···message.msg_type = PREV_EVT_START;//设置msg的type类型PREV_EVT_STARTmessage.sync_flag = CMR_MSG_SYNC_PROCESSED;//设置msg的flagmessage.data = (void *)((unsigned long)camera_id);//发送了msg消息ret = cmr_thread_msg_send(handle->thread_cxt.thread_handle, &message);···return ret;
}

这里主要调用cmr_thread_msg_send发送两条msg消息,

第一条msg=消息

assist_thread_handle=prev_assist_thread_proc
该handle的创建:
在prev_create_thread(struct prev_handle *handle)调用
ret = cmr_thread_create(&handle>thread_cxt.assist_thread_handle,PREV_MSG_QUEUE_SIZE, prev_assist_thread_proc, (void *)handle);

cmr_int prev_assist_thread_proc(struct cmr_msg *message, void *p_data) {
···msg_type = (cmr_u32)message->msg_type;//获得msg_tyoe//根据msg_type进行操作switch (msg_type) {case PREV_EVT_ASSIST_START:handle->frame_active = 1;break;
···case PREV_EVT_ASSIST_STOP:handle->frame_active = 0;break;
···return ret;
}

当msg_type=PREV_EVT_ASSIST_START:
仅仅操作handle->frame_active = 1;

第二条msg=消息

thread_cxt.thread_handle=prev_thread_proc

cmr_int prev_thread_proc(struct cmr_msg *message, void *p_data) {
···switch (msg_type) {
···case PREV_EVT_START:camera_id = (cmr_u32)((unsigned long)message->data);prev_recovery_reset(handle, camera_id);ret = prev_start(handle, camera_id, 0, 0);/*Notify preview started*/cb_data_info.cb_type = PREVIEW_EXIT_CB_PREPARE;cb_data_info.func_type = PREVIEW_FUNC_START_PREVIEW;cb_data_info.frame_data = NULL;prev_cb_start(handle, &cb_data_info);break;
···
}

分析:
1.ret = prev_start(handle, camera_id, 0, 0)调用流程如下:

ret = handle->ops.channel_start(···);【cmr_preview.c】->cmr_int camera_channel_start(···);【cmr_oem.c】->cmr_int cmr_grab_cap_start(···)【cmr_grab.c】->ret = ioctl(p_grab->fd, SPRD_IMG_IO_SET_CAP_SKIP_NUM, &num);【cmr_grab.c】

【kernel层】

通过ioctl的方式调用kernel层的方法
经过以上一系列复杂流程,后看到cmr_grab_cap_start()调入到kernel目录执行打开DCAM,cmr_grab_cap_start通过ioctl的方式调用kernel层的方法。

cmr_int cmr_grab_cap_start(cmr_handle grab_handle, cmr_u32 skip_num) {
···ret = ioctl(p_grab->fd, SPRD_IMG_IO_SET_CAP_SKIP_NUM, &num);ATRACE_BEGIN("dcam_stream_on");ret = ioctl(p_grab->fd, SPRD_IMG_IO_STREAM_ON, &stream_on);
···return ret;
}

kernel/drivers/misc/sprd_camera/dcam/dcam_if_r4p0/dcam_ioctrl.c

{SPRD_IMG_IO_STREAM_ON,     dcamio_stream_on},
static int dcamio_stream_on(struct camera_file *camerafile,unsigned long arg, unsigned int cmd)
{
···ret = sprd_img_get_dcam_dev(camerafile, &dev, &info);ret = sprd_camera_stream_on(camerafile);
···return ret;
}

2.prev_cb_start(handle, &cb_data_info)调用流程如下:

prev_cb_start(handle, &cb_data_info)//cmr_preview.c->ret = cmr_thread_msg_send(···);//cmr_preview.c//message.msg_type = PREV_EVT_CB_START;cb_thread_handle = prev_cb_thread_proc->ret = handle->oem_cb(···)//cmr_preview.c//handle->oem_cb = init_param_ptr->oem_cb=camera_preview_cb;->ret = cmr_thread_msg_send(···);// oem2v1/src/cmr_oem.c//message.sub_msg_type = oem_cb_type;//prev_cb_thr_handle = camera_preview_cb_thread_proc->callback(···);

vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_preview_cb_thread_proc(struct cmr_msg *message, void *data) {
···callback = cxt->camera_cb;callback(message->sub_msg_type, cxt->client_data, message->msg_type, message->data);
···return ret;
}

这里callback 为 cxt->camera_cb;具体实现在SprdCamera3OEMIf::camera_cb(···);

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

void SprdCamera3OEMIf::camera_cb(enum camera_cb_type cb,const void *client_data,enum camera_func_type func, void *parm4) {
···switch (func) {case CAMERA_FUNC_START_PREVIEW:obj->HandleStartPreview(cb, parm4);break;
···

这里在oem_func = CAMERA_FUNC_START_PREVIEW;因此继续调用HandleStartPreview(cb, parm4);

void SprdCamera3OEMIf::HandleStartPreview(enum camera_cb_type cb, void *parm4) {
···receivePreviewFrame((struct camera_frame_type *)parm4);
···
}

这里是调用receivePreviewFrame接收frame data

void SprdCamera3OEMIf::receivePreviewFrame(struct camera_frame_type *frame) {
···//接收frame datachannel->getStream(CAMERA_STREAM_TYPE_PREVIEW, &pre_stream);channel->getStream(CAMERA_STREAM_TYPE_VIDEO, &rec_stream);channel->getStream(CAMERA_STREAM_TYPE_CALLBACK, &callback_stream);HAL_LOGV("pre_stream %p, rec_stream %p, callback_stream %p", pre_stream,rec_stream, callback_stream);
//美颜
#ifdef CONFIG_FACE_BEAUTYint sx, sy, ex, ey, angle, pose;struct face_beauty_levels beautyLevels;beautyLevels.blemishLevel =(unsigned char)sprddefInfo.perfect_skin_level[0];beautyLevels.smoothLevel = (unsigned char)sprddefInfo.perfect_skin_level[1];beautyLevels.skinColor = (unsigned char)sprddefInfo.perfect_skin_level[2];beautyLevels.skinLevel = (unsigned char)sprddefInfo.perfect_skin_level[3];beautyLevels.brightLevel = (unsigned char)sprddefInfo.perfect_skin_level[4];beautyLevels.lipColor = (unsigned char)sprddefInfo.perfect_skin_level[5];beautyLevels.lipLevel = (unsigned char)sprddefInfo.perfect_skin_level[6];beautyLevels.slimLevel = (unsigned char)sprddefInfo.perfect_skin_level[7];beautyLevels.largeLevel = (unsigned char)sprddefInfo.perfect_skin_level[8];
#endif
···
}

这个函数实现很复杂,主要用来recevie Frame data here , 以及美颜等,具体细节以后分析。

四、拍照(snapshot)调用流程

【Hal层】

我们直接从SprdCamera3OEMIf::start开始分析,怎么调用到这个函数的,前面已经分析过了,就不在赘述!

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

int SprdCamera3OEMIf::start(camera_channel_type_t channel_type,uint32_t frame_number) {
···switch (channel_type) {
···case CAMERA_CHANNEL_TYPE_PICTURE: {if (···)setCamPreformaceScene(CAM_CAPTURE_S_LEVEL_NH);}if (mTakePictureMode == SNAPSHOT_NO_ZSL_MODE ||mTakePictureMode == SNAPSHOT_ONLY_MODE)···ret = takePicture();···else if (mTakePictureMode == SNAPSHOT_ZSL_MODE) {mVideoSnapshotFrameNum = frame_number;···ret = zslTakePicture();···} else if (mTakePictureMode == SNAPSHOT_VIDEO_MODE) {mVideoSnapshotFrameNum = frame_number;ret = VideoTakePicture();}break;}
···}
···
}

分析:首先channel_type=CAMERA_CHANNEL_TYPE_PICTURE,然后进行以下动作:
1.setCamPreformaceScene(CAM_CAPTURE_S_LEVEL_NH);设定Camera的场景,场景类型如下

typedef enum CAMERA_PERFORMACE_SCENE {CAM_OPEN_S,CAM_OPEN_E_LEVEL_H,     // DFS:veryhighCAM_OPEN_E_LEVEL_N,     // DFS:normalCAM_OPEN_E_LEVEL_L,     // DFS:lowCAM_PREVIEW_S_LEVEL_H,  // powerhint:performanceCAM_PREVIEW_S_LEVEL_N,  // powerhint:normalCAM_PREVIEW_S_LEVEL_L,  // powerhint:lowCAM_CAPTURE_S_LEVEL_HH, // powerhint:performance  DFS:veryhighCAM_CAPTURE_S_LEVEL_HN, // powerhint:performance  DFS:normalCAM_CAPTURE_S_LEVEL_NH, // powerhint:normal  DFS:veryhighCAM_CAPTURE_S_LEVEL_NN, // powerhint:normal  DFS:normalCAM_CAPTURE_E_LEVEL_NH, // powerhint:normal  DFS:veryhighCAM_CAPTURE_E_LEVEL_NN, // powerhint:normal  DFS:normalCAM_CAPTURE_E_LEVEL_NL, // powerhint:normal  DFS:lowCAM_CAPTURE_E_LEVEL_LN, // powerhint:low DFS:normalCAM_CAPTURE_E_LEVEL_LL, // powerhint:low  DFS:lowCAM_CAPTURE_E_LEVEL_LH, // powerhint:low  DFS:veryhighCAM_FLUSH_S,CAM_FLUSH_E,CAM_EXIT_S,CAM_EXIT_E,
} sys_performance_camera_scene;

2.根据mTakePictureMode调用不同的拍照方法

  • 第一种:普通拍照模式
    mTakePictureMode =SNAPSHOT_NO_ZSL_MODE 或者 SNAPSHOT_ONLY_MODE
    ret = takePicture();
  • 第二种:零延迟拍照(预览画面是啥,拍出来的就是啥,所见即所得)
    mTakePictureMode == SNAPSHOT_ZSL_MODE
    ret = zslTakePicture();
  • 第三种:视频模式
    mTakePictureMode == SNAPSHOT_VIDEO_MODE
    ret = VideoTakePicture();

有3条分支,这里我们选择普通的拍照模式分支继续分析。

int SprdCamera3OEMIf::takePicture() {
···mHalOem->ops->camera_take_picture(mCameraHandle, mCaptureMode)
···
}

其实takePicture函数有很多操作,比如:相机是否已经preview,没有的话,先进行preview,其次,相机是否正在capturing(截屏),如果是的话,等待,直到capturing结束等等。最后调用
mHalOem->ops->camera_take_picture(mCameraHandle, mCaptureMode)来调用到oem层。

OEM层(展讯自己封装的一层,Hal层和驱动层沟通的中间桥梁)

vendor/sprd/modules/libcamera/oem2v1/src/SprdOEMCamera.c

cmr_int camera_take_picture(cmr_handle camera_handle,enum takepicture_mode cap_mode) {
···ret = camera_local_start_snapshot(camera_handle, cap_mode, CAMERA_SNAPSHOT);if (ret) {CMR_LOGE("failed to start snapshot %ld", ret);}
···
}

分析:这个函很简单,就直接调用camera_local_start_snapshot进行拍照动作
vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_set_cap_size(cmr_handle oem_handle,cmr_u32 is_reprocessing, cmr_u32 camera_id,cmr_u32 width, cmr_u32 height) {//1ret = cmr_snapshot_post_proc(cxt->snp_cxt.snapshot_handle, &snp_param);//2ret = camera_local_start_capture(oem_handle);//3ret = cmr_snapshot_receive_data(cxt->snp_cxt.snapshot_handle,SNAPSHOT_EVT_CHANNEL_DONE,(void *)&frame);
}

分析:该函数主要做了以下事情:
1.调用cmr_snapshot_post_proc()函数发送一条msg消息
2.调用camera_local_start_capture()函数继续拍照流程
3.调用cmr_snapshot_receive_data()函数receive拍照的数据

先来看

1.调用cmr_snapshot_post_proc()函数发送一条msg消息


vendor/sprd/modules/libcamera/oem2v1/src/cmr_snapshot.c

cmr_int cmr_snapshot_post_proc(cmr_handle snapshot_handle,struct snapshot_param *param_ptr) {
···message.msg_type = SNP_EVT_START_PROC;message.sync_flag = CMR_MSG_SYNC_PROCESSED;message.alloc_flag = 0;message.data = param_ptr;ret = cmr_thread_msg_send(cxt->thread_cxt.main_thr_handle, &message);
···
}

消息类型: message.msg_type = SNP_EVT_START_PROC;
线程处理函数为:snp_main_thread_proc
我们来看这个处理函数:

cmr_int snp_main_thread_proc(struct cmr_msg *message, void *p_data) {
···switch (message->msg_type) {
···case SNP_EVT_START_PROC:ret = snp_set_post_proc_param(snp_handle,(struct snapshot_param *)message->data);break;
···}
···
}

分析:直接调用snp_set_post_proc_param函数

cmr_int snp_set_post_proc_param(cmr_handle snp_handle,struct snapshot_param *param_ptr) {
···ret = cxt->ops.get_sensor_info(cxt->oem_handle, cxt->req_param.camera_id,&cxt->sensor_info);ret = snp_set_jpeg_dec_param(snp_handle);ret = snp_set_isp_proc_param(snp_handle);ret = snp_set_channel_out_param(snp_handle);ret = snp_set_hdr_param(snp_handle);snp_get_is_scaling(snp_handle, is_normal_cap);ret = snp_set_rot_param(snp_handle);ret = snp_set_jpeg_enc_param(snp_handle);ret = snp_set_jpeg_exif_param(snp_handle);
···
}

分析:设置各种参数。


2.调用camera_local_start_capture()函数继续拍照流程


vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_start_capture(cmr_handle oem_handle) {//设置拍照的时候是否需要闪光灯camera_local_snapshot_is_need_flash(oem_handle, cxt->camera_id,&flash_status);    //继续调用cmr_grab_start_capture拍照ret = cmr_grab_start_capture(cxt->grab_cxt.grab_handle, capture_param);
}

这里继续调用cmr_grab_start_capture拍照。
vendor/sprd/modules/libcamera/oem2v1/src/cmr_grab.c

cmr_int cmr_grab_start_capture(cmr_handle grab_handle,struct sprd_img_capture_param capture_param) {struct cmr_grab *p_grab;p_grab = (struct cmr_grab *)grab_handle;ret = ioctl(p_grab->fd, SPRD_IMG_IO_START_CAPTURE, &capture_param);
···
}

从这里开始,就调用到我们驱动层了,通过ioctl的接口调用驱动的函数。那么通过SPRD_IMG_IO_START_CAPTURE这个cmd调用的时哪个函数呢?

【kernel层】

kernel/drivers/misc/sprd_camera/dcam/dcam_if_r4p0/

static struct dcam_io_ctrl_fun s_cam_io_ctrl_fun_tab[] = {
···
{SPRD_IMG_IO_START_CAPTURE,     dcamio_start_capture},
···
}

因此,可以看出调用的时dcamio_start_capture函数,好吧,我们就跟到kernel层一探究竟!!!
kernel/drivers/misc/sprd_camera/dcam/dcam_if_r4p0/dcam_ioctrl.c

static int dcamio_start_capture(struct camera_file *camerafile,unsigned long arg, unsigned int cmd)
{int ret = 0; unsigned int cap_flag = 0; struct camera_dev *dev = NULL;struct camera_info *info = NULL;struct camera_group *group = NULL;//获取设备信息ret = sprd_img_get_dcam_dev(camerafile, &dev, &info);if (ret) {pr_err("fail to get dcam dev\n");goto exit;}    group = camerafile->grp;//从用户空间获得数据,拷贝到cap_flag变量中ret = copy_from_user(&cap_flag, (void __user *) arg,sizeof(unsigned int));if (ret) {pr_err("fail to get user info\n");ret = -EFAULT;goto exit;}if (dev->cap_flag == DCAM_CAPTURE_STOP) {dev->cap_flag = DCAM_CAPTURE_START;if (dev->dcam_cxt.need_isp_tool)cap_flag = DCAM_CAPTURE_NONE;pr_info("start capture, cap_flag %d\n", cap_flag);//调用该函数进行拍照动作ret = sprd_isp_start_pipeline_full(dev->isp_dev_handle,cap_flag);if (ret) {pr_err("fail to start offline\n");goto exit;}}//拍照完成pr_info("start capture done\n");exit:return ret;
}

注释添加的很清晰了,这里简单说一下
调用sprd_isp_start_pipeline_full动作去执行拍照,最后的数据会保存在p_offline_frame中!【struct camera_frame *p_offline_frame = NULL,p_offline_frame是一个指针】

**p_offline_frame = &dev->offline_frame[ISP_OFF_BUF_FULL];**
**memcpy(p_offline_frame, &frame, sizeof(struct camera_frame));****complete(&dev->offline_full_thread_com);**

最后通过complete唤醒线程,让线程去接受数据。
PS:【complete是完成量的概念,用于保护共享数据,防止竞态,并且告诉另一个休眠的线程,说我边完事了,你醒醒,继续干你的活去。具体可以参考LDD这本书或者自行百度】

最后我们简单分析一下是如何收取数据的

3.调用cmr_snapshot_receive_data()函数receive拍照的数据

vendor/sprd/modules/libcamera/oem2v1/src/cmr_snapshot.c

cmr_int cmr_snapshot_receive_data(cmr_handle snapshot_handle, cmr_int evt,void *data) {
···switch (evt) {//normol拍照模式case SNAPSHOT_EVT_CHANNEL_DONE:malloc_len = sizeof(struct frm_info);CMR_LOGD("video %d zsl %d yaddr_vir 0x%x",cxt->req_param.is_video_snapshot,cxt->req_param.is_zsl_snapshot, frame_info_ptr->yaddr_vir);buffer_id = snp_get_buffer_id(snapshot_handle, data);buffer_id += frame_info_ptr->base;snp_evt = SNP_EVT_CHANNEL_DONE;if (1 == cxt->req_param.is_video_snapshot ||1 == cxt->req_param.is_zsl_snapshot) {flag = 1;width = cxt->req_param.post_proc_setting.chn_out_frm[0].size.width;height =cxt->req_param.post_proc_setting.chn_out_frm[0].size.height;act_width = cxt->req_param.post_proc_setting.actual_snp_size.width;act_height =cxt->req_param.post_proc_setting.actual_snp_size.height;//memcpy指的是c和c++使用的内存拷贝函数,从kernel中通过地址拷贝数据到oem层memcpy(&chn_data, data, sizeof(struct frm_info));chn_data.base = CMR_CAP0_ID_BASE;chn_data.frame_id = CMR_CAP0_ID_BASE;if (1 == cxt->req_param.is_zsl_snapshot) {chn_data.base = CMR_CAP1_ID_BASE;chn_data.frame_id = CMR_CAP1_ID_BASE;}}if (1 == cxt->req_param.is_video_snapshot) {//视频模式···cmr_copy((void *)dst_vir, (void *)src_vir, width * height / 2);cmr_snapshot_memory_flush(cxt, &(cxt->req_param.post_proc_setting.chn_out_frm[0]));···} else if (1 == cxt->req_param.is_zsl_snapshot) {//零延迟拍照模式···cmr_copy((void *)dst_vir, (void *)src_vir, width * height / 2);cmr_snapshot_memory_flush(cxt, &(cxt->req_param.post_proc_setting.chn_out_frm[0]));···}break;···}
···
}

拍完照片后,我们会受到一个msg消息,type=SNAPSHOT_EVT_CHANNEL_DONE,表示拍照完成!
调用memcpy(&chn_data, data, sizeof(struct frm_info));从kernel获取数据,还记得kernel中是吧数据保存在
struct camera_frame *p_offline_frame 指针中,我们通过地址把数据拷贝出来!
即memcpy(&chn_data, data, sizeof(struct frm_info));!

到此,完结!!!

【Camera专题】Sprd-深入浅出Camera驱动框架1(HAL层-Kernel层)相关推荐

  1. Camera 从应用层看V4L2驱动框架

    1.V4L2驱动框架简介 V4L2可用于采集图片.视频和音频数据的通用 API 接口,配合适当的视频采集设备和相应的驱 动程序,可以实现图片.视频.音频等的采集. 2.V4L2视频采集原理 当启动视频 ...

  2. clk子系统 - 驱动框架

    clk子系统负责为整个系统硬件提供时钟信号,这个要和linux的时钟系统区别开来:现在的ASoC上包含许多clk模块,比如晶振,pll,divider等,那么clk子系统就把这些模块抽象出来,并形成一 ...

  3. Android 系统(4)---Android HAL层与Linux Kernel层驱动开发简介

    Android HAL层与Linux Kernel层驱动开发简介 近日稍微对Android中的驱动开发做了一些简要的了解,稍稍理清了一下Android驱动开发的套路,总结一下笔记. HAL:Hardw ...

  4. Android HAL层与Linux Kernel层驱动开发简介

    Android HAL层与Linux Kernel层驱动开发简介 阅读数:5070 近日稍微对Android中的驱动开发做了一些简要的了解,稍稍理清了一下Android驱动开发的套路,总结一下笔记. ...

  5. camera驱动框架分析(上)

    前言 camera驱动框架涉及到的知识点比较多,特别是camera本身的接口就有很多,有些是直接连接到soc的camif口上的,有些是通过usb接口导出的,如usb camera.我这里主要讨论前者, ...

  6. 【Camera专题】Qcom-你应该掌握的Camera调试技巧2

    系列文章 [Camera专题]Qcom-你应该掌握的Camera调试技巧1 [Camera专题]Qcom-你应该掌握的Camera调试技巧2 0.APP端增加性能分析log 增加性能日志:Tag:[K ...

  7. Camera开发系列之六-使用mina框架实现视频推流

    章节 Camera开发系列之一-显示摄像头实时画面 Camera开发系列之二-相机预览数据回调 Camera开发系列之三-相机数据硬编码为h264 Camera开发系列之四-使用MediaMuxer封 ...

  8. 【Camera专题】Qcom-高通OTP完全调试指南-上

    一.前言 关于高通OTP编程的知识,网上少得可怜,官方文档又没有那么清晰,于是就来一篇干货吧! OTP编程完全指南分上.下2篇. 上:主要讲OTP的知识和调试流程. 下:主要讲OTP的源码. 本文知识 ...

  9. camera驱动电源配置_Camera driverV4L2驱动架构介绍

    大约一年前写的东西,介绍性部分是当时在网络上找的内容,后面的分析部分是基于当时的项目,基于Mavell Pxa920,希望对初学者有点点的帮助吧.转载请注明出. 1.       Camera相关介绍 ...

最新文章

  1. 不是所有的事情都要达成共识
  2. 组合表头_单双斜线表头——520,想单就单,想双就双
  3. SpringMVC执行流程图
  4. ionic2.0关于表单的验证
  5. sqlmap源码阅读系列init中的_cleanupOptions
  6. mysql pxc启动_Percona XtraDB Cluster(PXC) 无法正常启动
  7. jquery exif + lazyload实现延迟加载并显示相片exif信息
  8. 微信公众号接入百度天气API接口
  9. Python根据歌曲id爬取网易云音乐歌词
  10. 深度学习模型加速方法
  11. 余额宝不是吸血鬼,银行才是
  12. centos安装aria2c_CentOS安装aria2 + yaaw实现离线下载
  13. CAP--什么是CAP,为何三者不可兼得
  14. You must SET PASSWORD before executing this statement的解决方法 详细出处参考:http://www.jb51.net/article/39187.
  15. 「DLP-KDD 2021征文」及上届论文全集,包含深度学习推荐/广告系统、多目标、模型服务等
  16. struct 中 typedef的用法
  17. Node.js中的异步编程,个人理解及分化讲解
  18. flowable-bpmn2添加自定义节点属性
  19. 智能空气测试仪“清心”
  20. Tushare筛选成交量大于五日一倍的股票

热门文章

  1. questasim中点击 add wave 后,显示 no data 解决方法
  2. 免费的PDF转换工具,简单高效有它就够了!
  3. 罗马数字转整数(C++)
  4. 电脑连接手机热点用百度云下载一会后断网
  5. 极路由4增强版 倒闭后无法开启开发者模式
  6. 迷你四足机器人制作_从0到1
  7. QIP.ru即时通讯服务3300万明文密码被泄
  8. 资深工程师PCB经验介绍
  9. 5.20 按照邮箱账号的域名进行排序 [原创Excel教程]
  10. 全国关于省市区/县的行政区划数据-数据来源国家统计局