1.Camera 软件系统 QTI = Qualcomm Technologies, Inc
高通对于camera的代码组织大体是遵循Android架构的,但对于最核心的sensor端底层设置、ISP效果参数等进行了单独的剥离,放在daemon进程中进行。【但是我在比较新的文档里面有看到把daemon拿掉了。放在了camera_server里面。这个消息待验证】
1. Camera Daemon 进程
daemon进程作为单一进程,在代码中就是mm-qcamera-daemon,其main 函数的 入口,位置如下:
/project/vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server.c
/project/vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server_process.c
1.找到服务节点的名字并打开此节点
get_server_node_name(serv_al_node_name)//这里的serv_al_node_name为video0
......
hal_fd->fd[0] = open(dev_name, O_RDWR | O_NONBLOCK); //这里dev_name为节点名“/dev/video0”
2.初始化模块。目前有sensor、iface、isp、stats、pproc及imglib六个模块
server_process_module_sensor_init();------》module_sensor_init()
server_process_module_init();
其中,module_sensor_init()函数中,通过判断entity.type == MEDIA_ENT_T_V4L2_SUBDEV &&entity.group_id == MSM_CAMERA_SUBDEV_SENSOR_INIT,找到相应的/dev/v4l-subdevX节点并打开,并通过LOG_IOCTL(fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg),将sensor IC的有关信息拷贝到内核空间,调用msm_sensor_driver_probe()函数,
/* Power up and probe sensor */
rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); rc为0,表明sensro I2C通信正常,接着通过
msm_sensor_driver_create_i2c_v4l_subdev(),生成了/dev/media1&/dev/video1(后摄像头)和/dev/media2&/dev/video2(前摄像头)节点,并通过msm_sd_register(&s_ctrl->msm_sd)注册前后摄像头的sensor。
用户空间的module_sensor_find_sensor_subdev(module_ctrl)和module_sensor_find_other_subdev(module_ctrl)函数,把前后sensor,actuator,csi, isp等从设备的文件节点一一确定好,并加入到module_ctrl->sensor_bundle链表中。
3.进入主循环来处理来自HAL及MCT的事件及消息,处理完之后的结果反馈给kernel(msm.c)
typedef enum _read_fd_type {
RD_FD_HAL, ----------------server_process_hal_event(&event)---返回真,说明消息传递给 MCT,这时
不需要发送CMD ACK给kernel,因为MCT处理结束后会发出通知;反之没有,此时需要立即发送CMD ACK到kernel,
以免HAL发送此消息的线程阻塞住;处理通过hal传递下去给kernel的任何命令;用来处理kernel的node update
case MSM_CAMERA_NEW_SESSION:
mct_controller_new();
...
case MSM_CAMERA_DEL_SESSION:
mct_controller_destory();
...
mct_controller_proc_serv_msg();
RD_DS_FD_HAL, ----------server_process_hal_ds_packet()---来自 HAL 层的消息,通过domain socket 传;
用来处理mapping buffer的socket messages
RD_PIPE_FD_MCT, ----------------server_process_mct_msg()---来自 media controller 的消息,通过pipe;
用来处理mct的update buffer manager: buffer type: matedata 和frame buffers main
RD_FD_NONE
} read_fd_type;
我的理解上就是server处理来自hal的消息;发送给kernel。server也处理来自kernel的消息;发送给hal。
参考:http://blog.chinaunix.net/uid-7213935-id-5749311.html
http://blog.chinaunix.net/uid-7213935-id-5753785.html
https://www.cnblogs.com/potato-coffee/p/4172009.html
https://blog.csdn.net/qq_20678703/article/details/54091926
media controller线程
1.概述
MCT线程是camera新架构的引擎部分,负责对管道的监控,由此来完成一个camera设备的控制运转。它运行在daemon进程空间,由MSM_CAMERA_NEW_SESSION事件来开启,具体开启函数为server_process_hal_event--->mct_controller_new()。
2.mct_controller_new()函数
此函数创建一个新的MCT引擎,这将对应一个事务的pipeline。我们知道上层可以创建多个事务,每个对应一个camera,也对应自己的MCT及pipeline等。因此这个函数的主要完成以下几件事情:
1.mct_pipeline_new()
---->创建一个Pipeline及其bus,并完成pipeline函数的映射。
2.mct_pipeline_start_session()
---->开启camera的所有模块并查询其能力
3.pthread_create(..., mct_controller_thread_run, ...)
---->创建mct线程并开始执行
4.pthread_create(..., mct_bus_handler_thread_run, ...)
---->创建bus处理线程
3.mct_list_traverse()函数
此函数在整个mct线程中大量使用,主要用来遍历所有模块并执行一些处理工作。结合前面所讲,camera各模块都具有统一的接口,通过流来连接,模块中又包含模块,根据这种特性高通使用链表结构来保存这些模块并设计了此函数用来完成遍历操作。
1.先来看看此链表的节点结构。链表的节点其实也是一个链表,整个链表就好像是一串串同级的节点搭建而成,整个数据结构组成一颗树结构。
struct _mct_list {
void *data; // 节点数据
mct_list_t *prev; // 上一个节点地址
mct_list_t **next; // 下一个节点节点元素数组首地址
uint32_t next_num; // 下一个节点节点元素数,大部分情况下为1
}mct_list_t;
2.通过递归的深度优先算法来遍历整棵树。
4.MCT线程运行
MCT整个引擎部分主要处理server及bus两类事情,对应前面提到的MCT及bus两个线程。MCT线程主要用来处理来自image server的消息,先pop MCT queue,查看是否有消息,如果有则执行mct_controller_proc_serv_msg_internal()函数来处理。
mct_controller_proc_serv_msg_internal函数用来处理来自image server的消息,并返回类型MCT_PROCESS_RET_SERVER_MSG。这里处理的消息类型主要有SERV_MSG_DS与SERV_MSG_HAL两种,分别在pipline中给出了相应的处理函数,具体查看源码可知。
5.bus线程运行
bus线程跟MCT线程流程一样。从代码上我们看到两个线程都是从同一个queue上面pop消息,他们是通过各自的线程条件变量来进行区分,完成线程的阻塞及运行工作。MCT的条件变量mctl_cond可以看到是在server_process.c文件中标记的,而bus的条件变量mctl_bus_handle_cond未在源码中找到标志的位置?
sensor模块
1.概述
sensor模块是众多模块中的一个,主要是由模组的各个硬件模块组成,包括sensor、Flash、Af、EEprom、OIS、CSI等。这个模块主要描述了模组硬件的一些工作原理及部分驱动相关部分。
2.module_sensor_init()函数 module_sensor.c
在前面讲到的server process中提到,服务进程开始后会初始化各个模块,其中就包括sensor模块,sensor初始化入口函数即为module_sensor_init(...)。这个函数将创建sensor模块并返回其指针,另外将创建它的端口,填充一些功能函数等。它的主要执行流程如下:
1.创建sensor的MCT module。 —— mct_module_create(name)
创建完之后填充set_mode、query_mode、start_session、stop_session、set _session _data、get_session_data接口函数。
2.创建module_sensro_ctrl_t结构体,此结构体包含bundle信息,用来构建前面提到的模块树(方便添加、遍历等操作)。
3.sensor模块是source模块,所以其numsinkports应该设置为0。[s_module->numsinkports=0]
4.初始化EEPROM BIN设备[eebin_interface_init\eebin_interface_control]
5.sensor的探测操作,用来探测有效的sensor;填入所有已探测到sensor的信息。[sensor_init_probe]
7.填入所以sensor的其它信息(Actuator,Flash,CSID,OIS等)。[module_sensor_find_other_suvdev]
8.初始化sensor模块。[mct_list_traverse(module_ctrl->sensor_bundle,module_sensors_subinit,NULL)]
9.创建基于CID info的端口[mct_list_traverse(module_ctrl->sensor_bundle,prot_sensor_create,s_module)]
10.初始化eeprom[mct_list_traverse(module_ctrl->sensor_bundle,module_sensor_init_eeprom,module_ctrl->eebin_hdl)]
最后.附上server.c的main函数:
/** main:
*
**/
int main(int argc __unused, char *argv[] __unused)
{
struct v4l2_event_subscription subscribe;
struct v4l2_event event;
mct_list_t *listen_fd_list = NULL;
read_fd_info_t *hal_fd = NULL, *hal_ds_fd = NULL, *mct_fds = NULL;
server_select_fds_t select_fds;
serv_proc_ret_t proc_ret;
int mct_t_ret = 0;
struct timespec timeToWait;
struct msm_v4l2_event_data *ret_data;
char serv_hal_node_name[MAX_DEV_NAME_SIZE];
char dev_name[MAX_DEV_NAME_SIZE];
char probe_done_node_name[MAX_DEV_NAME_SIZE];
char probe_done_dev_name[MAX_DEV_NAME_SIZE];
int probe_done_fd;
struct sensor_init_cfg_data cfg;
int ret, i, j;
mode_t old_mode;
struct sigaction default_sa;
boolean is_signal_registered = FALSE;
boolean is_timer_cb_registered = FALSE;
timer_t timer_id = 0;
uint32_t num_sessions = 0;
int32_t enabled_savemem = 0;
char savemem[92];
timer_t mct_timerid;
int32_t ret_subdev;
struct rlimit rl;
char prop[PROPERTY_VALUE_MAX];
int enable_fdleak = 0;
#ifdef GCOV_TOOL
int is_exit = 0;
char property_value[PROPERTY_VALUE_MAX] = {0};
#endif
old_mode = umask(S_IRWXO);
if (getrlimit(RLIMIT_CORE, &rl) == 0) {
CLOGD(CAM_MCT_MODULE,"get rimit core %d",rl.rlim_cur);
rl.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_CORE, &rl) != 0) {
CLOGE(CAM_MCT_MODULE,"can't get rlimit");
}
if (getrlimit(RLIMIT_CORE, &rl) != 0) {
CLOGE(CAM_MCT_MODULE,"can't get rlimit");
}
CLOGD(CAM_MCT_MODULE," set rimit core %d",rl.rlim_cur);
}
#if defined(LOG_DEBUG)
cam_debug_open();
#endif
CLOGD(CAM_MCT_MODULE, "CAMERA_DAEMON: start of camera Daemon");
/* 1. find server node name and open the node */
if (get_server_node_name(serv_hal_node_name) == FALSE)
goto bad_node_fd;
hal_fd = malloc(sizeof(read_fd_info_t));
if (hal_fd == NULL)
goto bad_node_fd;
snprintf(dev_name, sizeof(dev_name), "/dev/%s", serv_hal_node_name);
hal_fd->fd[0] = open(dev_name, O_RDWR | O_NONBLOCK);
if (hal_fd->fd[0] < 0)
goto open_hal_fail;
hal_fd->type = RD_FD_HAL;
listen_fd_list = mct_list_append(listen_fd_list, hal_fd, NULL, NULL);
if (!listen_fd_list)
goto list_append_fail;
property_get("cameradaemon.SaveMemAtBoot", savemem, "0");
enabled_savemem = atoi(savemem);
CLOGD(CAM_MCT_MODULE, "CAMERA_DAEMON: start all modules init");
/* 2. after open node, initialize modules */
if(server_process_module_sensor_init() == FALSE)
goto module_init_fail;
CLOGD(CAM_MCT_MODULE, "CAMERA_DAEMON:End of all modules init");
if (enabled_savemem != 1) {
if (server_process_module_init() == FALSE)
goto module_init_fail;
}
/* Subcribe V4L2 event */
memset(&subscribe, 0, sizeof(struct v4l2_event_subscription));
subscribe.type = MSM_CAMERA_V4L2_EVENT_TYPE;
for (i = MSM_CAMERA_EVENT_MIN + 1; i < MSM_CAMERA_EVENT_MAX; i++) {
subscribe.id = i;
if (ioctl(hal_fd->fd[0], VIDIOC_SUBSCRIBE_EVENT, &subscribe) < 0)
goto subscribe_failed;
}
signal_received = 0;
select_fds.select_fd = hal_fd->fd[0];
/* create a timer */
mct_t_ret = mct_util_create_timer(&mct_timerid);
ret_subdev = mct_util_find_v4l2_subdev(probe_done_node_name);
if (ret_subdev < 0) {
CLOGE(CAM_MCT_MODULE, "can not find subdev");
goto subscribe_failed;
}
snprintf(probe_done_dev_name, sizeof(probe_done_dev_name), "/dev/%s",
probe_done_node_name);
probe_done_fd = open(probe_done_dev_name, O_RDWR | O_NONBLOCK);
if (probe_done_fd < 0)
goto subscribe_failed;
cfg.cfgtype = CFG_SINIT_PROBE_DONE;
if (ioctl(probe_done_fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg) < 0) {
CLOGE(CAM_MCT_MODULE, "ioctl SENSOR_INIT_CFG failed");
ret = FALSE;
}
close(probe_done_fd);
CLOGD(CAM_MCT_MODULE, "CAMERA_DAEMON:waiting for camera to open");
do {
FD_ZERO(&(select_fds.fds));
mct_list_traverse(listen_fd_list, server_reset_select_fd, &select_fds);
/* no timeout */
ret = select(select_fds.select_fd + 1, &(select_fds.fds), NULL, NULL, NULL);
if (ret > 0) {
mct_list_t *find_list;
read_fd_info_t *fd_info;
find_list = mct_list_find_custom(listen_fd_list, &(select_fds.fds),
server_check_listen_fd);
if (!find_list)
continue;
fd_info = (read_fd_info_t *)find_list->data;
switch (fd_info->type) {
case RD_FD_HAL: {
if (ioctl(fd_info->fd[0], VIDIOC_DQEVENT, &event) < 0) {
continue;
}
/* server process HAL event:
*
* 1. if it returns success, it means the event message has been
* posted to MCT, don't need to send CMD ACK back to kernel
* immediately, because MCT will notify us after process;
*
* 2. if it returns failure, it means the event message was not
* posted to MCT successfully, hence we need to send CMD ACK back
* to kernel immediately so that HAL thread which sends this
* event can be blocked.
*/
/* start timer for new and delete session*/
if ((event.id == MSM_CAMERA_NEW_SESSION) ||
(event.id == MSM_CAMERA_DEL_SESSION)) {
timeToWait.tv_sec = MCT_THREAD_TIMEOUT;
timeToWait.tv_nsec = 0;
if (!mct_t_ret)
mct_util_set_timer(&timeToWait,&mct_timerid);
}
proc_ret = server_process_hal_event(&event);
/* stop timer for new and delete session*/
if ((event.id == MSM_CAMERA_NEW_SESSION) ||
(event.id == MSM_CAMERA_DEL_SESSION)) {
timeToWait.tv_sec = 0;
timeToWait.tv_nsec = 0;
if (!mct_t_ret)
mct_util_set_timer(&timeToWait,&mct_timerid);
}
}
break;
case RD_DS_FD_HAL:
/* server process message sent by HAL through Domain Socket */
proc_ret = server_process_hal_ds_packet(fd_info->fd[0],
fd_info->session);
break;
case RD_PIPE_FD_MCT:
/* server process message sent by media controller
* through pipe: */
proc_ret = server_process_mct_msg(fd_info->fd[0],
fd_info->session);
break;
default:
continue;
} /* switch (fd_info->type) */
switch (proc_ret.result) {
case RESULT_NEW_SESSION: {
struct msm_v4l2_event_data *ret_data =
(struct msm_v4l2_event_data *)proc_ret.ret_to_hal.ret_event.u.data;
if( ret_data->status == MSM_CAMERA_CMD_SUCESS) {
num_sessions++;
hal_ds_fd = malloc(sizeof(read_fd_info_t));
if (!hal_ds_fd) {
/* Shouldn't end directly, need to shutdown MCT thread */
goto server_proc_new_session_error;
} else {
hal_ds_fd->session = proc_ret.new_session_info.session_idx;
hal_ds_fd->fd[0] = proc_ret.new_session_info.hal_ds_fd;
hal_ds_fd->type = RD_DS_FD_HAL;
}
mct_fds = malloc(sizeof(read_fd_info_t));
if (!mct_fds) {
free(hal_ds_fd);
goto server_proc_new_session_error;
} else {
mct_fds->session = proc_ret.new_session_info.session_idx;
mct_fds->fd[0] = proc_ret.new_session_info.mct_msg_rd_fd;
mct_fds->fd[1] = proc_ret.new_session_info.mct_msg_wt_fd;
mct_fds->type = RD_PIPE_FD_MCT;
}
listen_fd_list = mct_list_append(listen_fd_list,hal_ds_fd,NULL,NULL);
if (!listen_fd_list) {
free(hal_ds_fd);
free(mct_fds);
goto server_proc_new_session_error;
}
listen_fd_list = mct_list_append(listen_fd_list, mct_fds, NULL,NULL);
if (!listen_fd_list) {
free(hal_ds_fd);
free(mct_fds);
goto server_proc_new_session_error;
}
} else {
CLOGE(CAM_MCT_MODULE, "New session [%d] creation failed with error",
ret_data->session_id);
}
if (TRUE == server_register_for_kill_signal(&default_sa)) {
is_signal_registered = TRUE;
}
if (num_sessions == 1) {
if (TRUE == server_register_timer_cb(&timer_id)) {
is_timer_cb_registered = TRUE;
}
}
goto check_proc_ret;
} /* RESULT_NEW_SESSION */
break;
case RESULT_DEL_SESSION: {
mct_list_t *find_list;
read_fd_info_t fd_info_match;
read_fd_info_t *ds_fd_info = NULL, *mct_fd_info = NULL;
struct msm_v4l2_event_data *event_data = (struct msm_v4l2_event_data *)
&proc_ret.ret_to_hal.ret_event.u.data[0];
num_sessions--;
/* this is for Domain Socket FD */
fd_info_match.type = RD_DS_FD_HAL;
fd_info_match.session = event_data->session_id;
find_list = mct_list_find_custom(listen_fd_list, &fd_info_match,
server_find_listen_fd);
if (find_list) {
ds_fd_info = (read_fd_info_t *)find_list->data;
listen_fd_list = mct_list_remove(listen_fd_list, ds_fd_info);
FD_CLR(ds_fd_info->fd[0], &select_fds.fds);
close(ds_fd_info->fd[0]);
free(ds_fd_info);
}
/* this is for MCT FD */
fd_info_match.type = RD_PIPE_FD_MCT;
find_list = mct_list_find_custom(listen_fd_list, &fd_info_match,
server_find_listen_fd);
if (find_list) {
mct_fd_info = (read_fd_info_t *)find_list->data;
listen_fd_list = mct_list_remove(listen_fd_list, mct_fd_info);
FD_CLR(mct_fd_info->fd[0], &select_fds.fds);
close(mct_fd_info->fd[0]);
close(mct_fd_info->fd[1]);
free(mct_fd_info);
}
#ifdef FDLEAK_FLAG
property_get("persist.camera.fdleak.enable", prop, "0");
enable_fdleak = atoi(prop);
if (enable_fdleak) {
CLOGI(CAM_MCT_MODULE,"fdleak tool is disable for camera daemon");
mct_degug_dump_fdleak_trace();
}
#endif
if (num_sessions == 0 && is_timer_cb_registered) {
if (timer_delete(timer_id)) {
CLOGE(CAM_MCT_MODULE, "Failed to delete callback timer \
w/ errno %s", strerror(errno));
}
is_timer_cb_registered = FALSE;
}
} /* case RESULT_DEL_SESSION */
goto check_proc_ret;
break;
case RESULT_FAILURE:
goto server_proc_error;
break;
case RESULT_SUCCESS:
goto check_proc_ret;
break;
default:
break;
} /* switch (proc_ret.result) */
server_proc_new_session_error:
event.id = MSM_CAMERA_DEL_SESSION;
server_process_hal_event(&event);
server_proc_error:
proc_ret.ret_to_hal.ret = TRUE;
check_proc_ret:
if (proc_ret.ret_to_hal.ret == TRUE) {
switch (proc_ret.ret_to_hal.ret_type) {
/* @MSM_CAM_V4L2_IOCTL_CMD_ACK is Acknowledge to HAL's
* control command, which has command processing status.
*/
case SERV_RET_TO_HAL_CMDACK:
ioctl(hal_fd->fd[0], MSM_CAM_V4L2_IOCTL_CMD_ACK,
(struct msm_v4l2_event_data *)&(proc_ret.ret_to_hal.ret_event.u.data));
break;
/* @MSM_CAM_V4L2_IOCTL_NOTIFY is MCT originated event such
* as meta data, SOF etc. Normally it comes
* 1. domain socket buffer mapping process;
* 2. from MCT.
*/
case SERV_RET_TO_HAL_NOTIFY:
ioctl(hal_fd->fd[0], MSM_CAM_V4L2_IOCTL_NOTIFY,
&(proc_ret.ret_to_hal.ret_event.u.data));
break;
/* @MMSM_CAM_V4L2_IOCTL_NOTIFY_META is Meta data notification
* sent back to HAL during streaming. It is generated by
* BUS message.
*/
case SERV_RET_TO_KERNEL_NOTIFY:
ioctl(hal_fd->fd[0], MSM_CAM_V4L2_IOCTL_NOTIFY_DEBUG,
&(proc_ret.ret_to_hal.ret_event.u.data));
break;
case SERV_RET_TO_HAL_NOTIFY_ERROR:
CLOGE(CAM_MCT_MODULE, "main: camera daemon notify error");
ioctl(hal_fd->fd[0], MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR,
&(proc_ret.ret_to_hal.ret_event.u.data));
break;
default:
break;
}
}
#ifdef GCOV_TOOL
property_get("persist.camera.debug.exit", property_value, "0");
is_exit = atoi(property_value);
if(is_exit && (RESULT_DEL_SESSION == proc_ret.result)) {
extern void __gcov_flush();
__gcov_flush();
}
#else
if(signal_received) {
goto server_down;
}
#endif
} else {
/* select failed. it cannot time out.*/
/* TO DO: HANDLE ERROR */
int rc = 0;
CLOGE(CAM_MCT_MODULE, "Exit from select w/ errno %s", strerror(errno));
if(signal_received) {
CLOGE(CAM_MCT_MODULE, "sending MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR");
rc = ioctl(hal_fd->fd[0], MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR,
&(proc_ret.ret_to_hal.ret_event.u.data));
if (rc < 0) {
CLOGE(CAM_MCT_MODULE, "ioctl MSM_CAM_V4L2_IOCTL_NOTIFY_ERROR \
failed rc:%d", rc);
}
}
}
} while (1);
server_down:
// restore defualt signal handle
if(TRUE == is_signal_registered) {
if (sigaction(SIGTERM, &default_sa, NULL)) {
CLOGE(CAM_MCT_MODULE, "Failed to restore default SIGTERM signal handlers\
w/ errno %s", strerror(errno));
}
if (sigaction(SIGUSR1, &default_sa, NULL)) {
CLOGE(CAM_MCT_MODULE, "Failed to restore default SIGUSR1 signal handlers\
w/ errno %s", strerror(errno));
}
}
if(TRUE == is_timer_cb_registered) {
if (timer_delete(timer_id)) {
CLOGE(CAM_MCT_MODULE, "Failed to delete callback timer \
w/ errno %s", strerror(errno));
}
is_timer_cb_registered = FALSE;
}
/* while exit, if we let system to close domain socket FDn
* then there can be race condition where HAL gets DAEMON_DIED, calls unmap
* that can stuck as camera daemon is exiting.
*/
if (hal_ds_fd) {
close(hal_ds_fd->fd[0]);
}
subscribe_failed:
memset(&subscribe, 0, sizeof(struct v4l2_event_subscription));
subscribe.type = MSM_CAMERA_V4L2_EVENT_TYPE;
for (j = MSM_CAMERA_EVENT_MIN + 1; j < i; j++) {
subscribe.id = j;
ioctl(hal_fd->fd[0], VIDIOC_UNSUBSCRIBE_EVENT, &subscribe);
}
module_init_fail:
mct_list_remove(listen_fd_list, hal_fd);
list_append_fail:
close(hal_fd->fd[0]);
open_hal_fail:
free(hal_fd);
bad_node_fd:
#if defined(LOG_DEBUG)
cam_debug_close();
#endif
return 0;
}