/* * v4l2_device.cpp - v4l2 device * * Copyright (c) 2014-2015 Intel Corporation * * Licensed 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. * * Author: Wind Yuan * Author: John Ye */ #include "v4l2_device.h" #include #include #include #include #include #include #include #include "v4l2_buffer_proxy.h" namespace XCam { #define XCAM_V4L2_DEFAULT_BUFFER_COUNT 4 V4l2Device::V4l2Device (const char *name) : _name (NULL) , _fd (-1) , _sensor_id (0) , _capture_mode (0) , _buf_type (V4L2_BUF_TYPE_VIDEO_CAPTURE) , _buf_sync (false) , _memory_type (V4L2_MEMORY_MMAP) , _planes (NULL) , _fps_n (0) , _fps_d (0) , _active (false) , _buf_count (XCAM_V4L2_DEFAULT_BUFFER_COUNT) , _queued_bufcnt(0) ,_mplanes_count(FMT_NUM_PLANES) { if (name) _name = strndup (name, XCAM_MAX_STR_SIZE); xcam_mem_clear (_format); } V4l2Device::~V4l2Device () { close(); if (_name) xcam_free (_name); if (_planes) xcam_free (_planes); } bool V4l2Device::set_device_name (const char *name) { XCAM_ASSERT (name); if (is_opened()) { XCAM_LOG_WARNING ("can't set device name since device opened"); return false; } if (_name) xcam_free (_name); _name = strndup (name, XCAM_MAX_STR_SIZE); return true; } bool V4l2Device::set_sensor_id (int id) { if (is_opened()) { XCAM_LOG_WARNING ("can't set sensor id since device opened"); return false; } _sensor_id = id; return true; } bool V4l2Device::set_capture_mode (uint32_t capture_mode) { if (is_opened()) { XCAM_LOG_WARNING ("can't set sensor id since device opened"); return false; } _capture_mode = capture_mode; return true; } bool V4l2Device::set_mplanes_count (uint32_t planes_count) { if (is_activated ()) { XCAM_LOG_WARNING ("device(%s) set mplanes count failed", XCAM_STR (_name)); return false; } _mplanes_count = planes_count; return true; } bool V4l2Device::set_framerate (uint32_t n, uint32_t d) { if (_format.fmt.pix.pixelformat) { XCAM_LOG_WARNING ("device(%s) set framerate failed since formatted was already set.", XCAM_STR(_name)); return false; } _fps_n = n; _fps_d = d; return true; } void V4l2Device::get_framerate (uint32_t &n, uint32_t &d) { n = _fps_n; d = _fps_d; } bool V4l2Device::set_mem_type (enum v4l2_memory type) { if (is_activated ()) { XCAM_LOG_WARNING ("device(%s) set mem type failed", XCAM_STR (_name)); return false; } _memory_type = type; return true; } bool V4l2Device::set_buf_type (enum v4l2_buf_type type) { if (is_activated ()) { XCAM_LOG_WARNING ("device(%s) set buf type failed", XCAM_STR (_name)); return false; } _buf_type = type; return true; } bool V4l2Device::set_buf_sync (bool sync) { if (is_activated ()) { XCAM_LOG_WARNING ("device(%s) set buf sync failed", XCAM_STR (_name)); return false; } _buf_sync = sync; return true; } bool V4l2Device::set_buffer_count (uint32_t buf_count) { if (is_activated ()) { XCAM_LOG_WARNING ("device(%s) set buffer count failed", XCAM_STR (_name)); return false; } _buf_count = buf_count; return true; } XCamReturn V4l2Device::open (bool nonblock) { struct v4l2_streamparm param; if (is_opened()) { XCAM_LOG_DEBUG ("device(%s) was already opened", XCAM_STR(_name)); return XCAM_RETURN_NO_ERROR; } if (!_name) { XCAM_LOG_DEBUG ("v4l2 device open failed, there's no device name"); return XCAM_RETURN_ERROR_PARAM; } if (nonblock) _fd = ::open (_name, O_RDWR | O_CLOEXEC | O_NONBLOCK); else _fd = ::open (_name, O_RDWR | O_CLOEXEC); if (_fd == -1) { XCAM_LOG_ERROR ("open device(%s) failed", _name); return XCAM_RETURN_ERROR_IOCTL; } else { XCAM_LOG_DEBUG ("open device(%s) successed, fd: %d", _name, _fd); } #if 0 // set sensor id if (io_control (VIDIOC_S_INPUT, &_sensor_id) < 0) { XCAM_LOG_WARNING ("set sensor id(%d) failed but continue", _sensor_id); } // set capture mode xcam_mem_clear (param); param.type = _buf_type; param.parm.capture.capturemode = _capture_mode; if (io_control (VIDIOC_S_PARM, ¶m) < 0) { XCAM_LOG_WARNING ("set capture mode(0x%08x) failed but continue", _capture_mode); } #endif struct v4l2_capability cap; // only video node cay query cap if (_name && strstr(_name, "video")) { query_cap(cap); // get default foramt get_format (_format); } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::close () { if (!is_opened()) return XCAM_RETURN_NO_ERROR; ::close (_fd); _fd = -1; XCAM_LOG_INFO ("device(%s) closed", XCAM_STR (_name)); return XCAM_RETURN_NO_ERROR; } int V4l2Device::io_control (int cmd, void *arg) { if (_fd <= 0) return -1; return xcam_device_ioctl (_fd, cmd, arg); } int V4l2Device::poll_event (int timeout_msec, int stop_fd) { int num_fds = stop_fd == -1 ? 1 : 2; struct pollfd poll_fds[num_fds]; int ret = 0; XCAM_ASSERT (_fd > 0); memset(poll_fds, 0, sizeof(poll_fds)); poll_fds[0].fd = _fd; poll_fds[0].events = (POLLPRI | POLLIN | POLLOUT | POLLERR | POLLNVAL | POLLHUP); if (stop_fd != -1) { poll_fds[1].fd = stop_fd; poll_fds[1].events = POLLPRI | POLLIN | POLLOUT; poll_fds[1].revents = 0; } ret = poll (poll_fds, num_fds, timeout_msec); if (stop_fd != -1) { if ((poll_fds[1].revents & POLLIN) || (poll_fds[1].revents & POLLPRI)) { XCAM_LOG_DEBUG ("%s: Poll returning from flush", __FUNCTION__); return POLL_STOP_RET; } } if (ret > 0 && (poll_fds[0].revents & (POLLERR | POLLNVAL | POLLHUP))) { XCAM_LOG_DEBUG ("v4l2 subdev(%s) polled error", XCAM_STR(_name)); return -1; } return ret; } XCamReturn V4l2Device::query_cap (struct v4l2_capability &cap) { int ret = 0; XCAM_FAIL_RETURN (ERROR, is_opened (), XCAM_RETURN_ERROR_FILE, "Cannot query cap from v4l2 device while it is closed."); ret = io_control(VIDIOC_QUERYCAP, &cap); if (ret < 0) { XCAM_LOG_ERROR("VIDIOC_QUERYCAP returned: %d (%s)", ret, strerror(errno)); return XCAM_RETURN_ERROR_UNKNOWN; } if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) _buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) _buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) _buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) _buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; else if (cap.capabilities & V4L2_CAP_META_CAPTURE) _buf_type = V4L2_BUF_TYPE_META_CAPTURE; else if (cap.capabilities & V4L2_CAP_META_OUTPUT) _buf_type = V4L2_BUF_TYPE_META_OUTPUT; else { XCAM_LOG_ERROR("@%s: unsupported buffer type.", __FUNCTION__); return XCAM_RETURN_ERROR_UNKNOWN; } XCAM_LOG_INFO("------------------------------"); XCAM_LOG_INFO("driver: '%s'", cap.driver); XCAM_LOG_INFO("card: '%s'", cap.card); XCAM_LOG_INFO("bus_info: '%s'", cap.bus_info); XCAM_LOG_INFO("version: %x", cap.version); XCAM_LOG_INFO("capabilities: %x", cap.capabilities); XCAM_LOG_INFO("device caps: %x", cap.device_caps); XCAM_LOG_INFO("buffer type %d", _buf_type); XCAM_LOG_INFO("------------------------------"); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::set_format (struct v4l2_format &format) { XCamReturn ret = XCAM_RETURN_NO_ERROR; XCAM_FAIL_RETURN (ERROR, !is_activated (), XCAM_RETURN_ERROR_PARAM, "Cannot set format to v4l2 device while it is active."); XCAM_FAIL_RETURN (ERROR, is_opened (), XCAM_RETURN_ERROR_FILE, "Cannot set format to v4l2 device while it is closed."); struct v4l2_format tmp_format = format; ret = pre_set_format (format); if (ret != XCAM_RETURN_NO_ERROR) { XCAM_LOG_WARNING ("device(%s) pre_set_format failed", XCAM_STR (_name)); //RKTODO::rkisp no need to do set format op for subdev. //return ret; } if (io_control (VIDIOC_S_FMT, &format) < 0) { if (errno == EBUSY) { // TODO log device name XCAM_LOG_ERROR("Video device is busy, fail to set format."); } else { // TODO log format details and errno XCAM_LOG_ERROR("Fail to set format: %s", strerror(errno)); } return XCAM_RETURN_ERROR_IOCTL; } if (tmp_format.fmt.pix.width != format.fmt.pix.width || tmp_format.fmt.pix.height != format.fmt.pix.height) { XCAM_LOG_ERROR ( "device(%s) set v4l2 format failed, supported format: width:%d, height:%d", XCAM_STR (_name), format.fmt.pix.width, format.fmt.pix.height); return XCAM_RETURN_ERROR_PARAM; } while (_fps_n && _fps_d) { struct v4l2_streamparm param; xcam_mem_clear (param); param.type = _buf_type; if (io_control (VIDIOC_G_PARM, ¶m) < 0) { XCAM_LOG_WARNING ("device(%s) set framerate failed on VIDIOC_G_PARM but continue", XCAM_STR (_name)); break; } if (!(param.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) break; param.parm.capture.timeperframe.numerator = _fps_d; param.parm.capture.timeperframe.denominator = _fps_n; if (io_control (VIDIOC_S_PARM, ¶m) < 0) { XCAM_LOG_WARNING ("device(%s) set framerate failed on VIDIOC_S_PARM but continue", XCAM_STR (_name)); break; } _fps_n = param.parm.capture.timeperframe.denominator; _fps_d = param.parm.capture.timeperframe.numerator; XCAM_LOG_INFO ("device(%s) set framerate(%d/%d)", XCAM_STR (_name), _fps_n, _fps_d); // exit here, otherwise it is an infinite loop break; } ret = post_set_format (format); if (ret != XCAM_RETURN_NO_ERROR) { XCAM_LOG_WARNING ("device(%s) post_set_format failed", XCAM_STR (_name)); return ret; } _format = format; XCAM_LOG_INFO ( "device(%s) set format(w:%d, h:%d, pixelformat:%s, bytesperline:%d,image_size:%d)", XCAM_STR (_name), format.fmt.pix.width, format.fmt.pix.height, xcam_fourcc_to_string (format.fmt.pix.pixelformat), format.fmt.pix.bytesperline, format.fmt.pix.sizeimage); return XCAM_RETURN_NO_ERROR; } /*! \brief v4l2 set format * * \param[in] width format width * \param[in] height format height * \param[in] pixelformat fourcc * \param[in] field V4L2_FIELD_INTERLACED or V4L2_FIELD_NONE */ XCamReturn V4l2Device::set_format ( uint32_t width, uint32_t height, uint32_t pixelformat, enum v4l2_field field, uint32_t bytes_perline) { XCAM_LOG_INFO ( "device(%s) set format(w:%d, h:%d, pixelformat:%s, bytesperline:%d)", XCAM_STR (_name), width, height, xcam_fourcc_to_string (pixelformat), bytes_perline); struct v4l2_format format; xcam_mem_clear (format); format.type = _buf_type; format.fmt.pix.width = width; format.fmt.pix.height = height; format.fmt.pix.pixelformat = pixelformat; format.fmt.pix.field = field; if (bytes_perline != 0) format.fmt.pix.bytesperline = bytes_perline; return set_format (format); } XCamReturn V4l2Device::pre_set_format (struct v4l2_format &format) { XCAM_UNUSED (format); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::post_set_format (struct v4l2_format &format) { XCAM_UNUSED (format); return XCAM_RETURN_NO_ERROR; } std::list V4l2Device::enum_formats () { std::list formats; struct v4l2_fmtdesc format; uint32_t i = 0; while (1) { xcam_mem_clear (format); format.index = i++; format.type = _buf_type; if (this->io_control (VIDIOC_ENUM_FMT, &format) < 0) { if (errno == EINVAL) break; else { // error XCAM_LOG_DEBUG ("enum formats failed"); return formats; } } formats.push_back (format); } return formats; } XCamReturn V4l2Device::get_format (struct v4l2_format &format) { if (is_activated ()) { format = _format; return XCAM_RETURN_NO_ERROR; } if (!is_opened ()) return XCAM_RETURN_ERROR_IOCTL; xcam_mem_clear (format); format.type = _buf_type; if (this->io_control (VIDIOC_G_FMT, &format) < 0) { // FIXME: also log the device name? XCAM_LOG_ERROR("Fail to get format via ioctl VIDVIO_G_FMT."); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::prepare () { XCamReturn ret = XCAM_RETURN_NO_ERROR; // request buffer first ret = request_buffer (); XCAM_FAIL_RETURN ( ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "device(%s) start failed", XCAM_STR (_name)); _queued_bufcnt = 0; //alloc buffers ret = init_buffer_pool (); XCAM_FAIL_RETURN ( ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "device(%s) start failed", XCAM_STR (_name)); if (!V4L2_TYPE_IS_OUTPUT(_buf_type) && (_buf_type != V4L2_BUF_TYPE_META_OUTPUT)) { //queue all buffers for (uint32_t i = 0; i < _buf_count; ++i) { SmartPtr &buf = _buf_pool [i]; XCAM_ASSERT (buf.ptr()); XCAM_ASSERT (buf->get_buf().index == i); ret = queue_buffer (buf); if (ret != XCAM_RETURN_NO_ERROR) { XCAM_LOG_ERROR ( "device(%s) start failed on queue index:%d", XCAM_STR (_name), i); stop (); return ret; } } } return ret; } XCamReturn V4l2Device::get_crop (struct v4l2_crop &crop) { int ret = 0; XCAM_ASSERT (is_opened()); ret = this->io_control (VIDIOC_G_CROP, &crop); if (ret < 0) { XCAM_LOG_ERROR("subdev(%s) VIDIOC_G_CROP failed", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::set_selection (struct v4l2_selection &select) { int ret = 0; XCAM_ASSERT (is_opened()); ret = this->io_control (VIDIOC_S_SELECTION, &select); if (ret < 0) { XCAM_LOG_ERROR("videodev (%s) VIDIOC_S_SELECTION failed", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::set_crop (struct v4l2_crop &crop) { int ret = 0; XCAM_ASSERT (is_opened()); ret = this->io_control (VIDIOC_S_CROP, &crop); if (ret < 0) { XCAM_LOG_ERROR("subdev(%s) VIDIOC_S_CROP failed", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2SubDevice::set_selection (struct v4l2_subdev_selection &aSelection) { int ret = 0; XCAM_ASSERT (is_opened()); XCAM_LOG_DEBUG ("VIDIOC_SUBDEV_S_SELECTION: which: %d, pad: %d, target: 0x%x, " "flags: 0x%x, rect left: %d, rect top: %d, width: %d, height: %d", aSelection.which, aSelection.pad, aSelection.target, aSelection.flags, aSelection.r.left, aSelection.r.top, aSelection.r.width, aSelection.r.height); ret = this->io_control (VIDIOC_SUBDEV_S_SELECTION, &aSelection); if (ret < 0) { XCAM_LOG_ERROR("subdev(%s) VIDIOC_SUBDEV_S_SELECTION failed", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2SubDevice::getFormat(struct v4l2_subdev_format &aFormat) { int ret = 0; XCAM_ASSERT (is_opened()); ret = this->io_control(VIDIOC_SUBDEV_G_FMT, &aFormat); if (ret < 0) { XCAM_LOG_ERROR("subdev(%s) VIDIOC_SUBDEV_G_FMT failed: %s", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } XCAM_LOG_DEBUG ("VIDIOC_SUBDEV_G_FMT: pad: %d, which: %d, width: %d, " "height: %d, format: 0x%x, field: %d, color space: %d", aFormat.pad, aFormat.which, aFormat.format.width, aFormat.format.height, aFormat.format.code, aFormat.format.field, aFormat.format.colorspace); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2SubDevice::setFormat(struct v4l2_subdev_format &aFormat) { int ret = 0; XCAM_ASSERT (is_opened()); XCAM_LOG_DEBUG ("VIDIOC_SUBDEV_S_FMT: pad: %d, which: %d, width: %d, " "height: %d, format: 0x%x, field: %d, color space: %d", aFormat.pad, aFormat.which, aFormat.format.width, aFormat.format.height, aFormat.format.code, aFormat.format.field, aFormat.format.colorspace); ret = this->io_control(VIDIOC_SUBDEV_S_FMT, &aFormat); if (ret < 0) { XCAM_LOG_ERROR("subdev(%s) VIDIOC_SUBDEV_S_FMT failed: %s", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::start (bool prepared) { XCamReturn ret = XCAM_RETURN_NO_ERROR; if (!prepared) { // request buffer first ret = request_buffer (); XCAM_FAIL_RETURN ( ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "device(%s) start failed", XCAM_STR (_name)); _queued_bufcnt = 0; //alloc buffers ret = init_buffer_pool (); XCAM_FAIL_RETURN ( ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "device(%s) start failed", XCAM_STR (_name)); if (!V4L2_TYPE_IS_OUTPUT(_buf_type) && (_buf_type != V4L2_BUF_TYPE_META_OUTPUT)) { //queue all buffers for (uint32_t i = 0; i < _buf_count; ++i) { SmartPtr &buf = _buf_pool [i]; XCAM_ASSERT (buf.ptr()); XCAM_ASSERT (buf->get_buf().index == i); ret = queue_buffer (buf); if (ret != XCAM_RETURN_NO_ERROR) { XCAM_LOG_ERROR ( "device(%s) start failed on queue index:%d", XCAM_STR (_name), i); stop (); return ret; } } } } // stream on if (io_control (VIDIOC_STREAMON, &_buf_type) < 0) { XCAM_LOG_ERROR ( "device(%s) start failed on VIDIOC_STREAMON, fd=%d", XCAM_STR (_name), _fd); stop (); return XCAM_RETURN_ERROR_IOCTL; } _active = true; XCAM_LOG_INFO ("device(%s) started successfully", XCAM_STR (_name)); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::stop () { SmartLock auto_lock(_buf_mutex); XCAM_LOG_INFO ("device(%s) stop, already start: %d", XCAM_STR (_name), _active); // stream off if (_active) { if (io_control (VIDIOC_STREAMOFF, &_buf_type) < 0) { XCAM_LOG_WARNING ("device(%s) steamoff failed", XCAM_STR (_name)); } _active = false; /* while (_queued_bufcnt > 0) { */ /* struct v4l2_buffer v4l2_buf; */ /* struct v4l2_plane planes[_mplanes_count]; */ /* xcam_mem_clear (v4l2_buf); */ /* v4l2_buf.type = _buf_type; */ /* v4l2_buf.memory = _memory_type; */ /* if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || */ /* V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { */ /* memset(planes, 0, sizeof(struct v4l2_plane) * _mplanes_count); */ /* v4l2_buf.m.planes = planes; */ /* v4l2_buf.length = _mplanes_count; */ /* } */ /* if (this->io_control (VIDIOC_DQBUF, &v4l2_buf) < 0) { */ /* XCAM_LOG_WARNING ("device(%s) fail to dequeue buffer.", XCAM_STR (_name)); */ /* } */ /* _queued_bufcnt--; */ /* } */ /* fini_buffer_pool (); */ /* release the shared buf between mipi tx and rx */ #if 0 // moved to fini_buffer_pool, buffer may be requested at prepare stage if (_memory_type == V4L2_MEMORY_DMABUF) { struct v4l2_requestbuffers request_buf; xcam_mem_clear (request_buf); request_buf.type = _buf_type; request_buf.count = 0; request_buf.memory = _memory_type; if (io_control (VIDIOC_REQBUFS, &request_buf) < 0) { XCAM_LOG_ERROR ("device(%s) starts failed on VIDIOC_REQBUFS", XCAM_STR (_name)); //return XCAM_RETURN_ERROR_IOCTL; } } #endif } if (_buf_pool.size() > 0) fini_buffer_pool (); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::request_buffer () { struct v4l2_requestbuffers request_buf; XCAM_ASSERT (!is_activated()); xcam_mem_clear (request_buf); request_buf.type = _buf_type; request_buf.count = _buf_count; request_buf.memory = _memory_type; XCAM_LOG_INFO ("request buffers in device(%s): type: %d, count: %d, mem_type: %d", XCAM_STR (_name), request_buf.type, request_buf.count, request_buf.memory); if (io_control (VIDIOC_REQBUFS, &request_buf) < 0) { XCAM_LOG_INFO ("device(%s) starts failed on VIDIOC_REQBUFS", XCAM_STR (_name)); return XCAM_RETURN_ERROR_IOCTL; } XCAM_LOG_INFO ("device(%s) request buffer count: %d", XCAM_STR (_name), request_buf.count); if (request_buf.count != _buf_count) { XCAM_LOG_INFO ( "device(%s) request buffer count doesn't match user settings, reset buffer count to %d", XCAM_STR (_name), request_buf.count); _buf_count = request_buf.count; } if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) _planes = (struct v4l2_plane *)xcam_malloc0 (_buf_count * _mplanes_count * sizeof(struct v4l2_plane)); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::allocate_buffer ( SmartPtr &buf, const struct v4l2_format &format, const uint32_t index) { struct v4l2_buffer v4l2_buf; int expbuf_fd = -1; uintptr_t expbuf_ptr = 0; xcam_mem_clear (v4l2_buf); v4l2_buf.index = index; v4l2_buf.type = _buf_type; v4l2_buf.memory = _memory_type; if (_buf_sync) { v4l2_buf.flags = V4L2_BUF_FLAG_NO_CACHE_INVALIDATE | V4L2_BUF_FLAG_NO_CACHE_CLEAN; } if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { v4l2_buf.m.planes = &_planes[index * _mplanes_count]; v4l2_buf.length = _mplanes_count; } switch (_memory_type) { case V4L2_MEMORY_DMABUF: { v4l2_buf.length = format.fmt.pix.sizeimage; if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { v4l2_buf.length = _mplanes_count; for (int i=0; i<_mplanes_count; i++) { v4l2_buf.m.planes[i].length = format.fmt.pix.sizeimage; v4l2_buf.m.planes[i].bytesused = format.fmt.pix.sizeimage; } } } break; case V4L2_MEMORY_MMAP: { void *pointer = MAP_FAILED; int map_flags = MAP_SHARED; #ifdef NEED_MAP_32BIT map_flags |= MAP_32BIT; #endif if (io_control (VIDIOC_QUERYBUF, &v4l2_buf) < 0) { XCAM_LOG_ERROR("device(%s) query MMAP buf(%d) failed", XCAM_STR(_name), index); return XCAM_RETURN_ERROR_MEM; } if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { for (int i=0; i<_mplanes_count; i++) { XCAM_LOG_DEBUG ("device(%s) get planar(%d) of buf(%d) length: %d", XCAM_STR (_name), i, index, v4l2_buf.m.planes[i].length); pointer = mmap (0, v4l2_buf.m.planes[i].length, PROT_READ | PROT_WRITE, map_flags, _fd, v4l2_buf.m.planes[i].m.mem_offset); v4l2_buf.m.planes[i].m.userptr = (uintptr_t) pointer; if (pointer == MAP_FAILED) { XCAM_LOG_ERROR("device(%s) mmap planar(%d) of buf(%d) failed", XCAM_STR(_name), i, index); return XCAM_RETURN_ERROR_MEM; } } expbuf_ptr = v4l2_buf.m.planes[0].m.userptr; } else { XCAM_LOG_DEBUG ("device(%s) get buf(%d) length: %d", XCAM_STR (_name), index, v4l2_buf.length); pointer = mmap (0, v4l2_buf.length, PROT_READ | PROT_WRITE, map_flags, _fd, v4l2_buf.m.offset); if (pointer == MAP_FAILED) { XCAM_LOG_ERROR("device(%s) mmap buf(%d) failed", XCAM_STR(_name), index); return XCAM_RETURN_ERROR_MEM; } expbuf_ptr = v4l2_buf.m.userptr = (uintptr_t) pointer; } // export buf dma fd struct v4l2_exportbuffer expbuf; xcam_mem_clear (expbuf); expbuf.type = _buf_type; expbuf.index = index; expbuf.flags = O_CLOEXEC; if (io_control (VIDIOC_EXPBUF, &expbuf) < 0) { XCAM_LOG_ERROR ("device(%s) get dma buf(%d) failed", XCAM_STR (_name), index); return XCAM_RETURN_ERROR_MEM; } else { XCAM_LOG_INFO ("device(%s) get dma buf(%d)-fd: %d", XCAM_STR (_name), index, expbuf.fd); } expbuf_fd = expbuf.fd; } break; case V4L2_MEMORY_USERPTR: { v4l2_buf.length = format.fmt.pix.sizeimage; if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { v4l2_buf.length = _mplanes_count; for (int i=0; i<_mplanes_count; i++) { v4l2_buf.m.planes[i].length = format.fmt.pix.sizeimage; v4l2_buf.m.planes[i].bytesused = format.fmt.pix.sizeimage; } } } break; default: XCAM_ASSERT (false); XCAM_LOG_WARNING ( "device(%s) allocated buffer mem_type(%d) doesn't support", XCAM_STR (_name), _memory_type); return XCAM_RETURN_ERROR_MEM; } buf = new V4l2Buffer (v4l2_buf, _format); if (expbuf_fd != -1) buf->set_expbuf_fd (expbuf_fd); if (expbuf_ptr != 0) buf->set_expbuf_usrptr(expbuf_ptr); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::release_buffer (SmartPtr &buf) { int ret = 0; switch (_memory_type) { case V4L2_MEMORY_DMABUF: { } break; case V4L2_MEMORY_MMAP: { if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { for (int i=0; i<_mplanes_count; i++) { XCAM_LOG_DEBUG("release multi planar(%d) of buffer length: %d", i, buf->get_buf().m.planes[i].length); ret = munmap((void*)buf->get_buf().m.planes[i].m.userptr, buf->get_buf().m.planes[i].length); } ::close(buf->get_expbuf_fd()); } else { XCAM_LOG_DEBUG("release buffer length: %d", buf->get_buf().length); ret = munmap((void*)buf->get_buf().m.userptr, buf->get_buf().length); ::close(buf->get_expbuf_fd()); } if (ret != 0) { XCAM_LOG_ERROR ( "release buffer: munmap failed"); } } break; case V4L2_MEMORY_USERPTR: break; default: XCAM_ASSERT (false); XCAM_LOG_WARNING ( "device(%s) allocated buffer mem_type(%d) doesn't support", XCAM_STR (_name), _memory_type); return XCAM_RETURN_ERROR_MEM; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::init_buffer_pool () { XCamReturn ret = XCAM_RETURN_NO_ERROR; uint32_t i = 0; _buf_pool.clear (); _buf_pool.reserve (_buf_count); for (; i < _buf_count; i++) { SmartPtr new_buf; XCAM_LOG_DEBUG("allocate_buffer index: %d", i); ret = allocate_buffer (new_buf, _format, i); if (ret != XCAM_RETURN_NO_ERROR) { break; } _buf_pool.push_back (new_buf); } for (i = 0; i < _buf_count; i++) { SmartPtr &buf = _buf_pool [i]; struct v4l2_buffer v4l2_buf = buf->get_buf (); XCAM_LOG_DEBUG ("init_buffer_pool device(%s) index:%d, memory:%d, type:%d, length:%d, fd:%d, ptr:%p", XCAM_STR (_name), v4l2_buf.index, v4l2_buf.memory, v4l2_buf.type, v4l2_buf.length, buf->get_expbuf_fd(), buf->get_expbuf_usrptr()); } if (_buf_pool.empty()) { XCAM_LOG_ERROR ("No bufer allocated in device(%s)", XCAM_STR (_name)); return XCAM_RETURN_ERROR_MEM; } if (i != _buf_count) { XCAM_LOG_WARNING ( "device(%s) allocate buffer count:%d failback to %d", XCAM_STR (_name), _buf_count, i); _buf_count = i; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::fini_buffer_pool() { uint32_t i = 0; int32_t tried_time = 0; for (; i < _buf_pool.size(); i++) { release_buffer(_buf_pool [i]); } if (_memory_type == V4L2_MEMORY_MMAP) { struct v4l2_requestbuffers request_buf; xcam_mem_clear (request_buf); request_buf.type = _buf_type; request_buf.count = 0; request_buf.memory = _memory_type; do { if (io_control (VIDIOC_REQBUFS, &request_buf) < 0) { usleep (100); XCAM_LOG_ERROR ("device(%s) failed on VIDIOC_REQBUFS, retry %d < 50times", XCAM_STR (_name), tried_time); //return XCAM_RETURN_ERROR_IOCTL; } else { break; } } while(tried_time++ < 50); } else if (_memory_type == V4L2_MEMORY_DMABUF) { struct v4l2_requestbuffers request_buf; xcam_mem_clear (request_buf); request_buf.type = _buf_type; request_buf.count = 0; request_buf.memory = _memory_type; do { if (io_control (VIDIOC_REQBUFS, &request_buf) < 0) { usleep (100); XCAM_LOG_ERROR ("device(%s) failed on VIDIOC_REQBUFS, retry %d < 50times", XCAM_STR (_name), tried_time); //return XCAM_RETURN_ERROR_IOCTL; } else { break; } } while(tried_time++ < 50); } _buf_pool.clear (); if (_planes) { xcam_free (_planes); _planes = NULL; } return XCAM_RETURN_NO_ERROR; } SmartPtr V4l2Device::get_buffer_by_index (int index) { SmartLock auto_lock(_buf_mutex); SmartPtr &buf = _buf_pool [index]; return buf; } XCamReturn V4l2Device::dequeue_buffer(SmartPtr &buf) { struct v4l2_buffer v4l2_buf; struct v4l2_plane planes[_mplanes_count]; if (!is_activated()) { XCAM_LOG_DEBUG ( "device(%s) dequeue buffer failed since not activated", XCAM_STR (_name)); return XCAM_RETURN_ERROR_PARAM; } xcam_mem_clear (v4l2_buf); v4l2_buf.type = _buf_type; v4l2_buf.memory = _memory_type; if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { memset(planes, 0, sizeof(struct v4l2_plane) * _mplanes_count); v4l2_buf.m.planes = planes; v4l2_buf.length = _mplanes_count; } if (this->io_control (VIDIOC_DQBUF, &v4l2_buf) < 0) { XCAM_LOG_ERROR ("device(%s) fail to dequeue buffer.", XCAM_STR (_name)); return XCAM_RETURN_ERROR_IOCTL; } if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { XCAM_LOG_DEBUG ("device(%s) multi planar dequeue buffer index:%d, length: %dn", XCAM_STR (_name), v4l2_buf.index, v4l2_buf.m.planes[0].length); if (V4L2_MEMORY_DMABUF == _memory_type) { XCAM_LOG_DEBUG ("device(%s) multi planar index:%d, fd: %d", XCAM_STR (_name), v4l2_buf.index, v4l2_buf.m.planes[0].m.fd); } } else { XCAM_LOG_DEBUG ("device(%s) dequeue buffer index:%d, length: %d", XCAM_STR (_name), v4l2_buf.index, v4l2_buf.length); } if (v4l2_buf.index > _buf_count) { XCAM_LOG_ERROR ( "device(%s) dequeue wrong buffer index:%d", XCAM_STR (_name), v4l2_buf.index); return XCAM_RETURN_ERROR_ISP; } SmartLock auto_lock(_buf_mutex); buf = _buf_pool [v4l2_buf.index]; buf->set_timestamp (v4l2_buf.timestamp); buf->set_timecode (v4l2_buf.timecode); buf->set_sequence (v4l2_buf.sequence); if (!V4L2_TYPE_IS_OUTPUT(buf->get_buf ().type)) buf->set_queued(false); if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { buf->set_length (v4l2_buf.m.planes[0].length); } else { buf->set_length (v4l2_buf.length); } _queued_bufcnt--; return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::get_buffer (SmartPtr &buf, int index) const { SmartLock auto_lock(_buf_mutex); if (_buf_pool.size() <= 0) return XCAM_RETURN_ERROR_MEM; if (index != -1 && !(_buf_pool[index]->get_queued())) { buf = _buf_pool[index]; return XCAM_RETURN_NO_ERROR; } uint32_t i; for (i = 0; i < _buf_pool.size(); i++) { if (!(_buf_pool[i]->get_queued())) { buf = _buf_pool[i]; break; } } if (i == _buf_count) return XCAM_RETURN_ERROR_MEM; else return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::return_buffer_to_pool (SmartPtr &buf) { SmartLock auto_lock(_buf_mutex); XCAM_ASSERT (buf.ptr()); buf->reset (); return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::return_buffer (SmartPtr &buf) { SmartLock auto_lock(_buf_mutex); if (V4L2_TYPE_IS_OUTPUT(buf->get_buf ().type) || (buf->get_buf ().type == V4L2_BUF_TYPE_META_OUTPUT)) { XCAM_ASSERT (buf.ptr()); buf->reset (); return XCAM_RETURN_NO_ERROR; } else { if (!_active) buf->reset(); else return queue_buffer (buf, true); } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::queue_buffer (SmartPtr &buf, bool locked) { if (!locked) _buf_mutex.lock(); XCAM_ASSERT (buf.ptr()); buf->reset (); struct v4l2_buffer v4l2_buf = buf->get_buf (); struct v4l2_plane planes[_mplanes_count]; XCAM_ASSERT (v4l2_buf.index < _buf_count); if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == _buf_type || V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == _buf_type) { XCAM_LOG_DEBUG ("device(%s) queue buffer index:%d, memory:%d, type:%d, multiply planar:%d, length:%d, fd:%d, ptr:%p", XCAM_STR (_name), v4l2_buf.index, v4l2_buf.memory, v4l2_buf.type, v4l2_buf.length, v4l2_buf.m.planes[0].length, buf->get_expbuf_fd(), buf->get_expbuf_usrptr()); memset(planes, 0, sizeof(struct v4l2_plane) * _mplanes_count); v4l2_buf.m.planes = planes; planes[0] = buf->get_buf ().m.planes[0]; } else { XCAM_LOG_DEBUG ("device(%s) queue buffer index:%d, memory:%d, type:%d, length:%d, fd:%d", XCAM_STR (_name), v4l2_buf.index, v4l2_buf.memory, v4l2_buf.type, v4l2_buf.length, buf->get_expbuf_fd()); } if (v4l2_buf.type == V4L2_BUF_TYPE_META_OUTPUT) v4l2_buf.bytesused = v4l2_buf.length; if (v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { v4l2_buf.m.planes[0].bytesused = v4l2_buf.m.planes[0].length; if (V4L2_MEMORY_DMABUF == _memory_type) v4l2_buf.m.planes[0].m.fd = buf->get_expbuf_fd(); else if (V4L2_MEMORY_USERPTR == _memory_type) v4l2_buf.m.planes[0].m.userptr = buf->get_expbuf_usrptr(); } _queued_bufcnt++; buf->set_queued(true); if (!locked) _buf_mutex.unlock(); if (io_control (VIDIOC_QBUF, &v4l2_buf) < 0) { XCAM_LOG_ERROR("%s fail to enqueue buffer index:%d.", XCAM_STR(_name), v4l2_buf.index); // restore buf status { if (!locked) _buf_mutex.lock(); /* SmartLock auto_lock(_buf_mutex); */ buf->set_queued(false); _queued_bufcnt--; if (!locked) _buf_mutex.unlock(); } return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } V4l2SubDevice::V4l2SubDevice (const char *name) : V4l2Device (name) { } XCamReturn V4l2Device::subscribe_event (int event) { struct v4l2_event_subscription sub; int ret = 0; XCAM_ASSERT (is_opened()); xcam_mem_clear (sub); sub.type = event; ret = this->io_control (VIDIOC_SUBSCRIBE_EVENT, &sub); if (ret < 0) { XCAM_LOG_DEBUG ("subdev(%s) subscribe event(%d) failed", XCAM_STR(_name), event); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::unsubscribe_event (int event) { struct v4l2_event_subscription sub; int ret = 0; XCAM_ASSERT (is_opened()); xcam_mem_clear (sub); sub.type = event; ret = this->io_control (VIDIOC_UNSUBSCRIBE_EVENT, &sub); if (ret < 0) { XCAM_LOG_DEBUG ("subdev(%s) unsubscribe event(%d) failed", XCAM_STR(_name), event); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2Device::dequeue_event (struct v4l2_event &event) { int ret = 0; XCAM_ASSERT (is_opened()); ret = this->io_control (VIDIOC_DQEVENT, &event); if (ret < 0) { XCAM_LOG_DEBUG ("subdev(%s) dequeue event failed", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2SubDevice::get_selection (int pad, uint32_t target, struct v4l2_subdev_selection &select) { int ret = 0; XCAM_ASSERT (is_opened()); select.pad = pad; select.which = V4L2_SUBDEV_FORMAT_ACTIVE; select.target = target; ret = this->io_control (VIDIOC_SUBDEV_G_SELECTION, &select); if (ret < 0) { XCAM_LOG_DEBUG ("subdev(%s) get selection failed", XCAM_STR(_name)); return XCAM_RETURN_ERROR_IOCTL; } return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2SubDevice::start (bool prepared) { if (!is_opened()) return XCAM_RETURN_ERROR_PARAM; _active = true; return XCAM_RETURN_NO_ERROR; } XCamReturn V4l2SubDevice::stop () { if (_active) _active = false; return XCAM_RETURN_NO_ERROR; } };