本文转载自: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 */

最新文章

  1. python 生成器等语法
  2. Git版本控制管理学习笔记2--起步
  3. VC++ 关于 ON_UPDATE_COMMAND_UI 相关的作用.
  4. NPOI 读写Excel
  5. javascript onload队列
  6. 使用微信JS-SDK 实现 自定义 分享 功能
  7. codevs 1294 全排列 next_permuntation
  8. C++11包装引用
  9. Mint Linuxubuntu 字体配置文件
  10. Android 侧边栏(使用Support Library 4提供的扩展组件)
  11. Maven tomcat插件配置和使用
  12. android Activity 生命周期
  13. 【leetcode】Single Number II
  14. js调用跨域
  15. Codeforces899C Dividing the numbers(数论)
  16. TZOJ 3134: 渊子赛马修改版
  17. IE 浏览器background image 属性问题
  18. JAVA体系的线程的实现,线程的调度,状态的转换
  19. docker入门(三)
  20. oracle中的decode的使用(转)

热门文章

  1. springboot 整合 tobato 的 fastdfs 实现文件上传和下载
  2. 修改SpringBoot启动时的默认Banner图案
  3. 嘉馨学姐又双叒叕来吃包子了 QDUOJ 模拟 尺度法
  4. java int转Short
  5. CodeForces - 714E + POJ - 3666 (dp严格单调递增与非严格单调递增)
  6. PCIe基础篇(二)、协议详解
  7. vue项目1-pizza点餐系统9-axios实现数据存储
  8. 通过runtime对类别进行属性的扩展
  9. win10系统中photoshop cs6中界面字体太小的解决方法
  10. Oracle 赋予指定用户dba权限