2019-07-04 15:30:50 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* drivers/video/video.c
|
|
|
|
*
|
|
|
|
* Copyright 2018 Sony Semiconductor Solutions Corporation
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
* 3. Neither the name of Sony Semiconductor Solutions Corporation nor
|
|
|
|
* the names of its contributors may be used to endorse or promote
|
|
|
|
* products derived from this software without specific prior written
|
|
|
|
* permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <poll.h>
|
|
|
|
|
|
|
|
#include <nuttx/arch.h>
|
|
|
|
#include <nuttx/board.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
|
|
|
|
#include <arch/board/board.h>
|
|
|
|
|
|
|
|
#include <nuttx/video/video_halif.h>
|
|
|
|
|
|
|
|
#include "video_framebuff.h"
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#define video_printf(format, ...) _info(format, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
#define MAX_VIDEO_FILE_PATH (32)
|
|
|
|
|
|
|
|
#define VIDEO_TRUE (1)
|
|
|
|
#define VIDEO_FALSE (0)
|
|
|
|
|
|
|
|
#define VIDEO_REMAINING_CAPNUM_INFINITY (-1)
|
|
|
|
|
|
|
|
/* Debug option */
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_VIDEO_ERROR
|
|
|
|
#define videoerr(format, ...) _err(format, ##__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define videoerr(x...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_VIDEO_WARN
|
|
|
|
#define videowarn(format, ...) _warn(format, ##__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define videowarn(x...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_VIDEO_INFO
|
|
|
|
#define videoinfo(format, ...) _info(format, ##__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define videoinfo(x...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Types
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
enum video_state_e
|
|
|
|
{
|
|
|
|
VIDEO_STATE_STREAMOFF = 0, /* DMA trigger event is not received */
|
|
|
|
VIDEO_STATE_STREAMON = 1, /* DMA trigger event is received,
|
|
|
|
* but DMA is not operated.
|
|
|
|
*/
|
|
|
|
VIDEO_STATE_DMA = 2, /* On DMA */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum video_state_transition_cause
|
|
|
|
{
|
|
|
|
CAUSE_VIDEO_STOP = 0, /* Stop DMA event for video stream */
|
|
|
|
CAUSE_VIDEO_START = 1, /* Start DMA event for video stream */
|
|
|
|
CAUSE_VIDEO_DQBUF = 2, /* DQBUF timing for video stream */
|
|
|
|
CAUSE_STILL_STOP = 3, /* Stop DMA event for still stream */
|
|
|
|
CAUSE_STILL_START = 4, /* Start DMA event for still stream */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum video_waitend_cause_e
|
|
|
|
{
|
|
|
|
VIDEO_WAITEND_CAUSE_DMADONE = 0,
|
|
|
|
VIDEO_WAITEND_CAUSE_DQCANCEL = 1,
|
|
|
|
VIDEO_WAITEND_CAUSE_STILLSTOP = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct video_wait_dma_s
|
|
|
|
{
|
|
|
|
FAR sem_t dqbuf_wait_flg;
|
|
|
|
FAR vbuf_container_t *done_container; /* Save container which dma done */
|
|
|
|
enum video_waitend_cause_e waitend_cause;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct video_wait_dma_s video_wait_dma_t;
|
|
|
|
|
|
|
|
struct video_type_inf_s
|
|
|
|
{
|
|
|
|
sem_t lock_state;
|
|
|
|
enum video_state_e state;
|
|
|
|
int32_t remaining_capnum;
|
|
|
|
video_wait_dma_t wait_dma;
|
|
|
|
video_framebuff_t bufinf;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct video_type_inf_s video_type_inf_t;
|
|
|
|
|
|
|
|
struct video_mng_s
|
|
|
|
{
|
|
|
|
FAR char *devpath; /* parameter of video_initialize() */
|
|
|
|
sem_t lock_open_num;
|
|
|
|
uint8_t open_num;
|
|
|
|
FAR struct pollfd *poll_wait; /* poll(setup) information */
|
|
|
|
video_type_inf_t video_inf;
|
|
|
|
video_type_inf_t still_inf;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct video_mng_s video_mng_t;
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Function Prototypes
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/* Character driver methods. */
|
|
|
|
|
|
|
|
static int video_open(FAR struct file *filep);
|
|
|
|
static int video_close(FAR struct file *filep);
|
|
|
|
static int video_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
|
|
|
|
static int video_poll(FAR struct file *filep,
|
|
|
|
FAR struct pollfd *fds,
|
|
|
|
bool setup);
|
|
|
|
|
|
|
|
/* Common function */
|
|
|
|
|
|
|
|
static int video_lock(FAR sem_t *sem);
|
|
|
|
static int video_unlock(FAR sem_t *sem);
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* internal function for each cmds of ioctl */
|
|
|
|
|
|
|
|
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_enum_fmt(FAR struct v4l2_fmtdesc *fmt);
|
|
|
|
static int video_enum_framesizes(FAR struct v4l2_frmsizeenum *frmsize);
|
|
|
|
static int video_s_fmt(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct v4l2_format *fmt);
|
|
|
|
static int video_enum_frameintervals(FAR struct v4l2_frmivalenum *frmival);
|
|
|
|
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(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);
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Data
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static const struct file_operations g_video_fops =
|
|
|
|
{
|
|
|
|
video_open, /* open */
|
|
|
|
video_close, /* close */
|
|
|
|
0, /* read */
|
|
|
|
0, /* write */
|
|
|
|
0, /* seek */
|
|
|
|
video_ioctl, /* ioctl */
|
|
|
|
#ifndef CONFIG_DISABLE_POLL
|
|
|
|
video_poll, /* poll */
|
|
|
|
#endif
|
|
|
|
0 /* unlink */
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool is_initialized = false;
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Data
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
static int video_lock(FAR sem_t *sem)
|
|
|
|
{
|
|
|
|
if (sem == NULL)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
return nxsem_wait_uninterruptible(sem);
|
2019-07-04 15:30:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int video_unlock(FAR sem_t *sem)
|
|
|
|
{
|
|
|
|
if (sem == NULL)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
return nxsem_post(sem);
|
2019-07-04 15:30:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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_DMA;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CAUSE_STILL_STOP:
|
|
|
|
if (current_state == VIDEO_STATE_STREAMON)
|
|
|
|
{
|
|
|
|
return VIDEO_STATE_DMA;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return current_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CAUSE_STILL_START:
|
|
|
|
if (current_state == VIDEO_STATE_DMA)
|
|
|
|
{
|
|
|
|
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_DMA;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return current_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return current_state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
FAR vbuf_container_t *dma_container;
|
|
|
|
|
|
|
|
if ((current_state != VIDEO_STATE_DMA) &&
|
|
|
|
(next_state == VIDEO_STATE_DMA))
|
|
|
|
{
|
|
|
|
dma_container =
|
|
|
|
video_framebuff_get_dma_container(&vmng->video_inf.bufinf);
|
|
|
|
if (dma_container)
|
|
|
|
{
|
|
|
|
g_video_devops->set_buftype(V4L2_BUF_TYPE_VIDEO_CAPTURE);
|
|
|
|
g_video_devops->set_buf(dma_container->buf.m.userptr,
|
|
|
|
dma_container->buf.length);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
updated_next_state = VIDEO_STATE_STREAMON;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((current_state == VIDEO_STATE_DMA) &&
|
|
|
|
(next_state != VIDEO_STATE_DMA))
|
|
|
|
{
|
|
|
|
g_video_devops->cancel_dma();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vmng->video_inf.state = updated_next_state;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_DMA));
|
|
|
|
}
|
|
|
|
|
|
|
|
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_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;
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_init(&type_inf->lock_state, 0, 1);
|
|
|
|
nxsem_init(&type_inf->wait_dma.dqbuf_wait_flg, 0, 0);
|
2019-07-04 15:30:50 +02:00
|
|
|
video_framebuff_init(&type_inf->bufinf);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void initialize_resources(FAR video_mng_t *vmng)
|
|
|
|
{
|
|
|
|
initialize_streamresources(&vmng->video_inf);
|
|
|
|
initialize_streamresources(&vmng->still_inf);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_streamresources(FAR video_type_inf_t *type_inf)
|
|
|
|
{
|
|
|
|
video_framebuff_uninit(&type_inf->bufinf);
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_destroy(&type_inf->wait_dma.dqbuf_wait_flg);
|
|
|
|
nxsem_destroy(&type_inf->lock_state);
|
2019-07-04 15:30:50 +02:00
|
|
|
memset(type_inf, 0, sizeof(video_type_inf_t));
|
|
|
|
type_inf->remaining_capnum = VIDEO_REMAINING_CAPNUM_INFINITY;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_resources(FAR video_mng_t *vmng)
|
|
|
|
{
|
|
|
|
/* clean up resource */
|
|
|
|
|
|
|
|
if ((vmng->video_inf.state == VIDEO_STATE_DMA) ||
|
|
|
|
(vmng->still_inf.state == VIDEO_STATE_DMA))
|
|
|
|
{
|
|
|
|
/* If in DMA, stop */
|
|
|
|
|
|
|
|
g_video_devops->cancel_dma();
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup_streamresources(&vmng->video_inf);
|
|
|
|
cleanup_streamresources(&vmng->still_inf);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_sem_waited(FAR sem_t *sem)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int semcount;
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
ret = nxsem_getvalue(sem, &semcount);
|
2019-07-04 15:30:50 +02:00
|
|
|
if ((ret == OK) && (semcount < 0))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
video_lock(&priv->lock_open_num);
|
|
|
|
if (priv->open_num == 0)
|
|
|
|
{
|
|
|
|
/* Only in first execution, open device */
|
|
|
|
|
|
|
|
ret = g_video_devops->open(priv);
|
|
|
|
if (ret == OK)
|
|
|
|
{
|
|
|
|
initialize_resources(priv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In second or later execution, ret is initial value(=OK) */
|
|
|
|
|
|
|
|
if (ret == OK)
|
|
|
|
{
|
|
|
|
priv->open_num++;
|
|
|
|
}
|
|
|
|
video_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;
|
|
|
|
int ret = ERROR;
|
|
|
|
|
|
|
|
video_lock(&priv->lock_open_num);
|
|
|
|
if (priv->open_num == 0)
|
|
|
|
{
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv->open_num--;
|
|
|
|
|
|
|
|
if (priv->open_num == 0)
|
|
|
|
{
|
|
|
|
cleanup_resources(priv);
|
|
|
|
g_video_devops->close();
|
|
|
|
}
|
|
|
|
video_unlock(&priv->lock_open_num);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_reqbufs(FAR struct video_mng_s *vmng,
|
|
|
|
FAR struct v4l2_requestbuffers *reqbufs)
|
|
|
|
{
|
|
|
|
int ret = OK;
|
|
|
|
FAR video_type_inf_t *type_inf;
|
|
|
|
irqstate_t flags;
|
|
|
|
|
|
|
|
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_DMA)
|
|
|
|
{
|
|
|
|
/* In DMA, REQBUFS is not permitted */
|
|
|
|
|
|
|
|
ret = -EPERM;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
video_framebuff_change_mode(&type_inf->bufinf, reqbufs->mode);
|
|
|
|
|
|
|
|
ret = video_framebuff_realloc_container(&type_inf->bufinf,
|
|
|
|
reqbufs->count);
|
|
|
|
}
|
|
|
|
|
|
|
|
leave_critical_section(flags);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
video_framebuff_queue_container(&type_inf->bufinf, container);
|
|
|
|
|
|
|
|
video_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)
|
|
|
|
{
|
|
|
|
video_lock(&vmng->still_inf.lock_state);
|
|
|
|
next_video_state = estimate_next_video_state
|
|
|
|
(vmng, CAUSE_VIDEO_START);
|
|
|
|
change_video_state(vmng, next_video_state);
|
|
|
|
video_unlock(&vmng->still_inf.lock_state);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
container = video_framebuff_get_dma_container(&type_inf->bufinf);
|
|
|
|
if (container)
|
|
|
|
{
|
|
|
|
g_video_devops->set_buftype(buf->type);
|
|
|
|
g_video_devops->set_buf(container->buf.m.userptr,
|
|
|
|
container->buf.length);
|
|
|
|
type_inf->state = VIDEO_STATE_DMA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
leave_critical_section(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
video_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;
|
|
|
|
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 DMA. Wait done */
|
|
|
|
|
|
|
|
dqbuf_wait_flg = &type_inf->wait_dma.dqbuf_wait_flg;
|
|
|
|
|
|
|
|
/* Loop until semaphore is unlocked by DMA done or DQCANCEL */
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
|
|
{
|
|
|
|
/* If start DMA condition is satisfied, start DMA */
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_wait(dqbuf_wait_flg);
|
2019-07-04 15:30:50 +02:00
|
|
|
}
|
|
|
|
while (type_inf->wait_dma.waitend_cause ==
|
|
|
|
VIDEO_WAITEND_CAUSE_STILLSTOP);
|
|
|
|
|
|
|
|
container = type_inf->wait_dma.done_container;
|
|
|
|
|
|
|
|
if (!container)
|
|
|
|
{
|
|
|
|
/* Waking up without DMA data means abort.
|
|
|
|
* Therefore, Check cause.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (type_inf->wait_dma.waitend_cause
|
|
|
|
== VIDEO_WAITEND_CAUSE_DQCANCEL)
|
|
|
|
{
|
|
|
|
return -ECANCELED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type_inf->wait_dma.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_dma.dqbuf_wait_flg))
|
|
|
|
{
|
|
|
|
/* In not waiting DQBUF case, return OK */
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
type_inf->wait_dma.waitend_cause = VIDEO_WAITEND_CAUSE_DQCANCEL;
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
/* If DMA is done before nxsem_post, cause is overwritten */
|
2019-07-04 15:30:50 +02:00
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_post(&type_inf->wait_dma.dqbuf_wait_flg);
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_enum_fmt(FAR struct v4l2_fmtdesc *fmt)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) ||
|
|
|
|
(g_video_devops->get_range_of_fmt == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->get_range_of_fmt(fmt);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_enum_framesizes(FAR struct v4l2_frmsizeenum *frmsize)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) ||
|
|
|
|
(g_video_devops->get_range_of_framesize == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->get_range_of_framesize(frmsize);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_try_fmt(FAR struct v4l2_format *fmt)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) || (g_video_devops->try_format == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->try_format(fmt);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_s_fmt(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct v4l2_format *fmt)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) || (g_video_devops->set_format == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->set_format(fmt);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_enum_frameintervals(FAR struct v4l2_frmivalenum *frmival)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) ||
|
|
|
|
(g_video_devops->get_range_of_frameinterval == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->get_range_of_frameinterval(frmival);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_s_parm(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct v4l2_streamparm *parm)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) ||
|
|
|
|
(g_video_devops->set_frameinterval == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->set_frameinterval(parm);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_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);
|
|
|
|
}
|
|
|
|
|
|
|
|
video_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(bool enable)
|
|
|
|
{
|
|
|
|
if ((g_video_devops == NULL) || (g_video_devops->do_halfpush == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_video_devops->do_halfpush(enable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_takepict_start(FAR struct video_mng_s *vmng,
|
|
|
|
int32_t capture_num)
|
|
|
|
{
|
|
|
|
irqstate_t flags;
|
|
|
|
enum video_state_e next_video_state;
|
|
|
|
FAR vbuf_container_t *dma_container;
|
|
|
|
int ret = OK;
|
|
|
|
|
|
|
|
if (vmng == NULL)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_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);
|
|
|
|
|
|
|
|
dma_container = video_framebuff_get_dma_container
|
|
|
|
(&vmng->still_inf.bufinf);
|
|
|
|
if (dma_container)
|
|
|
|
{
|
|
|
|
/* Start video stream DMA */
|
|
|
|
|
|
|
|
g_video_devops->set_buftype(V4L2_BUF_TYPE_STILL_CAPTURE);
|
|
|
|
g_video_devops->set_buf(dma_container->buf.m.userptr,
|
|
|
|
dma_container->buf.length);
|
|
|
|
vmng->still_inf.state = VIDEO_STATE_DMA;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vmng->still_inf.state = VIDEO_STATE_STREAMON;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
video_unlock(&vmng->still_inf.lock_state);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_takepict_stop(FAR struct video_mng_s *vmng, bool halfpush)
|
|
|
|
{
|
|
|
|
int ret = OK;
|
|
|
|
irqstate_t flags;
|
|
|
|
enum video_state_e next_video_state;
|
|
|
|
|
|
|
|
if (vmng == NULL)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_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_DMA)
|
|
|
|
{
|
|
|
|
g_video_devops->cancel_dma();
|
|
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
|
|
|
|
|
|
vmng->still_inf.state = VIDEO_STATE_STREAMOFF;
|
|
|
|
vmng->still_inf.remaining_capnum = VIDEO_REMAINING_CAPNUM_INFINITY;
|
|
|
|
|
|
|
|
/* Control video stream */
|
|
|
|
|
|
|
|
video_lock(&vmng->video_inf.lock_state);
|
|
|
|
next_video_state = estimate_next_video_state(vmng,
|
|
|
|
CAUSE_STILL_STOP);
|
|
|
|
change_video_state(vmng, next_video_state);
|
|
|
|
video_unlock(&vmng->video_inf.lock_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
video_unlock(&vmng->still_inf.lock_state);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_queryctrl(FAR struct v4l2_queryctrl *ctrl)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct v4l2_query_ext_ctrl ext_ctrl;
|
|
|
|
|
|
|
|
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;
|
|
|
|
strncpy(ctrl->name, ext_ctrl.name, sizeof(ctrl->name));
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_query_ext_ctrl(FAR struct v4l2_query_ext_ctrl *ctrl)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) ||
|
|
|
|
(g_video_devops->get_range_of_ctrlvalue == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->get_range_of_ctrlvalue(ctrl);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_querymenu(FAR struct v4l2_querymenu *menu)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((g_video_devops == NULL) ||
|
|
|
|
(g_video_devops->get_menu_of_ctrlvalue == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->get_menu_of_ctrlvalue(menu);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_g_ctrl(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct v4l2_control *ctrl)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct v4l2_ext_controls ext_controls;
|
|
|
|
struct v4l2_ext_control control;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
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 */
|
|
|
|
|
|
|
|
ret = video_s_ext_ctrls(priv, &ext_controls);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_g_ext_ctrls(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct v4l2_ext_controls *ctrls)
|
|
|
|
{
|
|
|
|
int ret = OK;
|
|
|
|
int cnt;
|
|
|
|
FAR struct v4l2_ext_control *control;
|
|
|
|
|
|
|
|
if ((priv == NULL) || (ctrls == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cnt = 0, control = ctrls->controls;
|
|
|
|
cnt < ctrls->count;
|
|
|
|
cnt++, control++)
|
|
|
|
{
|
|
|
|
ret = g_video_devops->get_ctrlvalue(ctrls->ctrl_class, control);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2020-02-22 19:31:14 +01:00
|
|
|
/* Set cnt in that error occurred */
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
ctrls->error_idx = cnt;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_s_ext_ctrls(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct v4l2_ext_controls *ctrls)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int cnt;
|
|
|
|
FAR struct v4l2_ext_control *control;
|
|
|
|
|
|
|
|
if ((priv == NULL) || (ctrls == NULL))
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cnt = 0, control = ctrls->controls;
|
|
|
|
cnt < ctrls->count;
|
|
|
|
cnt++, control++)
|
|
|
|
{
|
|
|
|
ret = g_video_devops->set_ctrlvalue(ctrls->ctrl_class, control);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2020-02-22 19:31:14 +01:00
|
|
|
/* Set cnt in that error occurred */
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
ctrls->error_idx = cnt;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = g_video_devops->refresh();
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
ctrls->error_idx = cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_REQBUFS:
|
|
|
|
ret = video_reqbufs(priv, (FAR struct v4l2_requestbuffers *)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(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_ENUM_FMT:
|
|
|
|
ret = video_enum_fmt((FAR struct v4l2_fmtdesc *)arg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIDIOC_ENUM_FRAMESIZES:
|
|
|
|
ret = video_enum_framesizes((FAR struct v4l2_frmsizeenum *)arg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIDIOC_TRY_FMT:
|
|
|
|
ret = video_try_fmt((FAR struct v4l2_format *)arg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIDIOC_S_FMT:
|
|
|
|
ret = video_s_fmt(priv, (FAR struct v4l2_format *)arg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIDIOC_ENUM_FRAMEINTERVALS:
|
|
|
|
ret = video_enum_frameintervals((FAR struct v4l2_frmivalenum *)arg);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIDIOC_S_PARM:
|
|
|
|
ret = video_s_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;
|
|
|
|
|
|
|
|
default:
|
|
|
|
videoerr("Unrecognized cmd: %d\n", cmd);
|
|
|
|
ret = - ENOTTY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_poll_setup(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct pollfd *fds)
|
|
|
|
{
|
|
|
|
if ((fds->events & POLLIN) == 0)
|
|
|
|
{
|
|
|
|
return -EDEADLK;
|
|
|
|
}
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
/* TODO: If data exists, get and nxsem_post If no data, wait dma */
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_poll_teardown(FAR struct video_mng_s *priv,
|
|
|
|
FAR struct pollfd *fds)
|
|
|
|
{
|
|
|
|
/* TODO: Delete poll wait information */
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_poll(FAR struct file *filep,
|
|
|
|
FAR struct pollfd *fds,
|
|
|
|
bool setup)
|
|
|
|
{
|
|
|
|
FAR struct inode *inode = filep->f_inode;
|
|
|
|
FAR video_mng_t *priv = inode->i_private;
|
|
|
|
|
|
|
|
if (setup)
|
|
|
|
{
|
|
|
|
return video_poll_setup(priv, fds);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return video_poll_teardown(priv, fds);
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FAR void *video_register(FAR const char *devpath)
|
|
|
|
{
|
|
|
|
FAR video_mng_t *priv;
|
|
|
|
int ret;
|
|
|
|
size_t allocsize;
|
|
|
|
|
|
|
|
/* Input devpath Error Check */
|
|
|
|
|
|
|
|
if (!devpath)
|
|
|
|
{
|
|
|
|
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_malloc(sizeof(video_mng_t));
|
|
|
|
if (!priv)
|
|
|
|
{
|
|
|
|
videoerr("Failed to allocate instance\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(priv, 0, sizeof(video_mng_t));
|
|
|
|
|
|
|
|
/* Save device path */
|
|
|
|
|
|
|
|
priv->devpath = (FAR char *)kmm_malloc(allocsize + 1);
|
|
|
|
if (!priv->devpath)
|
|
|
|
{
|
|
|
|
kmm_free(priv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memcpy(priv->devpath, devpath, allocsize);
|
|
|
|
priv->devpath[allocsize] = '\0';
|
|
|
|
|
|
|
|
/* Initialize semaphore */
|
|
|
|
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_init(&priv->lock_open_num, 0, 1);
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
/* Register the character driver */
|
|
|
|
|
|
|
|
ret = register_driver(priv->devpath, &g_video_fops, 0666, priv);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
videoerr("Failed to register driver: %d\n", ret);
|
|
|
|
kmm_free(priv->devpath);
|
|
|
|
kmm_free(priv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (FAR void *)priv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int video_unregister(FAR video_mng_t *v_mgr)
|
|
|
|
{
|
|
|
|
int ret = OK;
|
|
|
|
|
|
|
|
if (!v_mgr)
|
|
|
|
{
|
|
|
|
ret = -ENODEV;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_destroy(&v_mgr->lock_open_num);
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
unregister_driver((const char *)v_mgr->devpath);
|
|
|
|
|
|
|
|
kmm_free(v_mgr->devpath);
|
|
|
|
kmm_free(v_mgr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
static FAR void *video_handler;
|
|
|
|
|
|
|
|
int video_initialize(FAR const char *devpath)
|
|
|
|
{
|
|
|
|
if (is_initialized)
|
|
|
|
{
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
video_handler = video_register(devpath);
|
|
|
|
|
|
|
|
is_initialized = true;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int video_uninitialize(void)
|
|
|
|
{
|
|
|
|
if (is_initialized)
|
|
|
|
{
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
video_unregister(video_handler);
|
|
|
|
|
|
|
|
is_initialized = false;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int video_common_notify_dma_done(uint8_t err_code,
|
|
|
|
uint32_t buf_type,
|
|
|
|
uint32_t datasize,
|
|
|
|
FAR void *priv)
|
|
|
|
{
|
|
|
|
FAR video_mng_t *vmng = (FAR video_mng_t *)priv;
|
|
|
|
FAR video_type_inf_t *type_inf;
|
|
|
|
FAR vbuf_container_t *container = NULL;
|
|
|
|
|
|
|
|
type_inf = get_video_type_inf(vmng, buf_type);
|
|
|
|
if (type_inf == NULL)
|
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err_code == 0)
|
|
|
|
{
|
|
|
|
type_inf->bufinf.vbuf_dma->buf.flags = 0;
|
|
|
|
if (type_inf->remaining_capnum > 0)
|
|
|
|
{
|
|
|
|
type_inf->remaining_capnum--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
type_inf->bufinf.vbuf_dma->buf.flags = V4L2_BUF_FLAG_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
type_inf->bufinf.vbuf_dma->buf.bytesused = datasize;
|
|
|
|
video_framebuff_dma_done(&type_inf->bufinf);
|
|
|
|
|
|
|
|
if (is_sem_waited(&type_inf->wait_dma.dqbuf_wait_flg))
|
|
|
|
{
|
|
|
|
/* If waiting DMA done in DQBUF,
|
|
|
|
* get/save container and unlock wait
|
|
|
|
*/
|
|
|
|
|
|
|
|
type_inf->wait_dma.done_container
|
|
|
|
= video_framebuff_pop_curr_container(&type_inf->bufinf);
|
|
|
|
type_inf->wait_dma.waitend_cause
|
|
|
|
= VIDEO_WAITEND_CAUSE_DMADONE;
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_post(&type_inf->wait_dma.dqbuf_wait_flg);
|
2019-07-04 15:30:50 +02:00
|
|
|
|
|
|
|
/* TODO: in poll wait, unlock wait */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type_inf->remaining_capnum == 0)
|
|
|
|
{
|
|
|
|
g_video_devops->cancel_dma();
|
|
|
|
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_dma.dqbuf_wait_flg))
|
|
|
|
{
|
|
|
|
vmng->video_inf.wait_dma.waitend_cause
|
|
|
|
= VIDEO_WAITEND_CAUSE_STILLSTOP;
|
2020-01-02 17:49:34 +01:00
|
|
|
nxsem_post(&vmng->video_inf.wait_dma.dqbuf_wait_flg);
|
2019-07-04 15:30:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
container = video_framebuff_get_dma_container(&type_inf->bufinf);
|
|
|
|
if (!container)
|
|
|
|
{
|
|
|
|
g_video_devops->cancel_dma();
|
|
|
|
type_inf->state = VIDEO_STATE_STREAMON;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_video_devops->set_buf(container->buf.m.userptr,
|
|
|
|
container->buf.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|