zoukankan      html  css  js  c++  java
  • 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档的地方:

    Androidvendorqcomproprietarymm-cameraappsappslibAndroid.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 */

  • 相关阅读:
    jQuery找出所有没有disabled属性的checkbox
    jQuery prop()方法
    Aliyun 中PHP如何升级
    The connection to the server localhost:8080 was refused
    ks8集群扩容新增节点,以及xshell无法访问的问题
    设置小程序模板消息keyword_id_list问题
    git如何新建仓库,并初始化代码
    k8s应用配置详解
    git如何把分支变成master
    nginx首页根据IP跳转
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/6518612.html
Copyright © 2011-2022 走看看