/**************************************************************************** * drivers/video/v4l2_cap.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "video_framebuff.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define MAX_CAPTURE_FMT (2) #define CAPTURE_FMT_MAIN (0) #define CAPTURE_FMT_SUB (1) #define REMAINING_CAPNUM_INFINITY (-1) /**************************************************************************** * Private Types ****************************************************************************/ enum capture_state_e { CAPTURE_STATE_STREAMOFF = 0, /* Capture trigger event is not received */ CAPTURE_STATE_STREAMON = 1, /* Capture trigger event is received, * but capture is not operated. */ CAPTURE_STATE_CAPTURE = 2, /* On capture */ }; enum capture_state_cause_e { CAUSE_CAPTURE_STOP = 0, /* Stop capture event for capture stream */ CAUSE_CAPTURE_START = 1, /* Start capture event for capture stream */ CAUSE_CAPTURE_DQBUF = 2, /* DQBUF timing for video stream */ CAUSE_STILL_STOP = 3, /* Stop capture event for still stream */ CAUSE_STILL_START = 4, /* Start capture event for still stream */ }; enum capture_waitend_cause_e { WAITEND_CAUSE_CAPTUREDONE = 0, WAITEND_CAUSE_DQCANCEL = 1, WAITEND_CAUSE_STILLSTOP = 2, }; struct video_format_s { uint16_t width; uint16_t height; uint32_t pixelformat; }; typedef struct video_format_s video_format_t; struct capture_wait_capture_s { sem_t dqbuf_wait_flg; /* Save container which capture is done */ FAR vbuf_container_t *done_container; enum capture_waitend_cause_e waitend_cause; }; typedef struct capture_wait_capture_s capture_wait_capture_t; struct capture_type_inf_s { mutex_t lock_state; enum capture_state_e state; int32_t remaining_capnum; capture_wait_capture_t wait_capture; uint8_t nr_fmt; video_format_t fmt[MAX_CAPTURE_FMT]; struct v4l2_rect clip; struct v4l2_fract frame_interval; video_framebuff_t bufinf; FAR uint8_t *bufheap; /* for V4L2_MEMORY_MMAP buffers */ FAR struct pollfd *fds; uint32_t seqnum; }; typedef struct capture_type_inf_s capture_type_inf_t; struct capture_scene_params_s { uint8_t mode; /* enum v4l2_scene_mode */ int32_t brightness; int32_t contrast; int32_t saturation; int32_t hue; bool awb; int32_t red; int32_t blue; int32_t gamma; uint32_t gamma_curve_sz; FAR uint8_t *gamma_curve; int32_t ev; bool hflip_video; bool vflip_video; bool hflip_still; bool vflip_still; int32_t sharpness; enum v4l2_colorfx colorfx; bool auto_brightness; int32_t rotate; enum v4l2_exposure_auto_type ae; int32_t exposure_time; int32_t focus; bool af; int32_t zoom; int32_t iris; enum v4l2_auto_n_preset_white_balance wb; int32_t wdr; bool stabilization; enum v4l2_iso_sensitivity_auto_type iso_auto; int32_t iso; enum v4l2_exposure_metering meter; int32_t spot_pos; int32_t threea_lock; enum v4l2_flash_led_mode led; int32_t jpeg_quality; }; typedef struct capture_scene_params_s capture_scene_params_t; struct capture_parameter_name_s { uint32_t id; FAR const char *name; }; typedef struct capture_parameter_name_s capture_parameter_name_t; struct capture_mng_s { struct v4l2_s v4l2; /* Parameter of capture_initialize() */ mutex_t lock_open_num; uint8_t open_num; capture_type_inf_t capture_inf; capture_type_inf_t still_inf; FAR struct imgdata_s *imgdata; FAR struct imgsensor_s *imgsensor; enum v4l2_scene_mode capture_scene_mode; uint8_t capture_scence_num; FAR capture_scene_params_t *capture_scene_param[V4L2_SCENE_MODE_MAX]; #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS bool unlinked; #endif }; typedef struct capture_mng_s capture_mng_t; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static FAR capture_type_inf_t * get_capture_type_inf(FAR capture_mng_t *cmng, uint8_t type); static enum capture_state_e estimate_next_capture_state(FAR capture_mng_t *cmng, enum capture_state_cause_e cause); static void change_capture_state(FAR capture_mng_t *cmng, enum capture_state_e next_state); static bool is_taking_still_picture(FAR capture_mng_t *cmng); static bool is_bufsize_sufficient(FAR capture_mng_t *cmng, uint32_t bufsize); static void cleanup_resources(FAR capture_mng_t *cmng); static bool is_sem_waited(FAR sem_t *sem); static int save_scene_param(FAR capture_mng_t *cmng, enum v4l2_scene_mode mode, uint32_t id, FAR struct v4l2_ext_control *control); static int complete_capture(uint8_t err_code, uint32_t datasize, FAR const struct timeval *ts, FAR void *arg); static int validate_frame_setting(FAR capture_mng_t *cmng, enum v4l2_buf_type type, uint8_t nr_fmt, FAR video_format_t *vfmt, FAR struct v4l2_rect *clip, FAR struct v4l2_fract *interval); static size_t get_bufsize(FAR video_format_t *vf); /* ioctl function for each cmds of ioctl */ static int capture_querycap(FAR struct file *filep, FAR struct v4l2_capability *cap); static int capture_g_input(FAR int *num); static int capture_enum_input(FAR struct file *filep, FAR struct v4l2_input *input); static int capture_reqbufs(FAR struct file *filep, FAR struct v4l2_requestbuffers *reqbufs); static int capture_querybuf(FAR struct file *filep, FAR struct v4l2_buffer *buf); static int capture_qbuf(FAR struct file *filep, FAR struct v4l2_buffer *buf); static int capture_dqbuf(FAR struct file *filep, FAR struct v4l2_buffer *buf); static int capture_cancel_dqbuf(FAR struct file *filep, enum v4l2_buf_type type); static int capture_g_fmt(FAR struct file *filep, FAR struct v4l2_format *fmt); static int capture_s_fmt(FAR struct file *filep, FAR struct v4l2_format *fmt); static int capture_try_fmt(FAR struct file *filep, FAR struct v4l2_format *fmt); static int capture_g_parm(FAR struct file *filep, FAR struct v4l2_streamparm *parm); static int capture_s_parm(FAR struct file *filep, FAR struct v4l2_streamparm *parm); static int capture_streamon(FAR struct file *filep, FAR enum v4l2_buf_type *type); static int capture_streamoff(FAR struct file *filep, FAR enum v4l2_buf_type *type); static int capture_do_halfpush(FAR struct file *filep, bool enable); static int capture_takepict_start(FAR struct file *filep, int32_t capture_num); static int capture_takepict_stop(FAR struct file *filep, bool halfpush); static int capture_s_selection(FAR struct file *filep, FAR struct v4l2_selection *clip); static int capture_g_selection(FAR struct file *filep, FAR struct v4l2_selection *clip); static int capture_queryctrl(FAR struct file *filep, FAR struct v4l2_queryctrl *ctrl); static int capture_query_ext_ctrl(FAR struct file *filep, FAR struct v4l2_query_ext_ctrl *ctrl); static int capture_querymenu(FAR struct file *filep, FAR struct v4l2_querymenu *menu); static int capture_g_ctrl(FAR struct file *filep, FAR struct v4l2_control *ctrl); static int capture_s_ctrl(FAR struct file *filep, FAR struct v4l2_control *ctrl); static int capture_g_ext_ctrls(FAR struct file *filep, FAR struct v4l2_ext_controls *ctrls); static int capture_s_ext_ctrls(FAR struct file *filep, FAR struct v4l2_ext_controls *ctrls); static int capture_query_ext_ctrl_scene(FAR struct file *filep, FAR struct v4s_query_ext_ctrl_scene *ctrl); static int capture_querymenu_scene(FAR struct file *filep, FAR struct v4s_querymenu_scene *menu); static int capture_g_ext_ctrls_scene(FAR struct file *filep, FAR struct v4s_ext_controls_scene *ctrls); static int capture_s_ext_ctrls_scene(FAR struct file *filep, FAR struct v4s_ext_controls_scene *ctrls); static int capture_enum_fmt(FAR struct file *filep, FAR struct v4l2_fmtdesc *f); static int capture_enum_frminterval(FAR struct file *filep, FAR struct v4l2_frmivalenum *f); static int capture_enum_frmsize(FAR struct file *filep, FAR struct v4l2_frmsizeenum *f); /* File operations function */ static int capture_open(FAR struct file *filep); static int capture_close(FAR struct file *filep); static int capture_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map); static int capture_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static int capture_unlink(FAR struct inode *inode); #endif /**************************************************************************** * Private Data ****************************************************************************/ static const struct v4l2_ops_s g_capture_vops = { capture_querycap, /* querycap */ capture_g_input, /* g_input */ capture_enum_input, /* enum_input */ capture_reqbufs, /* reqbufs */ capture_querybuf, /* querybuf */ capture_qbuf, /* qbuf */ capture_dqbuf, /* dqbuf */ capture_cancel_dqbuf, /* cancel_dqbuf */ capture_g_fmt, /* g_fmt */ capture_s_fmt, /* s_fmt */ capture_try_fmt, /* try_fmt */ capture_g_parm, /* g_parm */ capture_s_parm, /* s_parm */ capture_streamon, /* streamon */ capture_streamoff, /* streamoff */ capture_do_halfpush, /* do_halfpush */ capture_takepict_start, /* takepict_start */ capture_takepict_stop, /* takepict_stop */ capture_s_selection, /* s_selection */ capture_g_selection, /* g_selection */ capture_queryctrl, /* queryctrl */ capture_query_ext_ctrl, /* query_ext_ctrl */ capture_querymenu, /* querymenu */ capture_g_ctrl, /* g_ctrl */ capture_s_ctrl, /* s_ctrl */ capture_g_ext_ctrls, /* g_ext_ctrls */ capture_s_ext_ctrls, /* s_ext_ctrls */ capture_query_ext_ctrl_scene, /* query_ext_ctrl_scene */ capture_querymenu_scene, /* querymenu_scene */ capture_g_ext_ctrls_scene, /* g_ext_ctrls_scene */ capture_s_ext_ctrls_scene, /* s_ext_ctrls_scene */ capture_enum_fmt, /* enum_fmt */ capture_enum_frminterval, /* enum_frminterval */ capture_enum_frmsize /* enum_frmsize */ }; static const struct file_operations g_capture_fops = { capture_open, /* open */ capture_close, /* close */ NULL, /* read */ NULL, /* write */ NULL, /* seek */ NULL, /* ioctl */ capture_mmap, /* mmap */ NULL, /* truncate */ capture_poll, /* poll */ #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS capture_unlink, /* unlink */ #endif }; static const capture_parameter_name_t g_capture_parameter_name[] = { { IMGSENSOR_ID_BRIGHTNESS, "Brightness" }, { IMGSENSOR_ID_CONTRAST, "Contrast" }, { IMGSENSOR_ID_SATURATION, "Saturation" }, { IMGSENSOR_ID_HUE, "Hue" }, { IMGSENSOR_ID_AUTO_WHITE_BALANCE, "Automatic white balance" }, { IMGSENSOR_ID_RED_BALANCE, "Red balance" }, { IMGSENSOR_ID_BLUE_BALANCE, "Blue balance" }, { IMGSENSOR_ID_GAMMA, "Gamma value" }, { IMGSENSOR_ID_GAMMA_CURVE, "Gamma adjustment(curve)" }, { IMGSENSOR_ID_EXPOSURE, "Exposure value" }, { IMGSENSOR_ID_HFLIP_VIDEO, "Mirror horizontally(VIDEO)" }, { IMGSENSOR_ID_VFLIP_VIDEO, "Mirror vertically(VIDEO)" }, { IMGSENSOR_ID_HFLIP_STILL, "Mirror horizontally(STILL)" }, { IMGSENSOR_ID_VFLIP_STILL, "Mirror vertically(STILL)" }, { IMGSENSOR_ID_SHARPNESS, "Sharpness" }, { IMGSENSOR_ID_COLOR_KILLER, "Color killer" }, { IMGSENSOR_ID_COLORFX, "Color effect" }, { IMGSENSOR_ID_AUTOBRIGHTNESS, "Auto brightness" }, { IMGSENSOR_ID_ROTATE, "Rotate" }, { IMGSENSOR_ID_EXPOSURE_AUTO, "Auto Exposure" }, { IMGSENSOR_ID_EXPOSURE_ABSOLUTE, "Exposure time(100 usec)" }, { IMGSENSOR_ID_FOCUS_ABSOLUTE, "Focus(absolute value)" }, { IMGSENSOR_ID_FOCUS_RELATIVE, "Focus(relative value)" }, { IMGSENSOR_ID_FOCUS_AUTO, "Continuous Auto Focus" }, { IMGSENSOR_ID_ZOOM_ABSOLUTE, "Zoom(absolute value)" }, { IMGSENSOR_ID_ZOOM_RELATIVE, "Zoom(relative value)" }, { IMGSENSOR_ID_ZOOM_CONTINUOUS, "Continuous zoom" }, { IMGSENSOR_ID_IRIS_ABSOLUTE, "Iris(absolute value)" }, { IMGSENSOR_ID_IRIS_RELATIVE, "Iris(relative value)" }, { IMGSENSOR_ID_AUTO_N_PRESET_WB, "Preset white balance" }, { IMGSENSOR_ID_WIDE_DYNAMIC_RANGE, "Wide dynamic range" }, { IMGSENSOR_ID_IMG_STABILIZATION, "Image stabilization" }, { IMGSENSOR_ID_ISO_SENSITIVITY, "ISO sensitivity" }, { IMGSENSOR_ID_ISO_SENSITIVITY_AUTO, "Automatic ISO sensitivity" }, { IMGSENSOR_ID_EXPOSURE_METERING, "Photometry" }, { IMGSENSOR_ID_SPOT_POSITION, "Spot position" }, { IMGSENSOR_ID_3A_LOCK, "Lock AWB/AE" }, { IMGSENSOR_ID_AUTO_FOCUS_START, "Start single Auto Focus" }, { IMGSENSOR_ID_AUTO_FOCUS_STOP, "Stop single Auto Focus" }, { IMGSENSOR_ID_3A_PARAMETER, "3A parameter" }, { IMGSENSOR_ID_3A_STATUS, "3A status" }, { IMGSENSOR_ID_FLASH_LED_MODE, "LED mode" }, { IMGSENSOR_ID_JPEG_QUALITY, "JPEG compression quality" } }; static FAR struct imgsensor_s **g_capture_registered_sensor = NULL; static size_t g_capture_registered_sensor_num; static FAR struct imgdata_s *g_capture_data = NULL; /**************************************************************************** * Private Functions ****************************************************************************/ static FAR capture_type_inf_t * get_capture_type_inf(FAR capture_mng_t *cmng, uint8_t type) { FAR capture_type_inf_t *type_inf; switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: type_inf = &cmng->capture_inf; break; case V4L2_BUF_TYPE_STILL_CAPTURE: type_inf = &cmng->still_inf; break; default: /* Error case */ type_inf = NULL; break; } return type_inf; } static enum capture_state_e estimate_next_capture_state(FAR capture_mng_t *cmng, enum capture_state_cause_e cause) { enum capture_state_e current_state = cmng->capture_inf.state; switch (cause) { case CAUSE_CAPTURE_STOP: return CAPTURE_STATE_STREAMOFF; case CAUSE_CAPTURE_START: if (is_taking_still_picture(cmng)) { return CAPTURE_STATE_STREAMON; } else { return CAPTURE_STATE_CAPTURE; } case CAUSE_STILL_STOP: if (current_state == CAPTURE_STATE_STREAMON) { return CAPTURE_STATE_CAPTURE; } else { return current_state; } case CAUSE_STILL_START: if (current_state == CAPTURE_STATE_CAPTURE) { return CAPTURE_STATE_STREAMON; } else { return current_state; } case CAUSE_CAPTURE_DQBUF: if (current_state == CAPTURE_STATE_STREAMON && !is_taking_still_picture(cmng)) { return CAPTURE_STATE_CAPTURE; } else { return current_state; } default: return current_state; } } static void convert_to_imgdatafmt(FAR video_format_t *video, FAR imgdata_format_t *data) { ASSERT(video && data); data->width = video->width; data->height = video->height; switch (video->pixelformat) { case V4L2_PIX_FMT_NV12: data->pixelformat = IMGDATA_PIX_FMT_NV12; break; case V4L2_PIX_FMT_YUV420: data->pixelformat = IMGDATA_PIX_FMT_YUV420P; break; case V4L2_PIX_FMT_YUYV: data->pixelformat = IMGDATA_PIX_FMT_YUYV; break; case V4L2_PIX_FMT_UYVY: data->pixelformat = IMGDATA_PIX_FMT_UYVY; break; case V4L2_PIX_FMT_RGB565: data->pixelformat = IMGDATA_PIX_FMT_RGB565; break; case V4L2_PIX_FMT_JPEG: data->pixelformat = IMGDATA_PIX_FMT_JPEG; break; default: /* V4L2_PIX_FMT_JPEG_WITH_SUBIMG */ data->pixelformat = IMGDATA_PIX_FMT_JPEG_WITH_SUBIMG; break; } } static void convert_to_imgsensorfmt(FAR video_format_t *video, FAR imgsensor_format_t *sensor) { ASSERT(video && sensor); sensor->width = video->width; sensor->height = video->height; switch (video->pixelformat) { case V4L2_PIX_FMT_NV12: sensor->pixelformat = IMGSENSOR_PIX_FMT_NV12; break; case V4L2_PIX_FMT_YUV420: sensor->pixelformat = IMGSENSOR_PIX_FMT_YUV420P; break; case V4L2_PIX_FMT_YUYV: sensor->pixelformat = IMGSENSOR_PIX_FMT_YUYV; break; case V4L2_PIX_FMT_UYVY: sensor->pixelformat = IMGSENSOR_PIX_FMT_UYVY; break; case V4L2_PIX_FMT_RGB565: sensor->pixelformat = IMGSENSOR_PIX_FMT_RGB565; break; case V4L2_PIX_FMT_JPEG: sensor->pixelformat = IMGSENSOR_PIX_FMT_JPEG; break; default: /* V4L2_PIX_FMT_JPEG_WITH_SUBIMG */ sensor->pixelformat = IMGSENSOR_PIX_FMT_JPEG_WITH_SUBIMG; break; } } static void convert_to_imgdatainterval(FAR struct v4l2_fract *video, FAR imgdata_interval_t *data) { ASSERT(video && data); data->numerator = video->numerator; data->denominator = video->denominator; } static void convert_to_imgsensorinterval(FAR struct v4l2_fract *video, FAR imgsensor_interval_t *sensor) { ASSERT(video && sensor); sensor->numerator = video->numerator; sensor->denominator = video->denominator; } static bool is_clipped(FAR struct v4l2_rect *clip) { bool ret = false; if (clip) { if (clip->left != 0 || clip->top != 0 || clip->width != 0 || clip->height != 0) { ret = true; } } return ret; } static void get_clipped_format(uint8_t nr_fmt, FAR video_format_t *fmt, FAR struct v4l2_rect *clip, FAR video_format_t *c_fmt) { DEBUGASSERT(fmt && c_fmt); if (is_clipped(clip)) { c_fmt[CAPTURE_FMT_MAIN].width = clip->width; c_fmt[CAPTURE_FMT_MAIN].height = clip->height; c_fmt[CAPTURE_FMT_MAIN].pixelformat = fmt[CAPTURE_FMT_MAIN].pixelformat; if (nr_fmt > 1) { /* Clipped size of thumbnail is * small as ratio of main size and thumbnail size. */ memcpy(&c_fmt[CAPTURE_FMT_SUB], &fmt[CAPTURE_FMT_SUB], sizeof(video_format_t)); c_fmt[CAPTURE_FMT_SUB].width = (uint32_t)c_fmt[CAPTURE_FMT_SUB].width * clip->width / fmt[CAPTURE_FMT_MAIN].width; c_fmt[CAPTURE_FMT_SUB].height = (uint32_t)c_fmt[CAPTURE_FMT_SUB].height * clip->height / fmt[CAPTURE_FMT_MAIN].height; } } else { memcpy(c_fmt, fmt, nr_fmt * sizeof(video_format_t)); } } static int start_capture(FAR struct capture_mng_s *cmng, enum v4l2_buf_type type, uint8_t nr_fmt, FAR video_format_t *fmt, FAR struct v4l2_rect *clip, FAR struct v4l2_fract *interval, uintptr_t bufaddr, uint32_t bufsize) { video_format_t c_fmt[MAX_CAPTURE_FMT]; imgdata_format_t df[MAX_CAPTURE_FMT]; imgsensor_format_t sf[MAX_CAPTURE_FMT]; imgdata_interval_t di; imgsensor_interval_t si; ASSERT(fmt && interval && cmng->imgsensor && cmng->imgdata); get_clipped_format(nr_fmt, fmt, clip, c_fmt); convert_to_imgdatafmt(&c_fmt[CAPTURE_FMT_MAIN], &df[IMGDATA_FMT_MAIN]); convert_to_imgdatafmt(&c_fmt[CAPTURE_FMT_SUB], &df[IMGDATA_FMT_SUB]); convert_to_imgdatainterval(interval, &di); convert_to_imgsensorfmt(&fmt[CAPTURE_FMT_MAIN], &sf[IMGSENSOR_FMT_MAIN]); convert_to_imgsensorfmt(&fmt[CAPTURE_FMT_SUB], &sf[IMGSENSOR_FMT_SUB]); convert_to_imgsensorinterval(interval, &si); IMGDATA_SET_BUF(cmng->imgdata, nr_fmt, df, (FAR uint8_t *)bufaddr, bufsize); IMGDATA_START_CAPTURE(cmng->imgdata, nr_fmt, df, &di, complete_capture, cmng); IMGSENSOR_START_CAPTURE(cmng->imgsensor, type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? IMGSENSOR_STREAM_TYPE_VIDEO : IMGSENSOR_STREAM_TYPE_STILL, nr_fmt, sf, &si); return OK; } static void stop_capture(FAR struct capture_mng_s *cmng, enum v4l2_buf_type type) { ASSERT(cmng->imgsensor && cmng->imgdata); IMGDATA_STOP_CAPTURE(cmng->imgdata); IMGSENSOR_STOP_CAPTURE(cmng->imgsensor, type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? IMGSENSOR_STREAM_TYPE_VIDEO : IMGSENSOR_STREAM_TYPE_STILL); } static void change_capture_state(FAR capture_mng_t *cmng, enum capture_state_e next_state) { enum capture_state_e current_state = cmng->capture_inf.state; enum capture_state_e updated_next_state = next_state; if (current_state != CAPTURE_STATE_CAPTURE && next_state == CAPTURE_STATE_CAPTURE) { FAR vbuf_container_t *container = video_framebuff_get_vacant_container(&cmng->capture_inf.bufinf); if (container != NULL) { cmng->capture_inf.seqnum = 0; start_capture(cmng, V4L2_BUF_TYPE_VIDEO_CAPTURE, cmng->capture_inf.nr_fmt, cmng->capture_inf.fmt, &cmng->capture_inf.clip, &cmng->capture_inf.frame_interval, container->buf.m.userptr, container->buf.length); } else { updated_next_state = CAPTURE_STATE_STREAMON; } } else if (current_state == CAPTURE_STATE_CAPTURE && next_state != CAPTURE_STATE_CAPTURE) { stop_capture(cmng, V4L2_BUF_TYPE_VIDEO_CAPTURE); } cmng->capture_inf.state = updated_next_state; } static bool is_taking_still_picture(FAR capture_mng_t *cmng) { return cmng->still_inf.state == CAPTURE_STATE_STREAMON || cmng->still_inf.state == CAPTURE_STATE_CAPTURE; } static bool is_bufsize_sufficient(FAR capture_mng_t *cmng, uint32_t bufsize) { /* Depend on format, frame size, and JPEG compression quality */ return true; } static void initialize_frame_setting(FAR struct imgsensor_s *imgsensor, FAR uint8_t *nr_fmt, FAR video_format_t *fmt, FAR struct v4l2_fract *interval) { ASSERT(nr_fmt && fmt && interval); /* Initial setting : QVGA YUV4:2:2 15FPS */ *nr_fmt = 1; if (imgsensor && imgsensor->frmsizes) { if (imgsensor->frmsizes[0].type == V4L2_FRMSIZE_TYPE_DISCRETE) { fmt[CAPTURE_FMT_MAIN].width = imgsensor->frmsizes[0].discrete.width; fmt[CAPTURE_FMT_MAIN].height = imgsensor->frmsizes[0].discrete.height; } else { fmt[CAPTURE_FMT_MAIN].width = imgsensor->frmsizes[0].stepwise.min_width; fmt[CAPTURE_FMT_MAIN].height = imgsensor->frmsizes[0].stepwise.min_height; } } else { fmt[CAPTURE_FMT_MAIN].width = VIDEO_HSIZE_QVGA; fmt[CAPTURE_FMT_MAIN].height = VIDEO_VSIZE_QVGA; } if (imgsensor && imgsensor->fmtdescs) { fmt[CAPTURE_FMT_MAIN].pixelformat = imgsensor->fmtdescs[0].pixelformat; } else { fmt[CAPTURE_FMT_MAIN].pixelformat = V4L2_PIX_FMT_UYVY; } if (imgsensor && imgsensor->frmintervals) { if (imgsensor->frmintervals[0].type == V4L2_FRMIVAL_TYPE_DISCRETE) { interval->denominator = imgsensor->frmintervals[0].discrete.denominator; interval->numerator = imgsensor->frmintervals[0].discrete.numerator; } else { interval->denominator = imgsensor->frmintervals[0].stepwise.min.denominator; interval->numerator = imgsensor->frmintervals[0].stepwise.min.numerator; } } else { interval->denominator = 15; interval->numerator = 1; } } static void initialize_streamresources(FAR capture_type_inf_t *type_inf, FAR capture_mng_t *cmng) { memset(type_inf, 0, sizeof(capture_type_inf_t)); type_inf->remaining_capnum = REMAINING_CAPNUM_INFINITY; nxmutex_init(&type_inf->lock_state); nxsem_init(&type_inf->wait_capture.dqbuf_wait_flg, 0, 0); initialize_frame_setting(cmng->imgsensor, &type_inf->nr_fmt, type_inf->fmt, &type_inf->frame_interval); video_framebuff_init(&type_inf->bufinf); } static int32_t get_default_value(FAR capture_mng_t *cmng, uint32_t id) { imgsensor_supported_value_t value; int ret; if (cmng->imgsensor == NULL) { /* Don't care(unsupported parameter) */ return 0; } ret = IMGSENSOR_GET_SUPPORTED_VALUE(cmng->imgsensor, id, &value); if (ret != OK) { /* Don't care(unsupported parameter) */ return 0; } switch (value.type) { case IMGSENSOR_CTRL_TYPE_INTEGER_MENU: return value.u.discrete.default_value; case IMGSENSOR_CTRL_TYPE_U8: case IMGSENSOR_CTRL_TYPE_U16: case IMGSENSOR_CTRL_TYPE_U32: /* Don't care */ return 0; default: return value.u.range.default_value; } } static int32_t initialize_scene_gamma(FAR capture_mng_t *cmng, FAR uint8_t **gamma) { imgsensor_supported_value_t sup_val; imgsensor_value_t val; int32_t sz; int ret; *gamma = NULL; ASSERT(cmng->imgsensor); ret = IMGSENSOR_GET_SUPPORTED_VALUE(cmng->imgsensor, IMGSENSOR_ID_GAMMA_CURVE, &sup_val); if (ret != OK) { /* Unsupported parameter */ return 0; } switch (sup_val.type) { case IMGSENSOR_CTRL_TYPE_U8: sz = sup_val.u.elems.nr_elems * sizeof(uint8_t); if (sz / sizeof(uint8_t) != sup_val.u.elems.nr_elems) { /* Multiplication overflow */ return 0; } break; case IMGSENSOR_CTRL_TYPE_U16: sz = sup_val.u.elems.nr_elems * sizeof(uint16_t); if (sz / sizeof(uint16_t) != sup_val.u.elems.nr_elems) { /* Multiplication overflow */ return 0; } break; default: /* IMGSENSOR_CTRL_TYPE_U32 */ sz = sup_val.u.elems.nr_elems * sizeof(uint32_t); if (sz / sizeof(uint32_t) != sup_val.u.elems.nr_elems) { /* Multiplication overflow */ return 0; } break; } *gamma = kmm_malloc(sz); val.p_u8 = (FAR uint8_t *)*gamma; IMGSENSOR_GET_VALUE(cmng->imgsensor, IMGSENSOR_ID_GAMMA_CURVE, sz, &val); return sz; } static int initialize_scene_parameter(FAR capture_mng_t *cmng, enum v4l2_scene_mode mode, FAR capture_scene_params_t **vsp) { FAR capture_scene_params_t *sp = kmm_malloc(sizeof(capture_scene_params_t)); if (!sp) { return -ENOMEM; } sp->mode = mode; sp->brightness = get_default_value(cmng, IMGSENSOR_ID_BRIGHTNESS); sp->contrast = get_default_value(cmng, IMGSENSOR_ID_CONTRAST); sp->saturation = get_default_value(cmng, IMGSENSOR_ID_SATURATION); sp->hue = get_default_value(cmng, IMGSENSOR_ID_HUE); sp->awb = get_default_value(cmng, IMGSENSOR_ID_AUTO_WHITE_BALANCE); sp->red = get_default_value(cmng, IMGSENSOR_ID_RED_BALANCE); sp->blue = get_default_value(cmng, IMGSENSOR_ID_BLUE_BALANCE); sp->gamma = get_default_value(cmng, IMGSENSOR_ID_GAMMA); sp->gamma_curve_sz = initialize_scene_gamma(cmng, &sp->gamma_curve); sp->ev = get_default_value(cmng, IMGSENSOR_ID_EXPOSURE); sp->hflip_video = get_default_value(cmng, IMGSENSOR_ID_HFLIP_VIDEO); sp->vflip_video = get_default_value(cmng, IMGSENSOR_ID_VFLIP_VIDEO); sp->hflip_still = get_default_value(cmng, IMGSENSOR_ID_HFLIP_STILL); sp->vflip_still = get_default_value(cmng, IMGSENSOR_ID_VFLIP_STILL); sp->sharpness = get_default_value(cmng, IMGSENSOR_ID_SHARPNESS); sp->colorfx = get_default_value(cmng, IMGSENSOR_ID_COLORFX); sp->auto_brightness = get_default_value(cmng, IMGSENSOR_ID_AUTOBRIGHTNESS); sp->rotate = get_default_value(cmng, IMGSENSOR_ID_ROTATE); sp->ae = get_default_value(cmng, IMGSENSOR_ID_EXPOSURE_AUTO); sp->exposure_time = get_default_value(cmng, IMGSENSOR_ID_EXPOSURE_ABSOLUTE); sp->focus = get_default_value(cmng, IMGSENSOR_ID_FOCUS_ABSOLUTE); sp->af = get_default_value(cmng, IMGSENSOR_ID_FOCUS_AUTO); sp->zoom = get_default_value(cmng, IMGSENSOR_ID_ZOOM_ABSOLUTE); sp->iris = get_default_value(cmng, IMGSENSOR_ID_IRIS_ABSOLUTE); sp->wb = get_default_value(cmng, IMGSENSOR_ID_AUTO_N_PRESET_WB); sp->wdr = get_default_value(cmng, IMGSENSOR_ID_WIDE_DYNAMIC_RANGE); sp->stabilization = get_default_value(cmng, IMGSENSOR_ID_IMG_STABILIZATION); sp->iso_auto = get_default_value(cmng, IMGSENSOR_ID_ISO_SENSITIVITY_AUTO); sp->iso = get_default_value(cmng, IMGSENSOR_ID_ISO_SENSITIVITY); sp->meter = get_default_value(cmng, IMGSENSOR_ID_EXPOSURE_METERING); sp->threea_lock = get_default_value(cmng, IMGSENSOR_ID_3A_LOCK); sp->led = get_default_value(cmng, IMGSENSOR_ID_FLASH_LED_MODE); sp->jpeg_quality = get_default_value(cmng, IMGSENSOR_ID_JPEG_QUALITY); *vsp = sp; return OK; } static void initialize_scenes_parameter(FAR capture_mng_t *cmng) { memset(cmng->capture_scene_param, 0, sizeof(cmng->capture_scene_param)); initialize_scene_parameter(cmng, V4L2_SCENE_MODE_NONE, &cmng->capture_scene_param[cmng->capture_scence_num++]); #ifdef CONFIG_VIDEO_SCENE_BACKLIGHT initialize_scene_parameter(cmng, V4L2_SCENE_MODE_BACKLIGHT, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_BACKLIGHT */ #ifdef CONFIG_VIDEO_SCENE_BEACHSNOW initialize_scene_parameter(cmng, V4L2_SCENE_MODE_BEACH_SNOW, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_BEACHSNOW */ #ifdef CONFIG_VIDEO_SCENE_CANDLELIGHT initialize_scene_parameter(cmng, V4L2_SCENE_MODE_CANDLE_LIGHT, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_CANDLELIGHT */ #ifdef CONFIG_VIDEO_SCENE_DAWNDUSK initialize_scene_parameter(cmng, V4L2_SCENE_MODE_DAWN_DUSK, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_DAWNDUSK */ #ifdef CONFIG_VIDEO_SCENE_FALLCOLORS initialize_scene_parameter(cmng, V4L2_SCENE_MODE_FALL_COLORS, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_FALLCOLORS */ #ifdef CONFIG_VIDEO_SCENE_FIREWORKS initialize_scene_parameter(cmng, V4L2_SCENE_MODE_FIREWORKS, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_FIREWORKS */ #ifdef CONFIG_VIDEO_SCENE_LANDSCAPE initialize_scene_parameter(cmng, V4L2_SCENE_MODE_LANDSCAPE, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_LANDSCAPE */ #ifdef CONFIG_VIDEO_SCENE_NIGHT initialize_scene_parameter(cmng, V4L2_SCENE_MODE_NIGHT, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_NIGHT */ #ifdef CONFIG_VIDEO_SCENE_PARTYINDOOR initialize_scene_parameter(cmng, V4L2_SCENE_MODE_PARTY_INDOOR, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_PARTYINDOOR */ #ifdef CONFIG_VIDEO_SCENE_PORTRAIT initialize_scene_parameter(cmng, V4L2_SCENE_MODE_PORTRAIT, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_PORTRAIT */ #ifdef CONFIG_VIDEO_SCENE_SPORTS initialize_scene_parameter(cmng, V4L2_SCENE_MODE_SPORTS, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_SPORTS */ #ifdef CONFIG_VIDEO_SCENE_SUNSET initialize_scene_parameter(cmng, V4L2_SCENE_MODE_SUNSET, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_SUNSET */ #ifdef CONFIG_VIDEO_SCENE_TEXT initialize_scene_parameter(cmng, V4L2_SCENE_MODE_TEXT, &cmng->capture_scene_param[cmng->capture_scence_num++]); #endif /* CONFIG_VIDEO_SCENE_TEXT */ } static void initialize_resources(FAR capture_mng_t *cmng) { initialize_streamresources(&cmng->capture_inf, cmng); initialize_streamresources(&cmng->still_inf, cmng); initialize_scenes_parameter(cmng); } static void cleanup_streamresources(FAR capture_type_inf_t *type_inf, FAR capture_mng_t *cmng) { video_framebuff_uninit(&type_inf->bufinf); nxsem_destroy(&type_inf->wait_capture.dqbuf_wait_flg); nxmutex_destroy(&type_inf->lock_state); if (type_inf->bufheap != NULL) { if (cmng->imgdata->ops->free) { cmng->imgdata->ops->free(cmng->imgdata, type_inf->bufheap); } else { kumm_free(type_inf->bufheap); } type_inf->bufheap = NULL; } } static void cleanup_scene_parameter(FAR capture_scene_params_t **vsp) { FAR capture_scene_params_t *sp = *vsp; ASSERT(sp); if (sp->gamma_curve != NULL) { kmm_free(sp->gamma_curve); sp->gamma_curve = NULL; sp->gamma_curve_sz = 0; } kmm_free(sp); *vsp = NULL; } static void cleanup_scenes_parameter(FAR capture_mng_t *cmng) { int i; for (i = 0; i < cmng->capture_scence_num; i++) { cleanup_scene_parameter(&cmng->capture_scene_param[i]); } cmng->capture_scence_num = 0; } static void cleanup_resources(FAR capture_mng_t *cmng) { /* If in capture, stop */ if (cmng->capture_inf.state == CAPTURE_STATE_CAPTURE) { stop_capture(cmng, V4L2_BUF_TYPE_VIDEO_CAPTURE); } if (cmng->still_inf.state == CAPTURE_STATE_CAPTURE) { stop_capture(cmng, V4L2_BUF_TYPE_STILL_CAPTURE); } /* Clean up resource */ cleanup_streamresources(&cmng->capture_inf, cmng); cleanup_streamresources(&cmng->still_inf, cmng); cleanup_scenes_parameter(cmng); } static bool is_sem_waited(FAR sem_t *sem) { int semcount; return nxsem_get_value(sem, &semcount) == OK && semcount < 0; } static int validate_frame_setting(FAR capture_mng_t *cmng, enum v4l2_buf_type type, uint8_t nr_fmt, FAR video_format_t *vfmt, FAR struct v4l2_rect *clip, FAR struct v4l2_fract *interval) { video_format_t c_fmt[MAX_CAPTURE_FMT]; imgdata_format_t df[MAX_CAPTURE_FMT]; imgsensor_format_t sf[MAX_CAPTURE_FMT]; imgdata_interval_t di; imgsensor_interval_t si; int ret; ASSERT(vfmt && interval && cmng->imgsensor && cmng->imgdata); /* Return OK only in case both image data driver and * image sensor driver support. */ get_clipped_format(nr_fmt, vfmt, clip, c_fmt); convert_to_imgdatafmt(&c_fmt[CAPTURE_FMT_MAIN], &df[IMGDATA_FMT_MAIN]); convert_to_imgdatafmt(&c_fmt[CAPTURE_FMT_SUB], &df[IMGDATA_FMT_SUB]); convert_to_imgdatainterval(interval, &di); convert_to_imgsensorfmt(&vfmt[CAPTURE_FMT_MAIN], &sf[IMGSENSOR_FMT_MAIN]); convert_to_imgsensorfmt(&vfmt[CAPTURE_FMT_SUB], &sf[IMGSENSOR_FMT_SUB]); convert_to_imgsensorinterval(interval, &si); ret = IMGSENSOR_VALIDATE_FRAME_SETTING(cmng->imgsensor, type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? IMGSENSOR_STREAM_TYPE_VIDEO : IMGSENSOR_STREAM_TYPE_STILL, nr_fmt, sf, &si); if (ret != OK) { return ret; } return IMGDATA_VALIDATE_FRAME_SETTING(cmng->imgdata, nr_fmt, df, &di); } static size_t get_bufsize(FAR video_format_t *vf) { uint32_t width = vf->width; uint32_t height = vf->height; size_t ret = width * height; switch (vf->pixelformat) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV420: return ret * 3 / 2; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_JPEG: default: return ret * 2; } } static size_t get_heapsize(FAR capture_type_inf_t *type_inf) { return type_inf->bufinf.container_size * get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN]); } static bool validate_clip_range(int32_t pos, uint32_t c_sz, uint16_t frm_sz) { return pos >= 0 && c_sz <= frm_sz && pos + c_sz <= frm_sz; } static bool validate_clip_setting(FAR struct v4l2_rect *clip, FAR video_format_t *fmt) { DEBUGASSERT(clip && fmt); /* Not permit the setting which do not fit inside frame size. */ return validate_clip_range(clip->left, clip->width, fmt->width) && validate_clip_range(clip->top, clip->height, fmt->height); } static void set_parameter_name(uint32_t id, FAR char *name) { int size = sizeof(g_capture_parameter_name) / sizeof(capture_parameter_name_t); int cnt; for (cnt = 0; cnt < size; cnt++) { if (g_capture_parameter_name[cnt].id == id) { break; } } ASSERT(cnt < size); /* copy size = 32 is due to V4L2 specification. */ strlcpy(name, g_capture_parameter_name[cnt].name, 32); } static int set_intvalue(FAR struct capture_mng_s *cmng, uint32_t id, int32_t value32) { imgsensor_value_t value; ASSERT(cmng->imgsensor); value.value32 = value32; return IMGSENSOR_SET_VALUE(cmng->imgsensor, id, sizeof(int32_t), value); } static int set_pvalue(FAR struct capture_mng_s *cmng, uint32_t id, int size, void *pval) { imgsensor_value_t value; ASSERT(cmng->imgsensor); value.p_u8 = (FAR uint8_t *)pval; return IMGSENSOR_SET_VALUE(cmng->imgsensor, id, size, value); } static capture_scene_params_t *search_scene_param(FAR capture_mng_t *cmng, enum v4l2_scene_mode mode) { int i; for (i = 0; i < cmng->capture_scence_num; i++) { if (cmng->capture_scene_param[i]->mode == mode) { return cmng->capture_scene_param[i]; } } return NULL; } static int reflect_scene_parameter(FAR capture_mng_t *cmng, enum v4l2_scene_mode mode) { capture_scene_params_t *sp; sp = search_scene_param(cmng, mode); if (sp == NULL) { /* Unsupported scene mode */ return -EINVAL; } set_intvalue(cmng, IMGSENSOR_ID_BRIGHTNESS, sp->brightness); set_intvalue(cmng, IMGSENSOR_ID_CONTRAST, sp->contrast); set_intvalue(cmng, IMGSENSOR_ID_SATURATION, sp->saturation); set_intvalue(cmng, IMGSENSOR_ID_HUE , sp->hue); set_intvalue(cmng, IMGSENSOR_ID_AUTO_WHITE_BALANCE, sp->awb); set_intvalue(cmng, IMGSENSOR_ID_RED_BALANCE , sp->red); set_intvalue(cmng, IMGSENSOR_ID_BLUE_BALANCE, sp->blue); set_intvalue(cmng, IMGSENSOR_ID_GAMMA, sp->gamma); set_pvalue(cmng, IMGSENSOR_ID_GAMMA_CURVE, sp->gamma_curve_sz, sp->gamma_curve); set_intvalue(cmng, IMGSENSOR_ID_EXPOSURE, sp->ev); set_intvalue(cmng, IMGSENSOR_ID_HFLIP_VIDEO, sp->hflip_video); set_intvalue(cmng, IMGSENSOR_ID_VFLIP_VIDEO, sp->vflip_video); set_intvalue(cmng, IMGSENSOR_ID_HFLIP_STILL, sp->hflip_still); set_intvalue(cmng, IMGSENSOR_ID_VFLIP_STILL, sp->vflip_still); set_intvalue(cmng, IMGSENSOR_ID_SHARPNESS, sp->sharpness); set_intvalue(cmng, IMGSENSOR_ID_COLORFX, sp->colorfx); set_intvalue(cmng, IMGSENSOR_ID_AUTOBRIGHTNESS, sp->auto_brightness); set_intvalue(cmng, IMGSENSOR_ID_ROTATE, sp->rotate); set_intvalue(cmng, IMGSENSOR_ID_EXPOSURE_AUTO, sp->ae); if (sp->ae == V4L2_EXPOSURE_MANUAL || sp->ae == V4L2_EXPOSURE_SHUTTER_PRIORITY) { set_intvalue(cmng, IMGSENSOR_ID_EXPOSURE_ABSOLUTE, sp->exposure_time); } set_intvalue(cmng, IMGSENSOR_ID_FOCUS_ABSOLUTE, sp->focus); set_intvalue(cmng, IMGSENSOR_ID_FOCUS_AUTO, sp->af); set_intvalue(cmng, IMGSENSOR_ID_ZOOM_ABSOLUTE, sp->zoom); if (sp->ae == V4L2_EXPOSURE_MANUAL || sp->ae == V4L2_EXPOSURE_APERTURE_PRIORITY) { set_intvalue(cmng, IMGSENSOR_ID_IRIS_ABSOLUTE, sp->iris); } set_intvalue(cmng, IMGSENSOR_ID_AUTO_N_PRESET_WB, sp->wb); set_intvalue(cmng, IMGSENSOR_ID_WIDE_DYNAMIC_RANGE, sp->wdr); set_intvalue(cmng, IMGSENSOR_ID_IMG_STABILIZATION, sp->stabilization); set_intvalue(cmng, IMGSENSOR_ID_ISO_SENSITIVITY_AUTO, sp->iso_auto); if (sp->iso_auto == V4L2_ISO_SENSITIVITY_MANUAL) { set_intvalue(cmng, IMGSENSOR_ID_ISO_SENSITIVITY, sp->iso); } set_intvalue(cmng, IMGSENSOR_ID_EXPOSURE_METERING, sp->meter); set_intvalue(cmng, IMGSENSOR_ID_3A_LOCK, sp->threea_lock); set_intvalue(cmng, IMGSENSOR_ID_FLASH_LED_MODE, sp->led); set_intvalue(cmng, IMGSENSOR_ID_JPEG_QUALITY, sp->jpeg_quality); cmng->capture_scene_mode = mode; return OK; } static int read_scene_param(FAR struct capture_mng_s *cmng, enum v4l2_scene_mode mode, uint32_t id, FAR struct v4l2_ext_control *control) { imgsensor_supported_value_t value; capture_scene_params_t *sp; int ret = OK; ASSERT(cmng->imgsensor); if (control == NULL) { return -EINVAL; } sp = search_scene_param(cmng, mode); if (sp == NULL) { /* Unsupported scene mode */ return -EINVAL; } ret = IMGSENSOR_GET_SUPPORTED_VALUE(cmng->imgsensor, id, &value); if (ret < 0) { /* Unsupported camera parameter */ return ret; } switch (id) { case IMGSENSOR_ID_BRIGHTNESS: control->value = sp->brightness; break; case IMGSENSOR_ID_CONTRAST: control->value = sp->contrast; break; case IMGSENSOR_ID_SATURATION: control->value = sp->saturation; break; case IMGSENSOR_ID_HUE: control->value = sp->hue; break; case IMGSENSOR_ID_AUTO_WHITE_BALANCE: control->value = sp->awb; break; case IMGSENSOR_ID_RED_BALANCE: control->value = sp->red; break; case IMGSENSOR_ID_BLUE_BALANCE: control->value = sp->blue; break; case IMGSENSOR_ID_GAMMA: control->value = sp->gamma; break; case IMGSENSOR_ID_GAMMA_CURVE: memcpy(control->p_u8, sp->gamma_curve, sp->gamma_curve_sz); break; case IMGSENSOR_ID_EXPOSURE: control->value = sp->ev; break; case IMGSENSOR_ID_HFLIP_VIDEO: control->value = sp->hflip_video; break; case IMGSENSOR_ID_VFLIP_VIDEO: control->value = sp->vflip_video; break; case IMGSENSOR_ID_HFLIP_STILL: control->value = sp->hflip_still; break; case IMGSENSOR_ID_VFLIP_STILL: control->value = sp->vflip_still; break; case IMGSENSOR_ID_SHARPNESS: control->value = sp->sharpness; break; case IMGSENSOR_ID_COLOR_KILLER: control->value = sp->colorfx == V4L2_COLORFX_BW; break; case IMGSENSOR_ID_COLORFX: control->value = sp->colorfx; break; case IMGSENSOR_ID_AUTOBRIGHTNESS: control->value = sp->auto_brightness; break; case IMGSENSOR_ID_ROTATE: control->value = sp->rotate; break; case IMGSENSOR_ID_EXPOSURE_AUTO: control->value = sp->ae; break; case IMGSENSOR_ID_EXPOSURE_ABSOLUTE: control->value = sp->exposure_time; break; case IMGSENSOR_ID_FOCUS_ABSOLUTE: control->value = sp->focus; break; case IMGSENSOR_ID_FOCUS_AUTO: control->value = sp->af; break; case IMGSENSOR_ID_ZOOM_ABSOLUTE: control->value = sp->zoom; break; case IMGSENSOR_ID_IRIS_ABSOLUTE: control->value = sp->iris; break; case IMGSENSOR_ID_AUTO_N_PRESET_WB: control->value = sp->wb; break; case IMGSENSOR_ID_WIDE_DYNAMIC_RANGE: control->value = sp->wdr; break; case IMGSENSOR_ID_IMG_STABILIZATION: control->value = sp->stabilization; break; case IMGSENSOR_ID_ISO_SENSITIVITY: control->value = sp->iso; break; case IMGSENSOR_ID_ISO_SENSITIVITY_AUTO: control->value = sp->iso_auto; break; case IMGSENSOR_ID_EXPOSURE_METERING: control->value = sp->meter; break; case IMGSENSOR_ID_SPOT_POSITION: control->value = sp->spot_pos; break; case IMGSENSOR_ID_3A_LOCK: control->value = sp->threea_lock; break; case IMGSENSOR_ID_FLASH_LED_MODE: control->value = sp->led; break; case IMGSENSOR_ID_JPEG_QUALITY: control->value = sp->jpeg_quality; break; default: ret = -EINVAL; break; } return ret; } static int check_range(int64_t value, int64_t min, int64_t max, uint64_t step) { if (value < min || value > max || (value - min) % step != 0) { return -EINVAL; } return OK; } static int save_scene_param(FAR capture_mng_t *cmng, enum v4l2_scene_mode mode, uint32_t id, FAR struct v4l2_ext_control *control) { imgsensor_supported_value_t value; FAR imgsensor_capability_range_t *range = &value.u.range; FAR imgsensor_capability_discrete_t *disc = &value.u.discrete; FAR imgsensor_capability_elems_t *elem = &value.u.elems; FAR capture_scene_params_t *sp; int ret; int i; ASSERT(cmng->imgsensor); sp = search_scene_param(cmng, mode); if (sp == NULL) { /* Unsupported scene mode */ return -EINVAL; } ret = IMGSENSOR_GET_SUPPORTED_VALUE(cmng->imgsensor, id, &value); if (ret < 0) { /* Unsupported camera parameter */ return ret; } switch (value.type) { case IMGSENSOR_CTRL_TYPE_INTEGER_MENU: for (i = 0; i < disc->nr_values; i++) { if (control->value == disc->values[i]) { break; } } if (i >= disc->nr_values) { return -EINVAL; } break; case IMGSENSOR_CTRL_TYPE_U8: if (control->size < elem->nr_elems * sizeof(uint8_t)) { return -EINVAL; } for (i = 0; i < elem->nr_elems; i++) { ret = check_range(control->p_u8[i], elem->minimum, elem->maximum, elem->step); if (ret != OK) { return ret; } } break; case IMGSENSOR_CTRL_TYPE_U16: if (control->size < elem->nr_elems * sizeof(uint16_t)) { return -EINVAL; } for (i = 0; i < elem->nr_elems; i++) { ret = check_range(control->p_u16[i], elem->minimum, elem->maximum, elem->step); if (ret != OK) { return ret; } } break; case IMGSENSOR_CTRL_TYPE_U32: if (control->size < elem->nr_elems * sizeof(uint32_t)) { return -EINVAL; } for (i = 0; i < elem->nr_elems; i++) { ret = check_range(control->p_u32[i], elem->minimum, elem->maximum, elem->step); if (ret != OK) { return ret; } } break; default: ret = check_range(control->value, range->minimum, range->maximum, range->step); if (ret != OK) { return ret; } break; } switch (id) { case IMGSENSOR_ID_BRIGHTNESS: sp->brightness = control->value; break; case IMGSENSOR_ID_CONTRAST: sp->contrast = control->value; break; case IMGSENSOR_ID_SATURATION: sp->saturation = control->value; break; case IMGSENSOR_ID_HUE: sp->hue = control->value; break; case IMGSENSOR_ID_AUTO_WHITE_BALANCE: sp->awb = control->value; break; case IMGSENSOR_ID_RED_BALANCE: sp->red = control->value; break; case IMGSENSOR_ID_BLUE_BALANCE: sp->blue = control->value; break; case IMGSENSOR_ID_GAMMA: sp->gamma = control->value; break; case IMGSENSOR_ID_GAMMA_CURVE: memcpy(sp->gamma_curve, control->p_u8, sp->gamma_curve_sz); break; case IMGSENSOR_ID_EXPOSURE: sp->ev = control->value; break; case IMGSENSOR_ID_HFLIP_VIDEO: sp->hflip_video = control->value; break; case IMGSENSOR_ID_VFLIP_VIDEO: sp->vflip_video = control->value; break; case IMGSENSOR_ID_HFLIP_STILL: sp->hflip_still = control->value; break; case IMGSENSOR_ID_VFLIP_STILL: sp->vflip_still = control->value; break; case IMGSENSOR_ID_SHARPNESS: sp->sharpness = control->value; break; case IMGSENSOR_ID_COLOR_KILLER: sp->colorfx = control->value ? V4L2_COLORFX_BW : V4L2_COLORFX_NONE; break; case IMGSENSOR_ID_COLORFX: sp->colorfx = control->value; break; case IMGSENSOR_ID_AUTOBRIGHTNESS: sp->auto_brightness = control->value; break; case IMGSENSOR_ID_ROTATE: sp->rotate = control->value; break; case IMGSENSOR_ID_EXPOSURE_AUTO: sp->ae = control->value; break; case IMGSENSOR_ID_EXPOSURE_ABSOLUTE: sp->exposure_time = control->value; break; case IMGSENSOR_ID_FOCUS_ABSOLUTE: sp->focus = control->value; break; case IMGSENSOR_ID_FOCUS_AUTO: sp->af = control->value; break; case IMGSENSOR_ID_ZOOM_ABSOLUTE: sp->zoom = control->value; break; case IMGSENSOR_ID_IRIS_ABSOLUTE: sp->iris = control->value; break; case IMGSENSOR_ID_AUTO_N_PRESET_WB: sp->wb = control->value; break; case IMGSENSOR_ID_WIDE_DYNAMIC_RANGE: sp->wdr = control->value; break; case IMGSENSOR_ID_IMG_STABILIZATION: sp->stabilization = control->value; break; case IMGSENSOR_ID_ISO_SENSITIVITY: sp->iso = control->value; break; case IMGSENSOR_ID_ISO_SENSITIVITY_AUTO: sp->iso_auto = control->value; break; case IMGSENSOR_ID_EXPOSURE_METERING: sp->meter = control->value; break; case IMGSENSOR_ID_SPOT_POSITION: sp->spot_pos = control->value; break; case IMGSENSOR_ID_3A_LOCK: sp->threea_lock = control->value; break; case IMGSENSOR_ID_FLASH_LED_MODE: sp->led = control->value; break; case IMGSENSOR_ID_JPEG_QUALITY: sp->jpeg_quality = control->value; break; default: return -EINVAL; } return OK; } /* Callback function which device driver call when capture has done. */ static int complete_capture(uint8_t err_code, uint32_t datasize, FAR const struct timeval *ts, FAR void *arg) { FAR capture_mng_t *cmng = (FAR capture_mng_t *)arg; FAR capture_type_inf_t *type_inf; FAR vbuf_container_t *container = NULL; enum v4l2_buf_type buf_type; irqstate_t flags; imgdata_format_t df[MAX_CAPTURE_FMT]; video_format_t c_fmt[MAX_CAPTURE_FMT]; flags = enter_critical_section(); buf_type = cmng->still_inf.state == CAPTURE_STATE_CAPTURE ? V4L2_BUF_TYPE_STILL_CAPTURE : V4L2_BUF_TYPE_VIDEO_CAPTURE; type_inf = get_capture_type_inf(cmng, buf_type); if (type_inf == NULL) { leave_critical_section(flags); return -EINVAL; } poll_notify(&type_inf->fds, 1, POLLIN); if (err_code == 0) { type_inf->bufinf.vbuf_next->buf.flags = 0; if (type_inf->remaining_capnum > 0) { type_inf->remaining_capnum--; } } else { type_inf->bufinf.vbuf_next->buf.flags = V4L2_BUF_FLAG_ERROR; } type_inf->bufinf.vbuf_next->buf.bytesused = datasize; if (ts != NULL) { type_inf->bufinf.vbuf_next->buf.timestamp = *ts; } video_framebuff_capture_done(&type_inf->bufinf); if (is_sem_waited(&type_inf->wait_capture.dqbuf_wait_flg)) { /* If waiting capture in DQBUF, * get/save container and unlock wait */ type_inf->wait_capture.done_container = video_framebuff_pop_curr_container(&type_inf->bufinf); type_inf->wait_capture.waitend_cause = WAITEND_CAUSE_CAPTUREDONE; nxsem_post(&type_inf->wait_capture.dqbuf_wait_flg); } if (type_inf->remaining_capnum == 0) { stop_capture(cmng, buf_type); type_inf->state = CAPTURE_STATE_STREAMOFF; /* If stop still stream, notify it to video stream */ if (buf_type == V4L2_BUF_TYPE_STILL_CAPTURE && is_sem_waited(&cmng->capture_inf.wait_capture.dqbuf_wait_flg)) { cmng->capture_inf.wait_capture.waitend_cause = WAITEND_CAUSE_STILLSTOP; nxsem_post(&cmng->capture_inf.wait_capture.dqbuf_wait_flg); } } else { container = video_framebuff_get_vacant_container(&type_inf->bufinf); if (container == NULL) { stop_capture(cmng, buf_type); type_inf->state = CAPTURE_STATE_STREAMON; } else { get_clipped_format(type_inf->nr_fmt, type_inf->fmt, &type_inf->clip, c_fmt); convert_to_imgdatafmt(&c_fmt[CAPTURE_FMT_MAIN], &df[IMGDATA_FMT_MAIN]); convert_to_imgdatafmt(&c_fmt[CAPTURE_FMT_SUB], &df[IMGDATA_FMT_SUB]); IMGDATA_SET_BUF(cmng->imgdata, type_inf->nr_fmt, df, (FAR uint8_t *)container->buf.m.userptr, container->buf.length); container->buf.sequence = type_inf->seqnum++; } } leave_critical_section(flags); return OK; } static FAR struct imgsensor_s * get_connected_imgsensor(FAR struct imgsensor_s **sensors, size_t sensor_num) { FAR struct imgsensor_s *sensor = NULL; int i; for (i = 0; i < sensor_num; i++) { if (sensors[i] && IMGSENSOR_IS_AVAILABLE(sensors[i])) { sensor = sensors[i]; break; } } return sensor; } /**************************************************************************** * Ioctl Functions ****************************************************************************/ static int capture_querycap(FAR struct file *filep, FAR struct v4l2_capability *cap) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR const char *name; if (cmng == NULL || cap == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor); name = IMGSENSOR_GET_DRIVER_NAME(cmng->imgsensor); if (name == NULL) { return -ENOTTY; } memset(cap, 0, sizeof(struct v4l2_capability)); /* cap->driver needs to be NULL-terminated. */ strlcpy((FAR char *)cap->driver, name, sizeof(cap->driver)); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; return OK; } static int capture_g_input(FAR int *num) { *num = 0; return OK; } static int capture_enum_input(FAR struct file *filep, FAR struct v4l2_input *input) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR const char *name; if (cmng == NULL || input->index > 0) { return -EINVAL; } ASSERT(cmng->imgsensor); name = IMGSENSOR_GET_DRIVER_NAME(cmng->imgsensor); if (name == NULL) { return -ENOTTY; } memset(input, 0, sizeof(struct v4l2_input)); strlcpy((FAR char *)input->name, name, sizeof(input->name)); input->type = V4L2_INPUT_TYPE_CAMERA; return OK; } static int capture_reqbufs(FAR struct file *filep, FAR struct v4l2_requestbuffers *reqbufs) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; struct imgdata_s *imgdata; irqstate_t flags; int ret = OK; if (cmng == NULL || reqbufs == NULL) { return -EINVAL; } imgdata = cmng->imgdata; type_inf = get_capture_type_inf(cmng, reqbufs->type); if (type_inf == NULL) { return -EINVAL; } flags = enter_critical_section(); if (type_inf->state == CAPTURE_STATE_CAPTURE) { /* In capture, REQBUFS is not permitted */ ret = -EPERM; } else { if (reqbufs->count > V4L2_REQBUFS_COUNT_MAX) { reqbufs->count = V4L2_REQBUFS_COUNT_MAX; } video_framebuff_change_mode(&type_inf->bufinf, reqbufs->mode); ret = video_framebuff_realloc_container(&type_inf->bufinf, reqbufs->count); if (ret == OK && reqbufs->memory == V4L2_MEMORY_MMAP) { if (type_inf->bufheap != NULL) { if (imgdata->ops->free) { imgdata->ops->free(imgdata, type_inf->bufheap); } else { kumm_free(type_inf->bufheap); } } if (imgdata->ops->alloc) { type_inf->bufheap = imgdata->ops->alloc(imgdata, 32, reqbufs->count * get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])); } else { type_inf->bufheap = kumm_memalign(32, reqbufs->count * get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN])); } if (type_inf->bufheap == NULL) { ret = -ENOMEM; } } } leave_critical_section(flags); return ret; } static int capture_querybuf(FAR struct file *filep, FAR struct v4l2_buffer *buf) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; if (cmng == NULL || buf == NULL || buf->memory != V4L2_MEMORY_MMAP) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, buf->type); if (type_inf == NULL) { return -EINVAL; } if (buf->index >= type_inf->bufinf.container_size) { return -EINVAL; } buf->length = get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN]); buf->m.offset = buf->length * buf->index; return OK; } static int capture_qbuf(FAR struct file *filep, FAR struct v4l2_buffer *buf) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; FAR vbuf_container_t *container; enum capture_state_e next_capture_state; irqstate_t flags; if (cmng == NULL || buf == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, buf->type); if (type_inf == NULL) { return -EINVAL; } if (!is_bufsize_sufficient(cmng, buf->length)) { return -EINVAL; } container = video_framebuff_get_container(&type_inf->bufinf); if (container == NULL) { return -ENOMEM; } memcpy(&container->buf, buf, sizeof(struct v4l2_buffer)); if (buf->memory == V4L2_MEMORY_MMAP) { /* only use userptr inside the container */ container->buf.length = get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN]); container->buf.m.userptr = (unsigned long)(type_inf->bufheap + container->buf.length * buf->index); } video_framebuff_queue_container(&type_inf->bufinf, container); nxmutex_lock(&type_inf->lock_state); flags = enter_critical_section(); if (type_inf->state == CAPTURE_STATE_STREAMON) { leave_critical_section(flags); if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { nxmutex_lock(&cmng->still_inf.lock_state); next_capture_state = estimate_next_capture_state(cmng, CAUSE_CAPTURE_START); change_capture_state(cmng, next_capture_state); nxmutex_unlock(&cmng->still_inf.lock_state); } else { container = video_framebuff_get_vacant_container(&type_inf->bufinf); if (container != NULL) { type_inf->seqnum = 0; start_capture(cmng, buf->type, type_inf->nr_fmt, type_inf->fmt, &type_inf->clip, &type_inf->frame_interval, container->buf.m.userptr, container->buf.length); type_inf->state = CAPTURE_STATE_CAPTURE; } } } else { leave_critical_section(flags); } nxmutex_unlock(&type_inf->lock_state); return OK; } static int capture_dqbuf(FAR struct file *filep, FAR struct v4l2_buffer *buf) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; FAR vbuf_container_t *container; FAR sem_t *dqbuf_wait_flg; enum capture_state_e next_capture_state; irqstate_t flags; if (cmng == NULL || buf == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, buf->type); if (type_inf == NULL) { return -EINVAL; } container = video_framebuff_dq_valid_container(&type_inf->bufinf); if (container == NULL) { if (filep->f_oflags & O_NONBLOCK) { return -EAGAIN; } /* Not yet done capture. Wait done */ dqbuf_wait_flg = &type_inf->wait_capture.dqbuf_wait_flg; /* Loop until semaphore is unlocked by capture done or DQCANCEL */ do { if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { /* If start capture condition is satisfied, start capture */ flags = enter_critical_section(); next_capture_state = estimate_next_capture_state(cmng, CAUSE_CAPTURE_DQBUF); change_capture_state(cmng, next_capture_state); leave_critical_section(flags); } nxsem_wait_uninterruptible(dqbuf_wait_flg); } while (type_inf->wait_capture.waitend_cause == WAITEND_CAUSE_STILLSTOP); container = type_inf->wait_capture.done_container; if (container == NULL) { /* Waking up without captured data means abort. * Therefore, Check cause. */ DEBUGASSERT(type_inf->wait_capture.waitend_cause == WAITEND_CAUSE_DQCANCEL); return -ECANCELED; } type_inf->wait_capture.done_container = NULL; } memcpy(buf, &container->buf, sizeof(struct v4l2_buffer)); video_framebuff_free_container(&type_inf->bufinf, container); return OK; } static int capture_cancel_dqbuf(FAR struct file *filep, enum v4l2_buf_type type) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; if (cmng == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, type); if (type_inf == NULL) { return -EINVAL; } if (!is_sem_waited(&type_inf->wait_capture.dqbuf_wait_flg)) { /* In not waiting DQBUF case, return OK */ return OK; } type_inf->wait_capture.waitend_cause = WAITEND_CAUSE_DQCANCEL; /* If capture is done before nxsem_post, cause is overwritten */ return nxsem_post(&type_inf->wait_capture.dqbuf_wait_flg); } static int capture_g_fmt(FAR struct file *filep, FAR struct v4l2_format *fmt) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; if (cmng == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, fmt->type); if (type_inf == NULL) { return -EINVAL; } memset(&fmt->fmt, 0, sizeof(fmt->fmt)); fmt->fmt.pix.width = type_inf->fmt[CAPTURE_FMT_MAIN].width; fmt->fmt.pix.height = type_inf->fmt[CAPTURE_FMT_MAIN].height; fmt->fmt.pix.pixelformat = type_inf->fmt[CAPTURE_FMT_MAIN].pixelformat; return OK; } static int capture_s_fmt(FAR struct file *filep, FAR struct v4l2_format *fmt) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; int ret; if (cmng == NULL) { return -EINVAL; } ret = capture_try_fmt(filep, fmt); if (ret != 0) { return ret; } type_inf = get_capture_type_inf(cmng, fmt->type); if (type_inf == NULL) { return -EINVAL; } if (type_inf->state != CAPTURE_STATE_STREAMOFF) { return -EBUSY; } switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_SUBIMG_UYVY: case V4L2_PIX_FMT_SUBIMG_RGB565: if (type_inf->fmt[CAPTURE_FMT_MAIN].pixelformat != V4L2_PIX_FMT_JPEG_WITH_SUBIMG) { return -EPERM; } type_inf->fmt[CAPTURE_FMT_SUB].width = fmt->fmt.pix.width; type_inf->fmt[CAPTURE_FMT_SUB].height = fmt->fmt.pix.height; type_inf->fmt[CAPTURE_FMT_SUB].pixelformat = fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_SUBIMG_UYVY ? V4L2_PIX_FMT_UYVY : V4L2_PIX_FMT_RGB565; type_inf->nr_fmt = 2; break; default: type_inf->fmt[CAPTURE_FMT_MAIN].width = fmt->fmt.pix.width; type_inf->fmt[CAPTURE_FMT_MAIN].height = fmt->fmt.pix.height; type_inf->fmt[CAPTURE_FMT_MAIN].pixelformat = fmt->fmt.pix.pixelformat; type_inf->nr_fmt = 1; break; } return OK; } static int capture_try_fmt(FAR struct file *filep, FAR struct v4l2_format *fmt) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; video_format_t vf[MAX_CAPTURE_FMT]; uint8_t nr_fmt; if (cmng == NULL || fmt == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor && cmng->imgdata); type_inf = get_capture_type_inf(cmng, fmt->type); if (type_inf == NULL) { return -EINVAL; } switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_SUBIMG_UYVY: case V4L2_PIX_FMT_SUBIMG_RGB565: if (type_inf->fmt[CAPTURE_FMT_MAIN].pixelformat != V4L2_PIX_FMT_JPEG_WITH_SUBIMG) { return -EPERM; } /* Validate both main image and subimage. */ nr_fmt = 2; memcpy(&vf[CAPTURE_FMT_MAIN], &type_inf->fmt[CAPTURE_FMT_MAIN], sizeof(video_format_t)); vf[CAPTURE_FMT_SUB].width = fmt->fmt.pix.width; vf[CAPTURE_FMT_SUB].height = fmt->fmt.pix.height; vf[CAPTURE_FMT_SUB].pixelformat = fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_SUBIMG_UYVY ? V4L2_PIX_FMT_UYVY : V4L2_PIX_FMT_RGB565; break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_JPEG_WITH_SUBIMG: nr_fmt = 1; vf[CAPTURE_FMT_MAIN].width = fmt->fmt.pix.width; vf[CAPTURE_FMT_MAIN].height = fmt->fmt.pix.height; vf[CAPTURE_FMT_MAIN].pixelformat = fmt->fmt.pix.pixelformat; break; default: return -EINVAL; } return validate_frame_setting(cmng, fmt->type, nr_fmt, vf, &type_inf->clip, &type_inf->frame_interval); } static int capture_g_parm(FAR struct file *filep, FAR struct v4l2_streamparm *parm) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; int ret = -EINVAL; if (cmng == NULL || parm == NULL) { return -EINVAL; } DEBUGASSERT(cmng->imgsensor); type_inf = get_capture_type_inf(cmng, parm->type); if (type_inf == NULL) { return -EINVAL; } memset(&parm->parm, 0, sizeof(parm->parm)); if (type_inf->state == CAPTURE_STATE_CAPTURE) { /* If capture is started and lower driver has the get_frame_interval(), * query lower driver. */ ret = IMGSENSOR_GET_FRAME_INTERVAL(cmng->imgsensor, parm->type, (imgsensor_interval_t *)&parm->parm.capture.timeperframe); } if (ret != OK) { /* In no capture state or error case, return stored value. */ memcpy(&parm->parm.capture.timeperframe, &type_inf->frame_interval, sizeof(struct v4l2_fract)); } parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; return OK; } static int capture_s_parm(FAR struct file *filep, FAR struct v4l2_streamparm *parm) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; int ret; if (cmng == NULL || parm == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor && cmng->imgdata); type_inf = get_capture_type_inf(cmng, parm->type); if (type_inf == NULL) { return -EINVAL; } if (type_inf->state != CAPTURE_STATE_STREAMOFF) { return -EBUSY; } ret = validate_frame_setting(cmng, parm->type, type_inf->nr_fmt, type_inf->fmt, &type_inf->clip, &parm->parm.capture.timeperframe); if (ret != OK) { return ret; } memcpy(&type_inf->frame_interval, &parm->parm.capture.timeperframe, sizeof(struct v4l2_fract)); return ret; } static int capture_streamon(FAR struct file *filep, FAR enum v4l2_buf_type *type) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; enum capture_state_e next_capture_state; int ret = OK; if (cmng == NULL || type == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, *type); if (type_inf == NULL) { return -EINVAL; } if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { /* No procedure for VIDIOC_STREAMON(STILL_CAPTURE) */ return OK; } nxmutex_lock(&type_inf->lock_state); if (type_inf->state != CAPTURE_STATE_STREAMOFF) { ret = -EPERM; } else { next_capture_state = estimate_next_capture_state(cmng, CAUSE_CAPTURE_START); change_capture_state(cmng, next_capture_state); } nxmutex_unlock(&type_inf->lock_state); return ret; } static int capture_streamoff(FAR struct file *filep, FAR enum v4l2_buf_type *type) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; enum capture_state_e next_capture_state; irqstate_t flags; int ret = OK; if (cmng == NULL || type == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, *type); if (type_inf == NULL) { return -EINVAL; } if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { /* No procedure for VIDIOC_STREAMOFF(STILL_CAPTURE) */ return OK; } flags = enter_critical_section(); if (type_inf->state == CAPTURE_STATE_STREAMOFF) { ret = -EPERM; } else { next_capture_state = estimate_next_capture_state(cmng, CAUSE_CAPTURE_STOP); change_capture_state(cmng, next_capture_state); } leave_critical_section(flags); return ret; } static int capture_do_halfpush(FAR struct file *filep, bool enable) { struct v4l2_ext_controls ext_controls; struct v4l2_ext_control control[2]; /* Replace to VIDIOC_S_EXT_CTRLS format */ control[0].id = V4L2_CID_3A_LOCK; control[0].value = enable ? V4L2_LOCK_EXPOSURE | V4L2_LOCK_WHITE_BALANCE : 0; control[1].id = V4L2_CID_AUTO_FOCUS_START; control[1].value = enable ? true : false; ext_controls.ctrl_class = V4L2_CTRL_CLASS_CAMERA; ext_controls.count = 2; ext_controls.controls = control; /* Execute VIDIOC_S_EXT_CTRLS */ return capture_s_ext_ctrls(filep, &ext_controls); } static int capture_takepict_start(FAR struct file *filep, int32_t capture_num) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; enum capture_state_e next_capture_state; FAR vbuf_container_t *container; irqstate_t flags; int ret = OK; if (cmng == NULL) { return -EINVAL; } nxmutex_lock(&cmng->still_inf.lock_state); if (cmng->still_inf.state != CAPTURE_STATE_STREAMOFF) { ret = -EPERM; } else { if (capture_num > 0) { cmng->still_inf.remaining_capnum = capture_num; } else { cmng->still_inf.remaining_capnum = REMAINING_CAPNUM_INFINITY; } /* Control video stream prior to still stream */ flags = enter_critical_section(); next_capture_state = estimate_next_capture_state(cmng, CAUSE_STILL_START); change_capture_state(cmng, next_capture_state); leave_critical_section(flags); container = video_framebuff_get_vacant_container(&cmng->still_inf.bufinf); if (container != NULL) { /* Start still stream capture */ start_capture(cmng, V4L2_BUF_TYPE_STILL_CAPTURE, cmng->still_inf.nr_fmt, cmng->still_inf.fmt, &cmng->still_inf.clip, &cmng->still_inf.frame_interval, container->buf.m.userptr, container->buf.length); cmng->still_inf.state = CAPTURE_STATE_CAPTURE; } else { cmng->still_inf.state = CAPTURE_STATE_STREAMON; } } nxmutex_unlock(&cmng->still_inf.lock_state); return ret; } static int capture_takepict_stop(FAR struct file *filep, bool halfpush) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; enum capture_state_e next_capture_state; irqstate_t flags; int ret = OK; if (cmng == NULL) { return -EINVAL; } nxmutex_lock(&cmng->still_inf.lock_state); if (cmng->still_inf.state == CAPTURE_STATE_STREAMOFF && cmng->still_inf.remaining_capnum == REMAINING_CAPNUM_INFINITY) { ret = -EPERM; } else { flags = enter_critical_section(); if (cmng->still_inf.state == CAPTURE_STATE_CAPTURE) { stop_capture(cmng, V4L2_BUF_TYPE_STILL_CAPTURE); } leave_critical_section(flags); cmng->still_inf.state = CAPTURE_STATE_STREAMOFF; cmng->still_inf.remaining_capnum = REMAINING_CAPNUM_INFINITY; /* Control video stream */ nxmutex_lock(&cmng->capture_inf.lock_state); next_capture_state = estimate_next_capture_state(cmng, CAUSE_STILL_STOP); change_capture_state(cmng, next_capture_state); nxmutex_unlock(&cmng->capture_inf.lock_state); } nxmutex_unlock(&cmng->still_inf.lock_state); return ret; } static int capture_s_selection(FAR struct file *filep, FAR struct v4l2_selection *clip) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; uint32_t p_u32[IMGSENSOR_CLIP_NELEM]; imgsensor_value_t val; uint32_t id; int ret; if (cmng == NULL || clip == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor); type_inf = get_capture_type_inf(cmng, clip->type); if (type_inf == NULL) { return -EINVAL; } if (type_inf->state != CAPTURE_STATE_STREAMOFF) { return -EBUSY; } if (!validate_clip_setting(&clip->r, type_inf->fmt)) { return -EINVAL; } ret = validate_frame_setting(cmng, clip->type, type_inf->nr_fmt, type_inf->fmt, &clip->r, &type_inf->frame_interval); if (ret != OK) { return ret; } id = clip->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? IMGSENSOR_ID_CLIP_VIDEO : IMGSENSOR_ID_CLIP_STILL; p_u32[IMGSENSOR_CLIP_INDEX_LEFT] = clip->r.left; p_u32[IMGSENSOR_CLIP_INDEX_TOP] = clip->r.top; p_u32[IMGSENSOR_CLIP_INDEX_WIDTH] = clip->r.width; p_u32[IMGSENSOR_CLIP_INDEX_HEIGHT] = clip->r.height; val.p_u32 = p_u32; ret = IMGSENSOR_SET_VALUE(cmng->imgsensor, id, sizeof(p_u32), val); if (ret != OK) { return ret; } memcpy(&type_inf->clip, &clip->r, sizeof(struct v4l2_rect)); return ret; } static int capture_g_selection(FAR struct file *filep, FAR struct v4l2_selection *clip) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; if (cmng == NULL || clip == NULL) { return -EINVAL; } type_inf = get_capture_type_inf(cmng, clip->type); if (type_inf == NULL) { return -EINVAL; } memcpy(&clip->r, &type_inf->clip, sizeof(struct v4l2_rect)); return OK; } static int capture_queryctrl(FAR struct file *filep, FAR struct v4l2_queryctrl *ctrl) { struct v4l2_query_ext_ctrl ext_ctrl; int ret; if (ctrl == NULL) { return -EINVAL; } /* Replace to VIDIOC_QUERY_EXT_CTRL format */ ext_ctrl.ctrl_class = ctrl->ctrl_class; ext_ctrl.id = ctrl->id; ret = capture_query_ext_ctrl(filep, &ext_ctrl); if (ret != OK) { return ret; } if (ext_ctrl.type == V4L2_CTRL_TYPE_INTEGER64 || ext_ctrl.type == V4L2_CTRL_TYPE_U8 || ext_ctrl.type == V4L2_CTRL_TYPE_U16 || ext_ctrl.type == V4L2_CTRL_TYPE_U32) { /* Unsupported type in VIDIOC_QUERYCTRL */ return -EINVAL; } /* Replace gotten value to VIDIOC_QUERYCTRL */ ctrl->type = ext_ctrl.type; ctrl->minimum = ext_ctrl.minimum; ctrl->maximum = ext_ctrl.maximum; ctrl->step = ext_ctrl.step; ctrl->default_value = ext_ctrl.default_value; ctrl->flags = ext_ctrl.flags; strlcpy(ctrl->name, ext_ctrl.name, sizeof(ctrl->name)); return OK; } static int capture_query_ext_ctrl(FAR struct file *filep, FAR struct v4l2_query_ext_ctrl *attr) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; imgsensor_supported_value_t value; imgsensor_capability_range_t *range = &value.u.range; imgsensor_capability_discrete_t *disc = &value.u.discrete; imgsensor_capability_elems_t *elem = &value.u.elems; int ret; if (cmng == NULL || attr == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor); attr->flags = 0; attr->elem_size = 0; attr->elems = 1; attr->nr_of_dims = 0; memset(attr->dims, 0, sizeof(attr->dims)); if (attr->id == V4L2_CID_SCENE_MODE) { /* Scene mode is processed in only video driver. */ attr->type = V4L2_CTRL_TYPE_INTEGER_MENU; attr->minimum = 0; attr->maximum = cmng->capture_scence_num - 1; attr->step = 1; attr->default_value = 0; attr->flags = 0; strlcpy(attr->name, "Scene Mode", 32); } else { ret = IMGSENSOR_GET_SUPPORTED_VALUE(cmng->imgsensor, attr->id, &value); if (ret < 0) { return ret; } attr->type = value.type; attr->flags = 0; switch (value.type) { case IMGSENSOR_CTRL_TYPE_INTEGER_MENU: attr->minimum = 0; attr->maximum = disc->nr_values - 1; attr->step = 1; attr->default_value = disc->default_value; break; case IMGSENSOR_CTRL_TYPE_U8: case IMGSENSOR_CTRL_TYPE_U16: case IMGSENSOR_CTRL_TYPE_U32: attr->minimum = elem->minimum; attr->maximum = elem->maximum; attr->step = elem->step; attr->elems = elem->nr_elems; break; default: attr->minimum = range->minimum; attr->maximum = range->maximum; attr->step = range->step; attr->default_value = range->default_value; break; } set_parameter_name(attr->id, attr->name); } return OK; } static int capture_querymenu(FAR struct file *filep, FAR struct v4l2_querymenu *menu) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; imgsensor_supported_value_t value; int ret; if (cmng == NULL || menu == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor); if (menu->id == V4L2_CID_SCENE_MODE) { /* Scene mode is processed in only video driver. */ if (menu->index > cmng->capture_scence_num - 1) { return -EINVAL; } menu->value = cmng->capture_scene_param[menu->index]->mode; } else { ret = IMGSENSOR_GET_SUPPORTED_VALUE(cmng->imgsensor, menu->id, &value); if (ret < 0) { return ret; } if (value.type != IMGSENSOR_CTRL_TYPE_INTEGER_MENU) { /* VIDIOC_QUERYMENU is used only for * IMGSENSOR_CTRL_TYPE_INTEGER_MENU. */ return -EINVAL; } if (menu->index >= value.u.discrete.nr_values) { return -EINVAL; } menu->value = value.u.discrete.values[menu->index]; } return OK; } static int capture_g_ctrl(FAR struct file *filep, FAR struct v4l2_control *ctrl) { struct v4l2_ext_controls ext_controls; struct v4l2_ext_control control; int ret; if (ctrl == NULL) { return -EINVAL; } memset(&ext_controls, 0, sizeof(struct v4l2_ext_controls)); memset(&control, 0, sizeof(struct v4l2_ext_control)); /* Replace to VIDIOC_G_EXT_CTRLS format */ control.id = ctrl->id; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &control; /* Execute VIDIOC_G_EXT_CTRLS */ ret = capture_g_ext_ctrls(filep, &ext_controls); if (ret == OK) { /* Replace gotten value to VIDIOC_G_CTRL parameter */ ctrl->value = control.value; } return ret; } static int capture_s_ctrl(FAR struct file *filep, FAR struct v4l2_control *ctrl) { struct v4l2_ext_controls ext_controls; struct v4l2_ext_control control; if (ctrl == NULL) { return -EINVAL; } /* Replace to VIDIOC_S_EXT_CTRLS format */ control.id = ctrl->id; control.value = ctrl->value; ext_controls.ctrl_class = V4L2_CTRL_CLASS_USER; ext_controls.count = 1; ext_controls.controls = &control; /* Execute VIDIOC_S_EXT_CTRLS */ return capture_s_ext_ctrls(filep, &ext_controls); } static int capture_g_ext_ctrls(FAR struct file *filep, FAR struct v4l2_ext_controls *ctrls) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR struct v4l2_ext_control *control; int ret = OK; int cnt; if (cmng == NULL || ctrls == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor); for (cnt = 0, control = ctrls->controls; cnt < ctrls->count; cnt++, control++) { if (control->id == V4L2_CID_SCENE_MODE) { control->value = cmng->capture_scene_mode; } else { ret = IMGSENSOR_GET_VALUE(cmng->imgsensor, control->id, control->size, (imgsensor_value_t *)&control->value64); if (ret < 0) { /* Set cnt in that error occurred */ ctrls->error_idx = cnt; return ret; } } } return ret; } static int capture_s_ext_ctrls(FAR struct file *filep, FAR struct v4l2_ext_controls *ctrls) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR struct v4l2_ext_control *control; int ret = OK; int cnt; if (cmng == NULL || ctrls == NULL) { return -EINVAL; } ASSERT(cmng->imgsensor); for (cnt = 0, control = ctrls->controls; cnt < ctrls->count; cnt++, control++) { if (control->id == V4L2_CID_SCENE_MODE) { ret = reflect_scene_parameter(cmng, control->value); } else { ret = IMGSENSOR_SET_VALUE(cmng->imgsensor, control->id, control->size, (imgsensor_value_t)control->value64); if (ret == 0) { if (cmng->capture_scene_mode == V4L2_SCENE_MODE_NONE) { save_scene_param(cmng, V4L2_SCENE_MODE_NONE, control->id, control); } } } if (ret < 0) { /* Set cnt in that error occurred */ ctrls->error_idx = cnt; return ret; } } return ret; } static int capture_query_ext_ctrl_scene(FAR struct file *filep, FAR struct v4s_query_ext_ctrl_scene *attr) { if (attr == NULL) { return -EINVAL; } return capture_query_ext_ctrl(filep, &attr->control); } static int capture_querymenu_scene(FAR struct file *filep, FAR struct v4s_querymenu_scene *menu) { if (menu == NULL) { return -EINVAL; } return capture_querymenu(filep, &menu->menu); } static int capture_s_ext_ctrls_scene(FAR struct file *filep, FAR struct v4s_ext_controls_scene *ctrls) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR struct v4l2_ext_control *control; int ret = OK; int cnt; if (cmng == NULL || ctrls == NULL) { return -EINVAL; } for (cnt = 0, control = ctrls->control.controls; cnt < ctrls->control.count; cnt++, control++) { ret = save_scene_param(cmng, ctrls->mode, control->id, control); if (ret != OK) { ctrls->control.error_idx = cnt; return ret; } } return ret; } static int capture_g_ext_ctrls_scene(FAR struct file *filep, FAR struct v4s_ext_controls_scene *ctrls) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR struct v4l2_ext_control *control; int ret = OK; int cnt; if (cmng == NULL || ctrls == NULL) { return -EINVAL; } for (cnt = 0, control = ctrls->control.controls; cnt < ctrls->control.count; cnt++, control++) { ret = read_scene_param(cmng, ctrls->mode, control->id, control); if (ret != OK) { ctrls->control.error_idx = cnt; return ret; } } return ret; } static int capture_enum_fmt(FAR struct file *filep, FAR struct v4l2_fmtdesc *f) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; if (cmng == NULL || f == NULL) { return -EINVAL; } if (cmng->imgsensor && cmng->imgsensor->fmtdescs) { if (f->index > cmng->imgsensor->fmtdescs_num) { return -EINVAL; } else { f->pixelformat = cmng->imgsensor->fmtdescs[f->index].pixelformat; strlcpy(f->description, cmng->imgsensor->fmtdescs[f->index].description, sizeof(f->description)); } } else { if (f->index > 0) { return -EINVAL; } f->pixelformat = V4L2_PIX_FMT_UYVY; } return 0; } static int capture_enum_frmsize(FAR struct file *filep, FAR struct v4l2_frmsizeenum *f) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; if (cmng == NULL || f == NULL) { return -EINVAL; } if (cmng->imgsensor && cmng->imgsensor->frmsizes) { if (f->index > cmng->imgsensor->frmsizes_num) { return -EINVAL; } else { f->type = cmng->imgsensor->frmsizes[f->index].type; if (f->type == V4L2_FRMSIZE_TYPE_DISCRETE) { f->discrete = cmng->imgsensor->frmsizes[f->index].discrete; } else { f->stepwise = cmng->imgsensor->frmsizes[f->index].stepwise; } } } else { if (f->index > 0) { return -EINVAL; } f->type = V4L2_FRMIVAL_TYPE_DISCRETE; f->discrete.width = VIDEO_HSIZE_QVGA; f->discrete.height = VIDEO_VSIZE_QVGA; } return 0; } static int capture_enum_frminterval(FAR struct file *filep, FAR struct v4l2_frmivalenum *f) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; if (cmng == NULL || f == NULL) { return -EINVAL; } if (cmng->imgsensor && cmng->imgsensor->frmintervals) { if (f->index > cmng->imgsensor->frmintervals_num) { return -EINVAL; } else { f->type = cmng->imgsensor->frmintervals[f->index].type; if (f->type == V4L2_FRMIVAL_TYPE_DISCRETE) { f->discrete = cmng->imgsensor->frmintervals[f->index].discrete; } else { f->stepwise = cmng->imgsensor->frmintervals[f->index].stepwise; } } } else { if (f->index > 0) { return -EINVAL; } f->type = V4L2_FRMIVAL_TYPE_DISCRETE; f->discrete.denominator = 15; f->discrete.numerator = 1; } return 0; } /**************************************************************************** * File Opterations Functions ****************************************************************************/ static int capture_open(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; int ret = OK; if (cmng == NULL) { return -EINVAL; } nxmutex_lock(&cmng->lock_open_num); if (cmng->open_num == 0) { /* Only in first execution, open device */ ret = IMGSENSOR_INIT(cmng->imgsensor); if (ret == OK) { ret = IMGDATA_INIT(cmng->imgdata); if (ret == OK) { initialize_resources(cmng); } } else { ret = -ENODEV; } } /* In second or later execution, ret is initial value(=OK) */ if (ret == OK) { cmng->open_num++; } nxmutex_unlock(&cmng->lock_open_num); return ret; } static int capture_close(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; if (cmng == NULL) { return -EINVAL; } nxmutex_lock(&cmng->lock_open_num); if (--cmng->open_num == 0) { cleanup_resources(cmng); IMGSENSOR_UNINIT(cmng->imgsensor); IMGDATA_UNINIT(cmng->imgdata); #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS if (cmng->unlinked) { nxmutex_unlock(&cmng->lock_open_num); nxmutex_destroy(&cmng->lock_open_num); kmm_free(cmng); inode->i_private = NULL; return OK; } #endif } nxmutex_unlock(&cmng->lock_open_num); return OK; } static int capture_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; size_t heapsize; int ret = -EINVAL; if (cmng == NULL) { return -EINVAL; } type_inf = &cmng->capture_inf; heapsize = get_heapsize(type_inf); if (map->offset >= 0 && map->offset < heapsize && map->length && map->offset + map->length <= heapsize) { map->vaddr = type_inf->bufheap + map->offset; ret = OK; } return ret; } static int capture_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode = filep->f_inode; FAR capture_mng_t *cmng = inode->i_private; FAR capture_type_inf_t *type_inf; enum v4l2_buf_type buf_type; irqstate_t flags; if (cmng == NULL) { return -EINVAL; } buf_type = cmng->still_inf.state == CAPTURE_STATE_CAPTURE ? V4L2_BUF_TYPE_STILL_CAPTURE : V4L2_BUF_TYPE_VIDEO_CAPTURE; type_inf = get_capture_type_inf(cmng, buf_type); if (type_inf == NULL) { return -EINVAL; } flags = enter_critical_section(); if (setup) { if (type_inf->fds == NULL) { type_inf->fds = fds; fds->priv = &type_inf->fds; if (!video_framebuff_is_empty(&type_inf->bufinf)) { poll_notify(&fds, 1, POLLIN); } } else { leave_critical_section(flags); return -EBUSY; } } else if (fds->priv) { type_inf->fds = NULL; fds->priv = NULL; } leave_critical_section(flags); return OK; } #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS static int capture_unlink(FAR struct inode *inode) { FAR capture_mng_t *cmng = inode->i_private; if (cmng == NULL) { return -EINVAL; } nxmutex_lock(&cmng->lock_open_num); if (cmng->open_num == 0) { nxmutex_unlock(&cmng->lock_open_num); nxmutex_destroy(&cmng->lock_open_num); kmm_free(cmng); inode->i_private = NULL; } else { cmng->unlinked = true; nxmutex_unlock(&cmng->lock_open_num); } return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ int capture_initialize(FAR const char *devpath) { return capture_register(devpath, g_capture_data, g_capture_registered_sensor, g_capture_registered_sensor_num); } int capture_uninitialize(FAR const char *devpath) { return capture_unregister(devpath); } int capture_register(FAR const char *devpath, FAR struct imgdata_s *data, FAR struct imgsensor_s **sensors, size_t sensor_num) { FAR capture_mng_t *cmng; int ret; /* Input devpath Error Check */ if (devpath == NULL || data == NULL) { return -EINVAL; } /* Initialize capture device structure */ cmng = kmm_zalloc(sizeof(capture_mng_t)); if (cmng == NULL) { verr("Failed to allocate instance\n"); return -ENOMEM; } cmng->v4l2.vops = &g_capture_vops; cmng->v4l2.fops = &g_capture_fops; cmng->imgdata = data; cmng->imgsensor = get_connected_imgsensor(sensors, sensor_num); if (cmng->imgsensor == NULL) { kmm_free(cmng); return -EINVAL; } /* Initialize mutex */ nxmutex_init(&cmng->lock_open_num); /* Register the character driver */ ret = video_register(devpath, (FAR struct v4l2_s *)cmng); if (ret < 0) { verr("Failed to register driver: %d\n", ret); nxmutex_destroy(&cmng->lock_open_num); kmm_free(cmng); return ret; } return OK; } int capture_unregister(FAR const char *devpath) { return unregister_driver(devpath); } int imgsensor_register(FAR struct imgsensor_s *sensor) { FAR struct imgsensor_s **new_addr; int ret = -ENOMEM; new_addr = kmm_realloc(g_capture_registered_sensor, sizeof(sensor) * (g_capture_registered_sensor_num + 1)); if (new_addr != NULL) { new_addr[g_capture_registered_sensor_num++] = sensor; g_capture_registered_sensor = new_addr; ret = OK; } return ret; } void imgdata_register(FAR struct imgdata_s *data) { g_capture_data = data; }