• MM-Camera架构-Preview 流程分析


    image

    目录

    1 log开的好,问题都能搞

    • 22/12/01

    在看preview流程的时候(mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c ),上来有一句:
    CPP_HIGH("name=%s", name);
    查看定义在:mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_log.h

    #define CPP_LOW(fmt, args…) CLOGL(CAM_CPP_MODULE, fmt, ##args)
    #define CPP_HIGH(fmt, args…) CLOGH(CAM_CPP_MODULE, fmt, ##args)
    #define CPP_ERR(fmt, args…) CLOGE(CAM_CPP_MODULE, fmt, ##args)
    #define CPP_DBG(fmt, args…) CLOGD(CAM_CPP_MODULE, fmt, ##args)
    #define CPP_INFO(fmt, args…) CLOGI(CAM_CPP_MODULE, fmt, ##args)
    #define CPP_WARN(fmt, args…) CLOGW(CAM_CPP_MODULE, fmt, ##args)

    看起来是针对mm-camera2/media-controller/modules/pproc-new/cpp/目录下的文件单独定义的log level。
    CAM_CPP_MODULE是一个枚举值,同类型的还有以下这些。这些枚举值代表着不同模块的

    typedef enum {
      CAM_NO_MODULE,
      CAM_MCT_MODULE,
      CAM_SENSOR_MODULE,
      CAM_IFACE_MODULE,
      CAM_ISP_MODULE,
      CAM_PPROC_MODULE,
      CAM_IMGLIB_MODULE,
      CAM_CPP_MODULE,
      CAM_HAL_MODULE,
      CAM_JPEG_MODULE,
      CAM_C2D_MODULE,
      CAM_STATS_MODULE,
      CAM_STATS_AF_MODULE,
      CAM_STATS_AEC_MODULE,
      CAM_STATS_AWB_MODULE,
      CAM_STATS_ASD_MODULE,
      CAM_STATS_AFD_MODULE,
      CAM_STATS_Q3A_MODULE,
      CAM_STATS_IS_MODULE,
      CAM_STATS_HAF_MODULE,
      CAM_STATS_CAF_SCAN_MODULE,
      CAM_SHIM_LAYER,
      CAM_LAST_MODULE
    } cam_modules_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    而像CLOGI,CLOGD,CLOGL等等也都有定义:

    #undef CLOGI
    #define CLOGI(module, fmt, args...)                \
        CLOGx(module, CAM_GLBL_DBG_INFO, fmt, ##args)
    #undef CLOGD
    #define CLOGD(module, fmt, args...)                \
        CLOGx(module, CAM_GLBL_DBG_DEBUG, fmt, ##args)
    #undef CLOGL
    #define CLOGL(module, fmt, args...)                \
        CLOGx(module, CAM_GLBL_DBG_LOW, fmt, ##args)
    #undef CLOGW
    #define CLOGW(module, fmt, args...)                \
        CLOGx(module, CAM_GLBL_DBG_WARN, fmt, ##args)
    #undef CLOGH
    #define CLOGH(module, fmt, args...)                \
        CLOGx(module, CAM_GLBL_DBG_HIGH, fmt, ##args)
    #undef CLOGE
    #define CLOGE(module, fmt, args...)                \
        CLOGx(module, CAM_GLBL_DBG_ERR, fmt, ##args)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    CLOGx的定义如下,如果g_cam_log数组的元素存在,就可以打印log。

    /* logging macros */
    #undef CLOGx
    #define CLOGx(module, level, fmt, args...)                         \
      if (g_cam_log[module][level]) {                                  \
        cam_debug_log(module, level, __func__, __LINE__, fmt, ##args); \
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    g_cam_log是一个二维数组,代表当前跟踪日志记录配置。

    /* current trace logging configuration */
    /* g_cam_log[cam_modules_t][cam_global_debug_level_t] */
    extern int g_cam_log[CAM_LAST_MODULE][CAM_GLBL_DBG_INFO + 1];
    //CAM_LAST_MODULE和CAM_GLBL_DBG_INFO + 1代表两个enum的最大值
    
    • 1
    • 2
    • 3
    • 4

    其中module代表的是组别,也就是上面的enum cam_modules_t。level代表的是log的等级,有如下等级:

    /* values that persist.vendor.camera.global.debug can be set to */
    /* all camera modules need to map their internal debug levels to this range */
    typedef enum {
      CAM_GLBL_DBG_NONE  = 0,
      CAM_GLBL_DBG_ERR   = 1,
      CAM_GLBL_DBG_WARN  = 2,
      CAM_GLBL_DBG_HIGH  = 3,
      CAM_GLBL_DBG_DEBUG = 4,
      CAM_GLBL_DBG_LOW   = 5,
      CAM_GLBL_DBG_INFO  = 6
    } cam_global_debug_level_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 22/12/02
      突然发现,HIGH等级的log貌似不打印,于是在文件里加上这几个debug的语句:

    CPP_LOW(“sunmy_ low\n”);
    CPP_HIGH(“sunmy_ HIGH\n”);
    CPP_ERR(“sunmy_ ERR\n”);
    CPP_DBG(“sunmy_ DBG\n”);
    CPP_INFO(“sunmy_ INFO\n”);
    CPP_WARN(“sunmy_ WARN\n”);

    发现只有CPP_ERR,CPP_INFO,CPP_WARN等级的log默认打印,其余的默认不打印。
    在camx-chi架构中,确实有很多log等级(比如verbose)需要setprop才能打开。这个的原理应该一样。
    在文件mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c 发现一个函数:

    static void cpp_module_loglevel()
    {
      char cpp_prop[PROPERTY_VALUE_MAX];
      memset(cpp_prop, 0, sizeof(cpp_prop));
      property_get("persist.vendor.camera.cpp.debug.mask", cpp_prop, "1");
      gcam_cpp_loglevel = atoi(cpp_prop);
      /* Keep the deafault modules enabled  */
      property_get("persist.vendor.camera.cpp.mod.mask", cpp_prop, "2097407");
      g_cpp_log_featureMask = atoi(cpp_prop);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    发现setprop persist.vendor.camera.cpp.debug.mask为不同的值就可以打印不同level的log。

    这个结构体数组可以打印所有组别的log:

    /* current trace logging configuration */
    typedef struct {
       cam_global_debug_level_t  level;
       int                       initialized;
       const char               *name;
       const char               *prop;
    } module_debug_t;
    
    static module_debug_t cam_loginfo[(int)CAM_LAST_MODULE] = {
      {CAM_GLBL_DBG_ERR, 1,
          "",         "persist.vendor.camera.global.debug"     }, /* CAM_NO_MODULE     */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.mct.debug"        }, /* CAM_MCT_MODULE    */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.sensor.debug"     }, /* CAM_SENSOR_MODULE */
      {CAM_GLBL_DBG_WARN, 1,
          "", "persist.vendor.camera.iface.logs"       }, /* CAM_IFACE_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.isp.debug"        }, /* CAM_ISP_MODULE    */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.pproc.debug.mask" }, /* CAM_PPROC_MODULE  */
      {CAM_GLBL_DBG_WARN, 1,
          "", "persist.vendor.camera.imglib.logs"      }, /* CAM_IMGLIB_MODULE */
      {CAM_GLBL_DBG_WARN, 1,
          "", "persist.vendor.camera.cpp.debug.mask"   }, /* CAM_CPP_MODULE    */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.hal.debug"        }, /* CAM_HAL_MODULE    */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.mmstill.logs"     }, /* CAM_JPEG_MODULE   */
      {CAM_GLBL_DBG_WARN, 1,
          "", "persist.vendor.camera.c2d.debug.mask"   }, /* CAM_C2D_MODULE    */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.debug" }, /* CAM_STATS_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.af.debug"    }, /* CAM_STATS_AF_MODULE   */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.aec.debug"  }, /* CAM_STATS_AEC_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.awb.debug"  }, /* CAM_STATS_AWB_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.asd.debug"  }, /* CAM_STATS_ASD_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.afd.debug"  }, /* CAM_STATS_AFD_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.q3a.debug"  }, /* CAM_STATS_Q3A_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.is.debug"   }, /* CAM_STATS_IS_MODULE   */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.haf.debug"  }, /* CAM_STATS_HAF_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.stats.cafscan"  }, /* CAM_STATS_CAFSCAN_MODULE  */
      {CAM_GLBL_DBG_ERR, 1,
          "", "persist.vendor.camera.shim.debug"          }, /* CAM_SHIM_LAYER    */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    2 lib

    frameworks/av/services/camera/libcameraservice/ /system/lib/libcameraservice.so

    hardware/qcom/camera/QCamera2/ /vendor/lib/hw/camera.sdm660.so
    hardware/qcom/camera/QCamera2/stack/mm-camera-interface/ /vendor/lib/libmmcamera_interface.so

    hardware/interfaces/camera/device/1.0/ /system/lib/android.hardware.camera.device@1.0.so

    hardware/interfaces/camera/device/3.2/default/ /vendor/lib/camera.device@3.2-impl.so

    mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/ /vendor/lib/libmmcamera2_cpp_module.so

    mm-camera/mm-camera2/media-controller/mct_shim_layer/ /vendor/lib/libmmcamera2_mct_shimlayer.so

    mm-camera/mm-camera2/media-controller/mct/ /vendor/lib/libmmcamera2_mct.so

    3 preview

    framework preview
    image

    3.1 打开视频流

    3.1.1 cpp_module_start_session

    打开摄像头preview时,会调用到cpp_module_start_session方法: vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c

    boolean cpp_module_start_session(mct_module_t *module, uint32_t sessionid)
    {
        cpp_module_ctrl_t *ctrl = (cpp_module_ctrl_t *) MCT_OBJECT_PRIVATE(module);
        if(ctrl->session_params[i] == NULL) {
        //初始化ctrl->session_params[]的内容
        //1.申请空间
        ctrl->session_params[i] = (cpp_module_session_params_t*) CAM_MALLOC(sizeof(cpp_module_session_params_t))
        //2.清零
        memset(ctrl->session_params[i], 0x00, sizeof(cpp_module_session_params_t));
        //3.初始化session_params 参考struct _cpp_module_session_params_t的成员
        }
        
        /* start the thread only when first session starts */
        if(ctrl->session_count == 0) {
            /* spawn the cpp thread */
            rc = cpp_thread_create(module);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.1.2 cpp_thread_create

    将cpp_module_start_session的参数(mct_module_t *module)透传到cpp_thread_create中创建线程: vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

    int32_t cpp_thread_create(mct_module_t *module)
    {
        ...
        //设置cpp_thread_started的标志
        ctrl->cpp_thread_started = FALSE;
        //创建线程  cpp_thread_func  线程函数的入口地址
        rc = pthread_create(&(ctrl->cpp_thread), NULL, cpp_thread_func, module);
        //修改线程名
        pthread_setname_np(ctrl->cpp_thread, "CAM_cpp");
        /* wait to confirm if the thread is started */    
        //PTHREAD_COND_WAIT_TIME即pthread_cond_timedwait 当在指定时间内有信号传过来时,pthread_cond_timedwait()返回0,否则返回一个非0数,rc就是返回值
        PTHREAD_COND_WAIT_TIME(&(ctrl->th_start_cond), &(ctrl->cpp_mutex),&timeout, CPP_WAIT_TIMEOUT, rc);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.1.3 cpp_thread_func

    vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

    void* cpp_thread_func(void* data)
    {
      mct_module_t *module = (mct_module_t *) data;
      cpp_module_ctrl_t *ctrl = (cpp_module_ctrl_t *) MCT_OBJECT_PRIVATE(module);
      PTHREAD_MUTEX_LOCK(&(ctrl->cpp_mutex));
      //设置cpp_thread_started状态,代表cpp_thread已经跑起来了
      ctrl->cpp_thread_started = TRUE;
      //用于唤醒th_start_cond
      pthread_cond_signal(&(ctrl->th_start_cond));
      
      //------------------sundp-3.1--------------
      /* open the cpp hardware */
      rc = cpp_hardware_open_subdev(ctrl->cpphw);     
      
      /* subscribe for event on subdev fd */
      //注意cpp_hardware_cmd_t里的联合体
      cpp_hardware_cmd_t cmd;
      //cmd.type代表此命令的类型,除了这里的订阅事件,还有像CPP_HW_CMD_STREAMON,CPP_HW_CMD_STREAMON等等
      cmd.type = CPP_HW_CMD_SUBSCRIBE_EVENT;    
      //------------------sundp-3.2--------------
      rc = cpp_hardware_process_command(ctrl->cpphw, cmd);   
      
      /* poll on the pipe readfd and subdev fd */
      struct pollfd pollfds[2];
      uint32_t num_fds = 2;
      int ready = 0;
      uint32_t i = 0;
      pollfds[PIPE_FD_IDX].fd = ctrl->pfd[READ_FD];
      pollfds[PIPE_FD_IDX].events = POLLIN|POLLPRI;
      pollfds[SUBDEV_FD_IDX].fd = ctrl->cpphw->subdev_fd;
      pollfds[SUBDEV_FD_IDX].events = POLLIN|POLLPRI;
      while(1) {
        /* poll on the fds with no timeout */
        //------------------sundp-3.3--------------
        ready = poll(pollfds, (nfds_t)num_fds, -1);    
        if(ready > 0) {
          /* loop through the fds to see if any event has occured */
          for(i=0; i
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    sundp-3.1 cpp_hardware_open_subdev(ctrl->cpphw)

    这里打开v4l的subdev。
    ctrl->cpphw 是struct cpp_hardware_t定义的,结构体成员subdev_ids[MAX_CPP_DEVICES]代表的是:/dev/v4l-sundev*:

    sdm660_64:/dev # ls v4l
    v4l-subdev0 v4l-subdev11 v4l-subdev14 v4l-subdev17 v4l-subdev2 v4l-subdev22 v4l-subdev4 v4l-subdev7
    v4l-subdev1 v4l-subdev12 v4l-subdev15 v4l-subdev18 v4l-subdev20 v4l-subdev23 v4l-subdev5 v4l-subdev8
    v4l-subdev10 v4l-subdev13 v4l-subdev16 v4l-subdev19 v4l-subdev21 v4l-subdev3 v4l-subdev6 v4l-subdev9

    这里前后置 打开的都是v4l-subdev17

    代码路径:
    vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_hardware.c

    int32_t cpp_hardware_open_subdev(cpp_hardware_t *cpphw)
    {
      int fd;
      char dev_name[SUBDEV_NAME_SIZE_MAX];
      snprintf(dev_name, sizeof(dev_name), "/dev/v4l-subdev%d",cpphw->subdev_ids[0]);
      //打开v4l-subdevXX设备
      fd = open(dev_name, O_RDWR | O_NONBLOCK);
      cpphw->subdev_fd = fd;
      //flag已经打开设备
      cpphw->subdev_opened = TRUE;
      //通过ioctl,获取设备参数
      rc = ioctl(cpphw->subdev_fd, VIDIOC_MSM_CPP_GET_INST_INFO, &v4l2_ioctl);
      //填充ctrl->cpphw的信息:
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    open设备节点操作,对应到内核,kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
    cpp_open_node 主要做了两件事:1、初始化mem, 2、初始化硬件

    cpp_init_mem() : 其实就是获取cpp_dev->iommu_hdl,这个东西是在msm_cam_smmu设备driver中统一管理的,vfe中有记录过这里。
    cpp_init_hardware():设置一些硬件参数、时钟、注册中断以及buf管理接口:msm_cam_buf_mgr_register_ops()

    static int cpp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
    {
            cpp_dev = v4l2_get_subdevdata(sd);
    	    if (cpp_dev->cpp_open_cnt == 1) {
    		    rc = cpp_init_mem(cpp_dev);
    		    rc = cpp_init_hardware(cpp_dev);
    		    cpp_dev->state = CPP_STATE_IDLE;
        	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    VIDIOC_MSM_CPP_GET_INST_INFO不重要,简单记录:

    static long msm_cpp_subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
    {
        switch (cmd) {
         case VIDIOC_DQEVENT:
              return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
         case VIDIOC_SUBSCRIBE_EVENT:
              return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
         case VIDIOC_MSM_CPP_GET_INST_INFO: {
            uint32_t i;
            struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
            struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
            struct msm_cpp_frame_info_t inst_info;
    
            memset(&inst_info, 0, sizeof(struct msm_cpp_frame_info_t));
            for (i = 0; i < MAX_ACTIVE_CPP_INSTANCE; i++) {
                if (cpp_dev->cpp_subscribe_list[i].vfh == vfh) {
                    inst_info.inst_id = i;
                    break;
                }
            }
        }
        break;
        default:
            return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    sundp-3.2 cpp_hardware_process_command(ctrl->cpphw, cmd)

    cpp_hardware_process_command 用于处理给硬件的命令。在此过程中更新硬件状态。

    代码路径:vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_hardware.c

    int32_t cpp_hardware_process_command(cpp_hardware_t *cpphw,
      cpp_hardware_cmd_t cmd)
    {
        ...
        switch (cmd.type) {
            case CPP_HW_CMD_GET_CAPABILITIES:    rc = cpp_hardware_get_capabilities(cpphw);     break;
            case CPP_HW_CMD_SUBSCRIBE_EVENT:     rc = cpp_hardware_subcribe_v4l2_event(cpphw);  break;
            case CPP_HW_CMD_UNSUBSCRIBE_EVENT:   rc = cpp_hardware_unsubcribe_v4l2_event(cpphw);break;
            case CPP_HW_CMD_NOTIFY_EVENT:        rc = cpp_hardware_notify_event_get_data(cpphw, cmd.u.event_data); break;
            case CPP_HW_CMD_STREAMON:            rc = cpp_hardware_process_streamon(cpphw, cmd.u.buff_update);break;
            case CPP_HW_CMD_STREAMOFF:           rc = cpp_hardware_process_streamoff(cpphw, cmd.u.streamoff_data);  break;
            case CPP_HW_CMD_LOAD_FIRMWARE:       rc = cpp_hardware_load_firmware(cpphw);  break;
            case CPP_HW_CMD_PROCESS_FRAME:       rc = cpp_hardware_process_frame(cpphw, &cmd);  break;
            case CPP_HW_CMD_PROCESS_PARTIAL_FRAME:rc = cpp_hardware_process_partial_frame(cpphw, cmd.u.partial_frame);  break;
            case CPP_HW_CMD_QUEUE_BUF:           rc = cpp_hardware_send_buf_done(cpphw, cmd.u.event_data);  break;
            case CPP_HW_CMD_SET_CLK:             rc = cpp_hardware_set_clock(cpphw, &cmd.u.clock_settings);  break;
            case CPP_HW_CMD_BUF_UPDATE:          rc = cpp_hardware_update_buffer_list(cpphw, cmd.u.buff_update);  break;
            case CPP_HW_CMD_POP_STREAM_BUFFER:   rc = cpp_hardware_pop_stream_buffer(cpphw, cmd.u.event_data);  break;
            case CPP_HW_CMD_NOTIFY_BUF_DONE:     rc = cpp_hardware_notify_buf_done(cpphw, cmd.u.buf_done_identity);  break;
            case CPP_HW_CMD_UPDATE_PENDING_BUF:  rc = cpp_hardware_update_pending_buffer(cpphw, cmd.u.status); break;
            default:                             CPP_ERR("bad command type=%d", cmd.type); 
            }
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里的是CPP_HW_CMD_SUBSCRIBE_EVENT即cpp_hardware_subcribe_v4l2_event
    订阅subdevfd上的事件,通知硬件打开preview.
    v4l2_event_subscription定义在:kernel/msm-4.14/include/uapi/linux/videodev2.h

    struct v4l2_event_subscription {
    —__u32>–>—>—>—type;
    —__u32>–>—>—>—id;
    —__u32>–>—>—>—flags;
    —__u32>–>—>—>—reserved[5];
    };

    V4L2_EVENT_CPP_FRAME_DONE的定义:

    #define V4L2_EVENT_CPP_FRAME_DONE (V4L2_EVENT_PRIVATE_START + 0)

    V4L2_EVENT_PRIVATE_START的定义:

    #define V4L2_EVENT_PRIVATE_START>—>—0x08000000

    static int32_t cpp_hardware_subcribe_v4l2_event(cpp_hardware_t *cpphw)
    {
      struct v4l2_event_subscription sub;
      struct msm_camera_v4l2_ioctl_t v4l2_ioctl;
    
      sub.id = cpphw->inst_id;
      sub.type = V4L2_EVENT_CPP_FRAME_DONE;
      rc = ioctl(cpphw->subdev_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
    
      cpphw->event_subs_info.valid = TRUE;
      cpphw->event_subs_info.id = sub.id;
      cpphw->event_subs_info.type = sub.type;
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    ioctl发送到内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c

    static long msm_cpp_subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
    {
        //得到video device
        struct video_device *vdev = video_devdata(file);
        //得到v4l2_subdev
        struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
        struct v4l2_fh *vfh = file->private_data;
    
        switch (cmd) {
         ...
         case VIDIOC_SUBSCRIBE_EVENT:
              return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
         ...
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    v4l2_subdev_call(sd, core, subscribe_event, vfh, arg)是一个宏:

    #define v4l2_subdev_call(sd, o, f, args...)>            \
        ({                                \
            int __result;                        \
            if (!(sd))                        \
                __result = -ENODEV;>            \
            else if (!((sd)->ops->o && (sd)->ops->o->f))        \
                __result = -ENOIOCTLCMD;            \
            else                            \
                __result = (sd)->ops->o->f((sd), ##args);    \
            __result;                        \
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (sd)->ops->o->f((sd), ##args)对应的是函数指针:

    int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,struct v4l2_event_subscription *sub);

    指向的函数是msm_cpp_subscribe_event。

    static struct v4l2_subdev_core_ops msm_cpp_subdev_core_ops = {
    —.ioctl = msm_cpp_subdev_ioctl,
    —.subscribe_event = msm_cpp_subscribe_event,
    —.unsubscribe_event = msm_cpp_unsubscribe_event,
    };

    static int msm_cpp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
    {
    	    return v4l2_event_subscribe(fh, sub, MAX_CPP_V4l2_EVENTS, NULL);
    }
    
    • 1
    • 2
    • 3
    • 4

    V4L2 events 提供一种将event传递到用户空间的通用方式。
    v4l2_event_subscribe (fh, sub , elems, ops)
    用来订阅事件,ops参数可以让驱动指定一个回调函数:add,del,replace,merge。一共有4个可选的回调函数,如果不需要也可以传入NULL参数。 kernel/msm-4.14/drivers/media/v4l2-core/v4l2-event.c

    int v4l2_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub, unsigned elems,
    			 const struct v4l2_subscribed_event_ops *ops)
    {
    	struct v4l2_subscribed_event *sev, *found_ev;
    	sev = kvzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems,
    		       GFP_KERNEL);
    	for (i = 0; i < elems; i++) sev->events[i].sev = sev;
    	sev->type = sub->type;	sev->id = sub->id;
    	sev->flags = sub->flags;	sev->fh = fh;
    	sev->ops = ops;
    
    	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
    	if (!found_ev) list_add(&sev->list, &fh->subscribed);
    
    	if (found_ev) {
    		kvfree(sev);
    	} else if (sev->ops && sev->ops->add) {
    		ret = sev->ops->add(sev, elems);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.2 sensor视频流buffer队列

    3.2.1 cpp_thread_func

    上文在函数cpp_thread_func中说了sundp-3.1sundp-3.2,接下来说一下剩下的:

    ....
    /* poll on the pipe readfd and subdev fd */
    struct pollfd pollfds[2];
    uint32_t num_fds = 2;
    int ready = 0;
    uint32_t i = 0;
    pollfds[PIPE_FD_IDX].fd = ctrl->pfd[READ_FD];
    pollfds[PIPE_FD_IDX].events = POLLIN|POLLPRI;  
    //POLLIN   有数据可读。
    //POLLPRI  有紧迫数据可读。
    pollfds[SUBDEV_FD_IDX].fd = ctrl->cpphw->subdev_fd;
    pollfds[SUBDEV_FD_IDX].events = POLLIN|POLLPRI;
    CPP_HIGH("cpp_thread entering the polling loop...");
    while(1) {
        ready = poll(pollfds, (nfds_t)num_fds, -1);
    ....
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    sundp-3.3 poll(pollfds, (nfds_t)num_fds, -1);

    sundp-3.1sundp-3.2启动v4l2设备成功后,cpp_thread线程同时侦听一个pipe fd,和设备节点/dev/v4l-subdevXX

    ( 注:打开v4l-subdevXX设备
    fd = open(dev_name, O_RDWR | O_NONBLOCK);
    cpphw->subdev_fd = fd;)。

    struct pollfd pollfds[2];

    struct pollfd{
        int fd; /*文件描述符,如建立socket后获取的fd, 此处表示想查询的文件描述符*/
        short events;	/*等待的事件,就是要监测的感兴趣的事情*/
        short revents;	/*实际发生了的事情*/
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里的ready = poll(pollfds, (nfds_t)num_fds, -1);是将当前的文件指针挂到等待队列中

    结构介绍:
    int poll(struct pollfd *fds, unsigned int nfds, int timeout)
    参数介绍:
    pollfd *fds : 指向pollfd结构体数组,用于存放需要检测器状态的Socket 描述符或其它文件描述符。
    unsigned int nfds: 指定pollfd 结构体数组的个数,即监控几个pollfd.
    timeout:指poll() 函数调用阻塞的时间,单位是ms.如果timeout=0则不阻塞,如timeout=INFTIM 表 示一直阻塞直到感兴趣的事情发生。
    返回值:
    0 表示数组fds 中准备好读,写或出错状态的那些socket描述符的总数量
    ==0 表示数组fds 中都没有准备好读写或出错,当poll 阻塞超时timeout 就会返回。
    -1 表示poll() 函数调用失败,同时回自动设置全局变量errno.
    函数特点:
    每当函数调用后,不会清空这个fds指向的数组,适用于大量socket 描述符的情况。而select()函数调用后会清空它所检测的socket描述符集合,因此select()只是用检测一个socket 描述符的情况。
    如果待监测的socket 描述符为负值,则这个描述符的检测就会被忽略,poll()函数返回时直接把revents 设置为0
    该poll()函数不会受到socket 描述符上的O_NDELAY 标记和O_NONBLOCK 标记的影响。

    sundp-3.4 cpp_thread_process_pipe_message

    poll函数运行成功后,接下来会循环浏览num_fds(即pipe fd和subdev_fd)
    以查看是否发生了任何事件,当pipe fd上有消息时,先读到pipe_msg。

              case PIPE_FD_IDX: {
                int num_read=0;
                cpp_thread_msg_t pipe_msg;
                num_read = read(pollfds[i].fd, &(pipe_msg),
                             sizeof(cpp_thread_msg_t));
                rc = cpp_thread_process_pipe_message(ctrl, pipe_msg);    
                break;
              }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    接下来进入cpp_thread_process_pipe_message处理。 vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

    static int32_t cpp_thread_process_pipe_message(cpp_module_ctrl_t *ctrl,
      cpp_thread_msg_t msg)
    {
      cpp_hardware_cmd_t cmd;
      switch(msg.type) {
      
      //这里应该是出错才会走。信息中止
      case CPP_THREAD_MSG_ABORT: 
      ......
      //有事件
      case CPP_THREAD_MSG_NEW_EVENT_IN_Q: {
        cpp_module_event_t* cpp_event;
        /*当队列进程中有一些有效事件时,处理它 */
        while(1) {
          cpp_event = cpp_thread_get_event_from_queue(ctrl);
          if(!cpp_event) { break; }
          rc = cpp_thread_process_queue_event(ctrl, cpp_event);
        }
        break;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    cpp_thread_process_queue_event
    cpp_event->type的类型:

    typedef enum {
    CPP_MODULE_EVENT_PROCESS_BUF,
    CPP_MODULE_EVENT_DIVERT_BUF,
    CPP_MODULE_EVENT_CLOCK,
    CPP_MODULE_EVENT_PARTIAL_FRAME,
    CPP_MODULE_EVENT_ISP_BUFFER_DROP
    } cpp_module_event_type_t;

    static int32_t cpp_thread_process_queue_event(cpp_module_ctrl_t *ctrl, cpp_module_event_t* cpp_event)
    {
        //选择事件类型:预览的时候是CPP_MODULE_EVENT_PROCESS_BUF
        switch(cpp_event->type) {
            case CPP_MODULE_EVENT_DIVERT_BUF:
                rc = cpp_thread_handle_divert_buf_event(ctrl, cpp_event);
                break;
            case CPP_MODULE_EVENT_PROCESS_BUF:
                rc = cpp_thread_handle_process_buf_event(ctrl, cpp_event);
                break;
            case CPP_MODULE_EVENT_CLOCK:
                rc = cpp_thread_handle_clock_event(ctrl, cpp_event);
                break;
            case CPP_MODULE_EVENT_PARTIAL_FRAME:
                rc = cpp_thread_handle_partial_frame_event(ctrl, cpp_event);
                break;
            case CPP_MODULE_EVENT_ISP_BUFFER_DROP:
                cpp_thread_handle_isp_drop_buffer_event(ctrl, cpp_event);
                break;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    cpp_thread_handle_process_buf_event

    static int32_t cpp_thread_handle_process_buf_event(cpp_module_ctrl_t* ctrl, cpp_module_event_t* cpp_event)
    {
        cpp_module_stream_params_t  *stream_params = NULL;
        cpp_module_session_params_t *session_params = NULL;
        ......
        //填充cookie
        /*cookie用于将数据附加到内核帧,处理完成后将立即取回???*/
        cpp_module_hw_cookie_t *cookie = NULL;
        
        
        //用cpp_envent填充hw_params的参数
        cpp_hardware_params_t* hw_params = NULL;
        hw_params = &(cpp_event->u.process_buf_data.hw_params);
        hw_params->cookie = cookie;
        hw_params->frame_id = cpp_event->u.process_buf_data.isp_buf_divert.buffer.sequence; hw_params->timestamp = cpp_event->u.process_buf_data.isp_buf_divert.buffer.timestamp;
        hw_params->buffer_info.fd = in_frame_fd;
        ......
    
        /* get stream parameters based on the event identity */
        cpp_module_get_params_for_identity(ctrl, hw_params->identity,&session_params, &stream_params);
          
        /*Before validation, swap dimensions if 90 or 270 degrees rotation*/
        //画面的旋转角度交换?
        cpp_hardware_rotation_swap(hw_params,video_type_flag);
        
        /* before giving the frame to hw, make sure the parameters are good */
        //应该是验证当前帧的参数是否正确,不正确就丢弃
        if(FALSE == cpp_hardware_validate_params(hw_params))
        {......}
        
        //计算裁剪?
        rc = cpp_params_calculate_crop(hw_params);
        ......
        
        cmd.type = CPP_HW_CMD_PROCESS_FRAME;
        cmd.ctrl = ctrl;
        cmd.u.hw_params = hw_params;
        //这里可以dump,可以看一下 第五章 dump
    #ifdef _ANDROID_
        if (session_params->cpp_debug_enable)
            cpp_thread_dump_frame(hw_params,hw_params->buffer_info.fd,&cmd,1);
    #endif
    
        rc = cpp_hardware_process_command(ctrl->cpphw, cmd);  
        ......
        /* Update and post the current session's diag parameters */
        //更新并发布当前session的诊断参数
        cpp_module_util_update_session_diag_params(ctrl->p_module, hw_params);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    cpp_hardware_process_command
    执行到CPP_HW_CMD_PROCESS_FRAME,就是cpp_hardware_process_frame(): vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_hardware.c

    static int32_t cpp_hardware_process_frame(cpp_hardware_t *cpphw, cpp_hardware_cmd_t *cmd)
    {
        struct cpp_frame_info_t cpp_frame_info
        struct msm_cpp_frame_info_t *msm_cpp_frame_info;
        ......
        rc = cpp_params_create_frame_info(cpphw, hw_params, &cpp_frame_info);
        //将sysetm接口参数转换为msm frame cfg参数
        cpp_frame_info.frame_id = hw_params->frame_id;
        cpp_frame_info.timestamp = hw_params->timestamp;
        cpp_frame_info.identity = hw_params->identity;
        .......
    
        msm_cpp_frame_info = cpp_hardware_create_hw_frame(cpphw, &cpp_frame_info);
            
        /* send kernel ioctl for processing */
        struct msm_camera_v4l2_ioctl_t v4l2_ioctl;
        v4l2_ioctl.ioctl_ptr = (void *)msm_cpp_frame_info;
        v4l2_ioctl.len = sizeof(struct msm_cpp_frame_info_t);
    
        rc = ioctl(cpphw->subdev_fd, VIDIOC_MSM_CPP_CFG, &v4l2_ioctl);
        .......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    VIDIOC_MSM_CPP_CFG通过ioctl,到了内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c //内核的这部分我没看

    static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        bool is_copytouser_req = true
        struct msm_camera_v4l2_ioctl_t kp_ioctl
        void __user *up = (void __user *)arg;
        copy_from_user(&up32_ioctl, (void __user *)up, sizeof(up32_ioctl));
        kp_ioctl.id = up32_ioctl.id;
        kp_ioctl.len = up32_ioctl.len;
        kp_ioctl.trans_code = up32_ioctl.trans_code;
        kp_ioctl.ioctl_ptr = compat_ptr(up32_ioctl.ioctl_ptr);
    
        switch (cmd) {
    	    case VIDIOC_MSM_CPP_CFG32:
    	    {   
    	    copy_from_user(&k32_frame_info,	(void __user *)kp_ioctl.ioctl_ptr, sizeof(k32_frame_info));
    	    cpp_frame = get_64bit_cpp_frame_from_compat(&kp_ioctl);
    
    	    /* Configure the cpp frame */
    	    if (cpp_frame) {
    			    rc = msm_cpp_cfg_frame(cpp_dev, cpp_frame);
    			    /* Cpp_frame can be free'd by cfg_frame in error */
    			    if (rc >= 0) {
                        k32_frame_info.output_buffer_info[0] = cpp_frame->output_buffer_info[0];
    				                k32_frame_info.output_buffer_info[1] = cpp_frame->output_buffer_info[1];
    			            }
    	        }
    	        kp_ioctl.trans_code = rc;
    	        copy_to_user((void __user *)kp_ioctl.ioctl_ptr, &k32_frame_info,	sizeof(k32_frame_info));
    	        cmd = VIDIOC_MSM_CPP_CFG;
    	        break;
    	    }
            。。。。。。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    msm_cpp_cfg_frame

    static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, struct msm_cpp_frame_info_t *new_frame)
    {
    	in_phyaddr = msm_cpp_fetch_buffer_info(cpp_dev,
    		&new_frame->input_buffer_info,
    		((new_frame->input_buffer_info.identity >> 16) & 0xFFFF),
    		(new_frame->input_buffer_info.identity & 0xFFFF), &in_fd);
    	
    	op_index = new_frame->output_buffer_info[0].index;
    	dup_index = new_frame->duplicate_buffer_info.index;
    
    	if (new_frame->we_disable == 0) {
    		int32_t iden = new_frame->identity;
            。。。。。。
            out_phyaddr0 = msm_cpp_fetch_buffer_info(cpp_dev, &new_frame->output_buffer_info[0],
                    ((iden >> 16) & 0xFFFF), (iden & 0xFFFF), &new_frame->output_buffer_info[0].fd);
    	}
    	
    	out_phyaddr1 = out_phyaddr0;
    	if (new_frame->duplicate_output) { 。。。。。。} //这个分支不执行
    	。。。。。。
    	
        msm_cpp_update_frame_msg_phy_address(cpp_dev, new_frame,
            in_phyaddr, out_phyaddr0, out_phyaddr1, tnr_scratch_buffer0, tnr_scratch_buffer1);
        rc = msm_cpp_set_group_buffer(cpp_dev, new_frame, out_phyaddr0, num_output_bufs);
        frame_qcmd->command = new_frame;
    	rc = msm_cpp_send_frame_to_hardware(cpp_dev, frame_qcmd);  // 将new_frame参数发送到硬件
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    msm_cpp_send_frame_to_hardware()最终会执行对硬件地址的写操作,将frame的参数写入硬件。(TODO)

    sundp-3.5 cpp_thread_process_hardware_event

    sundp-3.1sundp-3.2启动v4l2设备成功后,cpp_thread线程侦听设备节点/dev/v4l-subdevXX的节点(sundp-3.3)。当有视频buffer准备好时,会向该节点写入数据,这样sundp-3.3返回。
    继续前面代码的sundp-3.4

    接下来就是 sundp-3.5
    poll函数运行成功后,接下来会循环浏览num_fds(即pipe fd和subdev_fd)以查看是否发生了任何事件,当subdev_fd上有消息时,进入 vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_thread.c

    static int32_t cpp_thread_process_hardware_event(cpp_module_ctrl_t *ctrl)
    {
      .......
      /* get the event data from hardware */
      //从硬件获取event数据
      cmd.type = CPP_HW_CMD_NOTIFY_EVENT;
      cmd.u.event_data = &event_data;
      rc = cpp_hardware_process_command(ctrl->cpphw, cmd);  //sundp-3.6
    
      /* get stream parameters based on the event identity */
      //基于事件标识获取流参数 
      cpp_module_get_params_for_identity(ctrl, event_data.identity, &session_params, &stream_params);
    
      /* update the pending ack for this buffer */
      cookie = (cpp_module_hw_cookie_t *)event_data.cookie;
      buffer_access = event_data.input_buffer_access;
      rc = cpp_module_do_ack(ctrl, cookie->key, buffer_access);  // 发送buf event  sundp-3.9
      .......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    sundp-3.6 cpp_hardware_process_command

    首先从/dev/v4l-subdevXX节点读取event内容:

    int32_t cpp_hardware_process_command(cpp_hardware_t *cpphw, cpp_hardware_cmd_t cmd)
    {
      .......
      switch (cmd.type) {
        ......
        case CPP_HW_CMD_NOTIFY_EVENT:
          rc = cpp_hardware_notify_event_get_data(cpphw, cmd.u.event_data);
          break;
        ......
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    cpp_hardware_notify_event_get_data

    static int32_t cpp_hardware_notify_event_get_data(cpp_hardware_t *cpphw,
      cpp_hardware_event_data_t *event_data)
    {
      struct v4l2_event event;
      //--------------------sundp-3.7----------
      rc = ioctl(cpphw->subdev_fd, VIDIOC_DQEVENT, &event);  
      v4l2_ioctl.ioctl_ptr = (void *)&frame;
      v4l2_ioctl.len = sizeof(struct msm_cpp_frame_info_t);
      //--------------------sundp-3.8----------
      rc = ioctl(cpphw->subdev_fd, VIDIOC_MSM_CPP_GET_EVENTPAYLOAD, &v4l2_ioctl); 
      event_data->frame_id = frame.frame_id;
      event_data->buf_idx = frame.input_buffer_info.index;
      event_data->out_fd = frame.output_buffer_info[0].fd;
      event_data->identity = frame.identity;
      ......
      // 设置event_data的内容
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    首先是sundp-3.7,通过对节点/dev/v4l-subdevXX的ioctl,到了内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c

    sundp-3.7 msm_cpp_subdev_do_ioctl
    static long msm_cpp_subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
    {
    	vdev = video_devdata(file);
    	sd = vdev_to_v4l2_subdev(vdev);
    	vfh = file->private_data;
    	switch (cmd) {
    	    case VIDIOC_DQEVENT:
    		    return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    v4l2_event_dequeue就是从v4l设备的avaliable链表中取出第一个event,通过copy_to_user,返回给camera hal。

    sundp-3.8 msm_cpp_subdev_fops_compat_ioctl

    然后是sundp-3.8,通过对节点/dev/v4l-subdevXX的ioctl,到了内核: kernel/msm-4.14/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c

    static long msm_cpp_subdev_fops_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
        bool is_copytouser_req = true
        struct msm_camera_v4l2_ioctl_t kp_ioctl
        void __user *up = (void __user *)arg;
        copy_from_user(&up32_ioctl, (void __user *)up, sizeof(up32_ioctl));
        kp_ioctl.id = up32_ioctl.id;
        kp_ioctl.len = up32_ioctl.len;
        kp_ioctl.trans_code = up32_ioctl.trans_code;
        kp_ioctl.ioctl_ptr = compat_ptr(up32_ioctl.ioctl_ptr);
    
        switch (cmd) {
            .......
    	        case VIDIOC_MSM_CPP_GET_EVENTPAYLOAD32:
    	        {
    	            struct msm_device_queue *queue = &cpp_dev->eventData_q;
    	            event_qcmd = msm_dequeue(queue, list_eventdata, POP_FRONT);
    	            get_compat_frame_from_64bit(process_frame, &k32_process_frame);
    	            copy_to_user((void __user *)kp_ioctl.ioctl_ptr, &k32_process_frame, sizeof(struct msm_cpp_frame_info32_t)));
                 cmd = VIDIOC_MSM_CPP_GET_EVENTPAYLOAD;
                 break;
                }
            ......
        }
        if (is_copytouser_req) {   // == true   //转换为32位(compat),再copy to user
    	    up32_ioctl.id = kp_ioctl.id;
    	    up32_ioctl.len = kp_ioctl.len;
    	    up32_ioctl.trans_code = kp_ioctl.trans_code;
    	    up32_ioctl.ioctl_ptr = ptr_to_compat(kp_ioctl.ioctl_ptr);
    	    copy_to_user((void __user *)up, &up32_ioctl, sizeof(up32_ioctl));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    这里,将一个视频帧(frame)参数通过ioctl,返回给camera hal。
    hal拿到帧参数后,将帧参数也填充到event_data中。最后,sundp-3.6返回,参数返回到event_data中。
    接下来到了sundp-3.9

    sundp-3.9 cpp_module_do_ack

    vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/pproc-new/cpp/cpp_module.c

    int32_t cpp_module_do_ack(cpp_module_ctrl_t *ctrl,  cpp_module_ack_key_t key, uint32_t buffer_access)
    {
        ......
        cpp_ack = cpp_module_find_ack_from_list(ctrl, key);
        cpp_ack->ref_count--;
        if(cpp_ack->ref_count == 0) {
        ......
        cpp_module_send_buf_divert_ack(ctrl, cpp_ack->isp_buf_divert_ack);
        }
    }
    
    static int32_t cpp_module_send_buf_divert_ack(cpp_module_ctrl_t *ctrl, isp_buf_divert_ack_t isp_ack)
    {
        //创建event
        mct_event_t event;  
        // sundp-3.10
        event.type = MCT_EVENT_MODULE_EVENT;
        event.direction = MCT_EVENT_UPSTREAM;
        event.identity = isp_ack.identity;
        //sundp-3.11
        event.u.module_event.type = MCT_EVENT_MODULE_BUF_DIVERT_ACK;
        event.u.module_event.module_event_data = &isp_ack;
    
        rc = cpp_module_send_event_upstream(ctrl->p_module, &event);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    sundp-3.10 MCT_EVENT_MODULE_EVENT

    接下来到了这儿:
    vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/iface2/port_iface.c

    static boolean port_iface_event_func(mct_port_t *mct_port, mct_event_t *event)
    {
      switch (event->type) {
      case MCT_EVENT_CONTROL_CMD:
        /* MCT ctrl event */
        rc = port_iface_proc_mct_ctrl_cmd(mct_port, event);
        break;
    
      case MCT_EVENT_MODULE_EVENT: //对应了前面的event.type
        /* Event among modules */
        rc = port_iface_proc_module_event(mct_port, event);
        break;
      return rc;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/iface2/iface_util.c

    int iface_util_divert_ack(iface_t *iface, iface_session_t *session,
        uint32_t user_stream_id, uint32_t buf_idx, 
        uint32_t is_dirty, boolean bayerdata, uint32_t buffer_access)
    {
        if (hw_stream->isp_split_output_info.is_split == TRUE 
            && hw_stream->stream_info.cam_stream_type != CAM_STREAM_TYPE_OFFLINE_PROC) 
            {
                /*if hw stream split, only enqueue once since its shared buf*/
                rc = iface_axi_divert_ack( iface->isp_axi_data.axi_data[VFE0].axi_hw_ops->ctrl,
                                            &axi_divert_ack, sizeof(axi_divert_ack));
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/iface2/axi/iface_axi.c:

    static boolean port_iface_proc_module_event(mct_port_t *mct_iface_port, mct_event_t *event)
    {
        switch (mod_event->type) {
        //对应了前面的event.u.module_event.type
        case MCT_EVENT_MODULE_BUF_DIVERT_ACK: {  
            isp_buf_divert_ack_t *buf_divert_ack = (isp_buf_divert_ack_t*) mod_event->module_event_data;
            ret = iface_util_divert_ack(iface, session, UNPACK_STREAM_ID(event->identity),
            buf_divert_ack->buf_idx, buf_divert_ack->is_buf_dirty,
            buf_divert_ack->bayerdata, buf_divert_ack->buffer_access);
        }
        break;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里,iface_quque_buf,将视频buf加入队列,送去显示。

    int iface_axi_divert_ack( iface_axi_hw_t *axi_hw, iface_axi_buf_divert_ack_t *ack, uint32_t data_size __unused)
    {
        stream = iface_axi_util_find_stream(axi_hw, ack->session_id, ack->stream_id);
        buf_handle = iface_find_matched_bufq_handle(axi_hw->buf_mgr,
        stream->hw_stream_info.session_id, ack->stream_id);
        rc = iface_queue_buf(axi_hw->buf_mgr,buf_handle, ack->buf_idx, ack->is_buf_dirty, axi_hw->fd,ack->buffer_access);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4 dump image

    这里说一下怎么dump图像:

    static int32_t cpp_thread_handle_process_buf_event(cpp_module_ctrl_t* ctrl,
      cpp_module_event_t* cpp_event)
    {
        ......
    #ifdef _ANDROID_
        if (session_params->cpp_debug_enable)
            //1  表示dump输入,有另外的函数dump输出
            cpp_thread_dump_frame(hw_params,hw_params->buffer_info.fd,&cmd,1);
    #endif
        .......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可见有两个条件:

    1.#ifdef ANDROID
    2.session_params->cpp_debug_enable为ture

    第一个条件好说,第二个赋值为ture的地方是:

    property_get("persist.vendor.camera.pproc.debug.en", value, "0");
    enabled = atoi(value);
    if (enabled)
      ctrl->session_params[i]->cpp_debug_enable = TRUE;
    
    • 1
    • 2
    • 3
    • 4

    所以需要setprop persist.vendor.camera.pproc.debug.en 1

    cpp_thread_dump_frame函数实现:

    参数:
    cpp_hardware_params_t hw_params:
    int fd:
    void *user:
    bool ip_op:1 dump Input ; 0 dump Output

    int cpp_thread_dump_frame(cpp_hardware_params_t *hw_params, int fd,void *user,bool ip_op)
    {
    
        ......
        //1.检查是否开启dump
        //这里也需要设置一下
        property_get("persist.vendor.camera.pproc.framedump", value, "0");
        enabled = atoi(value);
        if (!enabled) {  CPPFrameCnt = 0;  return 0; }
        
        //2.设置需要dump的frame数,默认20
        property_get("persist.vendor.camera.pproc.dump_cnt", value, "20");
        count = atoi(value);
        
        //3.dump输入or输出
        if(ip_op) { /* Input */
            cookie = (cpp_module_hw_cookie_t *)hw_params->cookie;
            memcpy(&dim_info,&hw_params->input_info,sizeof(cpp_params_dim_info_t));
            strlcpy(io,"Input",sizeof(io));
        } else { /* Output */
            cookie = cmd->u.event_data->cookie;
            memcpy(&dim_info,&hw_params->output_info,sizeof(cpp_params_dim_info_t));
            strlcpy(io,"Output",sizeof(io));
            hw_params->frame_id = cmd->u.event_data->frame_id;
        }
        
        //根据stream_type组包(即dump 图像的的文件名)
        switch(hw_params->stream_type) {
            //以preview举例:
            case CAM_STREAM_TYPE_PREVIEW: {
                snprintf(name, sizeof(name), QCAMERA_DUMP_FRM_LOCATION
                "%s_CPP_%s_Preview_%dx%d_%d.yuv",timeBuf,io,width[0],height[0],
                hw_params->frame_id);
                break;
                }
            .......
        }
        
        //open
        file_fd = open(name, O_RDWR | O_CREAT, 0777);
        //write
        written_len += write(file_fd, data, (size_t)(dim_info.plane_info[i].plane_len));
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    struct

    struct _cpp_module_ctrl_t

    struct _cpp_module_ctrl_t {
      mct_module_t                *p_module;
      mct_module_t                *parent_module;
      mct_queue_t                 *realtime_queue;
      mct_queue_t                 *partial_frame_queue;
      mct_queue_t                 *offline_queue;
      cpp_module_ack_list_t       ack_list;
      pthread_t                   cpp_thread;
      pthread_cond_t              th_start_cond;
      boolean                     cpp_thread_started;
      pthread_mutex_t             cpp_mutex;
      int                         pfd[2];
      int32_t                     session_count;
      cpp_hardware_t              *cpphw;
      cpp_module_clk_rate_list_t  clk_rate_list;
      unsigned long               clk_rate;
      boolean                     runtime_clk_update;
      pp_native_buf_mgr_t         pp_buf_mgr;
      pp_native_buf_mgr_t         pp_tnr_buf_mgr;
      cpp_module_session_params_t *session_params[CPP_MODULE_MAX_SESSIONS];
      cpp_submodule_func_tbl_t    tnr_module_func_tbl;
      cpp_submodule_func_tbl_t    pbf_module_func_tbl;
      mct_port_t                  *port_map[CPP_MODULE_MAX_SESSIONS][CPP_MODULE_MAX_STREAMS][2];
      /* last updated clock and bandwidth */
      int64_t                     clk;
      int64_t                     bw;
      /* current state for a clock update */
      cpp_clock_state             clk_state;
      /* threshold count to trigger a clock bump down */
      int32_t                     clock_threshold;
      int32_t                     clock_dcvs;
      int32_t                     turbo_caps;
      volatile bool               is_hw_error;
      uint32_t                    rtb_status;
      uint32_t                    sat_status;
      uint32_t                    soc_id;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    struct _cpp_hardware_t

    struct _cpp_hardware_t {
      uint32_t                            subdev_ids[MAX_CPP_DEVICES];
      int                                 num_subdev;
      int                                 subdev_fd;
      boolean                             subdev_opened;
      uint32_t                            inst_id;
      cpp_hardware_caps_t                 caps;
      cpp_hardware_info_t                 hwinfo;
      cpp_hardware_status_t               status;
      cpp_firmware_version_t              fw_version;
      cpp_hardware_event_subscribe_info_t event_subs_info;
      cpp_hardware_stream_status_t        stream_status[CPP_HARDWARE_MAX_STREAMS];
      pthread_cond_t                      subdev_cond;
      pthread_mutex_t                     mutex;
      int                                 num_iommu_cnt;
      int                                 max_pending_buffer;
      uint32_t                            dump_preview_cnt;
      uint32_t                            dump_video_cnt;
      uint32_t                            dump_snapshot_cnt;
      int32_t                             max_supported_padding;
      void                                *private_data;
      uint32_t                            preview_frame_counter;
      uint32_t                            video_frame_counter;
      uint32_t                            offline_frame_counter;
      uint32_t                            snapshot_frame_counter;
      uint32_t                            postview_frame_counter;
      uint32_t                            analysis_frame_counter;
    } ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    _cpp_thread_msg_t

    typedef enum {
      CPP_THREAD_MSG_NEW_EVENT_IN_Q,
      CPP_THREAD_MSG_ABORT             
    } cpp_thread_msg_type_t;
    
    typedef struct _cpp_thread_msg_t {
      cpp_thread_msg_type_t type;
      void *data;
    } cpp_thread_msg_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    _cpp_hardware_cmd_t

    typedef struct _cpp_hardware_cmd_t {
      cpp_hardware_cmd_type_t type;
      void                   *ctrl;
      void                   *return_payload;
      /* CPP_HW_CMD_PROCESS_FRAME - Partial frame, after the first payload is sent,
         the remainder is saved here and then split into more partial_frames
         in the thread */
      union {
        cpp_hardware_streamoff_event_t streamoff_data;
        cpp_hardware_event_data_t       *event_data;
        cpp_hardware_params_t           *hw_params;
        cpp_hardware_buff_update_t      *buff_update;
        struct msm_cpp_frame_info_t     *partial_frame;
        cpp_hardware_clock_settings_t   clock_settings;
        uint32_t                        buf_done_identity;
        uint32_t                        status;
      } u;
    } cpp_hardware_cmd_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    enum cpp_hardware_cmd_type_t

    typedef enum {
      CPP_HW_CMD_GET_CAPABILITIES,
      CPP_HW_CMD_SUBSCRIBE_EVENT,
      CPP_HW_CMD_UNSUBSCRIBE_EVENT,
      CPP_HW_CMD_NOTIFY_EVENT,
      CPP_HW_CMD_STREAMON,
      CPP_HW_CMD_STREAMOFF,
      CPP_HW_CMD_LOAD_FIRMWARE,
      CPP_HW_CMD_PROCESS_FRAME,
      CPP_HW_CMD_QUEUE_BUF,
      CPP_HW_CMD_GET_CUR_DIAG,
      CPP_HW_CMD_SET_CLK,
      CPP_HW_CMD_POP_STREAM_BUFFER,
      CPP_HW_CMD_BUF_UPDATE,
      CPP_HW_CMD_PROCESS_PARTIAL_FRAME,
      CPP_HW_CMD_NOTIFY_BUF_DONE,
      CPP_HW_CMD_UPDATE_PENDING_BUF,
    } cpp_hardware_cmd_type_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    struct _cpp_module_session_params_t

    /* session specific parameters */
    typedef struct _cpp_module_session_params_t {
      cpp_module_stream_params_t   *stream_params[CPP_MODULE_MAX_STREAMS];
      int32_t                       stream_count;
      cpp_hardware_params_t         hw_params;
      uint32_t                      session_id;
      cam_hfr_mode_t                hfr_mode;
      cpp_params_aec_trigger_info_t aec_trigger;
      /* DIS enable flag to be used for frame hold */
      int32_t                       dis_enable;
      /* Latest frame id received from DIS crop event */
      cpp_module_dis_hold_t         dis_hold;
      /* Hold frame until DIS crop is received for this frame */
      cpp_module_frame_hold_t       frame_hold;
      ez_pp_params_t                diag_params;
      cam_hal_version_t             hal_version;
      cpp_per_frame_params_t        per_frame_params;
      boolean                       is_stream_on;
      uint32_t                      stream_on_count;
      cam_fps_range_t               fps_range;
      cam_stream_ID_t               valid_stream_ids[FRAME_CTRL_SIZE];
      cam_stream_ID_t               verify_proc_stream_ids[FRAME_CTRL_SIZE];
      pthread_mutex_t               dis_mutex;
      modulesChromatix_t            module_chromatix;
      chromatix_cpp_stripped_type   *def_chromatix_stripped;
      boolean                       runtime_clk_update;
      cam_dimension_t               camif_dim;
      uint32_t                      turbo_frame_count;
      int32_t                       clk_ref_threshold_idx;
      /* for Dual Cam: Primary or Secondary */
      bool                          is_slave;
      /* for Dual Cam: Main or Aux */
      cam_sync_type_t               cam_type;
      int32_t                       link_session_id;
      int32_t                       native_buf_ref;
      cam_dual_camera_perf_control_t dualcam_perf;
      cpp_module_session_set_parm   sticky_set_parm;
      cam_dual_camera_role_t        cam_role;
      bool                          force_slave_process;
      boolean                       cpp_debug_enable;
    } cpp_module_session_params_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 相关阅读:
    神经网络算法数学建模,神经网络模型数据处理
    『手撕Vue-CLI』处理不同指令
    prettier常用配置项
    Python 将数据写入csv、xlsx、xls文件中(工厂方法、封装、优雅)
    pycharm 无法显示matplotlib的绘图 - 查询显示绘图的backend - Agg -module://backend_interagg
    Matlab绘图(2)通过代码进行局部放大绘图、多文件绘图
    vscode中vue项目引入的组件的颜色没区分解决办法
    3D可视化有哪些应用场景?
    白盒 SDK 加密 —— Go 语言中直调 C 动态库实现
    每日五问(java)
  • 原文地址:https://blog.csdn.net/weixin_42581177/article/details/133645303