/****************************************************************************
 * 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 <debug.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <poll.h>

#include <nuttx/mutex.h>
#include <nuttx/video/v4l2_cap.h>
#include <nuttx/video/video.h>

#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;
}