/**************************************************************************** * 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 #include #include #include #include #include #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; }