Qualcomm 8X camera daemon进程浅析

Camera

先看一下抽象层的主要流程:

首先启动一个守护进程

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 =

//反馈结果给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 */

时间: 2024-10-31 18:40:45

Qualcomm 8X camera daemon进程浅析的相关文章

qualcomm platform camera porting

1  camera基本代码架构 高通平台对于camera的代码组织,大体上还是遵循Android的框架:即上层应用和HAL层交互,高通平台在HAL层里面实现自己的一套管理策略:在kernel中实现sensor的底层驱动.但是,对于最核心的sensor端的底层设置.ISP效果相关等代码则是单独进行了抽离,放在了一个daemon进程中进行管理: 图1 Qualcomm平台camera代码架构简图 由于高通把大部分具体的设置及参数放到了daemon进程中,所以在kernel部分只是进行了V4L2的设备

Qualcomm平台camera调试移植入门

1  camera基本代码架构 高通平台对于camera的代码组织,大体上还是遵循Android的框架:即上层应用和HAL层交互,高通平台在HAL层里面实现自己的一套管理策略: 在kernel中实现sensor的底层驱动.但是,对于最核心的sensor端的底层设置.ISP效果相关等代码则是单独进行了抽离,放在了一个 daemon进程中进行管理: 图1 Qualcomm平台camera代码架构简图 由于高通把大部分具体的设置及参数放到了daemon进程中,所以在kernel部分只是进行了V4L2的

daemon进程

daemon进程创建步骤 mydaemon.c #include <stdio.h> #include <stdlib.h> #include <time.h> #include <sys/stat.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <signal.h> #include <fcntl.h&g

Linux daemon进程原理与使用

什么情况下用daemon进程 生产环境下,除了我们ssh登录上去,然后手动跑的那部分以外,其他都是自动运行的,这些大部分都应该是后台执行的.如何才能后台执行呢? nohup ./XXX & 由系统的其他daemon进程启动.这样的话,你的程序是它的子进程,跟终端没关系.退出终端也不会导致进程退出.如写在crontab里. 写成daemon程序,可以手动执行,退出终端时程序不退出. 如何选择呢? (1)首先,清理过期日志这一类需求,可以写一个死循环一直运行,也可以写在crontab里,每次执行完就

Linux daemon进程的应用、实现和原理

什么情况下用daemon进程 生产环境下,除了我们ssh登录上去,然后手动跑的那部分以外,其他都是自动运行的,这些大部分都应该是后台执行的.如何才能后台执行呢? nohup ./XXX & 由系统的其他daemon进程启动.这样的话,你的程序是它的子进程,跟终端没关系.退出终端也不会导致进程退出.如写在crontab里. 写成daemon程序,可以手动执行,退出终端时程序不退出. 如何选择呢? (1)首先,清理过期日志这一类需求,可以写一个死循环一直运行,也可以写在crontab里,每次执行完就

linux获取daemon进程的控制台数据

linux提供了一个daemon函数,使得进程可以脱离控制台运行,实现了后台运行的效果.但是进程后台运行后,原本在终端控制台输出的数据就看不到了.那么,怎样才能找回这些数据? 这里,文章主题就围绕着 如何获得后台进程的控制台数据,其中的原理要从daemon说起. daemon主要做两件事: 1.创建子进程,退出当前进程,并且以子进程创建新会话.这样,就算父进程退出,子进程也不会被关闭 2.将标准输入,标准输出,标准错误都重定向/dev/null daemon 实现大致如下: int daemon

daemon进程(转)

转自:http://www.pythoner.cn/home/blog/double-fork-when-creating-daemon/Daemon进程 守护进程(daemon)是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控.此类程序会被以进程的形式初始化.守护进程程序的名称通常以字母"d"结尾:例如,syslogd就是指管理系统日志的守护进程. 常见的Apache服务器(httpd),MySQL服务端(mysqld),作业规划进程(crond

Linux系统编程——Daemon进程

目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制终端.会话周期的执行某种任务. 那么为什么守护进程要脱离终端后台运行呢? 守护进程脱离终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的任何终端信息所打断. 那么为什么要引入守护进程呢? 由于在linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程

linux下的daemon进程

转自:http://www.cnblogs.com/xuxm2007/archive/2011/07/29/2121280.html   #include <unistd.h> int daemon(int nochdir,int noclose) 在创建精灵进程的时候,往往需要将精灵进程的工作目录修改为"/"根目录 并且将标准输入,输出和错误输出重定向到/dev/null daemon的作用就是当参数nochdir为0时,将根目录修改为工作目录 noclose为0时,做输