nuttx/drivers/video/video.c
Peter Bee 1949991fe0 drivers/video: add sequence number to video frames
Signed-off-by: Peter Bee <bijunda1@xiaomi.com>
2023-02-09 23:27:15 +08:00

3375 lines
87 KiB
C

/****************************************************************************
* drivers/video/video.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 <nuttx/config.h>
#include <debug.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <poll.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/video/imgsensor.h>
#include <nuttx/video/imgdata.h>
#include "video_framebuff.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MAX_VIDEO_FILE_PATH (32)
#define MAX_VIDEO_FMT (2)
#define VIDEO_FMT_MAIN (0)
#define VIDEO_FMT_SUB (1)
#define VIDEO_REMAINING_CAPNUM_INFINITY (-1)
#define VIDEO_SCENE_MAX (sizeof(g_video_scene_parameter) / \
sizeof(video_scene_params_t))
#define VIDEO_ID(x, y) (((x) << 16) | (y))
/****************************************************************************
* Private Types
****************************************************************************/
enum video_state_e
{
VIDEO_STATE_STREAMOFF = 0, /* Capture trigger event is not received */
VIDEO_STATE_STREAMON = 1, /* Capture trigger event is received,
* but capture is not operated.
*/
VIDEO_STATE_CAPTURE = 2, /* On capture */
};
enum video_state_transition_cause
{
CAUSE_VIDEO_STOP = 0, /* Stop capture event for video stream */
CAUSE_VIDEO_START = 1, /* Start capture event for video stream */
CAUSE_VIDEO_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 video_waitend_cause_e
{
VIDEO_WAITEND_CAUSE_CAPTUREDONE = 0,
VIDEO_WAITEND_CAUSE_DQCANCEL = 1,
VIDEO_WAITEND_CAUSE_STILLSTOP = 2,
};
struct video_wait_capture_s
{
sem_t dqbuf_wait_flg;
/* Save container which capture is done */
FAR vbuf_container_t *done_container;
enum video_waitend_cause_e waitend_cause;
};
typedef struct video_wait_capture_s video_wait_capture_t;
struct video_format_s
{
uint16_t width;
uint16_t height;
uint32_t pixelformat;
};
typedef struct video_format_s video_format_t;
struct video_type_inf_s
{
mutex_t lock_state;
enum video_state_e state;
int32_t remaining_capnum;
video_wait_capture_t wait_capture;
uint8_t nr_fmt;
video_format_t fmt[MAX_VIDEO_FMT];
struct v4l2_rect clip;
struct v4l2_fract frame_interval;
video_framebuff_t bufinf;
FAR uint8_t *bufheap; /* for V4L2_MEMORY_MMAP buffers */
uint32_t seqnum;
};
typedef struct video_type_inf_s video_type_inf_t;
struct video_mng_s
{
FAR char *devpath; /* Parameter of video_initialize() */
mutex_t lock_open_num;
uint8_t open_num;
video_type_inf_t video_inf;
video_type_inf_t still_inf;
};
typedef struct video_mng_s video_mng_t;
struct video_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;
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 threea_lock;
enum v4l2_flash_led_mode led;
int32_t jpeg_quality;
};
typedef struct video_scene_params_s video_scene_params_t;
struct video_parameter_name_s
{
uint32_t id;
const char *name;
};
typedef struct video_parameter_name_s video_parameter_name_t;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Character driver methods. */
static int video_open(FAR struct file *filep);
static int video_close(FAR struct file *filep);
static ssize_t video_read(FAR struct file *filep,
FAR char *buffer, size_t buflen);
static ssize_t video_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static int video_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
static int video_mmap(FAR struct file *filep,
FAR struct mm_map_entry_s *map);
/* Common function */
static FAR video_type_inf_t *
get_video_type_inf(FAR video_mng_t *vmng, uint8_t type);
static enum video_state_e
estimate_next_video_state(FAR video_mng_t *vmng,
enum video_state_transition_cause cause);
static void change_video_state(FAR video_mng_t *vmng,
enum video_state_e next_state);
static bool is_taking_still_picture(FAR video_mng_t *vmng);
static bool is_bufsize_sufficient(FAR video_mng_t *vmng, uint32_t bufsize);
static void cleanup_resources(FAR video_mng_t *vmng);
static bool is_sem_waited(FAR sem_t *sem);
static int save_scene_param(enum v4l2_scene_mode mode,
uint32_t id,
struct v4l2_ext_control *control);
static int video_complete_capture(uint8_t err_code, uint32_t datasize,
FAR const struct timeval *ts);
static int validate_frame_setting(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);
/* Internal function for each cmds of ioctl */
static int video_querycap(FAR struct v4l2_capability *cap);
static int video_g_input(FAR int *num);
static int video_enum_input(FAR struct v4l2_input *input);
static int video_reqbufs(FAR struct video_mng_s *vmng,
FAR struct v4l2_requestbuffers *reqbufs);
static int video_qbuf(FAR struct video_mng_s *vmng,
FAR struct v4l2_buffer *buf);
static int video_dqbuf(FAR struct video_mng_s *vmng,
FAR struct v4l2_buffer *buf);
static int video_cancel_dqbuf(FAR struct video_mng_s *vmng,
enum v4l2_buf_type type);
static int video_g_fmt(FAR struct video_mng_s *priv,
FAR struct v4l2_format *fmt);
static int video_s_fmt(FAR struct video_mng_s *priv,
FAR struct v4l2_format *fmt);
static int video_try_fmt(FAR struct video_mng_s *priv,
FAR struct v4l2_format *v4l2);
static int video_g_parm(FAR struct video_mng_s *priv,
FAR struct v4l2_streamparm *parm);
static int video_s_parm(FAR struct video_mng_s *priv,
FAR struct v4l2_streamparm *parm);
static int video_streamon(FAR struct video_mng_s *vmng,
FAR enum v4l2_buf_type *type);
static int video_streamoff(FAR struct video_mng_s *vmng,
FAR enum v4l2_buf_type *type);
static int video_do_halfpush(FAR struct video_mng_s *priv,
bool enable);
static int video_takepict_start(FAR struct video_mng_s *vmng,
int32_t capture_num);
static int video_takepict_stop(FAR struct video_mng_s *vmng,
bool halfpush);
static int video_queryctrl(FAR struct v4l2_queryctrl *ctrl);
static int video_query_ext_ctrl(FAR struct v4l2_query_ext_ctrl *ctrl);
static int video_querymenu(FAR struct v4l2_querymenu *menu);
static int video_g_ctrl(FAR struct video_mng_s *priv,
FAR struct v4l2_control *ctrl);
static int video_s_ctrl(FAR struct video_mng_s *priv,
FAR struct v4l2_control *ctrl);
static int video_g_ext_ctrls(FAR struct video_mng_s *priv,
FAR struct v4l2_ext_controls *ctrls);
static int video_s_ext_ctrls(FAR struct video_mng_s *priv,
FAR struct v4l2_ext_controls *ctrls);
static int video_query_ext_ctrl_scene(FAR struct v4s_query_ext_ctrl_scene
*ctrl);
static int video_querymenu_scene(FAR struct v4s_querymenu_scene *menu);
static int video_g_ext_ctrls_scene(FAR struct v4s_ext_controls_scene *ctrls);
static int video_s_ext_ctrls_scene(FAR struct v4s_ext_controls_scene *ctrls);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_video_fops =
{
video_open, /* open */
video_close, /* close */
video_read, /* read */
video_write, /* write */
NULL, /* seek */
video_ioctl, /* ioctl */
video_mmap, /* mmap */
};
static bool g_video_initialized = false;
static enum v4l2_scene_mode g_video_scene_mode = V4L2_SCENE_MODE_NONE;
static video_scene_params_t g_video_scene_parameter[] =
{
{
V4L2_SCENE_MODE_NONE
},
#ifdef CONFIG_VIDEO_SCENE_BACKLIGHT
{
V4L2_SCENE_MODE_BACKLIGHT
},
#endif /* CONFIG_VIDEO_SCENE_BACKLIGHT */
#ifdef CONFIG_VIDEO_SCENE_BEACHSNOW
{
V4L2_SCENE_MODE_BEACH_SNOW
},
#endif /* CONFIG_VIDEO_SCENE_BEACHSNOW */
#ifdef CONFIG_VIDEO_SCENE_CANDLELIGHT
{
V4L2_SCENE_MODE_CANDLE_LIGHT
},
#endif /* CONFIG_VIDEO_SCENE_CANDLELIGHT */
#ifdef CONFIG_VIDEO_SCENE_DAWNDUSK
{
V4L2_SCENE_MODE_DAWN_DUSK
},
#endif /* CONFIG_VIDEO_SCENE_DAWNDUSK */
#ifdef CONFIG_VIDEO_SCENE_FALLCOLORS
{
V4L2_SCENE_MODE_FALL_COLORS,
},
#endif /* CONFIG_VIDEO_SCENE_FALLCOLORS */
#ifdef CONFIG_VIDEO_SCENE_FIREWORKS
{
V4L2_SCENE_MODE_FIREWORKS
},
#endif /* CONFIG_VIDEO_SCENE_FIREWORKS */
#ifdef CONFIG_VIDEO_SCENE_LANDSCAPE
{
V4L2_SCENE_MODE_LANDSCAPE
},
#endif /* CONFIG_VIDEO_SCENE_LANDSCAPE */
#ifdef CONFIG_VIDEO_SCENE_NIGHT
{
V4L2_SCENE_MODE_NIGHT
},
#endif /* CONFIG_VIDEO_SCENE_NIGHT */
#ifdef CONFIG_VIDEO_SCENE_PARTYINDOOR
{
V4L2_SCENE_MODE_PARTY_INDOOR
},
#endif /* CONFIG_VIDEO_SCENE_PARTYINDOOR */
#ifdef CONFIG_VIDEO_SCENE_PORTRAIT
{
V4L2_SCENE_MODE_PORTRAIT
},
#endif /* CONFIG_VIDEO_SCENE_PORTRAIT */
#ifdef CONFIG_VIDEO_SCENE_SPORTS
{
V4L2_SCENE_MODE_SPORTS
},
#endif /* CONFIG_VIDEO_SCENE_SPORTS */
#ifdef CONFIG_VIDEO_SCENE_SUNSET
{
V4L2_SCENE_MODE_SUNSET
},
#endif /* CONFIG_VIDEO_SCENE_SUNSET */
#ifdef CONFIG_VIDEO_SCENE_TEXT
{
V4L2_SCENE_MODE_TEXT
},
#endif /* CONFIG_VIDEO_SCENE_TEXT */
};
static const video_parameter_name_t g_video_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_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 void *g_video_handler;
static FAR struct imgsensor_s **g_video_registered_sensor;
static int g_video_registered_sensor_num;
static FAR struct imgsensor_s *g_video_sensor;
static FAR struct imgdata_s *g_video_data;
/****************************************************************************
* Private Functions
****************************************************************************/
static FAR video_type_inf_t *
get_video_type_inf(FAR video_mng_t *vmng, uint8_t type)
{
FAR video_type_inf_t *type_inf;
switch (type)
{
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
type_inf = &vmng->video_inf;
break;
case V4L2_BUF_TYPE_STILL_CAPTURE:
type_inf = &vmng->still_inf;
break;
default: /* Error case */
type_inf = NULL;
break;
}
return type_inf;
}
static enum video_state_e
estimate_next_video_state(FAR video_mng_t *vmng,
enum video_state_transition_cause cause)
{
enum video_state_e current_state = vmng->video_inf.state;
switch (cause)
{
case CAUSE_VIDEO_STOP:
return VIDEO_STATE_STREAMOFF;
case CAUSE_VIDEO_START:
if (is_taking_still_picture(vmng))
{
return VIDEO_STATE_STREAMON;
}
else
{
return VIDEO_STATE_CAPTURE;
}
case CAUSE_STILL_STOP:
if (current_state == VIDEO_STATE_STREAMON)
{
return VIDEO_STATE_CAPTURE;
}
else
{
return current_state;
}
case CAUSE_STILL_START:
if (current_state == VIDEO_STATE_CAPTURE)
{
return VIDEO_STATE_STREAMON;
}
else
{
return current_state;
}
case CAUSE_VIDEO_DQBUF:
if (current_state == VIDEO_STATE_STREAMON &&
!is_taking_still_picture(vmng))
{
return VIDEO_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_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_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[VIDEO_FMT_MAIN].width = clip->width;
c_fmt[VIDEO_FMT_MAIN].height = clip->height;
c_fmt[VIDEO_FMT_MAIN].pixelformat = fmt[VIDEO_FMT_MAIN].pixelformat;
if (nr_fmt > 1)
{
/* Clipped size of thumbnail is
* small as ratio of main size and thumbnal size.
*/
memcpy(&c_fmt[VIDEO_FMT_SUB],
&fmt[VIDEO_FMT_SUB],
sizeof(video_format_t));
c_fmt[VIDEO_FMT_SUB].width =
(uint32_t)c_fmt[VIDEO_FMT_SUB].width *
clip->width / fmt[VIDEO_FMT_MAIN].width;
c_fmt[VIDEO_FMT_SUB].height =
(uint32_t)c_fmt[VIDEO_FMT_SUB].height *
clip->height / fmt[VIDEO_FMT_MAIN].height;
}
}
else
{
memcpy(c_fmt, fmt, nr_fmt * sizeof(video_format_t));
}
}
static int start_capture(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_VIDEO_FMT];
imgdata_format_t df[MAX_VIDEO_FMT];
imgsensor_format_t sf[MAX_VIDEO_FMT];
imgdata_interval_t di;
imgsensor_interval_t si;
ASSERT(fmt && interval && g_video_sensor && g_video_data);
get_clipped_format(nr_fmt, fmt, clip, c_fmt);
convert_to_imgdatafmt(&c_fmt[VIDEO_FMT_MAIN], &df[IMGDATA_FMT_MAIN]);
convert_to_imgdatafmt(&c_fmt[VIDEO_FMT_SUB], &df[IMGDATA_FMT_SUB]);
convert_to_imgdatainterval(interval, &di);
convert_to_imgsensorfmt(&fmt[VIDEO_FMT_MAIN], &sf[IMGSENSOR_FMT_MAIN]);
convert_to_imgsensorfmt(&fmt[VIDEO_FMT_SUB], &sf[IMGSENSOR_FMT_SUB]);
convert_to_imgsensorinterval(interval, &si);
IMGSENSOR_START_CAPTURE(g_video_sensor,
type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
IMGSENSOR_STREAM_TYPE_VIDEO : IMGSENSOR_STREAM_TYPE_STILL,
nr_fmt, sf, &si);
IMGDATA_START_CAPTURE(g_video_data,
nr_fmt, df, &di, video_complete_capture);
IMGDATA_SET_BUF(g_video_data, (FAR uint8_t *)bufaddr, bufsize);
return OK;
}
static void stop_capture(enum v4l2_buf_type type)
{
ASSERT(g_video_sensor && g_video_data);
IMGDATA_STOP_CAPTURE(g_video_data);
IMGSENSOR_STOP_CAPTURE(g_video_sensor,
type == V4L2_BUF_TYPE_VIDEO_CAPTURE ?
IMGSENSOR_STREAM_TYPE_VIDEO : IMGSENSOR_STREAM_TYPE_STILL);
}
static void change_video_state(FAR video_mng_t *vmng,
enum video_state_e next_state)
{
enum video_state_e current_state = vmng->video_inf.state;
enum video_state_e updated_next_state = next_state;
if (current_state != VIDEO_STATE_CAPTURE &&
next_state == VIDEO_STATE_CAPTURE)
{
FAR vbuf_container_t *container =
video_framebuff_get_vacant_container(&vmng->video_inf.bufinf);
if (container != NULL)
{
vmng->video_inf.seqnum = 0;
start_capture(V4L2_BUF_TYPE_VIDEO_CAPTURE,
vmng->video_inf.nr_fmt,
vmng->video_inf.fmt,
&vmng->video_inf.clip,
&vmng->video_inf.frame_interval,
container->buf.m.userptr,
container->buf.length);
}
else
{
updated_next_state = VIDEO_STATE_STREAMON;
}
}
else if (current_state == VIDEO_STATE_CAPTURE &&
next_state != VIDEO_STATE_CAPTURE)
{
stop_capture(V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
vmng->video_inf.state = updated_next_state;
}
static bool is_taking_still_picture(FAR video_mng_t *vmng)
{
return vmng->still_inf.state == VIDEO_STATE_STREAMON ||
vmng->still_inf.state == VIDEO_STATE_CAPTURE;
}
static bool is_bufsize_sufficient(FAR video_mng_t *vmng, uint32_t bufsize)
{
/* Depend on format, frame size, and JPEG compression quality */
return true;
}
static void initialize_frame_setting(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;
fmt[VIDEO_FMT_MAIN].width = VIDEO_HSIZE_QVGA;
fmt[VIDEO_FMT_MAIN].height = VIDEO_VSIZE_QVGA;
fmt[VIDEO_FMT_MAIN].pixelformat = V4L2_PIX_FMT_UYVY;
interval->denominator = 15;
interval->numerator = 1;
}
static void initialize_streamresources(FAR video_type_inf_t *type_inf)
{
memset(type_inf, 0, sizeof(video_type_inf_t));
type_inf->remaining_capnum = VIDEO_REMAINING_CAPNUM_INFINITY;
nxmutex_init(&type_inf->lock_state);
nxsem_init(&type_inf->wait_capture.dqbuf_wait_flg, 0, 0);
initialize_frame_setting(&type_inf->nr_fmt,
type_inf->fmt,
&type_inf->frame_interval);
video_framebuff_init(&type_inf->bufinf);
}
static int32_t get_default_value(uint32_t id)
{
imgsensor_supported_value_t value;
int ret;
if (g_video_sensor == NULL)
{
/* Don't care(unsupported parameter) */
return 0;
}
ret = IMGSENSOR_GET_SUPPORTED_VALUE(g_video_sensor, 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(uint8_t **gamma)
{
imgsensor_supported_value_t sup_val;
imgsensor_value_t val;
int32_t sz;
int ret;
*gamma = NULL;
ASSERT(g_video_sensor);
ret = IMGSENSOR_GET_SUPPORTED_VALUE(g_video_sensor,
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(g_video_sensor, IMGSENSOR_ID_GAMMA_CURVE, sz, &val);
return sz;
}
static void initialize_scene_parameter(video_scene_params_t *sp)
{
ASSERT(sp);
sp->brightness = get_default_value(IMGSENSOR_ID_BRIGHTNESS);
sp->contrast = get_default_value(IMGSENSOR_ID_CONTRAST);
sp->saturation = get_default_value(IMGSENSOR_ID_SATURATION);
sp->hue = get_default_value(IMGSENSOR_ID_HUE);
sp->awb = get_default_value(IMGSENSOR_ID_AUTO_WHITE_BALANCE);
sp->red = get_default_value(IMGSENSOR_ID_RED_BALANCE);
sp->blue = get_default_value(IMGSENSOR_ID_BLUE_BALANCE);
sp->gamma = get_default_value(IMGSENSOR_ID_GAMMA);
sp->gamma_curve_sz = initialize_scene_gamma(&sp->gamma_curve);
sp->ev = get_default_value(IMGSENSOR_ID_EXPOSURE);
sp->hflip_video = get_default_value(IMGSENSOR_ID_HFLIP_VIDEO);
sp->vflip_video = get_default_value(IMGSENSOR_ID_VFLIP_VIDEO);
sp->hflip_still = get_default_value(IMGSENSOR_ID_HFLIP_STILL);
sp->vflip_still = get_default_value(IMGSENSOR_ID_VFLIP_STILL);
sp->sharpness = get_default_value(IMGSENSOR_ID_SHARPNESS);
sp->colorfx = get_default_value(IMGSENSOR_ID_COLORFX);
sp->auto_brightness = get_default_value(IMGSENSOR_ID_AUTOBRIGHTNESS);
sp->rotate = get_default_value(IMGSENSOR_ID_ROTATE);
sp->ae = get_default_value(IMGSENSOR_ID_EXPOSURE_AUTO);
sp->exposure_time = get_default_value(IMGSENSOR_ID_EXPOSURE_ABSOLUTE);
sp->focus = get_default_value(IMGSENSOR_ID_FOCUS_ABSOLUTE);
sp->af = get_default_value(IMGSENSOR_ID_FOCUS_AUTO);
sp->zoom = get_default_value(IMGSENSOR_ID_ZOOM_ABSOLUTE);
sp->iris = get_default_value(IMGSENSOR_ID_IRIS_ABSOLUTE);
sp->wb = get_default_value(IMGSENSOR_ID_AUTO_N_PRESET_WB);
sp->wdr = get_default_value(IMGSENSOR_ID_WIDE_DYNAMIC_RANGE);
sp->stabilization = get_default_value(IMGSENSOR_ID_IMG_STABILIZATION);
sp->iso_auto = get_default_value(IMGSENSOR_ID_ISO_SENSITIVITY_AUTO);
sp->iso = get_default_value(IMGSENSOR_ID_ISO_SENSITIVITY);
sp->meter = get_default_value(IMGSENSOR_ID_EXPOSURE_METERING);
sp->threea_lock = get_default_value(IMGSENSOR_ID_3A_LOCK);
sp->led = get_default_value(IMGSENSOR_ID_FLASH_LED_MODE);
sp->jpeg_quality = get_default_value(IMGSENSOR_ID_JPEG_QUALITY);
}
static void initialize_scenes_parameter(void)
{
video_scene_params_t *sp = &g_video_scene_parameter[0];
int i;
for (i = 0; i < VIDEO_SCENE_MAX; i++)
{
initialize_scene_parameter(sp++);
}
}
static void initialize_resources(FAR video_mng_t *vmng)
{
initialize_streamresources(&vmng->video_inf);
initialize_streamresources(&vmng->still_inf);
initialize_scenes_parameter();
}
static void cleanup_streamresources(FAR video_type_inf_t *type_inf)
{
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)
{
kumm_free(type_inf->bufheap);
type_inf->bufheap = NULL;
}
}
static void cleanup_scene_parameter(video_scene_params_t *sp)
{
ASSERT(sp);
if (sp->gamma_curve != NULL)
{
kmm_free(sp->gamma_curve);
sp->gamma_curve = NULL;
sp->gamma_curve_sz = 0;
}
}
static void cleanup_scenes_parameter(void)
{
video_scene_params_t *sp = &g_video_scene_parameter[0];
int i;
for (i = 0; i < VIDEO_SCENE_MAX; i++)
{
cleanup_scene_parameter(sp++);
}
}
static void cleanup_resources(FAR video_mng_t *vmng)
{
/* If in capture, stop */
if (vmng->video_inf.state == VIDEO_STATE_CAPTURE)
{
stop_capture(V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
if (vmng->still_inf.state == VIDEO_STATE_CAPTURE)
{
stop_capture(V4L2_BUF_TYPE_STILL_CAPTURE);
}
/* Clean up resource */
cleanup_streamresources(&vmng->video_inf);
cleanup_streamresources(&vmng->still_inf);
cleanup_scenes_parameter();
}
static bool is_sem_waited(FAR sem_t *sem)
{
int semcount;
return nxsem_get_value(sem, &semcount) == OK && semcount < 0;
}
static FAR struct imgsensor_s *get_connected_imgsensor(void)
{
FAR struct imgsensor_s *sensor = NULL;
int i;
for (i = 0; i < g_video_registered_sensor_num; i++)
{
if (g_video_registered_sensor[i] &&
IMGSENSOR_IS_AVAILABLE(g_video_registered_sensor[i]))
{
sensor = g_video_registered_sensor[i];
break;
}
}
return sensor;
}
static int video_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR video_mng_t *priv = (FAR video_mng_t *)inode->i_private;
int ret = OK;
nxmutex_lock(&priv->lock_open_num);
if (priv->open_num == 0)
{
/* Only in first execution, open device */
g_video_sensor = get_connected_imgsensor();
if (g_video_sensor != NULL)
{
ret = IMGSENSOR_INIT(g_video_sensor);
if (ret == OK)
{
ret = IMGDATA_INIT(g_video_data);
if (ret == OK)
{
initialize_resources(priv);
}
}
}
else
{
ret = -ENODEV;
}
}
/* In second or later execution, ret is initial value(=OK) */
if (ret == OK)
{
priv->open_num++;
}
nxmutex_unlock(&priv->lock_open_num);
return ret;
}
static int video_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR video_mng_t *priv = (FAR video_mng_t *)inode->i_private;
nxmutex_lock(&priv->lock_open_num);
if (priv->open_num == 0)
{
nxmutex_unlock(&priv->lock_open_num);
return OK;
}
if (--priv->open_num == 0)
{
cleanup_resources(priv);
IMGSENSOR_UNINIT(g_video_sensor);
IMGDATA_UNINIT(g_video_data);
}
nxmutex_unlock(&priv->lock_open_num);
return OK;
}
static ssize_t video_read(FAR struct file *filep,
FAR char *buffer, size_t buflen)
{
return -ENOTSUP;
}
static ssize_t video_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
return -ENOTSUP;
}
static int video_querycap(FAR struct v4l2_capability *cap)
{
FAR const char *name;
ASSERT(g_video_sensor);
if (cap == NULL)
{
return -EINVAL;
}
name = IMGSENSOR_GET_DRIVER_NAME(g_video_sensor);
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 video_g_input(FAR int *num)
{
*num = 0;
return OK;
}
static int video_enum_input(FAR struct v4l2_input *input)
{
FAR const char *name;
ASSERT(g_video_sensor);
if (input->index > 0)
{
return -EINVAL;
}
name = IMGSENSOR_GET_DRIVER_NAME(g_video_sensor);
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 video_reqbufs(FAR struct video_mng_s *vmng,
FAR struct v4l2_requestbuffers *reqbufs)
{
FAR video_type_inf_t *type_inf;
irqstate_t flags;
int ret = OK;
if (vmng == NULL || reqbufs == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, reqbufs->type);
if (type_inf == NULL)
{
return -EINVAL;
}
flags = enter_critical_section();
if (type_inf->state == VIDEO_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)
{
kumm_free(type_inf->bufheap);
}
type_inf->bufheap = kumm_memalign(32,
reqbufs->count * get_bufsize(&type_inf->fmt[VIDEO_FMT_MAIN]));
if (type_inf->bufheap == NULL)
{
ret = -ENOMEM;
}
}
}
leave_critical_section(flags);
return ret;
}
static int video_querybuf(FAR struct video_mng_s *vmng,
FAR struct v4l2_buffer *buf)
{
FAR video_type_inf_t *type_inf;
if (vmng == NULL || buf == NULL || buf->memory != V4L2_MEMORY_MMAP)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, 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[VIDEO_FMT_MAIN]);
buf->m.offset = buf->length * buf->index;
return OK;
}
static int video_qbuf(FAR struct video_mng_s *vmng,
FAR struct v4l2_buffer *buf)
{
FAR video_type_inf_t *type_inf;
FAR vbuf_container_t *container;
enum video_state_e next_video_state;
irqstate_t flags;
if (vmng == NULL || buf == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, buf->type);
if (type_inf == NULL)
{
return -EINVAL;
}
if (!is_bufsize_sufficient(vmng, 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[VIDEO_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 == VIDEO_STATE_STREAMON)
{
leave_critical_section(flags);
if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
{
nxmutex_lock(&vmng->still_inf.lock_state);
next_video_state =
estimate_next_video_state(vmng, CAUSE_VIDEO_START);
change_video_state(vmng, next_video_state);
nxmutex_unlock(&vmng->still_inf.lock_state);
}
else
{
container =
video_framebuff_get_vacant_container(&type_inf->bufinf);
if (container != NULL)
{
type_inf->seqnum = 0;
start_capture(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 = VIDEO_STATE_CAPTURE;
}
}
}
else
{
leave_critical_section(flags);
}
nxmutex_unlock(&type_inf->lock_state);
return OK;
}
static int video_dqbuf(FAR struct video_mng_s *vmng,
FAR struct v4l2_buffer *buf)
{
irqstate_t flags;
FAR video_type_inf_t *type_inf;
FAR vbuf_container_t *container;
FAR sem_t *dqbuf_wait_flg;
enum video_state_e next_video_state;
if (vmng == NULL || buf == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, buf->type);
if (type_inf == NULL)
{
return -EINVAL;
}
container = video_framebuff_dq_valid_container(&type_inf->bufinf);
if (container == NULL)
{
/* 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_video_state =
estimate_next_video_state(vmng, CAUSE_VIDEO_DQBUF);
change_video_state(vmng, next_video_state);
leave_critical_section(flags);
}
nxsem_wait_uninterruptible(dqbuf_wait_flg);
}
while (type_inf->wait_capture.waitend_cause ==
VIDEO_WAITEND_CAUSE_STILLSTOP);
container = type_inf->wait_capture.done_container;
if (container == NULL)
{
/* Waking up without captured data means abort.
* Therefore, Check cause.
*/
if (type_inf->wait_capture.waitend_cause ==
VIDEO_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 video_cancel_dqbuf(FAR struct video_mng_s *vmng,
enum v4l2_buf_type type)
{
FAR video_type_inf_t *type_inf;
type_inf = get_video_type_inf(vmng, 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 = VIDEO_WAITEND_CAUSE_DQCANCEL;
/* If capture is done before nxsem_post, cause is overwritten */
return nxsem_post(&type_inf->wait_capture.dqbuf_wait_flg);
}
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 int video_s_selection(FAR struct video_mng_s *vmng,
FAR struct v4l2_selection *clip)
{
FAR video_type_inf_t *type_inf;
uint32_t p_u32[IMGSENSOR_CLIP_NELEM];
imgsensor_value_t val;
int32_t id;
int ret;
ASSERT(g_video_sensor && vmng);
if (clip == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, clip->type);
if (type_inf == NULL)
{
return -EINVAL;
}
if (type_inf->state != VIDEO_STATE_STREAMOFF)
{
return -EBUSY;
}
if (!validate_clip_setting(&clip->r, type_inf->fmt))
{
return -EINVAL;
}
ret = validate_frame_setting(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(g_video_sensor, id, sizeof(p_u32), val);
if (ret != OK)
{
return ret;
}
memcpy(&type_inf->clip, &clip->r, sizeof(struct v4l2_rect));
return ret;
}
static int video_g_selection(FAR struct video_mng_s *vmng,
FAR struct v4l2_selection *clip)
{
FAR video_type_inf_t *type_inf;
ASSERT(vmng);
if (clip == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, clip->type);
if (type_inf == NULL)
{
return -EINVAL;
}
memcpy(&clip->r, &type_inf->clip, sizeof(struct v4l2_rect));
return OK;
}
static int validate_frame_setting(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_VIDEO_FMT];
imgdata_format_t df[MAX_VIDEO_FMT];
imgsensor_format_t sf[MAX_VIDEO_FMT];
imgdata_interval_t di;
imgsensor_interval_t si;
int ret;
ASSERT(vfmt && interval && g_video_sensor && g_video_data);
/* 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[VIDEO_FMT_MAIN], &df[IMGDATA_FMT_MAIN]);
convert_to_imgdatafmt(&c_fmt[VIDEO_FMT_SUB], &df[IMGDATA_FMT_SUB]);
convert_to_imgdatainterval(interval, &di);
convert_to_imgsensorfmt(&vfmt[VIDEO_FMT_MAIN], &sf[IMGSENSOR_FMT_MAIN]);
convert_to_imgsensorfmt(&vfmt[VIDEO_FMT_SUB], &sf[IMGSENSOR_FMT_SUB]);
convert_to_imgsensorinterval(interval, &si);
ret = IMGSENSOR_VALIDATE_FRAME_SETTING(g_video_sensor,
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(g_video_data, nr_fmt, df, &di);
}
static size_t get_bufsize(FAR video_format_t *vf)
{
size_t ret = vf->width * vf->height;
switch (vf->pixelformat)
{
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 video_type_inf_t *type_inf)
{
return type_inf->bufinf.container_size *
get_bufsize(&type_inf->fmt[VIDEO_FMT_MAIN]);
}
static int video_try_fmt(FAR struct video_mng_s *priv,
FAR struct v4l2_format *v4l2)
{
FAR video_type_inf_t *type_inf;
video_format_t vf[MAX_VIDEO_FMT];
uint8_t nr_fmt;
ASSERT(priv && g_video_sensor && g_video_data);
if (v4l2 == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(priv, v4l2->type);
if (type_inf == NULL)
{
return -EINVAL;
}
switch (v4l2->fmt.pix.pixelformat)
{
case V4L2_PIX_FMT_SUBIMG_UYVY:
case V4L2_PIX_FMT_SUBIMG_RGB565:
if (type_inf->fmt[VIDEO_FMT_MAIN].pixelformat !=
V4L2_PIX_FMT_JPEG_WITH_SUBIMG)
{
return -EPERM;
}
/* Validate both main image and subimage. */
nr_fmt = 2;
memcpy(&vf[VIDEO_FMT_MAIN],
&type_inf->fmt[VIDEO_FMT_MAIN],
sizeof(video_format_t));
vf[VIDEO_FMT_SUB].width = v4l2->fmt.pix.width;
vf[VIDEO_FMT_SUB].height = v4l2->fmt.pix.height;
vf[VIDEO_FMT_SUB].pixelformat =
v4l2->fmt.pix.pixelformat == V4L2_PIX_FMT_SUBIMG_UYVY ?
V4L2_PIX_FMT_UYVY : V4L2_PIX_FMT_RGB565;
break;
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[VIDEO_FMT_MAIN].width = v4l2->fmt.pix.width;
vf[VIDEO_FMT_MAIN].height = v4l2->fmt.pix.height;
vf[VIDEO_FMT_MAIN].pixelformat = v4l2->fmt.pix.pixelformat;
break;
default:
return -EINVAL;
}
return validate_frame_setting(v4l2->type,
nr_fmt,
vf,
&type_inf->clip,
&type_inf->frame_interval);
}
static int video_g_fmt(FAR struct video_mng_s *priv,
FAR struct v4l2_format *fmt)
{
FAR video_type_inf_t *type_inf;
type_inf = get_video_type_inf(priv, fmt->type);
if (type_inf == NULL)
{
return -EINVAL;
}
memset(&fmt->fmt, 0, sizeof(fmt->fmt));
fmt->fmt.pix.width = type_inf->fmt[VIDEO_FMT_MAIN].width;
fmt->fmt.pix.height = type_inf->fmt[VIDEO_FMT_MAIN].height;
fmt->fmt.pix.pixelformat = type_inf->fmt[VIDEO_FMT_MAIN].pixelformat;
return OK;
}
static int video_s_fmt(FAR struct video_mng_s *priv,
FAR struct v4l2_format *fmt)
{
FAR video_type_inf_t *type_inf;
int ret;
ret = video_try_fmt(priv, fmt);
if (ret != 0)
{
return ret;
}
type_inf = get_video_type_inf(priv, fmt->type);
if (type_inf == NULL)
{
return -EINVAL;
}
if (type_inf->state != VIDEO_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[VIDEO_FMT_MAIN].pixelformat !=
V4L2_PIX_FMT_JPEG_WITH_SUBIMG)
{
return -EPERM;
}
type_inf->fmt[VIDEO_FMT_SUB].width = fmt->fmt.pix.width;
type_inf->fmt[VIDEO_FMT_SUB].height = fmt->fmt.pix.height;
type_inf->fmt[VIDEO_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[VIDEO_FMT_MAIN].width = fmt->fmt.pix.width;
type_inf->fmt[VIDEO_FMT_MAIN].height = fmt->fmt.pix.height;
type_inf->fmt[VIDEO_FMT_MAIN].pixelformat = fmt->fmt.pix.pixelformat;
type_inf->nr_fmt = 1;
break;
}
return OK;
}
static int video_s_parm(FAR struct video_mng_s *priv,
FAR struct v4l2_streamparm *parm)
{
FAR video_type_inf_t *type_inf;
int ret;
ASSERT(g_video_sensor && g_video_data);
type_inf = get_video_type_inf(priv, parm->type);
if (type_inf == NULL)
{
return -EINVAL;
}
if (type_inf->state != VIDEO_STATE_STREAMOFF)
{
return -EBUSY;
}
ret = validate_frame_setting(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 video_g_parm(FAR struct video_mng_s *vmng,
FAR struct v4l2_streamparm *parm)
{
FAR video_type_inf_t *type_inf;
int ret = -EINVAL;
DEBUGASSERT(vmng && g_video_sensor);
type_inf = get_video_type_inf(vmng, parm->type);
if (type_inf == NULL)
{
return -EINVAL;
}
memset(&parm->parm, 0, sizeof(parm->parm));
if (type_inf->state == VIDEO_STATE_CAPTURE)
{
/* If capture is started and lower driver has the get_frame_interval(),
* query lower driver.
*/
ret = IMGSENSOR_GET_FRAME_INTERVAL(g_video_sensor, 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 video_streamon(FAR struct video_mng_s *vmng,
FAR enum v4l2_buf_type *type)
{
FAR video_type_inf_t *type_inf;
enum video_state_e next_video_state;
int ret = OK;
if (vmng == NULL || type == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, *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 != VIDEO_STATE_STREAMOFF)
{
ret = -EPERM;
}
else
{
next_video_state =
estimate_next_video_state(vmng, CAUSE_VIDEO_START);
change_video_state(vmng, next_video_state);
}
nxmutex_unlock(&type_inf->lock_state);
return ret;
}
static int video_streamoff(FAR struct video_mng_s *vmng,
FAR enum v4l2_buf_type *type)
{
FAR video_type_inf_t *type_inf;
enum video_state_e next_video_state;
irqstate_t flags;
int ret = OK;
if (vmng == NULL || type == NULL)
{
return -EINVAL;
}
type_inf = get_video_type_inf(vmng, *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 == VIDEO_STATE_STREAMOFF)
{
ret = -EPERM;
}
else
{
next_video_state =
estimate_next_video_state(vmng, CAUSE_VIDEO_STOP);
change_video_state(vmng, next_video_state);
}
leave_critical_section(flags);
return ret;
}
static int video_do_halfpush(FAR struct video_mng_s *priv, 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 video_s_ext_ctrls(priv, &ext_controls);
}
static int video_takepict_start(FAR struct video_mng_s *vmng,
int32_t capture_num)
{
enum video_state_e next_video_state;
FAR vbuf_container_t *container;
irqstate_t flags;
int ret = OK;
if (vmng == NULL)
{
return -EINVAL;
}
nxmutex_lock(&vmng->still_inf.lock_state);
if (vmng->still_inf.state != VIDEO_STATE_STREAMOFF)
{
ret = -EPERM;
}
else
{
if (capture_num > 0)
{
vmng->still_inf.remaining_capnum = capture_num;
}
else
{
vmng->still_inf.remaining_capnum = VIDEO_REMAINING_CAPNUM_INFINITY;
}
/* Control video stream prior to still stream */
flags = enter_critical_section();
next_video_state = estimate_next_video_state(vmng,
CAUSE_STILL_START);
change_video_state(vmng, next_video_state);
leave_critical_section(flags);
container =
video_framebuff_get_vacant_container(&vmng->still_inf.bufinf);
if (container != NULL)
{
/* Start still stream capture */
start_capture(V4L2_BUF_TYPE_STILL_CAPTURE,
vmng->still_inf.nr_fmt,
vmng->still_inf.fmt,
&vmng->still_inf.clip,
&vmng->still_inf.frame_interval,
container->buf.m.userptr,
container->buf.length);
vmng->still_inf.state = VIDEO_STATE_CAPTURE;
}
else
{
vmng->still_inf.state = VIDEO_STATE_STREAMON;
}
}
nxmutex_unlock(&vmng->still_inf.lock_state);
return ret;
}
static int video_takepict_stop(FAR struct video_mng_s *vmng, bool halfpush)
{
enum video_state_e next_video_state;
irqstate_t flags;
int ret = OK;
if (vmng == NULL)
{
return -EINVAL;
}
nxmutex_lock(&vmng->still_inf.lock_state);
if (vmng->still_inf.state == VIDEO_STATE_STREAMOFF &&
vmng->still_inf.remaining_capnum == VIDEO_REMAINING_CAPNUM_INFINITY)
{
ret = -EPERM;
}
else
{
flags = enter_critical_section();
if (vmng->still_inf.state == VIDEO_STATE_CAPTURE)
{
stop_capture(V4L2_BUF_TYPE_STILL_CAPTURE);
}
leave_critical_section(flags);
vmng->still_inf.state = VIDEO_STATE_STREAMOFF;
vmng->still_inf.remaining_capnum = VIDEO_REMAINING_CAPNUM_INFINITY;
/* Control video stream */
nxmutex_lock(&vmng->video_inf.lock_state);
next_video_state = estimate_next_video_state(vmng,
CAUSE_STILL_STOP);
change_video_state(vmng, next_video_state);
nxmutex_unlock(&vmng->video_inf.lock_state);
}
nxmutex_unlock(&vmng->still_inf.lock_state);
return ret;
}
static int video_queryctrl(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 = video_query_ext_ctrl(&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 void set_parameter_name(uint32_t id, char *name)
{
int size =
sizeof(g_video_parameter_name) / sizeof(video_parameter_name_t);
int cnt;
for (cnt = 0; cnt < size; cnt++)
{
if (g_video_parameter_name[cnt].id == id)
{
break;
}
}
ASSERT(cnt < size);
/* copy size = 32 is due to V4L2 specification. */
strlcpy(name, g_video_parameter_name[cnt].name, 32);
}
static int video_query_ext_ctrl(FAR struct v4l2_query_ext_ctrl *attr)
{
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;
ASSERT(g_video_sensor);
if (attr == NULL)
{
return -EINVAL;
}
attr->flags = 0;
attr->elem_size = 0;
attr->nr_of_dims = 0;
memset(attr->dims, 0, sizeof(attr->dims));
if (attr->ctrl_class == V4L2_CTRL_CLASS_CAMERA &&
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 = VIDEO_SCENE_MAX - 1;
attr->step = 1;
attr->default_value = 0;
strlcpy(attr->name, "Scene Mode", 32);
}
else
{
ret = IMGSENSOR_GET_SUPPORTED_VALUE(g_video_sensor,
VIDEO_ID(attr->ctrl_class, attr->id),
&value);
if (ret < 0)
{
return ret;
}
attr->type = value.type;
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(VIDEO_ID(attr->ctrl_class, attr->id),
attr->name);
}
return OK;
}
static int video_querymenu(FAR struct v4l2_querymenu *menu)
{
imgsensor_supported_value_t value;
int ret;
ASSERT(g_video_sensor);
if (menu == NULL)
{
return -EINVAL;
}
if (menu->ctrl_class == V4L2_CTRL_CLASS_CAMERA &&
menu->id == V4L2_CID_SCENE_MODE)
{
/* Scene mode is processed in only video driver. */
if (menu->index > VIDEO_SCENE_MAX - 1)
{
return -EINVAL;
}
menu->value = g_video_scene_parameter[menu->index].mode;
}
else
{
ret = IMGSENSOR_GET_SUPPORTED_VALUE(g_video_sensor,
VIDEO_ID(menu->ctrl_class, 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 video_g_ctrl(FAR struct video_mng_s *priv,
FAR struct v4l2_control *ctrl)
{
struct v4l2_ext_controls ext_controls;
struct v4l2_ext_control control;
int ret;
if (ctrl == NULL)
{
return -EINVAL;
}
/* 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 = video_g_ext_ctrls(priv, &ext_controls);
if (ret == OK)
{
/* Replace gotten value to VIDIOC_G_CTRL parameter */
ctrl->value = control.value;
}
return ret;
}
static int video_s_ctrl(FAR struct video_mng_s *priv,
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 video_s_ext_ctrls(priv, &ext_controls);
}
static int video_g_ext_ctrls(FAR struct video_mng_s *priv,
FAR struct v4l2_ext_controls *ctrls)
{
FAR struct v4l2_ext_control *control;
int ret = OK;
int cnt;
ASSERT(g_video_sensor);
if (priv == NULL || ctrls == NULL)
{
return -EINVAL;
}
for (cnt = 0, control = ctrls->controls;
cnt < ctrls->count;
cnt++, control++)
{
ret = IMGSENSOR_GET_VALUE(g_video_sensor,
VIDEO_ID(ctrls->ctrl_class, 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 set_intvalue(uint32_t id, int32_t value32)
{
imgsensor_value_t value;
ASSERT(g_video_sensor);
value.value32 = value32;
return IMGSENSOR_SET_VALUE(g_video_sensor, id, sizeof(int32_t), value);
}
static int set_pvalue(uint32_t id, int size, void *pval)
{
imgsensor_value_t value;
ASSERT(g_video_sensor);
value.p_u8 = (FAR uint8_t *)pval;
return IMGSENSOR_SET_VALUE(g_video_sensor, id, size, value);
}
static video_scene_params_t *search_scene_param(enum v4l2_scene_mode mode)
{
video_scene_params_t *sp = &g_video_scene_parameter[0];
int i;
for (i = 0; i < VIDEO_SCENE_MAX; i++, sp++)
{
if (sp->mode == mode)
{
return sp;
}
}
return NULL;
}
static int reflect_scene_parameter(enum v4l2_scene_mode mode)
{
video_scene_params_t *sp;
sp = search_scene_param(mode);
if (sp == NULL)
{
/* Unsupported scene mode */
return -EINVAL;
}
set_intvalue(IMGSENSOR_ID_BRIGHTNESS, sp->brightness);
set_intvalue(IMGSENSOR_ID_CONTRAST, sp->contrast);
set_intvalue(IMGSENSOR_ID_SATURATION, sp->saturation);
set_intvalue(IMGSENSOR_ID_HUE , sp->hue);
set_intvalue(IMGSENSOR_ID_AUTO_WHITE_BALANCE, sp->awb);
set_intvalue(IMGSENSOR_ID_RED_BALANCE , sp->red);
set_intvalue(IMGSENSOR_ID_BLUE_BALANCE, sp->blue);
set_intvalue(IMGSENSOR_ID_GAMMA, sp->gamma);
set_pvalue(IMGSENSOR_ID_GAMMA_CURVE, sp->gamma_curve_sz, sp->gamma_curve);
set_intvalue(IMGSENSOR_ID_EXPOSURE, sp->ev);
set_intvalue(IMGSENSOR_ID_HFLIP_VIDEO, sp->hflip_video);
set_intvalue(IMGSENSOR_ID_VFLIP_VIDEO, sp->vflip_video);
set_intvalue(IMGSENSOR_ID_HFLIP_STILL, sp->hflip_still);
set_intvalue(IMGSENSOR_ID_VFLIP_STILL, sp->vflip_still);
set_intvalue(IMGSENSOR_ID_SHARPNESS, sp->sharpness);
set_intvalue(IMGSENSOR_ID_COLORFX, sp->colorfx);
set_intvalue(IMGSENSOR_ID_AUTOBRIGHTNESS, sp->auto_brightness);
set_intvalue(IMGSENSOR_ID_ROTATE, sp->rotate);
set_intvalue(IMGSENSOR_ID_EXPOSURE_AUTO, sp->ae);
if (sp->ae == V4L2_EXPOSURE_MANUAL ||
sp->ae == V4L2_EXPOSURE_SHUTTER_PRIORITY)
{
set_intvalue(IMGSENSOR_ID_EXPOSURE_ABSOLUTE, sp->exposure_time);
}
set_intvalue(IMGSENSOR_ID_FOCUS_ABSOLUTE, sp->focus);
set_intvalue(IMGSENSOR_ID_FOCUS_AUTO, sp->af);
set_intvalue(IMGSENSOR_ID_ZOOM_ABSOLUTE, sp->zoom);
if (sp->ae == V4L2_EXPOSURE_MANUAL ||
sp->ae == V4L2_EXPOSURE_APERTURE_PRIORITY)
{
set_intvalue(IMGSENSOR_ID_IRIS_ABSOLUTE, sp->iris);
}
set_intvalue(IMGSENSOR_ID_AUTO_N_PRESET_WB, sp->wb);
set_intvalue(IMGSENSOR_ID_WIDE_DYNAMIC_RANGE, sp->wdr);
set_intvalue(IMGSENSOR_ID_IMG_STABILIZATION, sp->stabilization);
set_intvalue(IMGSENSOR_ID_ISO_SENSITIVITY_AUTO, sp->iso_auto);
if (sp->iso_auto == V4L2_ISO_SENSITIVITY_MANUAL)
{
set_intvalue(IMGSENSOR_ID_ISO_SENSITIVITY, sp->iso);
}
set_intvalue(IMGSENSOR_ID_EXPOSURE_METERING, sp->meter);
set_intvalue(IMGSENSOR_ID_3A_LOCK, sp->threea_lock);
set_intvalue(IMGSENSOR_ID_FLASH_LED_MODE, sp->led);
set_intvalue(IMGSENSOR_ID_JPEG_QUALITY, sp->jpeg_quality);
g_video_scene_mode = mode;
return OK;
}
static int video_s_ext_ctrls(FAR struct video_mng_s *priv,
FAR struct v4l2_ext_controls *ctrls)
{
FAR struct v4l2_ext_control *control;
int ret = OK;
int cnt;
ASSERT(g_video_sensor);
if (priv == NULL || ctrls == NULL)
{
return -EINVAL;
}
for (cnt = 0, control = ctrls->controls;
cnt < ctrls->count;
cnt++, control++)
{
if (ctrls->ctrl_class == V4L2_CTRL_CLASS_CAMERA &&
control->id == V4L2_CID_SCENE_MODE)
{
ret = reflect_scene_parameter(control->value);
}
else
{
ret = IMGSENSOR_SET_VALUE(g_video_sensor,
VIDEO_ID(ctrls->ctrl_class, control->id),
control->size,
(imgsensor_value_t)control->value64);
if (ret == 0)
{
if (g_video_scene_mode == V4L2_SCENE_MODE_NONE)
{
save_scene_param(V4L2_SCENE_MODE_NONE,
VIDEO_ID(ctrls->ctrl_class, control->id),
control);
}
}
}
if (ret < 0)
{
/* Set cnt in that error occurred */
ctrls->error_idx = cnt;
return ret;
}
}
return ret;
}
static int video_query_ext_ctrl_scene(
FAR struct v4s_query_ext_ctrl_scene *attr)
{
if (attr == NULL)
{
return -EINVAL;
}
return video_query_ext_ctrl(&attr->control);
}
static int video_querymenu_scene(FAR struct v4s_querymenu_scene *menu)
{
if (menu == NULL)
{
return -EINVAL;
}
return video_querymenu(&menu->menu);
}
static int read_scene_param(enum v4l2_scene_mode mode,
uint32_t id,
struct v4l2_ext_control *control)
{
imgsensor_supported_value_t value;
video_scene_params_t *sp;
int ret = OK;
ASSERT(g_video_sensor);
if (control == NULL)
{
return -EINVAL;
}
sp = search_scene_param(mode);
if (sp == NULL)
{
/* Unsupported scene mode */
return -EINVAL;
}
ret = IMGSENSOR_GET_SUPPORTED_VALUE(g_video_sensor, 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_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 video_g_ext_ctrls_scene(FAR struct v4s_ext_controls_scene *ctrls)
{
FAR struct v4l2_ext_control *control;
int ret = OK;
int cnt;
if (ctrls == NULL)
{
return -EINVAL;
}
for (cnt = 0, control = ctrls->control.controls;
cnt < ctrls->control.count;
cnt++, control++)
{
ret = read_scene_param(ctrls->mode,
VIDEO_ID(ctrls->control.ctrl_class, control->id),
control);
if (ret != OK)
{
ctrls->control.error_idx = cnt;
return ret;
}
}
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(enum v4l2_scene_mode mode,
uint32_t id,
struct v4l2_ext_control *control)
{
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;
video_scene_params_t *sp;
int ret;
int i;
ASSERT(g_video_sensor);
sp = search_scene_param(mode);
if (sp == NULL)
{
/* Unsupported scene mode */
return -EINVAL;
}
ret = IMGSENSOR_GET_SUPPORTED_VALUE(g_video_sensor, 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_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;
}
static int video_s_ext_ctrls_scene(FAR struct v4s_ext_controls_scene *ctrls)
{
FAR struct v4l2_ext_control *control;
int ret = OK;
int cnt;
if (ctrls == NULL)
{
return -EINVAL;
}
for (cnt = 0, control = ctrls->control.controls;
cnt < ctrls->control.count;
cnt++, control++)
{
ret = save_scene_param(ctrls->mode,
VIDEO_ID(ctrls->control.ctrl_class, control->id),
control);
if (ret != OK)
{
ctrls->control.error_idx = cnt;
return ret;
}
}
return ret;
}
/****************************************************************************
* Name: video_ioctl
*
* Description:
* Standard character driver ioctl method.
*
****************************************************************************/
static int video_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR video_mng_t *priv = (FAR video_mng_t *)inode->i_private;
int ret = OK;
switch (cmd)
{
case VIDIOC_QUERYCAP:
ret = video_querycap((FAR struct v4l2_capability *)arg);
break;
case VIDIOC_G_INPUT:
ret = video_g_input((FAR int *)arg);
break;
case VIDIOC_ENUMINPUT:
ret = video_enum_input((FAR struct v4l2_input *)arg);
break;
case VIDIOC_REQBUFS:
ret = video_reqbufs(priv, (FAR struct v4l2_requestbuffers *)arg);
break;
case VIDIOC_QUERYBUF:
ret = video_querybuf(priv, (FAR struct v4l2_buffer *)arg);
break;
case VIDIOC_QBUF:
ret = video_qbuf(priv, (FAR struct v4l2_buffer *)arg);
break;
case VIDIOC_DQBUF:
ret = video_dqbuf(priv, (FAR struct v4l2_buffer *)arg);
break;
case VIDIOC_CANCEL_DQBUF:
ret = video_cancel_dqbuf(priv, (FAR enum v4l2_buf_type)arg);
break;
case VIDIOC_STREAMON:
ret = video_streamon(priv, (FAR enum v4l2_buf_type *)arg);
break;
case VIDIOC_STREAMOFF:
ret = video_streamoff(priv, (FAR enum v4l2_buf_type *)arg);
break;
case VIDIOC_DO_HALFPUSH:
ret = video_do_halfpush(priv, arg);
break;
case VIDIOC_TAKEPICT_START:
ret = video_takepict_start(priv, (int32_t)arg);
break;
case VIDIOC_TAKEPICT_STOP:
ret = video_takepict_stop(priv, arg);
break;
case VIDIOC_S_SELECTION:
ret = video_s_selection(priv, (FAR struct v4l2_selection *)arg);
break;
case VIDIOC_G_SELECTION:
ret = video_g_selection(priv, (FAR struct v4l2_selection *)arg);
break;
case VIDIOC_TRY_FMT:
ret = video_try_fmt(priv, (FAR struct v4l2_format *)arg);
break;
case VIDIOC_G_FMT:
ret = video_g_fmt(priv, (FAR struct v4l2_format *)arg);
break;
case VIDIOC_S_FMT:
ret = video_s_fmt(priv, (FAR struct v4l2_format *)arg);
break;
case VIDIOC_S_PARM:
ret = video_s_parm(priv, (FAR struct v4l2_streamparm *)arg);
break;
case VIDIOC_G_PARM:
ret = video_g_parm(priv, (FAR struct v4l2_streamparm *)arg);
break;
case VIDIOC_QUERYCTRL:
ret = video_queryctrl((FAR struct v4l2_queryctrl *)arg);
break;
case VIDIOC_QUERY_EXT_CTRL:
ret = video_query_ext_ctrl((FAR struct v4l2_query_ext_ctrl *)arg);
break;
case VIDIOC_QUERYMENU:
ret = video_querymenu((FAR struct v4l2_querymenu *)arg);
break;
case VIDIOC_G_CTRL:
ret = video_g_ctrl(priv, (FAR struct v4l2_control *)arg);
break;
case VIDIOC_S_CTRL:
ret = video_s_ctrl(priv, (FAR struct v4l2_control *)arg);
break;
case VIDIOC_G_EXT_CTRLS:
ret = video_g_ext_ctrls(priv, (FAR struct v4l2_ext_controls *)arg);
break;
case VIDIOC_S_EXT_CTRLS:
ret = video_s_ext_ctrls(priv, (FAR struct v4l2_ext_controls *)arg);
break;
case VIDIOC_G_STD:
ret = -ENODATA;
break;
case VIDIOC_S_STD:
ret = -EINVAL;
break;
case V4SIOC_QUERY_EXT_CTRL_SCENE:
ret = video_query_ext_ctrl_scene(
(FAR struct v4s_query_ext_ctrl_scene *)arg);
break;
case V4SIOC_QUERYMENU_SCENE:
ret = video_querymenu_scene((FAR struct v4s_querymenu_scene *)arg);
break;
case V4SIOC_G_EXT_CTRLS_SCENE:
ret = video_g_ext_ctrls_scene(
(FAR struct v4s_ext_controls_scene *)arg);
break;
case V4SIOC_S_EXT_CTRLS_SCENE:
ret = video_s_ext_ctrls_scene(
(FAR struct v4s_ext_controls_scene *)arg);
break;
default:
verr("Unrecognized cmd: %d\n", cmd);
ret = - ENOTTY;
break;
}
return ret;
}
static int video_mmap(FAR struct file *filep, FAR struct mm_map_entry_s *map)
{
FAR struct inode *inode = filep->f_inode;
FAR video_mng_t *priv = (FAR video_mng_t *)inode->i_private;
FAR video_type_inf_t *type_inf = &priv->video_inf;
size_t heapsize = get_heapsize(type_inf);
int ret = -EINVAL;
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 FAR void *video_register(FAR const char *devpath)
{
FAR video_mng_t *priv;
size_t allocsize;
int ret;
/* Input devpath Error Check */
if (devpath == NULL)
{
return NULL;
}
allocsize = strnlen(devpath, MAX_VIDEO_FILE_PATH - 1/* Space for '\0' */);
if (allocsize < 2 ||
devpath[0] != '/' ||
(allocsize == MAX_VIDEO_FILE_PATH - 1 &&
devpath[MAX_VIDEO_FILE_PATH] != '\0'))
{
return NULL;
}
/* Initialize video device structure */
priv = (FAR video_mng_t *)kmm_zalloc(sizeof(video_mng_t));
if (priv == NULL)
{
verr("Failed to allocate instance\n");
return NULL;
}
/* Save device path */
priv->devpath = (FAR char *)kmm_malloc(allocsize + 1);
if (priv->devpath == NULL)
{
kmm_free(priv);
return NULL;
}
memcpy(priv->devpath, devpath, allocsize);
priv->devpath[allocsize] = '\0';
/* Initialize mutex */
nxmutex_init(&priv->lock_open_num);
/* Register the character driver */
ret = register_driver(priv->devpath, &g_video_fops, 0666, priv);
if (ret < 0)
{
verr("Failed to register driver: %d\n", ret);
nxmutex_destroy(&priv->lock_open_num);
kmm_free(priv->devpath);
kmm_free(priv);
return NULL;
}
return priv;
}
static int video_unregister(FAR video_mng_t *priv)
{
int ret = OK;
if (priv == NULL)
{
ret = -ENODEV;
}
else
{
nxmutex_destroy(&priv->lock_open_num);
unregister_driver(priv->devpath);
kmm_free(priv->devpath);
kmm_free(priv);
}
return ret;
}
/* Callback function which device driver call when capture has done. */
static int video_complete_capture(uint8_t err_code, uint32_t datasize,
FAR const struct timeval *ts)
{
FAR video_mng_t *vmng = (FAR video_mng_t *)g_video_handler;
FAR video_type_inf_t *type_inf;
FAR vbuf_container_t *container = NULL;
enum v4l2_buf_type buf_type;
irqstate_t flags;
flags = enter_critical_section();
buf_type = vmng->still_inf.state == VIDEO_STATE_CAPTURE ?
V4L2_BUF_TYPE_STILL_CAPTURE : V4L2_BUF_TYPE_VIDEO_CAPTURE;
type_inf = get_video_type_inf(vmng, buf_type);
if (type_inf == NULL)
{
leave_critical_section(flags);
return -EINVAL;
}
if (err_code == 0)
{
type_inf->bufinf.vbuf_curr->buf.flags = 0;
if (type_inf->remaining_capnum > 0)
{
type_inf->remaining_capnum--;
}
}
else
{
type_inf->bufinf.vbuf_curr->buf.flags = V4L2_BUF_FLAG_ERROR;
}
type_inf->bufinf.vbuf_curr->buf.bytesused = datasize;
if (ts != NULL)
{
type_inf->bufinf.vbuf_curr->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 =
VIDEO_WAITEND_CAUSE_CAPTUREDONE;
nxsem_post(&type_inf->wait_capture.dqbuf_wait_flg);
}
if (type_inf->remaining_capnum == 0)
{
stop_capture(buf_type);
type_inf->state = VIDEO_STATE_STREAMOFF;
/* If stop still stream, notify it to video stream */
if (buf_type == V4L2_BUF_TYPE_STILL_CAPTURE &&
is_sem_waited(&vmng->video_inf.wait_capture.dqbuf_wait_flg))
{
vmng->video_inf.wait_capture.waitend_cause =
VIDEO_WAITEND_CAUSE_STILLSTOP;
nxsem_post(&vmng->video_inf.wait_capture.dqbuf_wait_flg);
}
}
else
{
container = video_framebuff_get_vacant_container(&type_inf->bufinf);
if (container == NULL)
{
stop_capture(buf_type);
type_inf->state = VIDEO_STATE_STREAMON;
}
else
{
IMGDATA_SET_BUF(g_video_data,
(FAR uint8_t *)container->buf.m.userptr,
container->buf.length);
container->buf.sequence = type_inf->seqnum++;
}
}
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int video_initialize(FAR const char *devpath)
{
if (g_video_initialized)
{
return OK;
}
g_video_handler = video_register(devpath);
g_video_initialized = true;
return OK;
}
int video_uninitialize(void)
{
if (!g_video_initialized)
{
return OK;
}
video_unregister(g_video_handler);
g_video_initialized = false;
return OK;
}
int imgsensor_register(FAR struct imgsensor_s *sensor)
{
FAR struct imgsensor_s **new_addr;
int ret = -ENOMEM;
new_addr = kmm_realloc(g_video_registered_sensor, sizeof(sensor) *
(g_video_registered_sensor_num + 1));
if (new_addr != NULL)
{
new_addr[g_video_registered_sensor_num++] = sensor;
g_video_registered_sensor = new_addr;
ret = OK;
}
return ret;
}
void imgdata_register(FAR struct imgdata_s *data)
{
g_video_data = data;
}