From patchwork Wed Feb 6 06:07:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 518 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 12EE361022 for ; Wed, 6 Feb 2019 07:08:24 +0100 (CET) Received: from pendragon.ideasonboard.com (d51A4137F.access.telenet.be [81.164.19.127]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id AE58141 for ; Wed, 6 Feb 2019 07:08:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1549433303; bh=wm9SKSfXHld43zjn4G73Pb8itDkjVwXqFj0dqKP0qL0=; h=From:To:Subject:Date:In-Reply-To:References:From; b=kq+hJ6WK2BmEBQBdreUYmgVrFCCCtsXj0LFwwSP5qQNCiqN0JCQ4mUYFjV6PMninb fuhyvx9p1diBhcVS13xdBiJyx1YID+LPLuAJVPOWiCkJwM776p/HtpsERqDsgpCEPG 5SpeMbuLurHxZroVSOUYyxh2/GyndrRC7eQkvLXw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 6 Feb 2019 08:07:54 +0200 Message-Id: <20190206060818.13907-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190206060818.13907-1-laurent.pinchart@ideasonboard.com> References: <20190206060818.13907-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 03/27] libcamera: v4l2_device: Request buffers from the device X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 Feb 2019 06:08:24 -0000 From: Kieran Bingham Provide an exportBuffers() function which allocates buffers with the MMAP method, exports them using the dmabuf API and populates the given BufferPool. Signed-off-by: Kieran Bingham Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart Signed-off-by: Niklas Söderlund --- src/libcamera/include/v4l2_device.h | 17 ++- src/libcamera/v4l2_device.cpp | 158 +++++++++++++++++++++++++++- 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h index 87cde10d2417..510b74f12d05 100644 --- a/src/libcamera/include/v4l2_device.h +++ b/src/libcamera/include/v4l2_device.h @@ -8,11 +8,16 @@ #define __LIBCAMERA_V4L2_DEVICE_H__ #include +#include #include namespace libcamera { +class Buffer; +class BufferPool; +class MediaEntity; + struct V4L2Capability final : v4l2_capability { const char *driver() const { @@ -67,7 +72,6 @@ public: unsigned int planesCount; }; -class MediaEntity; class V4L2Device { public: @@ -89,6 +93,9 @@ public: int getFormat(V4L2DeviceFormat *format); int setFormat(V4L2DeviceFormat *format); + int exportBuffers(unsigned int count, BufferPool *pool); + int releaseBuffers(); + private: int getFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format); @@ -96,10 +103,18 @@ private: int getFormatMultiplane(V4L2DeviceFormat *format); int setFormatMultiplane(V4L2DeviceFormat *format); + int requestBuffers(unsigned int count); + int createPlane(Buffer *buffer, unsigned int plane, + unsigned int length); + std::string deviceNode_; int fd_; V4L2Capability caps_; + enum v4l2_buf_type bufferType_; + enum v4l2_memory memoryType_; + + BufferPool *bufferPool_; }; } /* namespace libcamera */ diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 1823457529f5..2d0a1cb6abbe 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -10,6 +10,9 @@ #include #include #include +#include + +#include #include "log.h" #include "media_object.h" @@ -209,8 +212,14 @@ LOG_DEFINE_CATEGORY(V4L2) * \param deviceNode The file-system path to the video device node */ V4L2Device::V4L2Device(const std::string &deviceNode) - : deviceNode_(deviceNode), fd_(-1) + : deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr) { + /* + * We default to an MMAP based CAPTURE device, however this will be + * updated based upon the device capabilities. + */ + bufferType_ = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + memoryType_ = V4L2_MEMORY_MMAP; } /** @@ -305,6 +314,8 @@ void V4L2Device::close() if (fd_ < 0) return; + releaseBuffers(); + ::close(fd_); fd_ = -1; } @@ -475,4 +486,149 @@ int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format) return 0; } +int V4L2Device::requestBuffers(unsigned int count) +{ + struct v4l2_requestbuffers rb = {}; + int ret; + + rb.count = count; + rb.type = bufferType_; + rb.memory = memoryType_; + + ret = ioctl(fd_, VIDIOC_REQBUFS, &rb); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Unable to request " << count << " buffers: " + << strerror(-ret); + return ret; + } + + LOG(V4L2, Debug) + << deviceNode_ << ":" << rb.count << " buffers requested."; + + return rb.count; +} + +/** + * \brief Request \a count buffers to be allocated from the device and stored in + * the buffer pool provided. + * \param[in] count Number of buffers to allocate + * \param[out] pool BufferPool to populate with buffers + * \return 0 on success or a negative error code otherwise + */ +int V4L2Device::exportBuffers(unsigned int count, BufferPool *pool) +{ + unsigned int allocatedBuffers; + unsigned int i; + int ret; + + memoryType_ = V4L2_MEMORY_MMAP; + + ret = requestBuffers(count); + if (ret < 0) + return ret; + + allocatedBuffers = ret; + if (allocatedBuffers < count) { + LOG(V4L2, Error) << "Not enough buffers provided by V4L2Device"; + requestBuffers(0); + return -ENOMEM; + } + + count = ret; + + /* Map the buffers. */ + for (i = 0; i < count; ++i) { + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {}; + struct v4l2_buffer buf = {}; + struct Buffer &buffer = pool->buffers()[i]; + + buf.index = i; + buf.type = bufferType_; + buf.memory = memoryType_; + buf.length = VIDEO_MAX_PLANES; + buf.m.planes = planes; + + ret = ioctl(fd_, VIDIOC_QUERYBUF, &buf); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Unable to query buffer " << i << ": " + << strerror(-ret); + break; + } + + if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) { + for (unsigned int p = 0; p < buf.length; ++p) { + ret = createPlane(&buffer, p, + buf.m.planes[p].length); + if (ret) + break; + } + } else { + ret = createPlane(&buffer, 0, buf.length); + } + + if (ret) { + LOG(V4L2, Error) << "Failed to create plane"; + break; + } + } + + if (ret) { + requestBuffers(0); + pool->destroyBuffers(); + return ret; + } + + bufferPool_ = pool; + + return 0; +} + +int V4L2Device::createPlane(Buffer *buffer, unsigned int planeIndex, + unsigned int length) +{ + struct v4l2_exportbuffer expbuf = {}; + int ret; + + LOG(V4L2, Debug) + << "Buffer " << buffer->index() + << " plane " << planeIndex + << ": length=" << length; + + expbuf.type = bufferType_; + expbuf.index = buffer->index(); + expbuf.plane = planeIndex; + expbuf.flags = O_RDWR; + + ret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf); + if (ret < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to export buffer: " << strerror(-ret); + return ret; + } + + buffer->planes().emplace_back(); + Plane &plane = buffer->planes().back(); + plane.setDmabuf(expbuf.fd, length); + + return 0; +} + +/** + * \brief Release all internally allocated buffers + */ +int V4L2Device::releaseBuffers() +{ + LOG(V4L2, Debug) << "Releasing bufferPool"; + + requestBuffers(0); + bufferPool_ = nullptr; + + return 0; +} + } /* namespace libcamera */