Qualcomm 8X camera过程解析【转】
本文转载自:http://blog.csdn.net/gabbzang/article/details/19906687
http://www.01yun.com/mobile_development/20130327/283166.html
Qualcomm 8X camera daemon进程浅析
Camera
先看一下抽象层的主要流程:
首先启动一个守护进程
Main()(camdaemon.c)
int qcamsvr_start(void)( qcamsvr.c)
{
1. server_fd = open(server_dev_name, O_RDWR);//打开服务对应的文件节点
2. if (mctl_load_comps()) //加载所有需要的组件
3. rc = qcamsvr_load_gesture_lib(&gesture_info.gesture_lib);//加载手势库
4. ez_server_socket_id = eztune_setup_server("127.0.0.1", "55555");
if (pipe(ez_cmd_pipe)
ez_prev_server_socket_id = eztune_setup_server("127.0.0.1", "55556");
if(pipe(ez_prev_cmd_pipe)
//创建两个socket端口,同时建立两个pipe文件对两个端口进行监控
5. if (get_mctl_node_info(server_fd, &mctl_node_info))//通过服务节点获取服务的相关信息
{
//此处获取的是内核中调用msm_sensor_register()注册的sensor节点信息
}
6. sub.type = V4L2_EVENT_ALL;
rc = ioctl(server_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);//通过服务设备文件的ioctl接口,订阅所有的事件
7. config_arg.server_fd = server_fd;
config_arg.ez_read_fd = ez_cmd_pipe[0];
config_arg.ez_write_fd = ez_cmd_pipe[1];
config_arg.ez_prev_read_fd = ez_prev_cmd_pipe[0];
config_arg.ez_prev_write_fd = ez_prev_cmd_pipe[1];//初始化配置线程的参数
8.下面就是一个循环,对这几个文件进行poll
do {
fds[0].fd = server_fd;
fds[0].events = POLLPRI;
fds[1].fd = ez_server_socket_id;
fds[1].events = POLLIN;
fds[2].fd = ez_prev_server_socket_id;
fds[2].events = POLLIN;
rc = poll(fds, 3, timeoutms);
if (fds[0].revents & POLLPRI) { /* Server Node Wake Up *
//对服务的设备文件进行监视,当遇到打开事件的时候,立即创建一个配置线程
rc = qcamsvr_process_server_node_event(&config_arg, &mctl_node_info,
&gesture_info);
}
//线面就是对两个socket进程监视和处理。
if ((fds[1].revents & POLLIN) == POLLIN) { /* EzTune Server */
int client_socket_id;
client_socket_id = accept(ez_server_socket_id,
(struct sockaddr *)&addr_client_inet, &addr_client_len);
write(ez_cmd_pipe[1], &client_socket_id, sizeof(int));
}
}
if ((fds[2].revents & POLLIN) == POLLIN) { /* EzTune Prev Server */
int client_socket_id;
client_socket_id = accept(ez_prev_server_socket_id,
(struct sockaddr *)&addr_client_inet, &addr_client_len);
write(ez_prev_cmd_pipe[1], &client_socket_id, sizeof(int));
}
}
} /* Else for Poll rc */
} while (1);
}
下面进入配置线程创建的流程:
//取出服务节点产生的事件,然后根据配置节点的名称,分发给各自独立的主控制线程
1. static int qcamsvr_process_server_node_event()
{
//下命令让服务模块的事件出队列进行处理
rc = ioctl(config_arg->server_fd, VIDIOC_DQEVENT, &v4l2_evt);
if (v4l2_evt.type == V4L2_EVENT_PRIVATE_START + MSM_GES_RESP_V4L2)
{
//如果是手势事件,则进行一系列的处理
if (ctrl->type == MSM_V4L2_GES_OPEN) {
//设置主控线程的接口
p_gesture_info->cam_mctl.svr_ops.launch_mctl_thread =
create_v4l2_conf_thread;
//设置主控线程的退出接口
p_gesture_info->cam_mctl.svr_ops.release_mctl_thread =
destroy_v4l2_cam_conf_thread;
//设置camera使能
p_gesture_info->cam_mctl.svr_ops.camera_available =
qcamsvr_camera_available;
//设置服务设备文件的文件句柄
p_gesture_info->cam_mctl.svr_ops.server_fd = config_arg->server_fd;
//创建手势服务
status = p_gesture_info->gesture_lib.gesture_service_create(
&p_gesture_info->cam_mctl, &p_gesture_info->observer);
}
else if (ctrl->type == MSM_V4L2_GES_CLOSE) {
//消亡手势服务
status = p_gesture_info->gesture_lib.gesture_service_send_data(ctrl)
}
if ((status == CAMERA_SUCCESS) &&
(ctrl->type != MSM_V4L2_GES_CLOSE)) {
//如果成功,且文件打开,则向手势服务发送数据
status = p_gesture_info->gesture_lib.gesture_service_send_data(ctrl);
if (status != CAMERA_SUCCESS) {
LOGE("gesture_service_send_data failed");
}
} else {
if (ctrl->type == MSM_V4L2_GES_CLOSE) {
ctrl->status = CAM_CTRL_SUCCESS;
} else {
LOGE("gesture send failure message");
ctrl->status = CAM_CTRL_FAILED;
}
//将操作结果反馈给camera服务
v4l2_ioctl.ioctl_ptr = ctrl;
qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);
//如果是camera事件,则进行一系列的处理
}
else if (v4l2_evt.type == V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_V4L2)
{
if (ctrl->type == MSM_V4L2_OPEN) {
//通过pipe进行一些初始化工作
//创建一个核心的线程
if ((tmp_mctl_struct->handle =
create_v4l2_conf_thread(config_arg)) == NULL)
//反馈结果给camera服务端
ctrl->status = CAM_CTRL_SUCCESS;
v4l2_ioctl.ioctl_ptr = ctrl;
qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);
}
else if (ctrl->type == MSM_V4L2_CLOSE){
//进行一些消亡工作
//通过写一些pipe
if (destroy_v4l2_cam_conf_thread(tmp_mctl_struct->handle) < 0) //消亡只线程
ctrl->status = CAM_CTRL_SUCCESS;
v4l2_ioctl.ioctl_ptr = ctrl;
//反馈结果给camera服务
qcamsvr_send_ctrl_cmd_done(config_arg->server_fd, &v4l2_ioctl);
}
else {
//通过pipe写一些命令,等待配置返回
}
}
//首先来看一下刚刚的线程创建函数
void *create_v4l2_conf_thread(struct config_thread_arguments* arg)
{
//核心工作就是创建了个线程
rc = pthread_create(&pme->cam_mctl_thread_id, NULL, cam_mctl_thread, pme);
}
//下面进入创建的配置线程的主函数:
static void *cam_mctl_thread(void *data)(mctl.c)
{
//首先初始化需要监控的文件句柄
pipe_readfd = arg->read_fd;
pipe_writefd = arg->write_fd;
server_fd = arg->server_fd;
ez_pipe_readfd = arg->ez_read_fd;
ez_client_fd = -1;
ez_prev_pipe_readfd = arg->ez_prev_read_fd;
ez_prev_client_fd = -1;
//向对应的配置节点下命令监控所有事件(此文件句柄具体标识什么意思暂时还没搞清楚)
sub.type = V4L2_EVENT_ALL;
rc = ioctl(cam_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
//下面开始进入循环的监控
do {
//文件句柄初始化
fds[0].fd = cam_fd;
fds[0].events = POLLPRI;
fds[1].fd = pipe_readfd;
fds[1].events = POLLPRI | POLLIN;
fds[2].fd = ez_pipe_readfd;
fds[2].events = POLLIN;
fds[3].fd = ez_client_fd;
fds[3].events = POLLIN;
fds[4].fd = ez_prev_pipe_readfd;
fds[4].events = POLLIN;
fds[5].fd = ez_prev_client_fd;
fds[5].events = POLLIN;
/* evt/msg from qcam server */
if (ctrl->type == MSM_V4L2_CLOSE) {
//关闭所有的资源
config_shutdown_pp(pme->p_cfg_ctrl);
//反馈结果给服务
rc = mctl_send_ctrl_cmd_done(pme->p_cfg_ctrl, NULL, TRUE);
}
else {
//此函数为用户控件的APP处理对应的命令
if (mctl_proc_v4l2_request(pme, ctrl) < 0)
}
/* evt/msg from config node */
rc = ioctl(cam_fd, VIDIOC_DQEVENT, &v4l2_event);//下事件出队列的命令
if (v4l2_event.type ==
V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_DIV_FRAME_EVT_MSG) {
//进程对应的帧转移
mctl_pp_divert_frame(p_cfg_ctrl,
(void *)&(event_data.isp_data.div_frame));
}else if(v4l2_event.type ==
V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_MCTL_PP_EVENT) {
//处理后置的事件
mctl_pp_proc_event(p_cfg_ctrl,
(void *)&(event_data.isp_data.pp_event_info));
}
else if (v4l2_event.type ==
V4L2_EVENT_PRIVATE_START + MSM_CAM_RESP_STAT_EVT_MSG) {
//处理正常的事件消息
mctl_proc_event_message (pme, isp_adsp);
}
else {
CDBG_HIGH("%s: Error: should not be here", __func__);
}
/* evt/msg from eztune pipe */
if (ez_client_fd > 0)
mctl_eztune_server_connect(pme, ez_client_fd);
/* evt/msg from eztune client */
if (ez_client_fd > 0) {
mctl_eztune_read_and_proc_cmd(EZ_MCTL_SOCKET_CMD);
/* evt/msg from eztune prev pipe */
if (ez_prev_client_fd > 0)
mctl_eztune_prev_server_connect(pme, ez_prev_client_fd);
}
/* evt/msg from eztune prev client */
if ((fds[5].revents & POLLIN) == POLLIN) {
if (ez_prev_client_fd > 0) {
mctl_eztune_read_and_proc_cmd(EZ_MCTL_PREV_SOCKET_CMD);
}
}
}wile(TRUE)
//循环结束取消订阅所有消息
if (ioctl(cam_fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub) < 0)
}
先来看看camera在硬件抽象层的接口:
主要是三点:
1. preview:预览
2. recording 录像
3. picture 拍照
模块接口函数:
get_number_of_cameras: get_number_of_cameras,
get_camera_info: get_camera_info,
camera_info_t:
typedef struct {
int modes_supported;//支持的模式
int8_t camera_id;//id标识
cam_position_t position;//前摄还是后摄
uint32_t sensor_mount_angle;//角度
}camera_info_t;
我们目前使用的高通8X平台:
Camera模块:
此模块有一个全局的camera服务结构体实例,用于全局管理各种子系统设备。
子系统设备常见的有:
enum msm_cam_subdev_type {
CSIPHY_DEV,
CSID_DEV,
CSIC_DEV,
ISPIF_DEV,
VFE_DEV,
AXI_DEV,
VPE_DEV,
SENSOR_DEV,
ACTUATOR_DEV,
EEPROM_DEV,
GESTURE_DEV,
};
定义一个抽象的camera服务设备:
struct msm_cam_server_dev {
/* config node device*/
struct platform_device *server_pdev;
/* server node v4l2 device */
struct v4l2_device v4l2_dev;
struct video_device *video_dev;
struct media_device media_dev;
/* info of sensors successfully probed*/
struct msm_camera_info camera_info;
/* info of configs successfully created*/
struct msm_cam_config_dev_info config_info;
/* active working camera device - only one allowed at this time*/
struct msm_cam_v4l2_device *pcam_active;
/* number of camera devices opened*/
atomic_t number_pcam_active;
struct v4l2_queue_util server_command_queue;
/* This queue used by the config thread to send responses back to the
* control thread. It is accessed only from a process context.
*/
struct msm_cam_server_queue server_queue[MAX_NUM_ACTIVE_CAMERA];
uint32_t server_evt_id;
struct msm_cam_server_mctl_inst mctl[MAX_NUM_ACTIVE_CAMERA];
uint32_t mctl_handle_cnt;
int use_count;
/* all the registered ISP subdevice*/
struct msm_isp_ops *isp_subdev[MSM_MAX_CAMERA_CONFIGS];
/* info of MCTL nodes successfully probed*/
struct msm_mctl_node_info mctl_node_info;
struct mutex server_lock;
struct mutex server_queue_lock;
/*v4l2 subdevs*/
struct v4l2_subdev *csiphy_device[MAX_NUM_CSIPHY_DEV];
struct v4l2_subdev *csid_device[MAX_NUM_CSID_DEV];
struct v4l2_subdev *csic_device[MAX_NUM_CSIC_DEV];
struct v4l2_subdev *ispif_device;
struct v4l2_subdev *vfe_device[MAX_NUM_VFE_DEV];
struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];
struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];
struct v4l2_subdev *gesture_device;
};
从控制流的角度来分析下camera的流程。
首先camera会启动一个daemon进程来进行核心的操作。
启动daemon进程的地方:
在init.target.rc文件中
#start camera server as daemon
service qcamerasvr /system/bin/mm-qcamera-daemon
class late_start
user system
group system camera inet
生成此mm-qcamera-daemon bin档的地方:
Android\vendor\qcom\proprietary\mm-camera\apps\appslib\Android.mk
此mk文件生成了mm-qcamera-daemon bin档
Daemon进程的入口函数:mian()(camdaemon.c)
一个camera的守护进程在init进程中,开启的一个service |
此线程与具体的sensor相关联,负责对sensor进行具体细节的操作 |
此为daemon进程的主线程,从server node收集事件,纷发给mctl thread,根据config的name,与server节点进行队列,不断轮询其事件队列,获取command,进行全局处理 |
mctl_pp_poll_thread |
mctl thread |
main daemon thread |
Daemon |
此线程与kernel中config节点进行通信,轮询节点的消息队列中获得command,进行全局处理 (每一个config节点都对应一个mctl thread) |
抽象层到内核层的大致流程:
抽象层主要通过server node和config node将command下到内核,对应的节点驱动将command通过事件队列进行管理。
而daemon进程通过开启对应的线程,不停的对事件队列进行轮询,处理上层下的command
在main daemon thread中重要的任务:
一:将sensor操作关联的硬件组件加载进来,还要加载一些必备的库,为camera的正式工作铺垫环境:
① AXI_comp_create
② sensor_comp_create
③ flash_led_comp_create
④ flash_strobe_comp_create
⑤ CAMIF_comp_create
⑥ VFE_comp_create
⑦ ACTUATOR_comp_create
⑧ eeprom_comp_create
⑨ mctl_load_stats_proc_lib
⑩ mctl_load_frame_proc_lib
二.线程的循环工作
线程,顾名思义,肯定有一个封闭的循环体,在循环体中做一些核心的操作
而Daemon进程的主线程轮询服务节点的event queue,获取事件,纷发给各自的mctl thread
Daemon进行的主线程主要处理一下基类事件“
① MSM_GES_RESP_V4L2 :
Open:主要进行初始化,铺垫环境,开启处理camera细节活动的线程
Close:进行一些善后工作
② MSM_CAM_RESP_V4L2:处理open和colse
Open:主要进行初始化,铺垫环境,开启处理camera细节活动的线程
Close:进行一些善后工作
③ 其他一些事件都是通过pipe通信直接写入到①②两点创建的线程中(send command through pipe and wait for config to return)
在mctl thread中重要的任务:
一. 打开confing节点文件
二. 调用create_camfd_receive_socket猜测是与硬件抽象层进行直接通信的
三. 创建mctl_pp_poll_thread线程,
四. 初始化camera的几个feature:
①zoom_init_ctrl
②bestshot_init
③hdr_init
五.通过pipe通信获取server节点的控制事件,事件由Daemon进程的主控线程获取并且通过pipe传递过来
六.通过监测config节点的事件获取config节点对应的控制command
主要监测三类事件:
① MSM_CAM_RESP_DIV_FRAME_EVT_MSG
② MSM_CAM_RESP_MCTL_PP_EVENT
③ MSM_CAM_RESP_STAT_EVT_MSG
将这三个事件以command的形式,通过pipe通信发送到(一)中创建的PP线程中
在mctl_pp_poll_thread中重要的任务:
一:对几个pipe文件进行监测,与其他线程进行交互
几种事件:
①/* Events on pipe between mctl thread - mctl pp thread */
②/* Events on user created socket */
③/* Events on mctl pp node */
④/* Events on pipe between mctl pp thread and c2d thread */
Qualcomm 8X camera过程解析【转】的更多相关文章
- Qualcomm 8X camera daemon进程浅析
Camera 先看一下抽象层的主要流程: 首先启动一个守护进程 int qcamsvr_start(void)( qcamsvr.c) { 1. server_fd = open(server_dev ...
- Qualcomm平台camera调试移植入门
1 camera基本代码架构 高通平台对于camera的代码组织,大体上还是遵循Android的框架:即上层应用和HAL层交互,高通平台在HAL层里面实现自己的一套管理策略:在kernel中实现se ...
- MHA自动Failover过程解析(updated) 转
允许转载, 转载时请以超链接形式标明文章原始出处和网站信息 http://www.mysqlsystems.com/2012/03/figure-out-process-of-autofailover ...
- SpringBoot的自动配置原理过程解析
SpringBoot的最大好处就是实现了大部分的自动配置,使得开发者可以更多的关注于业务开发,避免繁琐的业务开发,但是SpringBoot如此好用的 自动注解过程着实让人忍不住的去了解一番,因为本文的 ...
- WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析
WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析 FeatureService也称要素服务,其最大的好处就是支持在线要素编辑,并将编辑同步更新到后 ...
- InnoDB recovery过程解析
本文来自网易云社区. InnoDB如果发生意外宕机了,数据会丢么?对于这个问题,稍微了解一点MySQL知识的人,都会斩钉截铁的回答:不会!为什么?他们也会毫不犹豫的说:因为有重做日志(redo log ...
- Mybatis拦截器执行过程解析
上一篇文章 Mybatis拦截器之数据加密解密 介绍了 Mybatis 拦截器的简单使用,这篇文章将透彻的分析 Mybatis 是怎样发现拦截器以及调用拦截器的 intercept 方法的 小伙伴先按 ...
- ASP.NET Core on K8S深入学习(2)部署过程解析与Dashboard
上一篇<K8S集群部署>中搭建好了一个最小化的K8S集群,这一篇我们来部署一个ASP.NET Core WebAPI项目来介绍一下整个部署过程的运行机制,然后部署一下Dashboard,完 ...
- 微信小程序 空白页重定向---二维码扫描第二次进入 不经过onLoad过程解析scene参数,跳转问题
在刚开始的时候将小程序的入口文件直接指向tabbar 的首页,此时出现问题:二维码扫描,第一次不关闭首页,第二次进入时:不会经过onLoad过程解析scene参数: 官方中解释:tabbar跳转方式触 ...
随机推荐
- Linux文件与目录操作 ls 命令(2)
说文件操作是最频繁地操作也不为过,在Linux中,使用ls命令可以列出当前目录中所有内容,本篇就先说说ls命令.本文所说的文件指文件和目录. ls命令常用选项 -a:显示指定目录下所有子目录与文件,包 ...
- P3958奶酪
这个题是2017noip提高组的真题,是一个深度搜索题,得分轨迹:10-80-100pts. 在三维空间里,存在可能连通的洞(半径r),问是否可以通过这些洞从底部到达顶部.一开始我联想到了01迷宫,以 ...
- tcp和udp详解??
TCP:面向连接的可靠传输 tcp规定了:传输服务必须建立连接 传输结束必须断开连接 传输数据必须保证可靠 数据的可靠性:无重复.无丢失.无失序.无差错. 建立连接(三次握手): ...
- C语言--一维数组,字符数组
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zuoyou1314/article/details/30799519 watermark/2/tex ...
- ISC2016训练赛 phrackCTF--Smali
测试文件:https://static2.ichunqiu.com/icq/resources/fileupload/phrackCTF/REVERSE/Crackme.smali 参考资料:http ...
- arcgis server10.2自带打印模板路径
找到arcgis server10.2安装目录路径,我的安装路径为C盘,如下: C:\Program Files\ArcGIS\Server\Templates\ExportWebMapTemplat ...
- 修改默认select样式
<style type="text/css"> .select_demo,.select_list { width: 400px; height: 60px; } .s ...
- vue : 无法加载文件 C:\Users\XXX\AppData\Roaming\npm\vue.ps1,因为在此系统上禁止运行脚本
问题: 使用命令行安装完成vue/cli后,使用vue ui无法创建demo vue : 无法加载文件 C:\Users\yangx\AppData\Roaming\npm\vue.ps1,因为在此系 ...
- 图解Qt安装(Windows平台)
http://c.biancheng.net/view/3858.html 本节介绍 Qt 5.9.0 在 Windows 平台下的安装,请提前下载好 Qt 5.9.0.不知道如何下载 Qt 的读者请 ...
- /proc/sys/fs/file-max
Linux的/proc/sys/fs/file-max决定了当前内核可以打开的最大的文件句柄数. 查看当前的值: cat /proc/sys/fs/file-max 这个值在kernel的文档里是这样 ...