nuttx/drivers/video/v4l2_m2m.c

1101 lines
28 KiB
C
Raw Normal View History

/****************************************************************************
* drivers/video/v4l2_m2m.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <debug.h>
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <nuttx/video/v4l2_m2m.h>
#include <nuttx/video/video.h>
#include "video_framebuff.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Offset base for buffers on the destination queue - used to distinguish
* between source and destination buffers when mmapping - they receive the
* same offsets but for different queues.
*/
#define CAPTURE_BUF_OFFSET (1 << 30)
#define CODEC_EVENT_COUNT 6
/****************************************************************************
* Private Types
****************************************************************************/
struct codec_event_s
{
sq_entry_t entry;
struct v4l2_event event;
};
typedef struct codec_event_s codec_event_t;
struct codec_type_inf_s
{
video_framebuff_t bufinf;
FAR uint8_t *bufheap; /* for V4L2_MEMORY_MMAP buffers */
bool buflast;
};
typedef struct codec_type_inf_s codec_type_inf_t;
struct codec_file_s
{
codec_type_inf_t capture_inf;
codec_type_inf_t output_inf;
sq_queue_t event_avail;
sq_queue_t event_free;
codec_event_t event_pool[CODEC_EVENT_COUNT];
FAR struct pollfd *fds;
FAR void *priv;
};
typedef struct codec_file_s codec_file_t;
struct codec_mng_s
{
struct v4l2_s v4l2;
FAR struct codec_s *codec;
};
typedef struct codec_mng_s codec_mng_t;
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Character driver methods. */
static int codec_open(FAR struct file *filep);
static int codec_close(FAR struct file *filep);
static int codec_mmap(FAR struct file *filep,
FAR struct mm_map_entry_s *map);
static int codec_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup);
/* Common function */
static FAR codec_type_inf_t *
codec_get_type_inf(FAR struct codec_file_s *cfile, int type);
/* ioctl function for each cmds of ioctl */
static int codec_querycap(FAR struct file *filep,
FAR struct v4l2_capability *cap);
static int codec_reqbufs(FAR struct file *filep,
FAR struct v4l2_requestbuffers *reqbufs);
static int codec_querybuf(FAR struct file *filep,
FAR struct v4l2_buffer *buf);
static int codec_qbuf(FAR struct file *filep,
FAR struct v4l2_buffer *buf);
static int codec_dqbuf(FAR struct file *filep,
FAR struct v4l2_buffer *buf);
static int codec_g_fmt(FAR struct file *filep,
FAR struct v4l2_format *fmt);
static int codec_s_fmt(FAR struct file *filep,
FAR struct v4l2_format *fmt);
static int codec_try_fmt(FAR struct file *filep,
FAR struct v4l2_format *fmt);
static int codec_g_parm(FAR struct file *filep,
FAR struct v4l2_streamparm *parm);
static int codec_s_parm(FAR struct file *filep,
FAR struct v4l2_streamparm *parm);
static int codec_streamon(FAR struct file *filep,
FAR enum v4l2_buf_type *type);
static int codec_streamoff(FAR struct file *filep,
FAR enum v4l2_buf_type *type);
static int codec_g_selection(FAR struct file *filep,
FAR struct v4l2_selection *clip);
static int codec_s_selection(FAR struct file *filep,
FAR struct v4l2_selection *clip);
static int codec_g_ext_ctrls(FAR struct file *filep,
FAR struct v4l2_ext_controls *ctrls);
static int codec_s_ext_ctrls(FAR struct file *filep,
FAR struct v4l2_ext_controls *ctrls);
static int codec_enum_fmt(FAR struct file *filep,
FAR struct v4l2_fmtdesc *fmt);
static int codec_cropcap(FAR struct file *filep,
FAR struct v4l2_cropcap *cropcap);
static int codec_dqevent(FAR struct file *filep,
FAR struct v4l2_event *event);
static int codec_subscribe_event(FAR struct file *filep,
FAR struct v4l2_event_subscription *sub);
static int codec_decoder_cmd(FAR struct file *filep,
FAR struct v4l2_decoder_cmd *cmd);
static int codec_encoder_cmd(FAR struct file *filep,
FAR struct v4l2_encoder_cmd *cmd);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct v4l2_ops_s g_codec_vops =
{
codec_querycap, /* querycap */
NULL, /* g_input */
NULL, /* enum_input */
codec_reqbufs, /* reqbufs */
codec_querybuf, /* querybuf */
codec_qbuf, /* qbuf */
codec_dqbuf, /* dqbuf */
NULL, /* cancel_dqbuf */
codec_g_fmt, /* g_fmt */
codec_s_fmt, /* s_fmt */
codec_try_fmt, /* try_fmt */
codec_g_parm, /* g_parm */
codec_s_parm, /* s_parm */
codec_streamon, /* streamon */
codec_streamoff, /* streamoff */
NULL, /* do_halfpush */
NULL, /* takepict_start */
NULL, /* takepict_stop */
codec_s_selection, /* s_selection */
codec_g_selection, /* g_selection */
NULL, /* queryctrl */
NULL, /* query_ext_ctrl */
NULL, /* querymenu */
NULL, /* g_ctrl */
NULL, /* s_ctrl */
codec_g_ext_ctrls, /* g_ext_ctrls */
codec_s_ext_ctrls, /* s_ext_ctrls */
NULL, /* query_ext_ctrl_scene */
NULL, /* querymenu_scene */
NULL, /* g_ext_ctrls_scene */
NULL, /* s_ext_ctrls_scene */
codec_enum_fmt, /* enum_fmt */
NULL, /* enum_frminterval */
NULL, /* enum_frmsize */
codec_cropcap, /* cropcap */
codec_dqevent, /* dqevent */
codec_subscribe_event, /* subscribe_event */
codec_decoder_cmd, /* decoder_cmd */
codec_encoder_cmd /* encoder_cmd */
};
static const struct file_operations g_codec_fops =
{
codec_open, /* open */
codec_close, /* close */
NULL, /* read */
NULL, /* write */
NULL, /* seek */
NULL, /* ioctl */
codec_mmap, /* mmap */
NULL, /* truncate */
codec_poll, /* poll */
};
/****************************************************************************
* Private Functions
****************************************************************************/
static FAR codec_type_inf_t *
codec_get_type_inf(FAR struct codec_file_s *cfile, int type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
{
return &cfile->output_inf;
}
else
{
return &cfile->capture_inf;
}
}
static int codec_querycap(FAR struct file *filep,
FAR struct v4l2_capability *cap)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
return CODEC_QUERYCAP(cmng->codec, cfile->priv, cap);
}
static int codec_enum_fmt(FAR struct file *filep,
FAR struct v4l2_fmtdesc *fmt)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (fmt == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(fmt->type))
{
return CODEC_OUTPUT_ENUM_FMT(cmng->codec, cfile->priv, fmt);
}
else
{
return CODEC_CAPTURE_ENUM_FMT(cmng->codec, cfile->priv, fmt);
}
}
static int codec_reqbufs(FAR struct file *filep,
FAR struct v4l2_requestbuffers *reqbufs)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
irqstate_t flags;
size_t buf_size;
int ret = OK;
if (reqbufs == NULL)
{
return -EINVAL;
}
reqbufs->mode = V4L2_BUF_MODE_FIFO;
if (reqbufs->count > V4L2_REQBUFS_COUNT_MAX)
{
reqbufs->count = V4L2_REQBUFS_COUNT_MAX;
}
if (V4L2_TYPE_IS_OUTPUT(reqbufs->type))
{
buf_size = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv);
}
else
{
buf_size = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv);
}
if (buf_size == 0)
{
return -EINVAL;
}
flags = enter_critical_section();
type_inf = codec_get_type_inf(cfile, reqbufs->type);
video_framebuff_change_mode(&type_inf->bufinf, reqbufs->mode);
ret = video_framebuff_realloc_container(&type_inf->bufinf,
reqbufs->count);
if (ret == 0 && reqbufs->memory == V4L2_MEMORY_MMAP)
{
kumm_free(type_inf->bufheap);
type_inf->bufheap = kumm_memalign(32, reqbufs->count * buf_size);
if (type_inf->bufheap == NULL)
{
ret = -ENOMEM;
}
}
leave_critical_section(flags);
return ret;
}
static int codec_querybuf(FAR struct file *filep,
FAR struct v4l2_buffer *buf)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
if (buf == NULL || buf->memory != V4L2_MEMORY_MMAP)
{
return -EINVAL;
}
type_inf = codec_get_type_inf(cfile, buf->type);
if (type_inf == NULL)
{
return -EINVAL;
}
if (buf->index >= type_inf->bufinf.container_size)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(buf->type))
{
buf->length = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv);
buf->m.offset = buf->length * buf->index;
}
else
{
buf->length = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv);
buf->m.offset = buf->length * buf->index + CAPTURE_BUF_OFFSET;
}
if (buf->length == 0)
{
return -EINVAL;
}
return OK;
}
static int codec_qbuf(FAR struct file *filep,
FAR struct v4l2_buffer *buf)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
FAR vbuf_container_t *container;
size_t buf_size;
if (buf == NULL)
{
return -EINVAL;
}
type_inf = codec_get_type_inf(cfile, buf->type);
if (type_inf == NULL)
{
return -EINVAL;
}
container = video_framebuff_get_container(&type_inf->bufinf);
if (container == NULL)
{
vwarn("get container failed\n");
return -EAGAIN;
}
memcpy(&container->buf, buf, sizeof(struct v4l2_buffer));
if (buf->memory == V4L2_MEMORY_MMAP)
{
/* only use userptr inside the container */
if (V4L2_TYPE_IS_OUTPUT(buf->type))
{
buf_size = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv);
}
else
{
buf_size = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv);
}
if (buf_size == 0)
{
return -EINVAL;
}
container->buf.length = buf_size;
container->buf.m.userptr = (unsigned long)(type_inf->bufheap +
container->buf.length * buf->index);
}
video_framebuff_queue_container(&type_inf->bufinf, container);
if (V4L2_TYPE_IS_OUTPUT(buf->type))
{
return CODEC_OUTPUT_AVAILABLE(cmng->codec, cfile->priv);
}
else
{
return CODEC_CAPTURE_AVAILABLE(cmng->codec, cfile->priv);
}
}
static int codec_dqbuf(FAR struct file *filep,
FAR struct v4l2_buffer *buf)
{
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
FAR vbuf_container_t *container;
irqstate_t flags;
if (buf == NULL)
{
return -EINVAL;
}
type_inf = codec_get_type_inf(cfile, buf->type);
if (type_inf == NULL)
{
return -EINVAL;
}
flags = enter_critical_section();
if (video_framebuff_is_empty(&type_inf->bufinf))
{
leave_critical_section(flags);
return -EAGAIN;
}
container = video_framebuff_dq_valid_container(&type_inf->bufinf);
if (container == NULL)
{
leave_critical_section(flags);
return -EAGAIN;
}
memcpy(buf, &container->buf, sizeof(struct v4l2_buffer));
video_framebuff_free_container(&type_inf->bufinf, container);
vinfo("%s dequeue done\n", V4L2_TYPE_IS_OUTPUT(buf->type) ?
"output" : "capture");
leave_critical_section(flags);
return OK;
}
static int codec_s_selection(FAR struct file *filep,
FAR struct v4l2_selection *clip)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (clip == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(clip->type))
{
return CODEC_OUTPUT_S_SELECTION(cmng->codec, cfile->priv, clip);
}
else
{
return CODEC_CAPTURE_S_SELECTION(cmng->codec, cfile->priv, clip);
}
}
static int codec_g_selection(FAR struct file *filep,
FAR struct v4l2_selection *clip)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (clip == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(clip->type))
{
return CODEC_OUTPUT_G_SELECTION(cmng->codec, cfile->priv, clip);
}
else
{
return CODEC_CAPTURE_G_SELECTION(cmng->codec, cfile->priv, clip);
}
}
static int codec_g_ext_ctrls(FAR struct file *filep,
FAR struct v4l2_ext_controls *ctrls)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (ctrls == NULL)
{
return -EINVAL;
}
return CODEC_G_EXT_CTRLS(cmng->codec, cfile->priv, ctrls);
}
static int codec_s_ext_ctrls(FAR struct file *filep,
FAR struct v4l2_ext_controls *ctrls)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (ctrls == NULL)
{
return -EINVAL;
}
return CODEC_S_EXT_CTRLS(cmng->codec, cfile->priv, ctrls);
}
static int codec_try_fmt(FAR struct file *filep,
FAR struct v4l2_format *fmt)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (fmt == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(fmt->type))
{
return CODEC_OUTPUT_TRY_FMT(cmng->codec, cfile->priv, fmt);
}
else
{
return CODEC_CAPTURE_TRY_FMT(cmng->codec, cfile->priv, fmt);
}
}
static int codec_g_fmt(FAR struct file *filep,
FAR struct v4l2_format *fmt)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (fmt == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(fmt->type))
{
return CODEC_OUTPUT_G_FMT(cmng->codec, cfile->priv, fmt);
}
else
{
return CODEC_CAPTURE_G_FMT(cmng->codec, cfile->priv, fmt);
}
}
static int codec_s_fmt(FAR struct file *filep,
FAR struct v4l2_format *fmt)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (fmt == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(fmt->type))
{
return CODEC_OUTPUT_S_FMT(cmng->codec, cfile->priv, fmt);
}
else
{
return CODEC_CAPTURE_S_FMT(cmng->codec, cfile->priv, fmt);
}
}
static int codec_g_parm(FAR struct file *filep,
FAR struct v4l2_streamparm *parm)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (parm == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(parm->type))
{
return CODEC_OUTPUT_G_PARM(cmng->codec, cfile->priv, parm);
}
else
{
return CODEC_CAPTURE_G_PARM(cmng->codec, cfile->priv, parm);
}
}
static int codec_s_parm(FAR struct file *filep,
FAR struct v4l2_streamparm *parm)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (parm == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(parm->type))
{
return CODEC_OUTPUT_S_PARM(cmng->codec, cfile->priv, parm);
}
else
{
return CODEC_CAPTURE_S_PARM(cmng->codec, cfile->priv, parm);
}
}
static int codec_streamon(FAR struct file *filep,
FAR enum v4l2_buf_type *type)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
if (type == NULL)
{
return -EINVAL;
}
type_inf = codec_get_type_inf(cfile, *type);
if (type_inf == NULL)
{
return -EINVAL;
}
type_inf->buflast = false;
if (V4L2_TYPE_IS_OUTPUT(*type))
{
return CODEC_OUTPUT_STREAMON(cmng->codec, cfile->priv);
}
else
{
return CODEC_CAPTURE_STREAMON(cmng->codec, cfile->priv);
}
}
static int codec_streamoff(FAR struct file *filep,
FAR enum v4l2_buf_type *type)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
if (type == NULL)
{
return -EINVAL;
}
type_inf = codec_get_type_inf(cfile, *type);
if (type_inf == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(*type))
{
return CODEC_OUTPUT_STREAMOFF(cmng->codec, cfile->priv);
}
else
{
return CODEC_CAPTURE_STREAMOFF(cmng->codec, cfile->priv);
}
}
int codec_cropcap(FAR struct file *filep,
FAR struct v4l2_cropcap *cropcap)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (cropcap == NULL)
{
return -EINVAL;
}
if (V4L2_TYPE_IS_OUTPUT(cropcap->type))
{
return CODEC_OUTPUT_CROPCAP(cmng->codec, cfile->priv, cropcap);
}
else
{
return CODEC_CAPTURE_CROPCAP(cmng->codec, cfile->priv, cropcap);
}
}
int codec_dqevent(FAR struct file *filep,
FAR struct v4l2_event *event)
{
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_event_t *cevt;
irqstate_t flags;
if (event == NULL)
{
return -EINVAL;
}
flags = enter_critical_section();
if (sq_empty(&cfile->event_avail))
{
leave_critical_section(flags);
return -ENOENT;
}
cevt = (FAR codec_event_t *)sq_remfirst(&cfile->event_avail);
memcpy(event, &cevt->event, sizeof(struct v4l2_event));
sq_addlast((FAR sq_entry_t *)cevt, &cfile->event_free);
leave_critical_section(flags);
return OK;
}
int codec_subscribe_event(FAR struct file *filep,
FAR struct v4l2_event_subscription *sub)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (sub == NULL)
{
return -EINVAL;
}
return CODEC_SUBSCRIBE_EVENT(cmng->codec, cfile->priv, sub);
}
int codec_decoder_cmd(FAR struct file *filep,
FAR struct v4l2_decoder_cmd *cmd)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (cmd == NULL)
{
return -EINVAL;
}
return CODEC_DECODER_CMD(cmng->codec, cfile->priv, cmd);
}
int codec_encoder_cmd(FAR struct file *filep,
FAR struct v4l2_encoder_cmd *cmd)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
if (cmd == NULL)
{
return -EINVAL;
}
return CODEC_ENCODER_CMD(cmng->codec, cfile->priv, cmd);
}
/* file operations */
static int codec_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct codec_mng_s *cmng = inode->i_private;
FAR struct codec_file_s *cfile;
int ret;
int i;
cfile = kmm_zalloc(sizeof(struct codec_file_s));
if (cfile == NULL)
{
return -ENOMEM;
}
filep->f_priv = cfile;
ret = CODEC_OPEN(cmng->codec, cfile, &cfile->priv);
if (ret != OK)
{
kmm_free(cfile);
return ret;
}
sq_init(&cfile->event_avail);
sq_init(&cfile->event_free);
for (i = 0; i < CODEC_EVENT_COUNT; i++)
{
sq_addlast((FAR sq_entry_t *)&cfile->event_pool[i],
&cfile->event_free);
}
video_framebuff_init(&cfile->capture_inf.bufinf);
video_framebuff_init(&cfile->output_inf.bufinf);
return OK;
}
static int codec_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
CODEC_CLOSE(cmng->codec, cfile->priv);
video_framebuff_uninit(&cfile->capture_inf.bufinf);
video_framebuff_uninit(&cfile->output_inf.bufinf);
kumm_free(cfile->capture_inf.bufheap);
kumm_free(cfile->output_inf.bufheap);
kmm_free(cfile);
return OK;
}
static int codec_munmap(FAR struct task_group_s *group,
FAR struct mm_map_entry_s *entry,
FAR void *start, size_t length)
{
return mm_map_remove(get_group_mm(group), entry);
}
static int codec_mmap(FAR struct file *filep,
FAR struct mm_map_entry_s *map)
{
FAR struct inode *inode = filep->f_inode;
FAR codec_mng_t *cmng = inode->i_private;
FAR codec_file_t *cfile = filep->f_priv;
FAR codec_type_inf_t *type_inf;
int ret = -EINVAL;
size_t total_size;
size_t buf_size;
if (map == NULL)
{
return -EINVAL;
}
if (map->offset < CAPTURE_BUF_OFFSET)
{
type_inf = &cfile->output_inf;
buf_size = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv);
}
else
{
type_inf = &cfile->capture_inf;
map->offset -= CAPTURE_BUF_OFFSET;
buf_size = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv);
}
if (buf_size == 0)
{
return -EINVAL;
}
total_size = type_inf->bufinf.container_size * buf_size;
if (map->offset >= 0 && map->offset < total_size &&
map->length && map->offset + map->length <= total_size)
{
map->vaddr = type_inf->bufheap + map->offset;
map->munmap = codec_munmap;
ret = mm_map_add(get_current_mm(), map);
}
return ret;
}
static int codec_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup)
{
FAR codec_file_t *cfile = filep->f_priv;
pollevent_t eventset = 0;
irqstate_t flags;
flags = enter_critical_section();
if (setup)
{
if (cfile->fds == NULL)
{
cfile->fds = fds;
fds->priv = &cfile->fds;
if (!video_framebuff_is_empty(&cfile->output_inf.bufinf))
{
eventset |= POLLOUT;
}
if (cfile->capture_inf.buflast ||
!video_framebuff_is_empty(&cfile->capture_inf.bufinf))
{
eventset |= POLLIN;
}
if (!sq_empty(&cfile->event_avail))
{
eventset |= POLLPRI;
}
if (eventset > 0)
{
poll_notify(&cfile->fds, 1, eventset);
}
}
else
{
leave_critical_section(flags);
return -EBUSY;
}
}
else if (fds->priv)
{
cfile->fds = NULL;
fds->priv = NULL;
}
leave_critical_section(flags);
return OK;
}
static FAR struct v4l2_buffer *codec_get_buf(FAR codec_type_inf_t *type_inf)
{
FAR vbuf_container_t *container;
container = video_framebuff_get_vacant_container(&type_inf->bufinf);
if (container == NULL)
{
vinfo("No buffer available\n");
return NULL;
}
return &container->buf;
}
static int codec_put_buf(FAR codec_file_t *cfile,
FAR codec_type_inf_t *type_inf,
FAR struct v4l2_buffer *buf)
{
if (cfile == NULL || type_inf == NULL || buf == NULL)
{
return -EINVAL;
}
if (buf->flags & V4L2_BUF_FLAG_LAST)
{
type_inf->buflast = true;
}
video_framebuff_capture_done(&type_inf->bufinf);
poll_notify(&cfile->fds, 1,
V4L2_TYPE_IS_OUTPUT(buf->type) ? POLLOUT : POLLIN);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int codec_register(FAR const char *devpath, FAR struct codec_s *codec)
{
FAR struct codec_mng_s *cmng;
int ret;
if (devpath == NULL || codec == NULL)
{
return -EINVAL;
}
cmng = kmm_zalloc(sizeof(struct codec_mng_s));
if (cmng == NULL)
{
verr("Failed to allocate codec instance\n");
return -ENOMEM;
}
cmng->v4l2.vops = &g_codec_vops;
cmng->v4l2.fops = &g_codec_fops;
cmng->codec = codec;
/* Register the character driver */
ret = video_register(devpath, (FAR struct v4l2_s *)cmng);
if (ret < 0)
{
verr("Failed to register driver: %d\n", ret);
kmm_free(cmng);
return ret;
}
return OK;
}
int codec_unregister(FAR const char *devpath)
{
return unregister_driver(devpath);
}
FAR struct v4l2_buffer *codec_output_get_buf(void *cookie)
{
FAR codec_file_t *cfile = cookie;
return codec_get_buf(&cfile->output_inf);
}
FAR struct v4l2_buffer *codec_capture_get_buf(void *cookie)
{
FAR codec_file_t *cfile = cookie;
return codec_get_buf(&cfile->capture_inf);
}
int codec_output_put_buf(FAR void *cookie, FAR struct v4l2_buffer *buf)
{
FAR codec_file_t *cfile = cookie;
return codec_put_buf(cfile, &cfile->output_inf, buf);
}
int codec_capture_put_buf(FAR void *cookie, FAR struct v4l2_buffer *buf)
{
FAR codec_file_t *cfile = cookie;
return codec_put_buf(cfile, &cfile->capture_inf, buf);
}
int codec_queue_event(FAR void *cookie, FAR struct v4l2_event *evt)
{
FAR codec_file_t *cfile = cookie;
FAR codec_event_t *cevt;
irqstate_t flags;
flags = enter_critical_section();
cevt = (FAR codec_event_t *)sq_remfirst(&cfile->event_free);
if (cevt == NULL)
{
leave_critical_section(flags);
return -EINVAL;
}
memcpy(&cevt->event, evt, sizeof(struct v4l2_event));
sq_addlast((FAR sq_entry_t *)cevt, &cfile->event_avail);
poll_notify(&cfile->fds, 1, POLLPRI);
leave_critical_section(flags);
return OK;
}