目录
文章目录
- 1 log开的好,问题都能搞
- 2 lib
- 3 preview
- 3.1 打开视频流
- 3.1.1 cpp\_module\_start\_session
- 3.1.2 cpp\_thread\_create
- 3.1.3 cpp\_thread\_func
- sundp-3.1 cpp\_hardware\_open\_subdev(ctrl->cpphw)
- sundp-3.2 cpp\_hardware\_process\_command(ctrl->cpphw, cmd)
- 3.2 sensor视频流buffer队列
- 3.2.1 cpp\_thread\_func
- sundp-3.3 poll(pollfds, (nfds\_t)num\_fds, -1);
- sundp-3.4 cpp\_thread\_process\_pipe\_message
- sundp-3.5 cpp\_thread\_process\_hardware\_event
- sundp-3.6 cpp\_hardware\_process\_command
- sundp-3.7 msm\_cpp\_subdev\_do\_ioctl
- sundp-3.8 msm\_cpp\_subdev\_fops\_compat\_ioctl
- sundp-3.9 cpp\_module\_do\_ack
- sundp-3.10 MCT\_EVENT\_MODULE\_EVENT
- 4 dump image
- struct
- struct \_cpp\_module\_ctrl\_t
- struct \_cpp\_hardware\_t
- \_cpp\_thread\_msg\_t
- \_cpp\_hardware\_cmd\_t
- enum cpp\_hardware\_cmd\_type\_t
- struct \_cpp\_module\_session\_params\_t
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;
而像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)
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); \
}
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的最大值
其中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;
- 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);
}
发现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,
"<MCT >", "persist.vendor.camera.mct.debug" }, /* CAM_MCT_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<SENSOR>", "persist.vendor.camera.sensor.debug" }, /* CAM_SENSOR_MODULE */
{CAM_GLBL_DBG_WARN, 1,
"<IFACE >", "persist.vendor.camera.iface.logs" }, /* CAM_IFACE_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<ISP >", "persist.vendor.camera.isp.debug" }, /* CAM_ISP_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<PPROC >", "persist.vendor.camera.pproc.debug.mask" }, /* CAM_PPROC_MODULE */
{CAM_GLBL_DBG_WARN, 1,
"<IMGLIB>", "persist.vendor.camera.imglib.logs" }, /* CAM_IMGLIB_MODULE */
{CAM_GLBL_DBG_WARN, 1,
"<CPP >", "persist.vendor.camera.cpp.debug.mask" }, /* CAM_CPP_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<HAL >", "persist.vendor.camera.hal.debug" }, /* CAM_HAL_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<JPEG >", "persist.vendor.camera.mmstill.logs" }, /* CAM_JPEG_MODULE */
{CAM_GLBL_DBG_WARN, 1,
"<C2D >", "persist.vendor.camera.c2d.debug.mask" }, /* CAM_C2D_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS >", "persist.vendor.camera.stats.debug" }, /* CAM_STATS_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_AF >", "persist.vendor.camera.stats.af.debug" }, /* CAM_STATS_AF_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_AEC >", "persist.vendor.camera.stats.aec.debug" }, /* CAM_STATS_AEC_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_AWB >", "persist.vendor.camera.stats.awb.debug" }, /* CAM_STATS_AWB_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_ASD >", "persist.vendor.camera.stats.asd.debug" }, /* CAM_STATS_ASD_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_AFD >", "persist.vendor.camera.stats.afd.debug" }, /* CAM_STATS_AFD_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_Q3A >", "persist.vendor.camera.stats.q3a.debug" }, /* CAM_STATS_Q3A_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_AIS >", "persist.vendor.camera.stats.is.debug" }, /* CAM_STATS_IS_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_HAF >", "persist.vendor.camera.stats.haf.debug" }, /* CAM_STATS_HAF_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<STATS_CAFSCAN >", "persist.vendor.camera.stats.cafscan" }, /* CAM_STATS_CAFSCAN_MODULE */
{CAM_GLBL_DBG_ERR, 1,
"<SHIM >", "persist.vendor.camera.shim.debug" }, /* CAM_SHIM_LAYER */
};
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
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);
}
}
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);
}
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<num_fds; i++) {
if(pollfds[i].revents & (POLLIN|POLLPRI)) {
//pollfds[0].revents = 1
//pollfds[1].revents = 0
//pollfds[0].revents = 0
//pollfds[1].revents = 2
switch(i) {
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));
//------------------sundp-3.4--------------
rc = cpp_thread_process_pipe_message(ctrl, pipe_msg);
break;
}
case SUBDEV_FD_IDX: {
//------------------sundp-3.5--------------
rc = cpp_thread_process_hardware_event(ctrl); //读到dev fd消息,处理内核事件
break;
}}}}}
}
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的信息:
}
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;
}
}
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);
}
}
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);
}
...
}
这里的是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;
}
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);
...
}
}
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; \
})
(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);
}
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);
}
}
3.2 sensor视频流buffer队列
3.2.1 cpp_thread_func
上文在函数cpp_thread_func中说了sundp-3.1和sundp-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);
....
sundp-3.3 poll(pollfds, (nfds_t)num_fds, -1);
sundp-3.1和sundp-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; /*实际发生了的事情*/
};
这里的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;
}
接下来进入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;
}
}
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;
}
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);
}
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);
.......
}
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;
}
。。。。。。
}
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参数发送到硬件
}
msm_cpp_send_frame_to_hardware()最终会执行对硬件地址的写操作,将frame的参数写入硬件。(TODO)
sundp-3.5 cpp_thread_process_hardware_event
sundp-3.1和sundp-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
.......
}
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;
......
}
}
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的内容
}
首先是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);
......
}
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));
}
}
这里,将一个视频帧(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;
}
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;
}
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));
......
}
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;
}
这里,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);
}
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.#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;
所以需要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));
......
}
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;
};
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;
} ;
_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;
_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;
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;
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;