/* * 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 #include #include #include #include #include "fake_v4l2_device.h" #include "v4l2_buffer_proxy.h" namespace XCam { XCamReturn FakeV4l2Device::open () { 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; } _fd = ::open (_name, O_RDWR); 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 (create_notify_pipe () < 0) { XCAM_LOG_ERROR ("create virtual tx pipe failed"); return XCAM_RETURN_ERROR_PARAM; } return XCAM_RETURN_NO_ERROR; } XCamReturn FakeV4l2Device::close () { if (!is_opened()) return XCAM_RETURN_NO_ERROR; ::close (_fd); _fd = -1; destroy_notify_pipe (); XCAM_LOG_INFO ("device(%s) closed", XCAM_STR (_name)); return XCAM_RETURN_NO_ERROR; } XCamReturn FakeV4l2Device::start () { _active = true; return XCAM_RETURN_NO_ERROR; } XCamReturn FakeV4l2Device::stop () { _active = false; _buf_list.clear(); return XCAM_RETURN_NO_ERROR; } int FakeV4l2Device::create_notify_pipe () { int status = 0; destroy_notify_pipe (); status = pipe(_pipe_fd); if (status < 0) { XCAM_LOG_ERROR("Failed to create virtual tx notify poll pipe: %s", strerror(errno)); goto exit_error; } status = fcntl(_pipe_fd[0], F_SETFL, O_NONBLOCK); if (status < 0) { XCAM_LOG_ERROR("Fail to set event virtual tx notify pipe flag: %s", strerror(errno)); goto exit_error; } status = fcntl(_pipe_fd[1], F_SETFL, O_NONBLOCK); if (status < 0) { XCAM_LOG_ERROR("Fail to set event virtual tx notify pipe flag: %s", strerror(errno)); goto exit_error; } return status; exit_error: destroy_notify_pipe(); return status; } void FakeV4l2Device::destroy_notify_pipe () { if (_pipe_fd[0] != -1 || _pipe_fd[1] != -1) { ::close(_pipe_fd[0]); ::close(_pipe_fd[1]); _pipe_fd[0] = -1; _pipe_fd[1] = -1; } } int FakeV4l2Device::io_control (int cmd, void *arg) { if (_fd <= 0) return -1; if ((int)VIDIOC_DQBUF == cmd) { struct v4l2_buffer *v4l2_buf = (struct v4l2_buffer *)arg; v4l2_buf->index = get_available_buffer_index(); _mutex.lock(); struct rk_aiq_vbuf_info vb_info; if(!_buf_list.empty()) { vb_info = _buf_list.front(); _buf_list.pop_front(); v4l2_buf->m.planes[0].length = vb_info.data_length; v4l2_buf->m.planes[0].bytesused = vb_info.data_length; v4l2_buf->sequence = vb_info.frame_id; v4l2_buf->m.planes[0].m.userptr = (unsigned long)vb_info.data_addr; v4l2_buf->reserved = vb_info.data_fd; gettimeofday(&v4l2_buf->timestamp, NULL); } _mutex.unlock(); } return 0; } XCamReturn FakeV4l2Device::get_format (struct v4l2_format &format) { if (is_activated ()) { format = _format; return XCAM_RETURN_NO_ERROR; } if (!is_opened ()) return XCAM_RETURN_ERROR_IOCTL; #if 0 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; } //TODO format.fmt.pix.width = 3840; format.fmt.pix.height = 2160; format.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG10; format.fmt.pix.bytesperline = 0; format.fmt.pix.sizeimage = 10506240; #endif return XCAM_RETURN_NO_ERROR; } int FakeV4l2Device::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 = _pipe_fd[0]; poll_fds[0].events = (POLLPRI | POLLIN | POLLOUT); 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 (ret > 0) { if (stop_fd != -1) { if (poll_fds[1].revents & (POLLIN | POLLPRI)) { XCAM_LOG_DEBUG ("%s: Poll returning from flush", __FUNCTION__); return POLL_STOP_RET; } } if (poll_fds[0].revents & (POLLIN | POLLPRI)) { char buf[8]; read(_pipe_fd[0], buf, sizeof(buf)); XCAM_LOG_DEBUG ("%s: Poll returning timer pipe", __FUNCTION__); } } return ret; } XCamReturn FakeV4l2Device::dequeue_buffer(SmartPtr &buf) { struct v4l2_buffer v4l2_buf; struct v4l2_plane planes[FMT_NUM_PLANES]; if (!is_activated()) { XCAM_LOG_ERROR ( "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) * FMT_NUM_PLANES); v4l2_buf.m.planes = planes; v4l2_buf.length = FMT_NUM_PLANES; } 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) dequeue 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, v4l2_buf.m.planes[0].m.fd, v4l2_buf.m.planes[0].m.userptr); 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); // if (_use_type != 2) { buf->set_expbuf_usrptr(v4l2_buf.m.planes[0].m.userptr); buf->set_expbuf_fd(v4l2_buf.reserved); // } } else { buf->set_length (v4l2_buf.length); } _queued_bufcnt--; return XCAM_RETURN_NO_ERROR; } uint32_t FakeV4l2Device::get_available_buffer_index () { uint32_t idx = 0; SmartPtr buf; _buf_mutex.lock(); for (; idx<_buf_count; idx++) { buf = _buf_pool[idx]; if (buf->get_queued()) { break; } } _buf_mutex.unlock(); return idx; } void FakeV4l2Device::enqueue_rawbuffer(struct rk_aiq_vbuf_info *vbinfo) { if (vbinfo != NULL) { _mutex.lock(); _buf_list.push_back(*vbinfo); _mutex.unlock(); } } static int icnt = 0; void FakeV4l2Device::on_timer_proc () { if (!_buf_list.empty() && _queued_bufcnt) { if (_pipe_fd[1] != -1) { char buf = 0xf; // random value to write to flush fd. unsigned int size = write(_pipe_fd[1], &buf, sizeof(char)); if (size != sizeof(char)) XCAM_LOG_ERROR("Flush write not completed"); } } } };