{"id":514,"url":"https://patchwork.libcamera.org/api/patches/514/?format=json","web_url":"https://patchwork.libcamera.org/patch/514/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20190205171010.1356-4-jacopo@jmondi.org>","date":"2019-02-05T17:10:10","name":"[libcamera-devel,3/3] libcamera: Add V4L2Subdevice","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"d09148740194c561183fca9f49262abb11f1af24","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/?format=json","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/514/mbox/","series":[{"id":169,"url":"https://patchwork.libcamera.org/api/series/169/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=169","date":"2019-02-05T17:10:07","name":"Add v4l2 subdevice","version":1,"mbox":"https://patchwork.libcamera.org/series/169/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/514/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/514/checks/","tags":{},"headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net\n\t[217.70.183.193])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BF17B60DC6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  5 Feb 2019 18:10:00 +0100 (CET)","from uno.localdomain (d51A4137F.access.telenet.be [81.164.19.127])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 2E14E240011;\n\tTue,  5 Feb 2019 17:10:00 +0000 (UTC)"],"X-Originating-IP":"81.164.19.127","From":"Jacopo Mondi <jacopo@jmondi.org>","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","Content-Transfer-Encoding":"8bit","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":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 05 Feb 2019 17:10:01 -0000"},"content":"Add V4L2Subdevice class that provides an interface to interact with\nV4L2 defined sub-devices.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n src/libcamera/include/v4l2_subdevice.h |  50 +++++\n src/libcamera/meson.build              |   1 +\n src/libcamera/v4l2_subdevice.cpp       | 280 +++++++++++++++++++++++++\n 3 files changed, 331 insertions(+)\n create mode 100644 src/libcamera/include/v4l2_subdevice.h\n create mode 100644 src/libcamera/v4l2_subdevice.cpp","diff":"diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\nnew file mode 100644\nindex 0000000..6f0bb69\n--- /dev/null\n+++ b/src/libcamera/include/v4l2_subdevice.h\n@@ -0,0 +1,50 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_subdevice.h - V4L2 Subdevice\n+ */\n+#ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__\n+#define __LIBCAMERA_V4L2_SUBDEVICE_H__\n+\n+#include <string>\n+\n+namespace libcamera {\n+\n+struct Rectangle;\n+\n+struct V4L2SubdeviceFormat\n+{\n+\tuint32_t mbus_code;\n+\tuint32_t width;\n+\tuint32_t height;\n+};\n+\n+class V4L2Subdevice\n+{\n+public:\n+\texplicit V4L2Subdevice(const MediaEntity *entity);\n+\tV4L2Subdevice(const V4L2Subdevice &) = delete;\n+\tV4L2Subdevice &operator=(const V4L2Subdevice &) = delete;\n+\n+\tint open();\n+\tbool isOpen() const;\n+\tvoid close();\n+\n+\tstd::string deviceNode() const { return deviceNode_; }\n+\n+\tint setCrop(unsigned int pad, Rectangle *rect);\n+\tint setCompose(unsigned int pad, Rectangle *rect);\n+\n+\tint getFormat(unsigned int pad, V4L2SubdeviceFormat *format);\n+\tint setFormat(unsigned int pad, V4L2SubdeviceFormat *format);\n+\n+private:\n+\tint setSelection(struct v4l2_subdev_selection *sel, Rectangle *rect);\n+\tstd::string deviceNode_;\n+\tint fd_;\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_V4L2_SUBDEVICE_H__ */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 3d6df2d..0f5daa2 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -14,6 +14,7 @@ libcamera_sources = files([\n     'stream.cpp',\n     'timer.cpp',\n     'v4l2_device.cpp',\n+    'v4l2_subdevice.cpp',\n ])\n \n libcamera_headers = files([\ndiff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\nnew file mode 100644\nindex 0000000..2cf205d\n--- /dev/null\n+++ b/src/libcamera/v4l2_subdevice.cpp\n@@ -0,0 +1,280 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_subdevice.cpp - V4L2 Subdevice\n+ */\n+\n+#include <fcntl.h>\n+#include <string.h>\n+#include <sys/ioctl.h>\n+#include <unistd.h>\n+\n+#include <linux/v4l2-subdev.h>\n+\n+#include \"geometry.h\"\n+#include \"log.h\"\n+#include \"media_object.h\"\n+#include \"v4l2_subdevice.h\"\n+\n+/**\n+ * \\file v4l2_subdevice.h\n+ * \\brief V4L2 Subdevice API\n+ */\n+\n+namespace libcamera {\n+\n+LOG_DEFINE_CATEGORY(V4L2Subdev)\n+\n+/**\n+ * \\struct V4L2SubdeviceFormat\n+ * \\brief The V4L2 sub-device image format and sizes\n+ *\n+ * This structure describes the format of images when transported between\n+ * separate components connected through a physical bus, such as image sensor\n+ * and image receiver or between components part of the same System-on-Chip that\n+ * realize an image transformation pipeline.\n+ *\n+ * The format of images when transported on physical interconnections is known\n+ * as the \"media bus format\", and it is identified by a resolution and a pixel\n+ * format identification code, known as the \"media bus code\", not to be confused\n+ * with the fourcc code that identify the format of images when stored in memory\n+ * (see V4L2Device::V4L2DeviceFormat).\n+ *\n+ * Media Bus formats supported by the V4L2 APIs are described in Section\n+ * 4.15.3.4.1 of the \"Part I - Video for Linux API\" chapter of the \"Linux Media\n+ * Infrastructure userspace API\", part of the Linux kernel documentation.\n+ *\n+ * Image media bus formats are properties of the subdev pads.  When images are\n+ * transported between two media pads identified by a 0-indexed number, the\n+ * image bus format configured on the two pads should match (according to the\n+ * underlying driver format matching criteria) in order to prepare for a\n+ * successful streaming operation. For a more detailed description of the image\n+ * format negotiation process when performed between V4L2 subdevices, refer to\n+ * Section 4.15.3.1 of the above mentioned Linux kernel documentation section.\n+ */\n+\n+/**\n+ * \\var V4L2SubdeviceFormat::width\n+ * \\brief The image width in pixels\n+ */\n+\n+/**\n+ * \\var V4L2SubdeviceFormat::height\n+ * \\brief The image height in pixels\n+ */\n+\n+/**\n+ * \\var V4L2SubdeviceFormat::mbus_code\n+ * \\brief The image format bus code\n+ */\n+\n+/**\n+ * \\class V4L2Subdevice\n+ * \\brief A V4L2 subdevice as exposed by the Linux kernel\n+ *\n+ * The V4L2Subdevice class provides an API to the \"Sub-device interface\" as\n+ * described in section 4.15 of the \"Linux Media Infrastructure userspace API\"\n+ * chapter of the Linux Kernel documentation.\n+ *\n+ * A V4L2Subdevice is constructed from a MediaEntity instance, using the system\n+ * path of the entity's device node. No API call other than open(), isOpen()\n+ * and close() shall be called on an unopened device instance. Upon destruction\n+ * any device left open will be closed, and any resources released.\n+ */\n+\n+/**\n+ * \\brief Create a V4L2 subdevice from a MediaEntity using its device node\n+ * path\n+ */\n+V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n+\t: deviceNode_(entity->deviceNode()), fd_(-1)\n+{\n+}\n+\n+/**\n+ * \\brief Open a V4L2 subdevice\n+ *\n+ * \\return 0 on success, a negative error code otherwise\n+ */\n+int V4L2Subdevice::open()\n+{\n+\tint ret;\n+\n+\tif (isOpen()) {\n+\t\tLOG(V4L2Subdev, Error) << \"Device already open\";\n+\t\treturn -EBUSY;\n+\t}\n+\n+\tret = ::open(deviceNode_.c_str(), O_RDWR);\n+\tif (ret < 0) {\n+\t\tret = -errno;\n+\t\tLOG(V4L2Subdev, Error)\n+\t\t\t<< \"Failed to open V4L2 subdevice '\" << deviceNode_\n+\t\t\t<< \"': \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\tfd_ = ret;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Check if the subdevice is open\n+ * \\return True if the subdevice is open, false otherwise\n+ */\n+bool V4L2Subdevice::isOpen() const\n+{\n+\treturn fd_ != -1;\n+}\n+\n+/**\n+ * \\brief Close the subdevice, releasing any resources acquired by open()\n+ */\n+void V4L2Subdevice::close()\n+{\n+\tif (!isOpen())\n+\t\treturn;\n+\n+\t::close(fd_);\n+\tfd_ = -1;\n+}\n+\n+/**\n+ * \\fn V4L2Subdevice::deviceNode\n+ * \\brief Retrieve the path of the device node associated with the subdevice\n+ *\n+ * \\return The subdevice's device node system path\n+ */\n+\n+/**\n+ * \\brief Set a crop rectangle on one of the V4L2 subdevice pads\n+ * \\param[in] pad The 0-indexed pad number the format has to be applied to\n+ * \\param[in] rect The rectangle describing crop target area\n+ *\n+ * \\return 0 on success, a negative error code otherwise\n+ */\n+int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect)\n+{\n+\tstruct v4l2_subdev_selection sel = {};\n+\n+\tsel.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n+\tsel.pad = pad;\n+\tsel.target = V4L2_SEL_TGT_CROP;\n+\tsel.flags = 0;\n+\n+\tint ret = setSelection(&sel, rect);\n+\tif (ret) {\n+\t\tret = -errno;\n+\t\tLOG(V4L2Subdev, Error)\n+\t\t\t<< \"Unable to set crop rectangle on pad \" << pad\n+\t\t\t<< \" of \" << deviceNode_ << \": \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Set a composition rectangle on one of the V4L2 subdevice pads\n+ * \\param[in] pad The 0-indexed pad number the format has to be applied to\n+ * \\param[in] rect The rectangle describing the compose target area\n+ *\n+ * \\return 0 on success, a negative error code otherwise\n+ */\n+int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect)\n+{\n+\tstruct v4l2_subdev_selection sel = {};\n+\n+\tsel.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n+\tsel.pad = pad;\n+\tsel.target = V4L2_SEL_TGT_COMPOSE;\n+\tsel.flags = 0;\n+\n+\tint ret = setSelection(&sel, rect);\n+\tif (ret) {\n+\t\tret = -errno;\n+\t\tLOG(V4L2Subdev, Error)\n+\t\t\t<< \"Unable to set compose rectangle on pad \" << pad\n+\t\t\t<< \" of \" << deviceNode_ << \": \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Retrieve the image format set on one of the V4L2 subdevice pads\n+ * \\param[in] pad The 0-indexed pad number the format has to be applied to\n+ * \\param[out] format The image bus format applied on the subdevice's pad\n+ *\n+ * \\return 0 for success, a negative error code otherwise\n+ */\n+int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n+{\n+\tstruct v4l2_subdev_format subdevFmt = {};\n+\tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n+\tsubdevFmt.pad = pad;\n+\n+\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n+\tif (ret) {\n+\t\tret = -errno;\n+\t\tLOG(V4L2Subdev, Error)\n+\t\t\t<< \"Unable to get format on pad \" << pad\n+\t\t\t<< \" of \" << deviceNode_ << \": \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tformat->width = subdevFmt.format.width;\n+\tformat->height = subdevFmt.format.height;\n+\tformat->mbus_code = subdevFmt.format.code;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Set an image format on one of the V4L2 subdevice pads\n+ * \\param[in] pad The 0-indexed pad number the format has to be applied to\n+ * \\param[in] format The image bus format to apply to the subdevice's pad\n+ *\n+ * Apply the requested image format to the desired media pad and return the\n+ * actually applied format parameters, as \\ref V4L2Subdevice::getFormat would\n+ * do.\n+ *\n+ * \\return 0 for success, a negative error code otherwise\n+ */\n+int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n+{\n+\tstruct v4l2_subdev_format subdevFmt = {};\n+\tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n+\tsubdevFmt.pad = pad;\n+\tsubdevFmt.format.width = format->width;\n+\tsubdevFmt.format.height = format->height;\n+\tsubdevFmt.format.code = format->mbus_code;\n+\n+\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n+\tif (ret) {\n+\t\tret = -errno;\n+\t\tLOG(Error) << \"Unable to set format: \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tformat->width = subdevFmt.format.width;\n+\tformat->height = subdevFmt.format.height;\n+\tformat->mbus_code = subdevFmt.format.code;\n+\n+\treturn 0;\n+}\n+\n+int V4L2Subdevice::setSelection(struct v4l2_subdev_selection *sel,\n+\t\t\t\tRectangle *rect)\n+{\n+\tsel->r.left = rect->y;\n+\tsel->r.top = rect->x;\n+\tsel->r.width = rect->w;\n+\tsel->r.height = rect->h;\n+\n+\treturn ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, sel);\n+}\n+\n+} /* namespace libcamera */\n","prefixes":["libcamera-devel","3/3"]}