From patchwork Tue Feb 5 17:10:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 514 Return-Path: Received: from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net [217.70.183.193]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BF17B60DC6 for ; Tue, 5 Feb 2019 18:10:00 +0100 (CET) X-Originating-IP: 81.164.19.127 Received: from uno.localdomain (d51A4137F.access.telenet.be [81.164.19.127]) (Authenticated sender: jacopo@jmondi.org) by relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 2E14E240011; Tue, 5 Feb 2019 17:10:00 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Tue, 5 Feb 2019 18:10:10 +0100 Message-Id: <20190205171010.1356-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190205171010.1356-1-jacopo@jmondi.org> References: <20190205171010.1356-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/3] libcamera: Add V4L2Subdevice 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: Tue, 05 Feb 2019 17:10:01 -0000 Add V4L2Subdevice class that provides an interface to interact with V4L2 defined sub-devices. Signed-off-by: Jacopo Mondi --- src/libcamera/include/v4l2_subdevice.h | 50 +++++ src/libcamera/meson.build | 1 + src/libcamera/v4l2_subdevice.cpp | 280 +++++++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 src/libcamera/include/v4l2_subdevice.h create mode 100644 src/libcamera/v4l2_subdevice.cpp diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h new file mode 100644 index 0000000..6f0bb69 --- /dev/null +++ b/src/libcamera/include/v4l2_subdevice.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_subdevice.h - V4L2 Subdevice + */ +#ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__ +#define __LIBCAMERA_V4L2_SUBDEVICE_H__ + +#include + +namespace libcamera { + +struct Rectangle; + +struct V4L2SubdeviceFormat +{ + uint32_t mbus_code; + uint32_t width; + uint32_t height; +}; + +class V4L2Subdevice +{ +public: + explicit V4L2Subdevice(const MediaEntity *entity); + V4L2Subdevice(const V4L2Subdevice &) = delete; + V4L2Subdevice &operator=(const V4L2Subdevice &) = delete; + + int open(); + bool isOpen() const; + void close(); + + std::string deviceNode() const { return deviceNode_; } + + int setCrop(unsigned int pad, Rectangle *rect); + int setCompose(unsigned int pad, Rectangle *rect); + + int getFormat(unsigned int pad, V4L2SubdeviceFormat *format); + int setFormat(unsigned int pad, V4L2SubdeviceFormat *format); + +private: + int setSelection(struct v4l2_subdev_selection *sel, Rectangle *rect); + std::string deviceNode_; + int fd_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_V4L2_SUBDEVICE_H__ */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 3d6df2d..0f5daa2 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -14,6 +14,7 @@ libcamera_sources = files([ 'stream.cpp', 'timer.cpp', 'v4l2_device.cpp', + 'v4l2_subdevice.cpp', ]) libcamera_headers = files([ diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp new file mode 100644 index 0000000..2cf205d --- /dev/null +++ b/src/libcamera/v4l2_subdevice.cpp @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_subdevice.cpp - V4L2 Subdevice + */ + +#include +#include +#include +#include + +#include + +#include "geometry.h" +#include "log.h" +#include "media_object.h" +#include "v4l2_subdevice.h" + +/** + * \file v4l2_subdevice.h + * \brief V4L2 Subdevice API + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(V4L2Subdev) + +/** + * \struct V4L2SubdeviceFormat + * \brief The V4L2 sub-device image format and sizes + * + * This structure describes the format of images when transported between + * separate components connected through a physical bus, such as image sensor + * and image receiver or between components part of the same System-on-Chip that + * realize an image transformation pipeline. + * + * The format of images when transported on physical interconnections is known + * as the "media bus format", and it is identified by a resolution and a pixel + * format identification code, known as the "media bus code", not to be confused + * with the fourcc code that identify the format of images when stored in memory + * (see V4L2Device::V4L2DeviceFormat). + * + * Media Bus formats supported by the V4L2 APIs are described in Section + * 4.15.3.4.1 of the "Part I - Video for Linux API" chapter of the "Linux Media + * Infrastructure userspace API", part of the Linux kernel documentation. + * + * Image media bus formats are properties of the subdev pads. When images are + * transported between two media pads identified by a 0-indexed number, the + * image bus format configured on the two pads should match (according to the + * underlying driver format matching criteria) in order to prepare for a + * successful streaming operation. For a more detailed description of the image + * format negotiation process when performed between V4L2 subdevices, refer to + * Section 4.15.3.1 of the above mentioned Linux kernel documentation section. + */ + +/** + * \var V4L2SubdeviceFormat::width + * \brief The image width in pixels + */ + +/** + * \var V4L2SubdeviceFormat::height + * \brief The image height in pixels + */ + +/** + * \var V4L2SubdeviceFormat::mbus_code + * \brief The image format bus code + */ + +/** + * \class V4L2Subdevice + * \brief A V4L2 subdevice as exposed by the Linux kernel + * + * The V4L2Subdevice class provides an API to the "Sub-device interface" as + * described in section 4.15 of the "Linux Media Infrastructure userspace API" + * chapter of the Linux Kernel documentation. + * + * A V4L2Subdevice is constructed from a MediaEntity instance, using the system + * path of the entity's device node. No API call other than open(), isOpen() + * and close() shall be called on an unopened device instance. Upon destruction + * any device left open will be closed, and any resources released. + */ + +/** + * \brief Create a V4L2 subdevice from a MediaEntity using its device node + * path + */ +V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity) + : deviceNode_(entity->deviceNode()), fd_(-1) +{ +} + +/** + * \brief Open a V4L2 subdevice + * + * \return 0 on success, a negative error code otherwise + */ +int V4L2Subdevice::open() +{ + int ret; + + if (isOpen()) { + LOG(V4L2Subdev, Error) << "Device already open"; + return -EBUSY; + } + + ret = ::open(deviceNode_.c_str(), O_RDWR); + if (ret < 0) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Failed to open V4L2 subdevice '" << deviceNode_ + << "': " << strerror(-ret); + return ret; + } + fd_ = ret; + + return 0; +} + +/** + * \brief Check if the subdevice is open + * \return True if the subdevice is open, false otherwise + */ +bool V4L2Subdevice::isOpen() const +{ + return fd_ != -1; +} + +/** + * \brief Close the subdevice, releasing any resources acquired by open() + */ +void V4L2Subdevice::close() +{ + if (!isOpen()) + return; + + ::close(fd_); + fd_ = -1; +} + +/** + * \fn V4L2Subdevice::deviceNode + * \brief Retrieve the path of the device node associated with the subdevice + * + * \return The subdevice's device node system path + */ + +/** + * \brief Set a crop rectangle on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the format has to be applied to + * \param[in] rect The rectangle describing crop target area + * + * \return 0 on success, a negative error code otherwise + */ +int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect) +{ + struct v4l2_subdev_selection sel = {}; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = pad; + sel.target = V4L2_SEL_TGT_CROP; + sel.flags = 0; + + int ret = setSelection(&sel, rect); + if (ret) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to set crop rectangle on pad " << pad + << " of " << deviceNode_ << ": " << strerror(-ret); + return ret; + } + + return 0; +} + +/** + * \brief Set a composition rectangle on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the format has to be applied to + * \param[in] rect The rectangle describing the compose target area + * + * \return 0 on success, a negative error code otherwise + */ +int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect) +{ + struct v4l2_subdev_selection sel = {}; + + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = pad; + sel.target = V4L2_SEL_TGT_COMPOSE; + sel.flags = 0; + + int ret = setSelection(&sel, rect); + if (ret) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to set compose rectangle on pad " << pad + << " of " << deviceNode_ << ": " << strerror(-ret); + return ret; + } + + return 0; +} + +/** + * \brief Retrieve the image format set on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the format has to be applied to + * \param[out] format The image bus format applied on the subdevice's pad + * + * \return 0 for success, a negative error code otherwise + */ +int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format) +{ + struct v4l2_subdev_format subdevFmt = {}; + subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.pad = pad; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt); + if (ret) { + ret = -errno; + LOG(V4L2Subdev, Error) + << "Unable to get format on pad " << pad + << " of " << deviceNode_ << ": " << strerror(-ret); + return ret; + } + + format->width = subdevFmt.format.width; + format->height = subdevFmt.format.height; + format->mbus_code = subdevFmt.format.code; + + return 0; +} + +/** + * \brief Set an image format on one of the V4L2 subdevice pads + * \param[in] pad The 0-indexed pad number the format has to be applied to + * \param[in] format The image bus format to apply to the subdevice's pad + * + * Apply the requested image format to the desired media pad and return the + * actually applied format parameters, as \ref V4L2Subdevice::getFormat would + * do. + * + * \return 0 for success, a negative error code otherwise + */ +int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format) +{ + struct v4l2_subdev_format subdevFmt = {}; + subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdevFmt.pad = pad; + subdevFmt.format.width = format->width; + subdevFmt.format.height = format->height; + subdevFmt.format.code = format->mbus_code; + + int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt); + if (ret) { + ret = -errno; + LOG(Error) << "Unable to set format: " << strerror(-ret); + return ret; + } + + format->width = subdevFmt.format.width; + format->height = subdevFmt.format.height; + format->mbus_code = subdevFmt.format.code; + + return 0; +} + +int V4L2Subdevice::setSelection(struct v4l2_subdev_selection *sel, + Rectangle *rect) +{ + sel->r.left = rect->y; + sel->r.top = rect->x; + sel->r.width = rect->w; + sel->r.height = rect->h; + + return ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, sel); +} + +} /* namespace libcamera */