[{"id":1933,"web_url":"https://patchwork.libcamera.org/comment/1933/","msgid":"<20190619122029.GE5045@pendragon.ideasonboard.com>","date":"2019-06-19T12:20:29","subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nThank you for the patch.\n\nOn Wed, Jun 19, 2019 at 01:05:46PM +0200, Jacopo Mondi wrote:\n> The V4L2 devices and subdevices share a few common operations,like\n> opening and closing a device node, and perform IOCTLs on the device.\n> \n> With the forthcoming introduction of support for V4L2 controls, the\n> quantity of shared code will increase, as the control support\n> implementation is identical for the two derived classes.\n> \n> To maximize code re-use and avoid duplications, provide a V4L2Device\n> base class which groups the common operations and members.\n> \n> The newly introduced base class provides methods to open/close a device\n> node, access the file descriptor, and perform IOCTLs on the device.\n> \n> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  src/libcamera/include/v4l2_device.h      |  43 +++++++\n>  src/libcamera/include/v4l2_subdevice.h   |   9 +-\n>  src/libcamera/include/v4l2_videodevice.h |  10 +-\n>  src/libcamera/meson.build                |   2 +\n>  src/libcamera/v4l2_device.cpp            | 144 +++++++++++++++++++++++\n>  src/libcamera/v4l2_subdevice.cpp         |  84 +++----------\n>  src/libcamera/v4l2_videodevice.cpp       | 103 +++++-----------\n>  7 files changed, 239 insertions(+), 156 deletions(-)\n>  create mode 100644 src/libcamera/include/v4l2_device.h\n>  create mode 100644 src/libcamera/v4l2_device.cpp\n> \n> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> new file mode 100644\n> index 000000000000..2c26f3eae2b4\n> --- /dev/null\n> +++ b/src/libcamera/include/v4l2_device.h\n> @@ -0,0 +1,43 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_device.h - Common base for V4L2 video devices and subdevices\n> + */\n> +#ifndef __LIBCAMERA_V4L2_DEVICE_H__\n> +#define __LIBCAMERA_V4L2_DEVICE_H__\n> +\n> +#include <string>\n> +\n> +#include \"log.h\"\n> +\n> +namespace libcamera {\n> +\n> +class V4L2Device : protected Loggable\n> +{\n> +public:\n> +\tvoid close();\n> +\tbool isOpen() const { return fd_ != -1; }\n> +\tconst std::string &deviceNode() const { return deviceNode_; }\n> +\n> +protected:\n> +\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> +\t~V4L2Device() {}\n> +\n> +\tint fd() { return fd_; }\n> +\n> +\tint open(unsigned int flags);\n> +\n> +\tint ioctl(unsigned long request, void *argp);\n> +\n> +\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n\nLet's try not to inline methods that are more complex than just\nreturning a member of the class.\n\nI didn't notice in my previous review that V4L2Subdevice was using the\nentity name as a prefix. I'm afraid my comment about moving logPrefix()\nto V4L2Device was a bad one :-/ However, V4L2Device should still inherit\nfrom Loggable. It will thus have a pure virtual logPrefix() inherited\nfrom Loggable, requiring the derived classes to implement that method,\nbut allowing LOG() statements in V4L2Device to use the log prefix.\n\nHere's the diff I came up with, it can be squashed with this patch if\nyou agree on the comments.\n\ndiff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\nindex 2c26f3eae2b4..8c83adab4d43 100644\n--- a/src/libcamera/include/v4l2_device.h\n+++ b/src/libcamera/include/v4l2_device.h\n@@ -21,7 +21,7 @@ public:\n \tconst std::string &deviceNode() const { return deviceNode_; }\n\n protected:\n-\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n+\tV4L2Device(const std::string &deviceNode);\n \t~V4L2Device() {}\n\n \tint fd() { return fd_; }\n@@ -30,11 +30,8 @@ protected:\n\n \tint ioctl(unsigned long request, void *argp);\n\n-\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n-\n private:\n \tstd::string deviceNode_;\n-\tstd::string logTag_;\n \tint fd_;\n };\n\ndiff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\nindex b1da4a87c553..9c077674f997 100644\n--- a/src/libcamera/include/v4l2_subdevice.h\n+++ b/src/libcamera/include/v4l2_subdevice.h\n@@ -52,6 +52,9 @@ public:\n \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n \t\t\t\t\t     const std::string &entity);\n\n+protected:\n+\tstd::string logPrefix() const;\n+\n private:\n \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\ndiff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\nindex e73dec13f847..734b34f1dc53 100644\n--- a/src/libcamera/include/v4l2_videodevice.h\n+++ b/src/libcamera/include/v4l2_videodevice.h\n@@ -147,6 +147,9 @@ public:\n \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n \t\t\t\t\t       const std::string &entity);\n\n+protected:\n+\tstd::string logPrefix() const;\n+\n private:\n \tint getFormatMeta(V4L2DeviceFormat *format);\n \tint setFormatMeta(V4L2DeviceFormat *format);\ndiff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\nindex eeed0a70fb0e..1f113780ba45 100644\n--- a/src/libcamera/v4l2_device.cpp\n+++ b/src/libcamera/v4l2_device.cpp\n@@ -68,17 +68,15 @@ void V4L2Device::close()\n /**\n  * \\brief Construct a V4L2Device\n  * \\param[in] deviceNode The device node filesystem path\n- * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n- * the device in the log output\n  *\n  * The V4L2Device constructor is protected and can only be accessed by the\n  * V4L2VideoDevice and V4L2Subdevice derived classes.\n  *\n  * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n  * at open() time, and the \\a logTag to prefix log messages with.\n  */\n-V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n-\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n+V4L2Device::V4L2Device(const std::string &deviceNode)\n+\t: deviceNode_(deviceNode), fd_(-1)\n {\n }\n\n@@ -99,6 +94,7 @@ V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n  */\n int V4L2Device::open(unsigned int flags)\n {\n+\tLOG(V4L2, Info) << \"Opening device\";\n \tif (isOpen()) {\n \t\tLOG(V4L2, Error) << \"Device already open\";\n \t\treturn -EBUSY;\n@@ -135,10 +131,4 @@ int V4L2Device::ioctl(unsigned long request, void *argp)\n \treturn 0;\n }\n\n-/**\n- * \\fn V4L2Device::logPrefix()\n- * \\brief Retrieve the tag to prefix log messages with\n- * \\return The tag to prefix log messages with\n- */\n-\n } /* namespace libcamera */\ndiff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\nindex d0e1d717b26c..a188298de34c 100644\n--- a/src/libcamera/v4l2_subdevice.cpp\n+++ b/src/libcamera/v4l2_subdevice.cpp\n@@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n  * path\n  */\n V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n-\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n+\t: V4L2Device(entity->deviceNode()), entity_(entity)\n {\n }\n\n@@ -265,6 +265,11 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n \treturn new V4L2Subdevice(mediaEntity);\n }\n\n+std::string V4L2Subdevice::logPrefix() const\n+{\n+\treturn \"'\" + entity_->name() + \"'\";\n+}\n+\n std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n {\n \tstd::vector<unsigned int> codes;\ndiff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\nindex 403e0b2e0653..2de3751f467e 100644\n--- a/src/libcamera/v4l2_videodevice.cpp\n+++ b/src/libcamera/v4l2_videodevice.cpp\n@@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n  * \\param[in] deviceNode The file-system path to the video device node\n  */\n V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n-\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n+\t: V4L2Device(deviceNode), bufferPool_(nullptr),\n \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n {\n \t/*\n@@ -389,6 +389,11 @@ void V4L2VideoDevice::close()\n  * \\return The string containing the device location\n  */\n\n+std::string V4L2VideoDevice::logPrefix() const\n+{\n+\treturn deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n+}\n+\n /**\n  * \\brief Retrieve the image format set on the V4L2 device\n  * \\param[out] format The image format applied on the device\n\n> +\n> +private:\n> +\tstd::string deviceNode_;\n> +\tstd::string logTag_;\n> +\tint fd_;\n> +};\n> +\n> +} /* namespace libcamera */\n> +\n> +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */\n> diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> index 9afd28b6db02..b1da4a87c553 100644\n> --- a/src/libcamera/include/v4l2_subdevice.h\n> +++ b/src/libcamera/include/v4l2_subdevice.h\n> @@ -16,6 +16,7 @@\n>  #include \"formats.h\"\n>  #include \"log.h\"\n>  #include \"media_object.h\"\n> +#include \"v4l2_device.h\"\n>  \n>  namespace libcamera {\n>  \n> @@ -28,7 +29,7 @@ struct V4L2SubdeviceFormat {\n>  \tconst std::string toString() const;\n>  };\n>  \n> -class V4L2Subdevice : protected Loggable\n> +class V4L2Subdevice : public V4L2Device\n>  {\n>  public:\n>  \texplicit V4L2Subdevice(const MediaEntity *entity);\n> @@ -37,8 +38,6 @@ public:\n>  \t~V4L2Subdevice();\n>  \n>  \tint open();\n> -\tbool isOpen() const;\n> -\tvoid close();\n>  \n>  \tconst MediaEntity *entity() const { return entity_; }\n>  \n> @@ -53,9 +52,6 @@ public:\n>  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n>  \t\t\t\t\t     const std::string &entity);\n>  \n> -protected:\n> -\tstd::string logPrefix() const;\n> -\n>  private:\n>  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n>  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> @@ -65,7 +61,6 @@ private:\n>  \t\t\t Rectangle *rect);\n>  \n>  \tconst MediaEntity *entity_;\n> -\tint fd_;\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> index 895658805b47..e73dec13f847 100644\n> --- a/src/libcamera/include/v4l2_videodevice.h\n> +++ b/src/libcamera/include/v4l2_videodevice.h\n> @@ -18,6 +18,7 @@\n>  \n>  #include \"formats.h\"\n>  #include \"log.h\"\n> +#include \"v4l2_device.h\"\n>  \n>  namespace libcamera {\n>  \n> @@ -112,7 +113,7 @@ public:\n>  \tconst std::string toString() const;\n>  };\n>  \n> -class V4L2VideoDevice : protected Loggable\n> +class V4L2VideoDevice : public V4L2Device\n>  {\n>  public:\n>  \texplicit V4L2VideoDevice(const std::string &deviceNode);\n> @@ -123,13 +124,11 @@ public:\n>  \tV4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;\n>  \n>  \tint open();\n> -\tbool isOpen() const;\n>  \tvoid close();\n>  \n>  \tconst char *driverName() const { return caps_.driver(); }\n>  \tconst char *deviceName() const { return caps_.card(); }\n>  \tconst char *busName() const { return caps_.bus_info(); }\n> -\tconst std::string &deviceNode() const { return deviceNode_; }\n>  \n>  \tint getFormat(V4L2DeviceFormat *format);\n>  \tint setFormat(V4L2DeviceFormat *format);\n> @@ -148,9 +147,6 @@ public:\n>  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n>  \t\t\t\t\t       const std::string &entity);\n>  \n> -protected:\n> -\tstd::string logPrefix() const;\n> -\n>  private:\n>  \tint getFormatMeta(V4L2DeviceFormat *format);\n>  \tint setFormatMeta(V4L2DeviceFormat *format);\n> @@ -171,8 +167,6 @@ private:\n>  \tBuffer *dequeueBuffer();\n>  \tvoid bufferAvailable(EventNotifier *notifier);\n>  \n> -\tstd::string deviceNode_;\n> -\tint fd_;\n>  \tV4L2Capability caps_;\n>  \n>  \tenum v4l2_buf_type bufferType_;\n> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> index 15ab53b1abbe..f26ad5b2dc57 100644\n> --- a/src/libcamera/meson.build\n> +++ b/src/libcamera/meson.build\n> @@ -23,6 +23,7 @@ libcamera_sources = files([\n>      'stream.cpp',\n>      'timer.cpp',\n>      'utils.cpp',\n> +    'v4l2_device.cpp',\n>      'v4l2_subdevice.cpp',\n>      'v4l2_videodevice.cpp',\n>  ])\n> @@ -41,6 +42,7 @@ libcamera_headers = files([\n>      'include/media_object.h',\n>      'include/pipeline_handler.h',\n>      'include/utils.h',\n> +    'include/v4l2_device.h',\n>      'include/v4l2_subdevice.h',\n>      'include/v4l2_videodevice.h',\n>  ])\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> new file mode 100644\n> index 000000000000..eeed0a70fb0e\n> --- /dev/null\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -0,0 +1,144 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_device.cpp - Common base for V4L2 video devices and subdevices\n> + */\n> +\n> +#include \"v4l2_device.h\"\n> +\n> +#include <fcntl.h>\n> +#include <string.h>\n> +#include <sys/ioctl.h>\n> +#include <unistd.h>\n> +\n> +#include \"log.h\"\n> +\n> +/**\n> + * \\file v4l2_device.h\n> + * \\brief Common base for V4L2 devices and subdevices\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(V4L2)\n> +\n> +/**\n> + * \\class V4L2Device\n> + * \\brief Base class for V4L2VideoDevice and V4L2Subdevice\n> + *\n> + * The V4L2Device class groups together the methods and fields common to\n> + * both the V4L2VideoDevice and V4L2Subdevice classes, and provides a base\n> + * class whith methods to open and close the device node associated with the\n> + * device and to perform IOCTL system calls on it.\n> + *\n> + * The V4L2Device class cannot be instantiated directly, as its constructor\n> + * is protected. Users should instead create instances of one the derived\n> + * classes to model either a V4L2 video device or a V4L2 subdevice.\n> + */\n> +\n> +/**\n> + * \\brief Close the device node\n> + *\n> + * Reset the file descriptor to -1\n> + */\n> +void V4L2Device::close()\n> +{\n> +\tif (!isOpen())\n> +\t\treturn;\n> +\n> +\tif (::close(fd_) < 0)\n> +\t\tLOG(V4L2, Error) << \"Failed to close V4L2 device: \"\n> +\t\t\t\t << strerror(errno);\n> +\tfd_ = -1;\n> +}\n> +\n> +/**\n> + * \\fn V4L2Device::isOpen()\n> + * \\brief Check if the V4L2 device node is open\n> + * \\return True if the V4L2 device node is open, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn V4L2Device::deviceNode()\n> + * \\brief Retrieve the device node path\n> + * \\return The device node path\n> + */\n> +\n> +/**\n> + * \\brief Construct a V4L2Device\n> + * \\param[in] deviceNode The device node filesystem path\n> + * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> + * the device in the log output\n> + *\n> + * The V4L2Device constructor is protected and can only be accessed by the\n> + * V4L2VideoDevice and V4L2Subdevice derived classes.\n\nI would drop this sentence.\n\n> + *\n> + * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> + * at open() time, and the \\a logTag to prefix log messages with.\n> + */\n> +V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> +\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> +{\n> +}\n> +\n> +/**\n> + * \\fn V4L2Device::fd()\n> + * \\brief Retrieve the V4L2 device file descriptor\n> + * \\return The V4L2 device file descriptor, -1 if the device node is not open\n> + */\n> +\n> +/**\n> + * \\brief Open a V4L2 device node\n> + * \\param[in] flags Access mode flags\n> + *\n> + * Open the device node path with the provided access mode \\a flags and\n> + * initialize the file descriptor, which was initially set to -1.\n> + *\n> + * \\return 0 on success or a negative error code otherwise\n> + */\n> +int V4L2Device::open(unsigned int flags)\n> +{\n> +\tif (isOpen()) {\n> +\t\tLOG(V4L2, Error) << \"Device already open\";\n> +\t\treturn -EBUSY;\n> +\t}\n> +\n> +\tint ret = ::open(deviceNode_.c_str(), flags);\n> +\tif (ret < 0) {\n> +\t\tret = -errno;\n> +\t\tLOG(V4L2, Error) << \"Failed to open V4L2 device: \"\n> +\t\t\t\t << strerror(-ret);\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tfd_ = ret;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Perform an IOCTL system call on the device node\n> + * \\param[in] request The IOCTL request code\n> + * \\param[in] argp A pointer to the IOCTL argument\n> + * \\return 0 on success or a negative error code otherwise\n> + */\n> +int V4L2Device::ioctl(unsigned long request, void *argp)\n> +{\n> +\t/*\n> +\t * Printing out an error message is usually better performed\n> +\t * in the caller, which can provide more context.\n> +\t */\n> +\tif (::ioctl(fd_, request, argp) < 0)\n> +\t\treturn -errno;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\fn V4L2Device::logPrefix()\n> + * \\brief Retrieve the tag to prefix log messages with\n> + * \\return The tag to prefix log messages with\n> + */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> index 48f1fcb30ed4..d0e1d717b26c 100644\n> --- a/src/libcamera/v4l2_subdevice.cpp\n> +++ b/src/libcamera/v4l2_subdevice.cpp\n> @@ -29,7 +29,7 @@\n>  \n>  namespace libcamera {\n>  \n> -LOG_DEFINE_CATEGORY(V4L2Subdev)\n> +LOG_DECLARE_CATEGORY(V4L2)\n>  \n>  /**\n>   * \\struct V4L2SubdeviceFormat\n> @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n>   * path\n>   */\n>  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> -\t: entity_(entity), fd_(-1)\n> +\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n>  {\n>  }\n>  \n> @@ -117,45 +117,7 @@ V4L2Subdevice::~V4L2Subdevice()\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(entity_->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 '\"\n> -\t\t\t<< entity_->deviceNode() << \"': \" << 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> +\treturn V4L2Device::open(O_RDWR);\n>  }\n>  \n>  /**\n> @@ -200,7 +162,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n>  \tImageFormats formats;\n>  \n>  \tif (pad >= entity_->pads().size()) {\n> -\t\tLOG(V4L2Subdev, Error) << \"Invalid pad: \" << pad;\n> +\t\tLOG(V4L2, Error) << \"Invalid pad: \" << pad;\n>  \t\treturn {};\n>  \t}\n>  \n> @@ -210,7 +172,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n>  \t\t\treturn {};\n>  \n>  \t\tif (formats.addFormat(code, sizes)) {\n> -\t\t\tLOG(V4L2Subdev, Error)\n> +\t\t\tLOG(V4L2, Error)\n>  \t\t\t\t<< \"Could not add sizes for media bus code \"\n>  \t\t\t\t<< code << \" on pad \" << pad;\n>  \t\t\treturn {};\n> @@ -232,10 +194,9 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n>  \tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n>  \tsubdevFmt.pad = pad;\n>  \n> -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> +\tint ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n>  \tif (ret) {\n> -\t\tret = -errno;\n> -\t\tLOG(V4L2Subdev, Error)\n> +\t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to get format on pad \" << pad\n>  \t\t\t<< \": \" << strerror(-ret);\n>  \t\treturn ret;\n> @@ -268,10 +229,9 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n>  \tsubdevFmt.format.height = format->size.height;\n>  \tsubdevFmt.format.code = format->mbus_code;\n>  \n> -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> +\tint ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n>  \tif (ret) {\n> -\t\tret = -errno;\n> -\t\tLOG(V4L2Subdev, Error)\n> +\t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to set format on pad \" << pad\n>  \t\t\t<< \": \" << strerror(-ret);\n>  \t\treturn ret;\n> @@ -305,11 +265,6 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n>  \treturn new V4L2Subdevice(mediaEntity);\n>  }\n>  \n> -std::string V4L2Subdevice::logPrefix() const\n> -{\n> -\treturn \"'\" + entity_->name() + \"'\";\n> -}\n> -\n>  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n>  {\n>  \tstd::vector<unsigned int> codes;\n> @@ -321,18 +276,17 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n>  \t\tmbusEnum.index = index;\n>  \t\tmbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n>  \n> -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n>  \t\tif (ret)\n>  \t\t\tbreak;\n>  \n>  \t\tcodes.push_back(mbusEnum.code);\n>  \t}\n>  \n> -\tif (ret && errno != EINVAL) {\n> -\t\tret = errno;\n> -\t\tLOG(V4L2Subdev, Error)\n> +\tif (ret < 0 && ret != -EINVAL) {\n> +\t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to enumerate formats on pad \" << pad\n> -\t\t\t<< \": \" << strerror(ret);\n> +\t\t\t<< \": \" << strerror(-ret);\n>  \t\treturn {};\n>  \t}\n>  \n> @@ -352,7 +306,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n>  \t\tsizeEnum.code = code;\n>  \t\tsizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n>  \n> -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n>  \t\tif (ret)\n>  \t\t\tbreak;\n>  \n> @@ -360,9 +314,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n>  \t\t\t\t   sizeEnum.max_width, sizeEnum.max_height);\n>  \t}\n>  \n> -\tif (ret && (errno != EINVAL && errno != ENOTTY)) {\n> -\t\tret = -errno;\n> -\t\tLOG(V4L2Subdev, Error)\n> +\tif (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {\n> +\t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to enumerate sizes on pad \" << pad\n>  \t\t\t<< \": \" << strerror(-ret);\n>  \t\treturn {};\n> @@ -386,10 +339,9 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,\n>  \tsel.r.width = rect->w;\n>  \tsel.r.height = rect->h;\n>  \n> -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);\n> +\tint ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n> -\t\tLOG(V4L2Subdev, Error)\n> +\t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to set rectangle \" << target << \" on pad \"\n>  \t\t\t<< pad << \": \" << strerror(-ret);\n>  \t\treturn ret;\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 0e70498c8bb1..403e0b2e0653 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -30,7 +30,7 @@\n>   */\n>  namespace libcamera {\n>  \n> -LOG_DEFINE_CATEGORY(V4L2)\n> +LOG_DECLARE_CATEGORY(V4L2)\n>  \n>  /**\n>   * \\struct V4L2Capability\n> @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n>   * \\param[in] deviceNode The file-system path to the video device node\n>   */\n>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> -\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n> +\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n>  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n>  {\n>  \t/*\n> @@ -305,23 +305,12 @@ int V4L2VideoDevice::open()\n>  {\n>  \tint ret;\n>  \n> -\tif (isOpen()) {\n> -\t\tLOG(V4L2, Error) << \"Device already open\";\n> -\t\treturn -EBUSY;\n> -\t}\n> -\n> -\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n> -\tif (ret < 0) {\n> -\t\tret = -errno;\n> -\t\tLOG(V4L2, Error)\n> -\t\t\t<< \"Failed to open V4L2 device: \" << strerror(-ret);\n> +\tret = V4L2Device::open(O_RDWR | O_NONBLOCK);\n> +\tif (ret < 0)\n>  \t\treturn ret;\n> -\t}\n> -\tfd_ = ret;\n>  \n> -\tret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);\n> +\tret = ioctl(VIDIOC_QUERYCAP, &caps_);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to query device capabilities: \"\n>  \t\t\t<< strerror(-ret);\n> @@ -342,20 +331,20 @@ int V4L2VideoDevice::open()\n>  \t * (POLLIN), and write notifications for OUTPUT devices (POLLOUT).\n>  \t */\n>  \tif (caps_.isVideoCapture()) {\n> -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n>  \t\tbufferType_ = caps_.isMultiplanar()\n>  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE\n>  \t\t\t    : V4L2_BUF_TYPE_VIDEO_CAPTURE;\n>  \t} else if (caps_.isVideoOutput()) {\n> -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n>  \t\tbufferType_ = caps_.isMultiplanar()\n>  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n>  \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n>  \t} else if (caps_.isMetaCapture()) {\n> -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n>  \t\tbufferType_ = V4L2_BUF_TYPE_META_CAPTURE;\n>  \t} else if (caps_.isMetaOutput()) {\n> -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n>  \t\tbufferType_ = V4L2_BUF_TYPE_META_OUTPUT;\n>  \t} else {\n>  \t\tLOG(V4L2, Error) << \"Device is not a supported type\";\n> @@ -368,28 +357,18 @@ int V4L2VideoDevice::open()\n>  \treturn 0;\n>  }\n>  \n> -/**\n> - * \\brief Check if device is successfully opened\n> - * \\return True if the device is open, false otherwise\n> - */\n> -bool V4L2VideoDevice::isOpen() const\n> -{\n> -\treturn fd_ != -1;\n> -}\n> -\n>  /**\n>   * \\brief Close the device, releasing any resources acquired by open()\n>   */\n>  void V4L2VideoDevice::close()\n>  {\n> -\tif (fd_ < 0)\n> +\tif (!isOpen())\n>  \t\treturn;\n>  \n>  \treleaseBuffers();\n>  \tdelete fdEvent_;\n>  \n> -\t::close(fd_);\n> -\tfd_ = -1;\n> +\tV4L2Device::close();\n>  }\n>  \n>  /**\n> @@ -410,17 +389,6 @@ void V4L2VideoDevice::close()\n>   * \\return The string containing the device location\n>   */\n>  \n> -/**\n> - * \\fn V4L2VideoDevice::deviceNode()\n> - * \\brief Retrieve the video device node path\n> - * \\return The video device device node path\n> - */\n> -\n> -std::string V4L2VideoDevice::logPrefix() const\n> -{\n> -\treturn deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> -}\n> -\n>  /**\n>   * \\brief Retrieve the image format set on the V4L2 device\n>   * \\param[out] format The image format applied on the device\n> @@ -462,9 +430,8 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)\n>  \tint ret;\n>  \n>  \tv4l2Format.type = bufferType_;\n> -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n>  \tif (ret) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n> @@ -488,9 +455,8 @@ int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)\n>  \tv4l2Format.type = bufferType_;\n>  \tpix->dataformat = format->fourcc;\n>  \tpix->buffersize = format->planes[0].size;\n> -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n>  \tif (ret) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n> @@ -516,9 +482,8 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)\n>  \tint ret;\n>  \n>  \tv4l2Format.type = bufferType_;\n> -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n>  \tif (ret) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n> @@ -554,9 +519,8 @@ int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)\n>  \t\tpix->plane_fmt[i].sizeimage = format->planes[i].size;\n>  \t}\n>  \n> -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n>  \tif (ret) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n> @@ -584,9 +548,8 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)\n>  \tint ret;\n>  \n>  \tv4l2Format.type = bufferType_;\n> -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n>  \tif (ret) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n> @@ -613,9 +576,8 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)\n>  \tpix->pixelformat = format->fourcc;\n>  \tpix->bytesperline = format->planes[0].bpl;\n>  \tpix->field = V4L2_FIELD_NONE;\n> -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n>  \tif (ret) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n>  \t\treturn ret;\n>  \t}\n> @@ -670,9 +632,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n>  \trb.type = bufferType_;\n>  \trb.memory = memoryType_;\n>  \n> -\tret = ioctl(fd_, VIDIOC_REQBUFS, &rb);\n> +\tret = ioctl(VIDIOC_REQBUFS, &rb);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to request \" << count << \" buffers: \"\n>  \t\t\t<< strerror(-ret);\n> @@ -722,9 +683,8 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n>  \t\tbuf.length = VIDEO_MAX_PLANES;\n>  \t\tbuf.m.planes = planes;\n>  \n> -\t\tret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);\n> +\t\tret = ioctl(VIDIOC_QUERYBUF, &buf);\n>  \t\tif (ret < 0) {\n> -\t\t\tret = -errno;\n>  \t\t\tLOG(V4L2, Error)\n>  \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n>  \t\t\t\t<< strerror(-ret);\n> @@ -775,9 +735,8 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex,\n>  \texpbuf.plane = planeIndex;\n>  \texpbuf.flags = O_RDWR;\n>  \n> -\tret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);\n> +\tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to export buffer: \" << strerror(-ret);\n>  \t\treturn ret;\n> @@ -801,15 +760,14 @@ std::vector<unsigned int> V4L2VideoDevice::enumPixelformats()\n>  \t\tpixelformatEnum.index = index;\n>  \t\tpixelformatEnum.type = bufferType_;\n>  \n> -\t\tret = ioctl(fd_, VIDIOC_ENUM_FMT, &pixelformatEnum);\n> +\t\tret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum);\n>  \t\tif (ret)\n>  \t\t\tbreak;\n>  \n>  \t\tformats.push_back(pixelformatEnum.pixelformat);\n>  \t}\n>  \n> -\tif (ret && errno != EINVAL) {\n> -\t\tret = -errno;\n> +\tif (ret && ret != -EINVAL) {\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to enumerate pixel formats: \"\n>  \t\t\t<< strerror(-ret);\n> @@ -829,7 +787,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n>  \t\tframeSize.index = index;\n>  \t\tframeSize.pixel_format = pixelFormat;\n>  \n> -\t\tret = ioctl(fd_, VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> +\t\tret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize);\n>  \t\tif (ret)\n>  \t\t\tbreak;\n>  \n> @@ -867,8 +825,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n>  \t\t}\n>  \t}\n>  \n> -\tif (ret && errno != EINVAL) {\n> -\t\tret = -errno;\n> +\tif (ret && ret != -EINVAL) {\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Unable to enumerate frame sizes: \"\n>  \t\t\t<< strerror(-ret);\n> @@ -969,9 +926,8 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n>  \n>  \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n>  \n> -\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n> +\tret = ioctl(VIDIOC_QBUF, &buf);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n>  \t\t\t<< strerror(-ret);\n> @@ -1006,9 +962,8 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n>  \t\tbuf.m.planes = planes;\n>  \t}\n>  \n> -\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n> +\tret = ioctl(VIDIOC_DQBUF, &buf);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n>  \t\treturn nullptr;\n> @@ -1066,9 +1021,8 @@ int V4L2VideoDevice::streamOn()\n>  {\n>  \tint ret;\n>  \n> -\tret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);\n> +\tret = ioctl(VIDIOC_STREAMON, &bufferType_);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to start streaming: \" << strerror(-ret);\n>  \t\treturn ret;\n> @@ -1089,9 +1043,8 @@ int V4L2VideoDevice::streamOff()\n>  {\n>  \tint ret;\n>  \n> -\tret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);\n> +\tret = ioctl(VIDIOC_STREAMOFF, &bufferType_);\n>  \tif (ret < 0) {\n> -\t\tret = -errno;\n>  \t\tLOG(V4L2, Error)\n>  \t\t\t<< \"Failed to stop streaming: \" << strerror(-ret);\n>  \t\treturn ret;","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C17DB61A15\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Jun 2019 14:20:47 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 24A5A333;\n\tWed, 19 Jun 2019 14:20:47 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1560946847;\n\tbh=DK8gmWJE7otPicwES0qKYz20Zj9VUuFqAe0ym4nnUhY=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=XJVmFU7yc5g+qdNmzhaqXwCaAtmosg34F4pFFgAjw5wuE/YxUBK6FbuF6+Krqks/r\n\ttJb6pJs/b7yKPDYB5aYUWWWb69WIdsueSH3zBUxtRjwq3W6U4c2SfjsXn4+FX2T1kp\n\tWYaO8b9cm7O/OnPr0vw2hqmByMOSxTh8deAIxO9w=","Date":"Wed, 19 Jun 2019 15:20:29 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190619122029.GE5045@pendragon.ideasonboard.com>","References":"<20190619110548.20742-1-jacopo@jmondi.org>\n\t<20190619110548.20742-3-jacopo@jmondi.org>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20190619110548.20742-3-jacopo@jmondi.org>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","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":"Wed, 19 Jun 2019 12:20:47 -0000"}},{"id":1934,"web_url":"https://patchwork.libcamera.org/comment/1934/","msgid":"<20190619122654.nbffm2d7mrya3f73@uno.localdomain>","date":"2019-06-19T12:26:54","subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent,\n\nOn Wed, Jun 19, 2019 at 03:20:29PM +0300, Laurent Pinchart wrote:\n> Hi Jacopo,\n>\n> Thank you for the patch.\n>\n> On Wed, Jun 19, 2019 at 01:05:46PM +0200, Jacopo Mondi wrote:\n> > The V4L2 devices and subdevices share a few common operations,like\n> > opening and closing a device node, and perform IOCTLs on the device.\n> >\n> > With the forthcoming introduction of support for V4L2 controls, the\n> > quantity of shared code will increase, as the control support\n> > implementation is identical for the two derived classes.\n> >\n> > To maximize code re-use and avoid duplications, provide a V4L2Device\n> > base class which groups the common operations and members.\n> >\n> > The newly introduced base class provides methods to open/close a device\n> > node, access the file descriptor, and perform IOCTLs on the device.\n> >\n> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  src/libcamera/include/v4l2_device.h      |  43 +++++++\n> >  src/libcamera/include/v4l2_subdevice.h   |   9 +-\n> >  src/libcamera/include/v4l2_videodevice.h |  10 +-\n> >  src/libcamera/meson.build                |   2 +\n> >  src/libcamera/v4l2_device.cpp            | 144 +++++++++++++++++++++++\n> >  src/libcamera/v4l2_subdevice.cpp         |  84 +++----------\n> >  src/libcamera/v4l2_videodevice.cpp       | 103 +++++-----------\n> >  7 files changed, 239 insertions(+), 156 deletions(-)\n> >  create mode 100644 src/libcamera/include/v4l2_device.h\n> >  create mode 100644 src/libcamera/v4l2_device.cpp\n> >\n> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > new file mode 100644\n> > index 000000000000..2c26f3eae2b4\n> > --- /dev/null\n> > +++ b/src/libcamera/include/v4l2_device.h\n> > @@ -0,0 +1,43 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2019, Google Inc.\n> > + *\n> > + * v4l2_device.h - Common base for V4L2 video devices and subdevices\n> > + */\n> > +#ifndef __LIBCAMERA_V4L2_DEVICE_H__\n> > +#define __LIBCAMERA_V4L2_DEVICE_H__\n> > +\n> > +#include <string>\n> > +\n> > +#include \"log.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +class V4L2Device : protected Loggable\n> > +{\n> > +public:\n> > +\tvoid close();\n> > +\tbool isOpen() const { return fd_ != -1; }\n> > +\tconst std::string &deviceNode() const { return deviceNode_; }\n> > +\n> > +protected:\n> > +\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > +\t~V4L2Device() {}\n> > +\n> > +\tint fd() { return fd_; }\n> > +\n> > +\tint open(unsigned int flags);\n> > +\n> > +\tint ioctl(unsigned long request, void *argp);\n> > +\n> > +\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n>\n> Let's try not to inline methods that are more complex than just\n> returning a member of the class.\n>\n\nOk. I always assumed the compiler knows better, and for simple methods\nlike this one, implementing it here or in the cpp file it's a matter\nof tastes\n\n> I didn't notice in my previous review that V4L2Subdevice was using the\n> entity name as a prefix. I'm afraid my comment about moving logPrefix()\n> to V4L2Device was a bad one :-/ However, V4L2Device should still inherit\n> from Loggable. It will thus have a pure virtual logPrefix() inherited\n> from Loggable, requiring the derived classes to implement that method,\n> but allowing LOG() statements in V4L2Device to use the log prefix.\n>\n\nWhy is this (using the entity name as log prefix) bad for you? The\ntag is passed to the base class constructor. Might be a bit of a\nshortcut maybe, is this your concern ?\n\n> Here's the diff I came up with, it can be squashed with this patch if\n> you agree on the comments.\n>\n> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> index 2c26f3eae2b4..8c83adab4d43 100644\n> --- a/src/libcamera/include/v4l2_device.h\n> +++ b/src/libcamera/include/v4l2_device.h\n> @@ -21,7 +21,7 @@ public:\n>  \tconst std::string &deviceNode() const { return deviceNode_; }\n>\n>  protected:\n> -\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> +\tV4L2Device(const std::string &deviceNode);\n>  \t~V4L2Device() {}\n>\n>  \tint fd() { return fd_; }\n> @@ -30,11 +30,8 @@ protected:\n>\n>  \tint ioctl(unsigned long request, void *argp);\n>\n> -\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> -\n>  private:\n>  \tstd::string deviceNode_;\n> -\tstd::string logTag_;\n>  \tint fd_;\n>  };\n>\n> diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> index b1da4a87c553..9c077674f997 100644\n> --- a/src/libcamera/include/v4l2_subdevice.h\n> +++ b/src/libcamera/include/v4l2_subdevice.h\n> @@ -52,6 +52,9 @@ public:\n>  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n>  \t\t\t\t\t     const std::string &entity);\n>\n> +protected:\n> +\tstd::string logPrefix() const;\n> +\n>  private:\n>  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n>  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> index e73dec13f847..734b34f1dc53 100644\n> --- a/src/libcamera/include/v4l2_videodevice.h\n> +++ b/src/libcamera/include/v4l2_videodevice.h\n> @@ -147,6 +147,9 @@ public:\n>  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n>  \t\t\t\t\t       const std::string &entity);\n>\n> +protected:\n> +\tstd::string logPrefix() const;\n> +\n>  private:\n>  \tint getFormatMeta(V4L2DeviceFormat *format);\n>  \tint setFormatMeta(V4L2DeviceFormat *format);\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> index eeed0a70fb0e..1f113780ba45 100644\n> --- a/src/libcamera/v4l2_device.cpp\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -68,17 +68,15 @@ void V4L2Device::close()\n>  /**\n>   * \\brief Construct a V4L2Device\n>   * \\param[in] deviceNode The device node filesystem path\n> - * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> - * the device in the log output\n>   *\n>   * The V4L2Device constructor is protected and can only be accessed by the\n>   * V4L2VideoDevice and V4L2Subdevice derived classes.\n>   *\n>   * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n>   * at open() time, and the \\a logTag to prefix log messages with.\n>   */\n> -V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> -\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> +V4L2Device::V4L2Device(const std::string &deviceNode)\n> +\t: deviceNode_(deviceNode), fd_(-1)\n>  {\n>  }\n>\n> @@ -99,6 +94,7 @@ V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n>   */\n>  int V4L2Device::open(unsigned int flags)\n>  {\n> +\tLOG(V4L2, Info) << \"Opening device\";\n>  \tif (isOpen()) {\n>  \t\tLOG(V4L2, Error) << \"Device already open\";\n>  \t\treturn -EBUSY;\n> @@ -135,10 +131,4 @@ int V4L2Device::ioctl(unsigned long request, void *argp)\n>  \treturn 0;\n>  }\n>\n> -/**\n> - * \\fn V4L2Device::logPrefix()\n> - * \\brief Retrieve the tag to prefix log messages with\n> - * \\return The tag to prefix log messages with\n> - */\n> -\n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> index d0e1d717b26c..a188298de34c 100644\n> --- a/src/libcamera/v4l2_subdevice.cpp\n> +++ b/src/libcamera/v4l2_subdevice.cpp\n> @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n>   * path\n>   */\n>  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> -\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> +\t: V4L2Device(entity->deviceNode()), entity_(entity)\n>  {\n>  }\n>\n> @@ -265,6 +265,11 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n>  \treturn new V4L2Subdevice(mediaEntity);\n>  }\n>\n> +std::string V4L2Subdevice::logPrefix() const\n> +{\n> +\treturn \"'\" + entity_->name() + \"'\";\n> +}\n> +\n>  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n>  {\n>  \tstd::vector<unsigned int> codes;\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 403e0b2e0653..2de3751f467e 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n>   * \\param[in] deviceNode The file-system path to the video device node\n>   */\n>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> -\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> +\t: V4L2Device(deviceNode), bufferPool_(nullptr),\n>  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n>  {\n>  \t/*\n> @@ -389,6 +389,11 @@ void V4L2VideoDevice::close()\n>   * \\return The string containing the device location\n>   */\n>\n> +std::string V4L2VideoDevice::logPrefix() const\n> +{\n> +\treturn deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> +}\n> +\n>  /**\n>   * \\brief Retrieve the image format set on the V4L2 device\n>   * \\param[out] format The image format applied on the device\n>\n> > +\n> > +private:\n> > +\tstd::string deviceNode_;\n> > +\tstd::string logTag_;\n> > +\tint fd_;\n> > +};\n> > +\n> > +} /* namespace libcamera */\n> > +\n> > +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */\n> > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > index 9afd28b6db02..b1da4a87c553 100644\n> > --- a/src/libcamera/include/v4l2_subdevice.h\n> > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > @@ -16,6 +16,7 @@\n> >  #include \"formats.h\"\n> >  #include \"log.h\"\n> >  #include \"media_object.h\"\n> > +#include \"v4l2_device.h\"\n> >\n> >  namespace libcamera {\n> >\n> > @@ -28,7 +29,7 @@ struct V4L2SubdeviceFormat {\n> >  \tconst std::string toString() const;\n> >  };\n> >\n> > -class V4L2Subdevice : protected Loggable\n> > +class V4L2Subdevice : public V4L2Device\n> >  {\n> >  public:\n> >  \texplicit V4L2Subdevice(const MediaEntity *entity);\n> > @@ -37,8 +38,6 @@ public:\n> >  \t~V4L2Subdevice();\n> >\n> >  \tint open();\n> > -\tbool isOpen() const;\n> > -\tvoid close();\n> >\n> >  \tconst MediaEntity *entity() const { return entity_; }\n> >\n> > @@ -53,9 +52,6 @@ public:\n> >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t     const std::string &entity);\n> >\n> > -protected:\n> > -\tstd::string logPrefix() const;\n> > -\n> >  private:\n> >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > @@ -65,7 +61,6 @@ private:\n> >  \t\t\t Rectangle *rect);\n> >\n> >  \tconst MediaEntity *entity_;\n> > -\tint fd_;\n> >  };\n> >\n> >  } /* namespace libcamera */\n> > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > index 895658805b47..e73dec13f847 100644\n> > --- a/src/libcamera/include/v4l2_videodevice.h\n> > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > @@ -18,6 +18,7 @@\n> >\n> >  #include \"formats.h\"\n> >  #include \"log.h\"\n> > +#include \"v4l2_device.h\"\n> >\n> >  namespace libcamera {\n> >\n> > @@ -112,7 +113,7 @@ public:\n> >  \tconst std::string toString() const;\n> >  };\n> >\n> > -class V4L2VideoDevice : protected Loggable\n> > +class V4L2VideoDevice : public V4L2Device\n> >  {\n> >  public:\n> >  \texplicit V4L2VideoDevice(const std::string &deviceNode);\n> > @@ -123,13 +124,11 @@ public:\n> >  \tV4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;\n> >\n> >  \tint open();\n> > -\tbool isOpen() const;\n> >  \tvoid close();\n> >\n> >  \tconst char *driverName() const { return caps_.driver(); }\n> >  \tconst char *deviceName() const { return caps_.card(); }\n> >  \tconst char *busName() const { return caps_.bus_info(); }\n> > -\tconst std::string &deviceNode() const { return deviceNode_; }\n> >\n> >  \tint getFormat(V4L2DeviceFormat *format);\n> >  \tint setFormat(V4L2DeviceFormat *format);\n> > @@ -148,9 +147,6 @@ public:\n> >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t       const std::string &entity);\n> >\n> > -protected:\n> > -\tstd::string logPrefix() const;\n> > -\n> >  private:\n> >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > @@ -171,8 +167,6 @@ private:\n> >  \tBuffer *dequeueBuffer();\n> >  \tvoid bufferAvailable(EventNotifier *notifier);\n> >\n> > -\tstd::string deviceNode_;\n> > -\tint fd_;\n> >  \tV4L2Capability caps_;\n> >\n> >  \tenum v4l2_buf_type bufferType_;\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 15ab53b1abbe..f26ad5b2dc57 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -23,6 +23,7 @@ libcamera_sources = files([\n> >      'stream.cpp',\n> >      'timer.cpp',\n> >      'utils.cpp',\n> > +    'v4l2_device.cpp',\n> >      'v4l2_subdevice.cpp',\n> >      'v4l2_videodevice.cpp',\n> >  ])\n> > @@ -41,6 +42,7 @@ libcamera_headers = files([\n> >      'include/media_object.h',\n> >      'include/pipeline_handler.h',\n> >      'include/utils.h',\n> > +    'include/v4l2_device.h',\n> >      'include/v4l2_subdevice.h',\n> >      'include/v4l2_videodevice.h',\n> >  ])\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > new file mode 100644\n> > index 000000000000..eeed0a70fb0e\n> > --- /dev/null\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -0,0 +1,144 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2019, Google Inc.\n> > + *\n> > + * v4l2_device.cpp - Common base for V4L2 video devices and subdevices\n> > + */\n> > +\n> > +#include \"v4l2_device.h\"\n> > +\n> > +#include <fcntl.h>\n> > +#include <string.h>\n> > +#include <sys/ioctl.h>\n> > +#include <unistd.h>\n> > +\n> > +#include \"log.h\"\n> > +\n> > +/**\n> > + * \\file v4l2_device.h\n> > + * \\brief Common base for V4L2 devices and subdevices\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DEFINE_CATEGORY(V4L2)\n> > +\n> > +/**\n> > + * \\class V4L2Device\n> > + * \\brief Base class for V4L2VideoDevice and V4L2Subdevice\n> > + *\n> > + * The V4L2Device class groups together the methods and fields common to\n> > + * both the V4L2VideoDevice and V4L2Subdevice classes, and provides a base\n> > + * class whith methods to open and close the device node associated with the\n> > + * device and to perform IOCTL system calls on it.\n> > + *\n> > + * The V4L2Device class cannot be instantiated directly, as its constructor\n> > + * is protected. Users should instead create instances of one the derived\n> > + * classes to model either a V4L2 video device or a V4L2 subdevice.\n> > + */\n> > +\n> > +/**\n> > + * \\brief Close the device node\n> > + *\n> > + * Reset the file descriptor to -1\n> > + */\n> > +void V4L2Device::close()\n> > +{\n> > +\tif (!isOpen())\n> > +\t\treturn;\n> > +\n> > +\tif (::close(fd_) < 0)\n> > +\t\tLOG(V4L2, Error) << \"Failed to close V4L2 device: \"\n> > +\t\t\t\t << strerror(errno);\n> > +\tfd_ = -1;\n> > +}\n> > +\n> > +/**\n> > + * \\fn V4L2Device::isOpen()\n> > + * \\brief Check if the V4L2 device node is open\n> > + * \\return True if the V4L2 device node is open, false otherwise\n> > + */\n> > +\n> > +/**\n> > + * \\fn V4L2Device::deviceNode()\n> > + * \\brief Retrieve the device node path\n> > + * \\return The device node path\n> > + */\n> > +\n> > +/**\n> > + * \\brief Construct a V4L2Device\n> > + * \\param[in] deviceNode The device node filesystem path\n> > + * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > + * the device in the log output\n> > + *\n> > + * The V4L2Device constructor is protected and can only be accessed by the\n> > + * V4L2VideoDevice and V4L2Subdevice derived classes.\n>\n> I would drop this sentence.\n>\n> > + *\n> > + * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> > + * at open() time, and the \\a logTag to prefix log messages with.\n> > + */\n> > +V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > +\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\fn V4L2Device::fd()\n> > + * \\brief Retrieve the V4L2 device file descriptor\n> > + * \\return The V4L2 device file descriptor, -1 if the device node is not open\n> > + */\n> > +\n> > +/**\n> > + * \\brief Open a V4L2 device node\n> > + * \\param[in] flags Access mode flags\n> > + *\n> > + * Open the device node path with the provided access mode \\a flags and\n> > + * initialize the file descriptor, which was initially set to -1.\n> > + *\n> > + * \\return 0 on success or a negative error code otherwise\n> > + */\n> > +int V4L2Device::open(unsigned int flags)\n> > +{\n> > +\tif (isOpen()) {\n> > +\t\tLOG(V4L2, Error) << \"Device already open\";\n> > +\t\treturn -EBUSY;\n> > +\t}\n> > +\n> > +\tint ret = ::open(deviceNode_.c_str(), flags);\n> > +\tif (ret < 0) {\n> > +\t\tret = -errno;\n> > +\t\tLOG(V4L2, Error) << \"Failed to open V4L2 device: \"\n> > +\t\t\t\t << strerror(-ret);\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tfd_ = ret;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Perform an IOCTL system call on the device node\n> > + * \\param[in] request The IOCTL request code\n> > + * \\param[in] argp A pointer to the IOCTL argument\n> > + * \\return 0 on success or a negative error code otherwise\n> > + */\n> > +int V4L2Device::ioctl(unsigned long request, void *argp)\n> > +{\n> > +\t/*\n> > +\t * Printing out an error message is usually better performed\n> > +\t * in the caller, which can provide more context.\n> > +\t */\n> > +\tif (::ioctl(fd_, request, argp) < 0)\n> > +\t\treturn -errno;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +/**\n> > + * \\fn V4L2Device::logPrefix()\n> > + * \\brief Retrieve the tag to prefix log messages with\n> > + * \\return The tag to prefix log messages with\n> > + */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > index 48f1fcb30ed4..d0e1d717b26c 100644\n> > --- a/src/libcamera/v4l2_subdevice.cpp\n> > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > @@ -29,7 +29,7 @@\n> >\n> >  namespace libcamera {\n> >\n> > -LOG_DEFINE_CATEGORY(V4L2Subdev)\n> > +LOG_DECLARE_CATEGORY(V4L2)\n> >\n> >  /**\n> >   * \\struct V4L2SubdeviceFormat\n> > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> >   * path\n> >   */\n> >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > -\t: entity_(entity), fd_(-1)\n> > +\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> >  {\n> >  }\n> >\n> > @@ -117,45 +117,7 @@ V4L2Subdevice::~V4L2Subdevice()\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(entity_->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 '\"\n> > -\t\t\t<< entity_->deviceNode() << \"': \" << 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> > +\treturn V4L2Device::open(O_RDWR);\n> >  }\n> >\n> >  /**\n> > @@ -200,7 +162,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> >  \tImageFormats formats;\n> >\n> >  \tif (pad >= entity_->pads().size()) {\n> > -\t\tLOG(V4L2Subdev, Error) << \"Invalid pad: \" << pad;\n> > +\t\tLOG(V4L2, Error) << \"Invalid pad: \" << pad;\n> >  \t\treturn {};\n> >  \t}\n> >\n> > @@ -210,7 +172,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> >  \t\t\treturn {};\n> >\n> >  \t\tif (formats.addFormat(code, sizes)) {\n> > -\t\t\tLOG(V4L2Subdev, Error)\n> > +\t\t\tLOG(V4L2, Error)\n> >  \t\t\t\t<< \"Could not add sizes for media bus code \"\n> >  \t\t\t\t<< code << \" on pad \" << pad;\n> >  \t\t\treturn {};\n> > @@ -232,10 +194,9 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> >  \tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> >  \tsubdevFmt.pad = pad;\n> >\n> > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > +\tint ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to get format on pad \" << pad\n> >  \t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -268,10 +229,9 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> >  \tsubdevFmt.format.height = format->size.height;\n> >  \tsubdevFmt.format.code = format->mbus_code;\n> >\n> > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > +\tint ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to set format on pad \" << pad\n> >  \t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -305,11 +265,6 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> >  \treturn new V4L2Subdevice(mediaEntity);\n> >  }\n> >\n> > -std::string V4L2Subdevice::logPrefix() const\n> > -{\n> > -\treturn \"'\" + entity_->name() + \"'\";\n> > -}\n> > -\n> >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> >  {\n> >  \tstd::vector<unsigned int> codes;\n> > @@ -321,18 +276,17 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> >  \t\tmbusEnum.index = index;\n> >  \t\tmbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> >  \t\tcodes.push_back(mbusEnum.code);\n> >  \t}\n> >\n> > -\tif (ret && errno != EINVAL) {\n> > -\t\tret = errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\tif (ret < 0 && ret != -EINVAL) {\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate formats on pad \" << pad\n> > -\t\t\t<< \": \" << strerror(ret);\n> > +\t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn {};\n> >  \t}\n> >\n> > @@ -352,7 +306,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> >  \t\tsizeEnum.code = code;\n> >  \t\tsizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> > @@ -360,9 +314,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> >  \t\t\t\t   sizeEnum.max_width, sizeEnum.max_height);\n> >  \t}\n> >\n> > -\tif (ret && (errno != EINVAL && errno != ENOTTY)) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\tif (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate sizes on pad \" << pad\n> >  \t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn {};\n> > @@ -386,10 +339,9 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,\n> >  \tsel.r.width = rect->w;\n> >  \tsel.r.height = rect->h;\n> >\n> > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > +\tint ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to set rectangle \" << target << \" on pad \"\n> >  \t\t\t<< pad << \": \" << strerror(-ret);\n> >  \t\treturn ret;\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 0e70498c8bb1..403e0b2e0653 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -30,7 +30,7 @@\n> >   */\n> >  namespace libcamera {\n> >\n> > -LOG_DEFINE_CATEGORY(V4L2)\n> > +LOG_DECLARE_CATEGORY(V4L2)\n> >\n> >  /**\n> >   * \\struct V4L2Capability\n> > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> >   * \\param[in] deviceNode The file-system path to the video device node\n> >   */\n> >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > -\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n> > +\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> >  {\n> >  \t/*\n> > @@ -305,23 +305,12 @@ int V4L2VideoDevice::open()\n> >  {\n> >  \tint ret;\n> >\n> > -\tif (isOpen()) {\n> > -\t\tLOG(V4L2, Error) << \"Device already open\";\n> > -\t\treturn -EBUSY;\n> > -\t}\n> > -\n> > -\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n> > -\tif (ret < 0) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2, Error)\n> > -\t\t\t<< \"Failed to open V4L2 device: \" << strerror(-ret);\n> > +\tret = V4L2Device::open(O_RDWR | O_NONBLOCK);\n> > +\tif (ret < 0)\n> >  \t\treturn ret;\n> > -\t}\n> > -\tfd_ = ret;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);\n> > +\tret = ioctl(VIDIOC_QUERYCAP, &caps_);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to query device capabilities: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -342,20 +331,20 @@ int V4L2VideoDevice::open()\n> >  \t * (POLLIN), and write notifications for OUTPUT devices (POLLOUT).\n> >  \t */\n> >  \tif (caps_.isVideoCapture()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> >  \t\tbufferType_ = caps_.isMultiplanar()\n> >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE\n> >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> >  \t} else if (caps_.isVideoOutput()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> >  \t\tbufferType_ = caps_.isMultiplanar()\n> >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n> >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n> >  \t} else if (caps_.isMetaCapture()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> >  \t\tbufferType_ = V4L2_BUF_TYPE_META_CAPTURE;\n> >  \t} else if (caps_.isMetaOutput()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> >  \t\tbufferType_ = V4L2_BUF_TYPE_META_OUTPUT;\n> >  \t} else {\n> >  \t\tLOG(V4L2, Error) << \"Device is not a supported type\";\n> > @@ -368,28 +357,18 @@ int V4L2VideoDevice::open()\n> >  \treturn 0;\n> >  }\n> >\n> > -/**\n> > - * \\brief Check if device is successfully opened\n> > - * \\return True if the device is open, false otherwise\n> > - */\n> > -bool V4L2VideoDevice::isOpen() const\n> > -{\n> > -\treturn fd_ != -1;\n> > -}\n> > -\n> >  /**\n> >   * \\brief Close the device, releasing any resources acquired by open()\n> >   */\n> >  void V4L2VideoDevice::close()\n> >  {\n> > -\tif (fd_ < 0)\n> > +\tif (!isOpen())\n> >  \t\treturn;\n> >\n> >  \treleaseBuffers();\n> >  \tdelete fdEvent_;\n> >\n> > -\t::close(fd_);\n> > -\tfd_ = -1;\n> > +\tV4L2Device::close();\n> >  }\n> >\n> >  /**\n> > @@ -410,17 +389,6 @@ void V4L2VideoDevice::close()\n> >   * \\return The string containing the device location\n> >   */\n> >\n> > -/**\n> > - * \\fn V4L2VideoDevice::deviceNode()\n> > - * \\brief Retrieve the video device node path\n> > - * \\return The video device device node path\n> > - */\n> > -\n> > -std::string V4L2VideoDevice::logPrefix() const\n> > -{\n> > -\treturn deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > -}\n> > -\n> >  /**\n> >   * \\brief Retrieve the image format set on the V4L2 device\n> >   * \\param[out] format The image format applied on the device\n> > @@ -462,9 +430,8 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)\n> >  \tint ret;\n> >\n> >  \tv4l2Format.type = bufferType_;\n> > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -488,9 +455,8 @@ int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)\n> >  \tv4l2Format.type = bufferType_;\n> >  \tpix->dataformat = format->fourcc;\n> >  \tpix->buffersize = format->planes[0].size;\n> > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -516,9 +482,8 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)\n> >  \tint ret;\n> >\n> >  \tv4l2Format.type = bufferType_;\n> > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -554,9 +519,8 @@ int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)\n> >  \t\tpix->plane_fmt[i].sizeimage = format->planes[i].size;\n> >  \t}\n> >\n> > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -584,9 +548,8 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)\n> >  \tint ret;\n> >\n> >  \tv4l2Format.type = bufferType_;\n> > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -613,9 +576,8 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)\n> >  \tpix->pixelformat = format->fourcc;\n> >  \tpix->bytesperline = format->planes[0].bpl;\n> >  \tpix->field = V4L2_FIELD_NONE;\n> > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -670,9 +632,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n> >  \trb.type = bufferType_;\n> >  \trb.memory = memoryType_;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_REQBUFS, &rb);\n> > +\tret = ioctl(VIDIOC_REQBUFS, &rb);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to request \" << count << \" buffers: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -722,9 +683,8 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n> >  \t\tbuf.length = VIDEO_MAX_PLANES;\n> >  \t\tbuf.m.planes = planes;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);\n> > +\t\tret = ioctl(VIDIOC_QUERYBUF, &buf);\n> >  \t\tif (ret < 0) {\n> > -\t\t\tret = -errno;\n> >  \t\t\tLOG(V4L2, Error)\n> >  \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n> >  \t\t\t\t<< strerror(-ret);\n> > @@ -775,9 +735,8 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex,\n> >  \texpbuf.plane = planeIndex;\n> >  \texpbuf.flags = O_RDWR;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);\n> > +\tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to export buffer: \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -801,15 +760,14 @@ std::vector<unsigned int> V4L2VideoDevice::enumPixelformats()\n> >  \t\tpixelformatEnum.index = index;\n> >  \t\tpixelformatEnum.type = bufferType_;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > +\t\tret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> >  \t\tformats.push_back(pixelformatEnum.pixelformat);\n> >  \t}\n> >\n> > -\tif (ret && errno != EINVAL) {\n> > -\t\tret = -errno;\n> > +\tif (ret && ret != -EINVAL) {\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate pixel formats: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -829,7 +787,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> >  \t\tframeSize.index = index;\n> >  \t\tframeSize.pixel_format = pixelFormat;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > +\t\tret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> > @@ -867,8 +825,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> >  \t\t}\n> >  \t}\n> >\n> > -\tif (ret && errno != EINVAL) {\n> > -\t\tret = -errno;\n> > +\tif (ret && ret != -EINVAL) {\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate frame sizes: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -969,9 +926,8 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n> >\n> >  \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n> > +\tret = ioctl(VIDIOC_QBUF, &buf);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -1006,9 +962,8 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> >  \t\tbuf.m.planes = planes;\n> >  \t}\n> >\n> > -\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n> > +\tret = ioctl(VIDIOC_DQBUF, &buf);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> >  \t\treturn nullptr;\n> > @@ -1066,9 +1021,8 @@ int V4L2VideoDevice::streamOn()\n> >  {\n> >  \tint ret;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);\n> > +\tret = ioctl(VIDIOC_STREAMON, &bufferType_);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to start streaming: \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -1089,9 +1043,8 @@ int V4L2VideoDevice::streamOff()\n> >  {\n> >  \tint ret;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);\n> > +\tret = ioctl(VIDIOC_STREAMOFF, &bufferType_);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to stop streaming: \" << strerror(-ret);\n> >  \t\treturn ret;\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay10.mail.gandi.net (relay10.mail.gandi.net\n\t[217.70.178.230])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EB6DA61A15\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Jun 2019 14:25:41 +0200 (CEST)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay10.mail.gandi.net (Postfix) with ESMTPSA id 49CFD240003;\n\tWed, 19 Jun 2019 12:25:41 +0000 (UTC)"],"Date":"Wed, 19 Jun 2019 14:26:54 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190619122654.nbffm2d7mrya3f73@uno.localdomain>","References":"<20190619110548.20742-1-jacopo@jmondi.org>\n\t<20190619110548.20742-3-jacopo@jmondi.org>\n\t<20190619122029.GE5045@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"mdvnmiyjezxs3fbx\"","Content-Disposition":"inline","In-Reply-To":"<20190619122029.GE5045@pendragon.ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","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":"Wed, 19 Jun 2019 12:25:42 -0000"}},{"id":1936,"web_url":"https://patchwork.libcamera.org/comment/1936/","msgid":"<20190619122908.GG5045@pendragon.ideasonboard.com>","date":"2019-06-19T12:29:08","subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Wed, Jun 19, 2019 at 02:26:54PM +0200, Jacopo Mondi wrote:\n> On Wed, Jun 19, 2019 at 03:20:29PM +0300, Laurent Pinchart wrote:\n> > On Wed, Jun 19, 2019 at 01:05:46PM +0200, Jacopo Mondi wrote:\n> > > The V4L2 devices and subdevices share a few common operations,like\n> > > opening and closing a device node, and perform IOCTLs on the device.\n> > >\n> > > With the forthcoming introduction of support for V4L2 controls, the\n> > > quantity of shared code will increase, as the control support\n> > > implementation is identical for the two derived classes.\n> > >\n> > > To maximize code re-use and avoid duplications, provide a V4L2Device\n> > > base class which groups the common operations and members.\n> > >\n> > > The newly introduced base class provides methods to open/close a device\n> > > node, access the file descriptor, and perform IOCTLs on the device.\n> > >\n> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > ---\n> > >  src/libcamera/include/v4l2_device.h      |  43 +++++++\n> > >  src/libcamera/include/v4l2_subdevice.h   |   9 +-\n> > >  src/libcamera/include/v4l2_videodevice.h |  10 +-\n> > >  src/libcamera/meson.build                |   2 +\n> > >  src/libcamera/v4l2_device.cpp            | 144 +++++++++++++++++++++++\n> > >  src/libcamera/v4l2_subdevice.cpp         |  84 +++----------\n> > >  src/libcamera/v4l2_videodevice.cpp       | 103 +++++-----------\n> > >  7 files changed, 239 insertions(+), 156 deletions(-)\n> > >  create mode 100644 src/libcamera/include/v4l2_device.h\n> > >  create mode 100644 src/libcamera/v4l2_device.cpp\n> > >\n> > > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > > new file mode 100644\n> > > index 000000000000..2c26f3eae2b4\n> > > --- /dev/null\n> > > +++ b/src/libcamera/include/v4l2_device.h\n> > > @@ -0,0 +1,43 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2019, Google Inc.\n> > > + *\n> > > + * v4l2_device.h - Common base for V4L2 video devices and subdevices\n> > > + */\n> > > +#ifndef __LIBCAMERA_V4L2_DEVICE_H__\n> > > +#define __LIBCAMERA_V4L2_DEVICE_H__\n> > > +\n> > > +#include <string>\n> > > +\n> > > +#include \"log.h\"\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +class V4L2Device : protected Loggable\n> > > +{\n> > > +public:\n> > > +\tvoid close();\n> > > +\tbool isOpen() const { return fd_ != -1; }\n> > > +\tconst std::string &deviceNode() const { return deviceNode_; }\n> > > +\n> > > +protected:\n> > > +\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > > +\t~V4L2Device() {}\n> > > +\n> > > +\tint fd() { return fd_; }\n> > > +\n> > > +\tint open(unsigned int flags);\n> > > +\n> > > +\tint ioctl(unsigned long request, void *argp);\n> > > +\n> > > +\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> >\n> > Let's try not to inline methods that are more complex than just\n> > returning a member of the class.\n> \n> Ok. I always assumed the compiler knows better, and for simple methods\n> like this one, implementing it here or in the cpp file it's a matter\n> of tastes\n> \n> > I didn't notice in my previous review that V4L2Subdevice was using the\n> > entity name as a prefix. I'm afraid my comment about moving logPrefix()\n> > to V4L2Device was a bad one :-/ However, V4L2Device should still inherit\n> > from Loggable. It will thus have a pure virtual logPrefix() inherited\n> > from Loggable, requiring the derived classes to implement that method,\n> > but allowing LOG() statements in V4L2Device to use the log prefix.\n> \n> Why is this (using the entity name as log prefix) bad for you? The\n> tag is passed to the base class constructor. Might be a bit of a\n> shortcut maybe, is this your concern ?\n\nMy concern is two-fold. First of all, it requires passing a log tag to\nthe V4L2Device constructor, which isn't very nice. I thought both\nderived classes used the device node name as a log tag, but that was not\ncorrect.\n\nThen, V4L2VideoDevice adds a suffix to the device node name to indicate\nthe queue direction. For mem-to-mem devices this is really useful, as a\nsingle video device node exposes two queues, each of them handled by a\nV4L2VideoDevice instance. I don't want to lose this debugging aid.\n\n> > Here's the diff I came up with, it can be squashed with this patch if\n> > you agree on the comments.\n> >\n> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > index 2c26f3eae2b4..8c83adab4d43 100644\n> > --- a/src/libcamera/include/v4l2_device.h\n> > +++ b/src/libcamera/include/v4l2_device.h\n> > @@ -21,7 +21,7 @@ public:\n> >  \tconst std::string &deviceNode() const { return deviceNode_; }\n> >\n> >  protected:\n> > -\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > +\tV4L2Device(const std::string &deviceNode);\n> >  \t~V4L2Device() {}\n> >\n> >  \tint fd() { return fd_; }\n> > @@ -30,11 +30,8 @@ protected:\n> >\n> >  \tint ioctl(unsigned long request, void *argp);\n> >\n> > -\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> > -\n> >  private:\n> >  \tstd::string deviceNode_;\n> > -\tstd::string logTag_;\n> >  \tint fd_;\n> >  };\n> >\n> > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > index b1da4a87c553..9c077674f997 100644\n> > --- a/src/libcamera/include/v4l2_subdevice.h\n> > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > @@ -52,6 +52,9 @@ public:\n> >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t     const std::string &entity);\n> >\n> > +protected:\n> > +\tstd::string logPrefix() const;\n> > +\n> >  private:\n> >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > index e73dec13f847..734b34f1dc53 100644\n> > --- a/src/libcamera/include/v4l2_videodevice.h\n> > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > @@ -147,6 +147,9 @@ public:\n> >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t       const std::string &entity);\n> >\n> > +protected:\n> > +\tstd::string logPrefix() const;\n> > +\n> >  private:\n> >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > index eeed0a70fb0e..1f113780ba45 100644\n> > --- a/src/libcamera/v4l2_device.cpp\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -68,17 +68,15 @@ void V4L2Device::close()\n> >  /**\n> >   * \\brief Construct a V4L2Device\n> >   * \\param[in] deviceNode The device node filesystem path\n> > - * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > - * the device in the log output\n> >   *\n> >   * The V4L2Device constructor is protected and can only be accessed by the\n> >   * V4L2VideoDevice and V4L2Subdevice derived classes.\n> >   *\n> >   * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> >   * at open() time, and the \\a logTag to prefix log messages with.\n> >   */\n> > -V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > -\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > +V4L2Device::V4L2Device(const std::string &deviceNode)\n> > +\t: deviceNode_(deviceNode), fd_(-1)\n> >  {\n> >  }\n> >\n> > @@ -99,6 +94,7 @@ V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> >   */\n> >  int V4L2Device::open(unsigned int flags)\n> >  {\n> > +\tLOG(V4L2, Info) << \"Opening device\";\n> >  \tif (isOpen()) {\n> >  \t\tLOG(V4L2, Error) << \"Device already open\";\n> >  \t\treturn -EBUSY;\n> > @@ -135,10 +131,4 @@ int V4L2Device::ioctl(unsigned long request, void *argp)\n> >  \treturn 0;\n> >  }\n> >\n> > -/**\n> > - * \\fn V4L2Device::logPrefix()\n> > - * \\brief Retrieve the tag to prefix log messages with\n> > - * \\return The tag to prefix log messages with\n> > - */\n> > -\n> >  } /* namespace libcamera */\n> > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > index d0e1d717b26c..a188298de34c 100644\n> > --- a/src/libcamera/v4l2_subdevice.cpp\n> > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> >   * path\n> >   */\n> >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > -\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> > +\t: V4L2Device(entity->deviceNode()), entity_(entity)\n> >  {\n> >  }\n> >\n> > @@ -265,6 +265,11 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> >  \treturn new V4L2Subdevice(mediaEntity);\n> >  }\n> >\n> > +std::string V4L2Subdevice::logPrefix() const\n> > +{\n> > +\treturn \"'\" + entity_->name() + \"'\";\n> > +}\n> > +\n> >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> >  {\n> >  \tstd::vector<unsigned int> codes;\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 403e0b2e0653..2de3751f467e 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> >   * \\param[in] deviceNode The file-system path to the video device node\n> >   */\n> >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > -\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> > +\t: V4L2Device(deviceNode), bufferPool_(nullptr),\n> >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> >  {\n> >  \t/*\n> > @@ -389,6 +389,11 @@ void V4L2VideoDevice::close()\n> >   * \\return The string containing the device location\n> >   */\n> >\n> > +std::string V4L2VideoDevice::logPrefix() const\n> > +{\n> > +\treturn deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > +}\n> > +\n> >  /**\n> >   * \\brief Retrieve the image format set on the V4L2 device\n> >   * \\param[out] format The image format applied on the device\n> >\n> > > +\n> > > +private:\n> > > +\tstd::string deviceNode_;\n> > > +\tstd::string logTag_;\n> > > +\tint fd_;\n> > > +};\n> > > +\n> > > +} /* namespace libcamera */\n> > > +\n> > > +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */\n> > > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > > index 9afd28b6db02..b1da4a87c553 100644\n> > > --- a/src/libcamera/include/v4l2_subdevice.h\n> > > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > > @@ -16,6 +16,7 @@\n> > >  #include \"formats.h\"\n> > >  #include \"log.h\"\n> > >  #include \"media_object.h\"\n> > > +#include \"v4l2_device.h\"\n> > >\n> > >  namespace libcamera {\n> > >\n> > > @@ -28,7 +29,7 @@ struct V4L2SubdeviceFormat {\n> > >  \tconst std::string toString() const;\n> > >  };\n> > >\n> > > -class V4L2Subdevice : protected Loggable\n> > > +class V4L2Subdevice : public V4L2Device\n> > >  {\n> > >  public:\n> > >  \texplicit V4L2Subdevice(const MediaEntity *entity);\n> > > @@ -37,8 +38,6 @@ public:\n> > >  \t~V4L2Subdevice();\n> > >\n> > >  \tint open();\n> > > -\tbool isOpen() const;\n> > > -\tvoid close();\n> > >\n> > >  \tconst MediaEntity *entity() const { return entity_; }\n> > >\n> > > @@ -53,9 +52,6 @@ public:\n> > >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> > >  \t\t\t\t\t     const std::string &entity);\n> > >\n> > > -protected:\n> > > -\tstd::string logPrefix() const;\n> > > -\n> > >  private:\n> > >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> > >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > > @@ -65,7 +61,6 @@ private:\n> > >  \t\t\t Rectangle *rect);\n> > >\n> > >  \tconst MediaEntity *entity_;\n> > > -\tint fd_;\n> > >  };\n> > >\n> > >  } /* namespace libcamera */\n> > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > > index 895658805b47..e73dec13f847 100644\n> > > --- a/src/libcamera/include/v4l2_videodevice.h\n> > > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > > @@ -18,6 +18,7 @@\n> > >\n> > >  #include \"formats.h\"\n> > >  #include \"log.h\"\n> > > +#include \"v4l2_device.h\"\n> > >\n> > >  namespace libcamera {\n> > >\n> > > @@ -112,7 +113,7 @@ public:\n> > >  \tconst std::string toString() const;\n> > >  };\n> > >\n> > > -class V4L2VideoDevice : protected Loggable\n> > > +class V4L2VideoDevice : public V4L2Device\n> > >  {\n> > >  public:\n> > >  \texplicit V4L2VideoDevice(const std::string &deviceNode);\n> > > @@ -123,13 +124,11 @@ public:\n> > >  \tV4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;\n> > >\n> > >  \tint open();\n> > > -\tbool isOpen() const;\n> > >  \tvoid close();\n> > >\n> > >  \tconst char *driverName() const { return caps_.driver(); }\n> > >  \tconst char *deviceName() const { return caps_.card(); }\n> > >  \tconst char *busName() const { return caps_.bus_info(); }\n> > > -\tconst std::string &deviceNode() const { return deviceNode_; }\n> > >\n> > >  \tint getFormat(V4L2DeviceFormat *format);\n> > >  \tint setFormat(V4L2DeviceFormat *format);\n> > > @@ -148,9 +147,6 @@ public:\n> > >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> > >  \t\t\t\t\t       const std::string &entity);\n> > >\n> > > -protected:\n> > > -\tstd::string logPrefix() const;\n> > > -\n> > >  private:\n> > >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> > >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > > @@ -171,8 +167,6 @@ private:\n> > >  \tBuffer *dequeueBuffer();\n> > >  \tvoid bufferAvailable(EventNotifier *notifier);\n> > >\n> > > -\tstd::string deviceNode_;\n> > > -\tint fd_;\n> > >  \tV4L2Capability caps_;\n> > >\n> > >  \tenum v4l2_buf_type bufferType_;\n> > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > > index 15ab53b1abbe..f26ad5b2dc57 100644\n> > > --- a/src/libcamera/meson.build\n> > > +++ b/src/libcamera/meson.build\n> > > @@ -23,6 +23,7 @@ libcamera_sources = files([\n> > >      'stream.cpp',\n> > >      'timer.cpp',\n> > >      'utils.cpp',\n> > > +    'v4l2_device.cpp',\n> > >      'v4l2_subdevice.cpp',\n> > >      'v4l2_videodevice.cpp',\n> > >  ])\n> > > @@ -41,6 +42,7 @@ libcamera_headers = files([\n> > >      'include/media_object.h',\n> > >      'include/pipeline_handler.h',\n> > >      'include/utils.h',\n> > > +    'include/v4l2_device.h',\n> > >      'include/v4l2_subdevice.h',\n> > >      'include/v4l2_videodevice.h',\n> > >  ])\n> > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > > new file mode 100644\n> > > index 000000000000..eeed0a70fb0e\n> > > --- /dev/null\n> > > +++ b/src/libcamera/v4l2_device.cpp\n> > > @@ -0,0 +1,144 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2019, Google Inc.\n> > > + *\n> > > + * v4l2_device.cpp - Common base for V4L2 video devices and subdevices\n> > > + */\n> > > +\n> > > +#include \"v4l2_device.h\"\n> > > +\n> > > +#include <fcntl.h>\n> > > +#include <string.h>\n> > > +#include <sys/ioctl.h>\n> > > +#include <unistd.h>\n> > > +\n> > > +#include \"log.h\"\n> > > +\n> > > +/**\n> > > + * \\file v4l2_device.h\n> > > + * \\brief Common base for V4L2 devices and subdevices\n> > > + */\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DEFINE_CATEGORY(V4L2)\n> > > +\n> > > +/**\n> > > + * \\class V4L2Device\n> > > + * \\brief Base class for V4L2VideoDevice and V4L2Subdevice\n> > > + *\n> > > + * The V4L2Device class groups together the methods and fields common to\n> > > + * both the V4L2VideoDevice and V4L2Subdevice classes, and provides a base\n> > > + * class whith methods to open and close the device node associated with the\n> > > + * device and to perform IOCTL system calls on it.\n> > > + *\n> > > + * The V4L2Device class cannot be instantiated directly, as its constructor\n> > > + * is protected. Users should instead create instances of one the derived\n> > > + * classes to model either a V4L2 video device or a V4L2 subdevice.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Close the device node\n> > > + *\n> > > + * Reset the file descriptor to -1\n> > > + */\n> > > +void V4L2Device::close()\n> > > +{\n> > > +\tif (!isOpen())\n> > > +\t\treturn;\n> > > +\n> > > +\tif (::close(fd_) < 0)\n> > > +\t\tLOG(V4L2, Error) << \"Failed to close V4L2 device: \"\n> > > +\t\t\t\t << strerror(errno);\n> > > +\tfd_ = -1;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::isOpen()\n> > > + * \\brief Check if the V4L2 device node is open\n> > > + * \\return True if the V4L2 device node is open, false otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::deviceNode()\n> > > + * \\brief Retrieve the device node path\n> > > + * \\return The device node path\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Construct a V4L2Device\n> > > + * \\param[in] deviceNode The device node filesystem path\n> > > + * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > > + * the device in the log output\n> > > + *\n> > > + * The V4L2Device constructor is protected and can only be accessed by the\n> > > + * V4L2VideoDevice and V4L2Subdevice derived classes.\n> >\n> > I would drop this sentence.\n> >\n> > > + *\n> > > + * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> > > + * at open() time, and the \\a logTag to prefix log messages with.\n> > > + */\n> > > +V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > > +\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::fd()\n> > > + * \\brief Retrieve the V4L2 device file descriptor\n> > > + * \\return The V4L2 device file descriptor, -1 if the device node is not open\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Open a V4L2 device node\n> > > + * \\param[in] flags Access mode flags\n> > > + *\n> > > + * Open the device node path with the provided access mode \\a flags and\n> > > + * initialize the file descriptor, which was initially set to -1.\n> > > + *\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +int V4L2Device::open(unsigned int flags)\n> > > +{\n> > > +\tif (isOpen()) {\n> > > +\t\tLOG(V4L2, Error) << \"Device already open\";\n> > > +\t\treturn -EBUSY;\n> > > +\t}\n> > > +\n> > > +\tint ret = ::open(deviceNode_.c_str(), flags);\n> > > +\tif (ret < 0) {\n> > > +\t\tret = -errno;\n> > > +\t\tLOG(V4L2, Error) << \"Failed to open V4L2 device: \"\n> > > +\t\t\t\t << strerror(-ret);\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tfd_ = ret;\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Perform an IOCTL system call on the device node\n> > > + * \\param[in] request The IOCTL request code\n> > > + * \\param[in] argp A pointer to the IOCTL argument\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +int V4L2Device::ioctl(unsigned long request, void *argp)\n> > > +{\n> > > +\t/*\n> > > +\t * Printing out an error message is usually better performed\n> > > +\t * in the caller, which can provide more context.\n> > > +\t */\n> > > +\tif (::ioctl(fd_, request, argp) < 0)\n> > > +\t\treturn -errno;\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::logPrefix()\n> > > + * \\brief Retrieve the tag to prefix log messages with\n> > > + * \\return The tag to prefix log messages with\n> > > + */\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > > index 48f1fcb30ed4..d0e1d717b26c 100644\n> > > --- a/src/libcamera/v4l2_subdevice.cpp\n> > > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > > @@ -29,7 +29,7 @@\n> > >\n> > >  namespace libcamera {\n> > >\n> > > -LOG_DEFINE_CATEGORY(V4L2Subdev)\n> > > +LOG_DECLARE_CATEGORY(V4L2)\n> > >\n> > >  /**\n> > >   * \\struct V4L2SubdeviceFormat\n> > > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> > >   * path\n> > >   */\n> > >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > > -\t: entity_(entity), fd_(-1)\n> > > +\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> > >  {\n> > >  }\n> > >\n> > > @@ -117,45 +117,7 @@ V4L2Subdevice::~V4L2Subdevice()\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(entity_->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 '\"\n> > > -\t\t\t<< entity_->deviceNode() << \"': \" << 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> > > +\treturn V4L2Device::open(O_RDWR);\n> > >  }\n> > >\n> > >  /**\n> > > @@ -200,7 +162,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> > >  \tImageFormats formats;\n> > >\n> > >  \tif (pad >= entity_->pads().size()) {\n> > > -\t\tLOG(V4L2Subdev, Error) << \"Invalid pad: \" << pad;\n> > > +\t\tLOG(V4L2, Error) << \"Invalid pad: \" << pad;\n> > >  \t\treturn {};\n> > >  \t}\n> > >\n> > > @@ -210,7 +172,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> > >  \t\t\treturn {};\n> > >\n> > >  \t\tif (formats.addFormat(code, sizes)) {\n> > > -\t\t\tLOG(V4L2Subdev, Error)\n> > > +\t\t\tLOG(V4L2, Error)\n> > >  \t\t\t\t<< \"Could not add sizes for media bus code \"\n> > >  \t\t\t\t<< code << \" on pad \" << pad;\n> > >  \t\t\treturn {};\n> > > @@ -232,10 +194,9 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> > >  \tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > >  \tsubdevFmt.pad = pad;\n> > >\n> > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > > +\tint ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to get format on pad \" << pad\n> > >  \t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -268,10 +229,9 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> > >  \tsubdevFmt.format.height = format->size.height;\n> > >  \tsubdevFmt.format.code = format->mbus_code;\n> > >\n> > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > > +\tint ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to set format on pad \" << pad\n> > >  \t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -305,11 +265,6 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> > >  \treturn new V4L2Subdevice(mediaEntity);\n> > >  }\n> > >\n> > > -std::string V4L2Subdevice::logPrefix() const\n> > > -{\n> > > -\treturn \"'\" + entity_->name() + \"'\";\n> > > -}\n> > > -\n> > >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > >  {\n> > >  \tstd::vector<unsigned int> codes;\n> > > @@ -321,18 +276,17 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > >  \t\tmbusEnum.index = index;\n> > >  \t\tmbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > >  \t\tcodes.push_back(mbusEnum.code);\n> > >  \t}\n> > >\n> > > -\tif (ret && errno != EINVAL) {\n> > > -\t\tret = errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\tif (ret < 0 && ret != -EINVAL) {\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate formats on pad \" << pad\n> > > -\t\t\t<< \": \" << strerror(ret);\n> > > +\t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn {};\n> > >  \t}\n> > >\n> > > @@ -352,7 +306,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> > >  \t\tsizeEnum.code = code;\n> > >  \t\tsizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > > @@ -360,9 +314,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> > >  \t\t\t\t   sizeEnum.max_width, sizeEnum.max_height);\n> > >  \t}\n> > >\n> > > -\tif (ret && (errno != EINVAL && errno != ENOTTY)) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\tif (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate sizes on pad \" << pad\n> > >  \t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn {};\n> > > @@ -386,10 +339,9 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,\n> > >  \tsel.r.width = rect->w;\n> > >  \tsel.r.height = rect->h;\n> > >\n> > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > > +\tint ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to set rectangle \" << target << \" on pad \"\n> > >  \t\t\t<< pad << \": \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > index 0e70498c8bb1..403e0b2e0653 100644\n> > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > @@ -30,7 +30,7 @@\n> > >   */\n> > >  namespace libcamera {\n> > >\n> > > -LOG_DEFINE_CATEGORY(V4L2)\n> > > +LOG_DECLARE_CATEGORY(V4L2)\n> > >\n> > >  /**\n> > >   * \\struct V4L2Capability\n> > > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> > >   * \\param[in] deviceNode The file-system path to the video device node\n> > >   */\n> > >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > > -\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n> > > +\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> > >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> > >  {\n> > >  \t/*\n> > > @@ -305,23 +305,12 @@ int V4L2VideoDevice::open()\n> > >  {\n> > >  \tint ret;\n> > >\n> > > -\tif (isOpen()) {\n> > > -\t\tLOG(V4L2, Error) << \"Device already open\";\n> > > -\t\treturn -EBUSY;\n> > > -\t}\n> > > -\n> > > -\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n> > > -\tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2, Error)\n> > > -\t\t\t<< \"Failed to open V4L2 device: \" << strerror(-ret);\n> > > +\tret = V4L2Device::open(O_RDWR | O_NONBLOCK);\n> > > +\tif (ret < 0)\n> > >  \t\treturn ret;\n> > > -\t}\n> > > -\tfd_ = ret;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);\n> > > +\tret = ioctl(VIDIOC_QUERYCAP, &caps_);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to query device capabilities: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -342,20 +331,20 @@ int V4L2VideoDevice::open()\n> > >  \t * (POLLIN), and write notifications for OUTPUT devices (POLLOUT).\n> > >  \t */\n> > >  \tif (caps_.isVideoCapture()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> > >  \t\tbufferType_ = caps_.isMultiplanar()\n> > >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE\n> > >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> > >  \t} else if (caps_.isVideoOutput()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> > >  \t\tbufferType_ = caps_.isMultiplanar()\n> > >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n> > >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n> > >  \t} else if (caps_.isMetaCapture()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> > >  \t\tbufferType_ = V4L2_BUF_TYPE_META_CAPTURE;\n> > >  \t} else if (caps_.isMetaOutput()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> > >  \t\tbufferType_ = V4L2_BUF_TYPE_META_OUTPUT;\n> > >  \t} else {\n> > >  \t\tLOG(V4L2, Error) << \"Device is not a supported type\";\n> > > @@ -368,28 +357,18 @@ int V4L2VideoDevice::open()\n> > >  \treturn 0;\n> > >  }\n> > >\n> > > -/**\n> > > - * \\brief Check if device is successfully opened\n> > > - * \\return True if the device is open, false otherwise\n> > > - */\n> > > -bool V4L2VideoDevice::isOpen() const\n> > > -{\n> > > -\treturn fd_ != -1;\n> > > -}\n> > > -\n> > >  /**\n> > >   * \\brief Close the device, releasing any resources acquired by open()\n> > >   */\n> > >  void V4L2VideoDevice::close()\n> > >  {\n> > > -\tif (fd_ < 0)\n> > > +\tif (!isOpen())\n> > >  \t\treturn;\n> > >\n> > >  \treleaseBuffers();\n> > >  \tdelete fdEvent_;\n> > >\n> > > -\t::close(fd_);\n> > > -\tfd_ = -1;\n> > > +\tV4L2Device::close();\n> > >  }\n> > >\n> > >  /**\n> > > @@ -410,17 +389,6 @@ void V4L2VideoDevice::close()\n> > >   * \\return The string containing the device location\n> > >   */\n> > >\n> > > -/**\n> > > - * \\fn V4L2VideoDevice::deviceNode()\n> > > - * \\brief Retrieve the video device node path\n> > > - * \\return The video device device node path\n> > > - */\n> > > -\n> > > -std::string V4L2VideoDevice::logPrefix() const\n> > > -{\n> > > -\treturn deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > > -}\n> > > -\n> > >  /**\n> > >   * \\brief Retrieve the image format set on the V4L2 device\n> > >   * \\param[out] format The image format applied on the device\n> > > @@ -462,9 +430,8 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)\n> > >  \tint ret;\n> > >\n> > >  \tv4l2Format.type = bufferType_;\n> > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -488,9 +455,8 @@ int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)\n> > >  \tv4l2Format.type = bufferType_;\n> > >  \tpix->dataformat = format->fourcc;\n> > >  \tpix->buffersize = format->planes[0].size;\n> > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -516,9 +482,8 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)\n> > >  \tint ret;\n> > >\n> > >  \tv4l2Format.type = bufferType_;\n> > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -554,9 +519,8 @@ int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)\n> > >  \t\tpix->plane_fmt[i].sizeimage = format->planes[i].size;\n> > >  \t}\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -584,9 +548,8 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)\n> > >  \tint ret;\n> > >\n> > >  \tv4l2Format.type = bufferType_;\n> > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -613,9 +576,8 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)\n> > >  \tpix->pixelformat = format->fourcc;\n> > >  \tpix->bytesperline = format->planes[0].bpl;\n> > >  \tpix->field = V4L2_FIELD_NONE;\n> > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -670,9 +632,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n> > >  \trb.type = bufferType_;\n> > >  \trb.memory = memoryType_;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_REQBUFS, &rb);\n> > > +\tret = ioctl(VIDIOC_REQBUFS, &rb);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to request \" << count << \" buffers: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -722,9 +683,8 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n> > >  \t\tbuf.length = VIDEO_MAX_PLANES;\n> > >  \t\tbuf.m.planes = planes;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);\n> > > +\t\tret = ioctl(VIDIOC_QUERYBUF, &buf);\n> > >  \t\tif (ret < 0) {\n> > > -\t\t\tret = -errno;\n> > >  \t\t\tLOG(V4L2, Error)\n> > >  \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n> > >  \t\t\t\t<< strerror(-ret);\n> > > @@ -775,9 +735,8 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex,\n> > >  \texpbuf.plane = planeIndex;\n> > >  \texpbuf.flags = O_RDWR;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);\n> > > +\tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to export buffer: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -801,15 +760,14 @@ std::vector<unsigned int> V4L2VideoDevice::enumPixelformats()\n> > >  \t\tpixelformatEnum.index = index;\n> > >  \t\tpixelformatEnum.type = bufferType_;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > > +\t\tret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > >  \t\tformats.push_back(pixelformatEnum.pixelformat);\n> > >  \t}\n> > >\n> > > -\tif (ret && errno != EINVAL) {\n> > > -\t\tret = -errno;\n> > > +\tif (ret && ret != -EINVAL) {\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate pixel formats: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -829,7 +787,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> > >  \t\tframeSize.index = index;\n> > >  \t\tframeSize.pixel_format = pixelFormat;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > > +\t\tret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > > @@ -867,8 +825,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> > >  \t\t}\n> > >  \t}\n> > >\n> > > -\tif (ret && errno != EINVAL) {\n> > > -\t\tret = -errno;\n> > > +\tif (ret && ret != -EINVAL) {\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate frame sizes: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -969,9 +926,8 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n> > >\n> > >  \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n> > > +\tret = ioctl(VIDIOC_QBUF, &buf);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -1006,9 +962,8 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > >  \t\tbuf.m.planes = planes;\n> > >  \t}\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n> > > +\tret = ioctl(VIDIOC_DQBUF, &buf);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> > >  \t\treturn nullptr;\n> > > @@ -1066,9 +1021,8 @@ int V4L2VideoDevice::streamOn()\n> > >  {\n> > >  \tint ret;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);\n> > > +\tret = ioctl(VIDIOC_STREAMON, &bufferType_);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to start streaming: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -1089,9 +1043,8 @@ int V4L2VideoDevice::streamOff()\n> > >  {\n> > >  \tint ret;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);\n> > > +\tret = ioctl(VIDIOC_STREAMOFF, &bufferType_);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to stop streaming: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> >\n> > --\n> > Regards,\n> >\n> > Laurent Pinchart","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2E9E161A15\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Jun 2019 14:29:26 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8FD4A333;\n\tWed, 19 Jun 2019 14:29:25 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1560947365;\n\tbh=ZN05nX9zKljHpa1R70jd8LhDufaFDadWU7KFa7EAqTo=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=m61NFmf5CX5+g8hzVrspAj9Q6Tdix7a1HS1p0dhvoO7jiJhRCYW26aeIYhD+IzVfG\n\tPPsSaEQiwZCjVG9i4ixi7pywzACKrWJVt/6/LyVyBe+fRSgNv4zQuGheYXMkodj8O8\n\tf9PcEQGNxTFJLkaBPauR2zslXfWTBbUDEzkii/eM=","Date":"Wed, 19 Jun 2019 15:29:08 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190619122908.GG5045@pendragon.ideasonboard.com>","References":"<20190619110548.20742-1-jacopo@jmondi.org>\n\t<20190619110548.20742-3-jacopo@jmondi.org>\n\t<20190619122029.GE5045@pendragon.ideasonboard.com>\n\t<20190619122654.nbffm2d7mrya3f73@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20190619122654.nbffm2d7mrya3f73@uno.localdomain>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","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":"Wed, 19 Jun 2019 12:29:26 -0000"}},{"id":1938,"web_url":"https://patchwork.libcamera.org/comment/1938/","msgid":"<20190619123840.32wfzfjaq6anemxq@uno.localdomain>","date":"2019-06-19T12:38:40","subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"HI Laurent,\n\nOn Wed, Jun 19, 2019 at 03:29:08PM +0300, Laurent Pinchart wrote:\n> Hi Jacopo,\n>\n> On Wed, Jun 19, 2019 at 02:26:54PM +0200, Jacopo Mondi wrote:\n> > On Wed, Jun 19, 2019 at 03:20:29PM +0300, Laurent Pinchart wrote:\n> > > On Wed, Jun 19, 2019 at 01:05:46PM +0200, Jacopo Mondi wrote:\n> > > > The V4L2 devices and subdevices share a few common operations,like\n> > > > opening and closing a device node, and perform IOCTLs on the device.\n> > > >\n> > > > With the forthcoming introduction of support for V4L2 controls, the\n> > > > quantity of shared code will increase, as the control support\n> > > > implementation is identical for the two derived classes.\n> > > >\n> > > > To maximize code re-use and avoid duplications, provide a V4L2Device\n> > > > base class which groups the common operations and members.\n> > > >\n> > > > The newly introduced base class provides methods to open/close a device\n> > > > node, access the file descriptor, and perform IOCTLs on the device.\n> > > >\n> > > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > ---\n> > > >  src/libcamera/include/v4l2_device.h      |  43 +++++++\n> > > >  src/libcamera/include/v4l2_subdevice.h   |   9 +-\n> > > >  src/libcamera/include/v4l2_videodevice.h |  10 +-\n> > > >  src/libcamera/meson.build                |   2 +\n> > > >  src/libcamera/v4l2_device.cpp            | 144 +++++++++++++++++++++++\n> > > >  src/libcamera/v4l2_subdevice.cpp         |  84 +++----------\n> > > >  src/libcamera/v4l2_videodevice.cpp       | 103 +++++-----------\n> > > >  7 files changed, 239 insertions(+), 156 deletions(-)\n> > > >  create mode 100644 src/libcamera/include/v4l2_device.h\n> > > >  create mode 100644 src/libcamera/v4l2_device.cpp\n> > > >\n> > > > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > > > new file mode 100644\n> > > > index 000000000000..2c26f3eae2b4\n> > > > --- /dev/null\n> > > > +++ b/src/libcamera/include/v4l2_device.h\n> > > > @@ -0,0 +1,43 @@\n> > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > > +/*\n> > > > + * Copyright (C) 2019, Google Inc.\n> > > > + *\n> > > > + * v4l2_device.h - Common base for V4L2 video devices and subdevices\n> > > > + */\n> > > > +#ifndef __LIBCAMERA_V4L2_DEVICE_H__\n> > > > +#define __LIBCAMERA_V4L2_DEVICE_H__\n> > > > +\n> > > > +#include <string>\n> > > > +\n> > > > +#include \"log.h\"\n> > > > +\n> > > > +namespace libcamera {\n> > > > +\n> > > > +class V4L2Device : protected Loggable\n> > > > +{\n> > > > +public:\n> > > > +\tvoid close();\n> > > > +\tbool isOpen() const { return fd_ != -1; }\n> > > > +\tconst std::string &deviceNode() const { return deviceNode_; }\n> > > > +\n> > > > +protected:\n> > > > +\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > > > +\t~V4L2Device() {}\n> > > > +\n> > > > +\tint fd() { return fd_; }\n> > > > +\n> > > > +\tint open(unsigned int flags);\n> > > > +\n> > > > +\tint ioctl(unsigned long request, void *argp);\n> > > > +\n> > > > +\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> > >\n> > > Let's try not to inline methods that are more complex than just\n> > > returning a member of the class.\n> >\n> > Ok. I always assumed the compiler knows better, and for simple methods\n> > like this one, implementing it here or in the cpp file it's a matter\n> > of tastes\n> >\n> > > I didn't notice in my previous review that V4L2Subdevice was using the\n> > > entity name as a prefix. I'm afraid my comment about moving logPrefix()\n> > > to V4L2Device was a bad one :-/ However, V4L2Device should still inherit\n> > > from Loggable. It will thus have a pure virtual logPrefix() inherited\n> > > from Loggable, requiring the derived classes to implement that method,\n> > > but allowing LOG() statements in V4L2Device to use the log prefix.\n> >\n> > Why is this (using the entity name as log prefix) bad for you? The\n> > tag is passed to the base class constructor. Might be a bit of a\n> > shortcut maybe, is this your concern ?\n>\n> My concern is two-fold. First of all, it requires passing a log tag to\n> the V4L2Device constructor, which isn't very nice. I thought both\n> derived classes used the device node name as a log tag, but that was not\n> correct.\n\nI agree it's a bit of a shortcut, yes\n\n>\n> Then, V4L2VideoDevice adds a suffix to the device node name to indicate\n> the queue direction. For mem-to-mem devices this is really useful, as a\n> single video device node exposes two queues, each of them handled by a\n> V4L2VideoDevice instance. I don't want to lose this debugging aid.\n>\n\nThat I considered not that relevant, but I ignore the case of devices\nwith multiple queues\n\nI'll drop that part, I'll squash your patch on top.\n\nThanks\n   j\n\n> > > Here's the diff I came up with, it can be squashed with this patch if\n> > > you agree on the comments.\n> > >\n> > > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > > index 2c26f3eae2b4..8c83adab4d43 100644\n> > > --- a/src/libcamera/include/v4l2_device.h\n> > > +++ b/src/libcamera/include/v4l2_device.h\n> > > @@ -21,7 +21,7 @@ public:\n> > >  \tconst std::string &deviceNode() const { return deviceNode_; }\n> > >\n> > >  protected:\n> > > -\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > > +\tV4L2Device(const std::string &deviceNode);\n> > >  \t~V4L2Device() {}\n> > >\n> > >  \tint fd() { return fd_; }\n> > > @@ -30,11 +30,8 @@ protected:\n> > >\n> > >  \tint ioctl(unsigned long request, void *argp);\n> > >\n> > > -\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> > > -\n> > >  private:\n> > >  \tstd::string deviceNode_;\n> > > -\tstd::string logTag_;\n> > >  \tint fd_;\n> > >  };\n> > >\n> > > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > > index b1da4a87c553..9c077674f997 100644\n> > > --- a/src/libcamera/include/v4l2_subdevice.h\n> > > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > > @@ -52,6 +52,9 @@ public:\n> > >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> > >  \t\t\t\t\t     const std::string &entity);\n> > >\n> > > +protected:\n> > > +\tstd::string logPrefix() const;\n> > > +\n> > >  private:\n> > >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> > >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > > index e73dec13f847..734b34f1dc53 100644\n> > > --- a/src/libcamera/include/v4l2_videodevice.h\n> > > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > > @@ -147,6 +147,9 @@ public:\n> > >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> > >  \t\t\t\t\t       const std::string &entity);\n> > >\n> > > +protected:\n> > > +\tstd::string logPrefix() const;\n> > > +\n> > >  private:\n> > >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> > >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > > index eeed0a70fb0e..1f113780ba45 100644\n> > > --- a/src/libcamera/v4l2_device.cpp\n> > > +++ b/src/libcamera/v4l2_device.cpp\n> > > @@ -68,17 +68,15 @@ void V4L2Device::close()\n> > >  /**\n> > >   * \\brief Construct a V4L2Device\n> > >   * \\param[in] deviceNode The device node filesystem path\n> > > - * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > > - * the device in the log output\n> > >   *\n> > >   * The V4L2Device constructor is protected and can only be accessed by the\n> > >   * V4L2VideoDevice and V4L2Subdevice derived classes.\n> > >   *\n> > >   * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> > >   * at open() time, and the \\a logTag to prefix log messages with.\n> > >   */\n> > > -V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > > -\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > > +V4L2Device::V4L2Device(const std::string &deviceNode)\n> > > +\t: deviceNode_(deviceNode), fd_(-1)\n> > >  {\n> > >  }\n> > >\n> > > @@ -99,6 +94,7 @@ V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > >   */\n> > >  int V4L2Device::open(unsigned int flags)\n> > >  {\n> > > +\tLOG(V4L2, Info) << \"Opening device\";\n> > >  \tif (isOpen()) {\n> > >  \t\tLOG(V4L2, Error) << \"Device already open\";\n> > >  \t\treturn -EBUSY;\n> > > @@ -135,10 +131,4 @@ int V4L2Device::ioctl(unsigned long request, void *argp)\n> > >  \treturn 0;\n> > >  }\n> > >\n> > > -/**\n> > > - * \\fn V4L2Device::logPrefix()\n> > > - * \\brief Retrieve the tag to prefix log messages with\n> > > - * \\return The tag to prefix log messages with\n> > > - */\n> > > -\n> > >  } /* namespace libcamera */\n> > > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > > index d0e1d717b26c..a188298de34c 100644\n> > > --- a/src/libcamera/v4l2_subdevice.cpp\n> > > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> > >   * path\n> > >   */\n> > >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > > -\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> > > +\t: V4L2Device(entity->deviceNode()), entity_(entity)\n> > >  {\n> > >  }\n> > >\n> > > @@ -265,6 +265,11 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> > >  \treturn new V4L2Subdevice(mediaEntity);\n> > >  }\n> > >\n> > > +std::string V4L2Subdevice::logPrefix() const\n> > > +{\n> > > +\treturn \"'\" + entity_->name() + \"'\";\n> > > +}\n> > > +\n> > >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > >  {\n> > >  \tstd::vector<unsigned int> codes;\n> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > index 403e0b2e0653..2de3751f467e 100644\n> > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> > >   * \\param[in] deviceNode The file-system path to the video device node\n> > >   */\n> > >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > > -\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> > > +\t: V4L2Device(deviceNode), bufferPool_(nullptr),\n> > >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> > >  {\n> > >  \t/*\n> > > @@ -389,6 +389,11 @@ void V4L2VideoDevice::close()\n> > >   * \\return The string containing the device location\n> > >   */\n> > >\n> > > +std::string V4L2VideoDevice::logPrefix() const\n> > > +{\n> > > +\treturn deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > > +}\n> > > +\n> > >  /**\n> > >   * \\brief Retrieve the image format set on the V4L2 device\n> > >   * \\param[out] format The image format applied on the device\n> > >\n> > > > +\n> > > > +private:\n> > > > +\tstd::string deviceNode_;\n> > > > +\tstd::string logTag_;\n> > > > +\tint fd_;\n> > > > +};\n> > > > +\n> > > > +} /* namespace libcamera */\n> > > > +\n> > > > +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */\n> > > > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > > > index 9afd28b6db02..b1da4a87c553 100644\n> > > > --- a/src/libcamera/include/v4l2_subdevice.h\n> > > > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > > > @@ -16,6 +16,7 @@\n> > > >  #include \"formats.h\"\n> > > >  #include \"log.h\"\n> > > >  #include \"media_object.h\"\n> > > > +#include \"v4l2_device.h\"\n> > > >\n> > > >  namespace libcamera {\n> > > >\n> > > > @@ -28,7 +29,7 @@ struct V4L2SubdeviceFormat {\n> > > >  \tconst std::string toString() const;\n> > > >  };\n> > > >\n> > > > -class V4L2Subdevice : protected Loggable\n> > > > +class V4L2Subdevice : public V4L2Device\n> > > >  {\n> > > >  public:\n> > > >  \texplicit V4L2Subdevice(const MediaEntity *entity);\n> > > > @@ -37,8 +38,6 @@ public:\n> > > >  \t~V4L2Subdevice();\n> > > >\n> > > >  \tint open();\n> > > > -\tbool isOpen() const;\n> > > > -\tvoid close();\n> > > >\n> > > >  \tconst MediaEntity *entity() const { return entity_; }\n> > > >\n> > > > @@ -53,9 +52,6 @@ public:\n> > > >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> > > >  \t\t\t\t\t     const std::string &entity);\n> > > >\n> > > > -protected:\n> > > > -\tstd::string logPrefix() const;\n> > > > -\n> > > >  private:\n> > > >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> > > >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > > > @@ -65,7 +61,6 @@ private:\n> > > >  \t\t\t Rectangle *rect);\n> > > >\n> > > >  \tconst MediaEntity *entity_;\n> > > > -\tint fd_;\n> > > >  };\n> > > >\n> > > >  } /* namespace libcamera */\n> > > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > > > index 895658805b47..e73dec13f847 100644\n> > > > --- a/src/libcamera/include/v4l2_videodevice.h\n> > > > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > > > @@ -18,6 +18,7 @@\n> > > >\n> > > >  #include \"formats.h\"\n> > > >  #include \"log.h\"\n> > > > +#include \"v4l2_device.h\"\n> > > >\n> > > >  namespace libcamera {\n> > > >\n> > > > @@ -112,7 +113,7 @@ public:\n> > > >  \tconst std::string toString() const;\n> > > >  };\n> > > >\n> > > > -class V4L2VideoDevice : protected Loggable\n> > > > +class V4L2VideoDevice : public V4L2Device\n> > > >  {\n> > > >  public:\n> > > >  \texplicit V4L2VideoDevice(const std::string &deviceNode);\n> > > > @@ -123,13 +124,11 @@ public:\n> > > >  \tV4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;\n> > > >\n> > > >  \tint open();\n> > > > -\tbool isOpen() const;\n> > > >  \tvoid close();\n> > > >\n> > > >  \tconst char *driverName() const { return caps_.driver(); }\n> > > >  \tconst char *deviceName() const { return caps_.card(); }\n> > > >  \tconst char *busName() const { return caps_.bus_info(); }\n> > > > -\tconst std::string &deviceNode() const { return deviceNode_; }\n> > > >\n> > > >  \tint getFormat(V4L2DeviceFormat *format);\n> > > >  \tint setFormat(V4L2DeviceFormat *format);\n> > > > @@ -148,9 +147,6 @@ public:\n> > > >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> > > >  \t\t\t\t\t       const std::string &entity);\n> > > >\n> > > > -protected:\n> > > > -\tstd::string logPrefix() const;\n> > > > -\n> > > >  private:\n> > > >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> > > >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > > > @@ -171,8 +167,6 @@ private:\n> > > >  \tBuffer *dequeueBuffer();\n> > > >  \tvoid bufferAvailable(EventNotifier *notifier);\n> > > >\n> > > > -\tstd::string deviceNode_;\n> > > > -\tint fd_;\n> > > >  \tV4L2Capability caps_;\n> > > >\n> > > >  \tenum v4l2_buf_type bufferType_;\n> > > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > > > index 15ab53b1abbe..f26ad5b2dc57 100644\n> > > > --- a/src/libcamera/meson.build\n> > > > +++ b/src/libcamera/meson.build\n> > > > @@ -23,6 +23,7 @@ libcamera_sources = files([\n> > > >      'stream.cpp',\n> > > >      'timer.cpp',\n> > > >      'utils.cpp',\n> > > > +    'v4l2_device.cpp',\n> > > >      'v4l2_subdevice.cpp',\n> > > >      'v4l2_videodevice.cpp',\n> > > >  ])\n> > > > @@ -41,6 +42,7 @@ libcamera_headers = files([\n> > > >      'include/media_object.h',\n> > > >      'include/pipeline_handler.h',\n> > > >      'include/utils.h',\n> > > > +    'include/v4l2_device.h',\n> > > >      'include/v4l2_subdevice.h',\n> > > >      'include/v4l2_videodevice.h',\n> > > >  ])\n> > > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > > > new file mode 100644\n> > > > index 000000000000..eeed0a70fb0e\n> > > > --- /dev/null\n> > > > +++ b/src/libcamera/v4l2_device.cpp\n> > > > @@ -0,0 +1,144 @@\n> > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > > +/*\n> > > > + * Copyright (C) 2019, Google Inc.\n> > > > + *\n> > > > + * v4l2_device.cpp - Common base for V4L2 video devices and subdevices\n> > > > + */\n> > > > +\n> > > > +#include \"v4l2_device.h\"\n> > > > +\n> > > > +#include <fcntl.h>\n> > > > +#include <string.h>\n> > > > +#include <sys/ioctl.h>\n> > > > +#include <unistd.h>\n> > > > +\n> > > > +#include \"log.h\"\n> > > > +\n> > > > +/**\n> > > > + * \\file v4l2_device.h\n> > > > + * \\brief Common base for V4L2 devices and subdevices\n> > > > + */\n> > > > +\n> > > > +namespace libcamera {\n> > > > +\n> > > > +LOG_DEFINE_CATEGORY(V4L2)\n> > > > +\n> > > > +/**\n> > > > + * \\class V4L2Device\n> > > > + * \\brief Base class for V4L2VideoDevice and V4L2Subdevice\n> > > > + *\n> > > > + * The V4L2Device class groups together the methods and fields common to\n> > > > + * both the V4L2VideoDevice and V4L2Subdevice classes, and provides a base\n> > > > + * class whith methods to open and close the device node associated with the\n> > > > + * device and to perform IOCTL system calls on it.\n> > > > + *\n> > > > + * The V4L2Device class cannot be instantiated directly, as its constructor\n> > > > + * is protected. Users should instead create instances of one the derived\n> > > > + * classes to model either a V4L2 video device or a V4L2 subdevice.\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\brief Close the device node\n> > > > + *\n> > > > + * Reset the file descriptor to -1\n> > > > + */\n> > > > +void V4L2Device::close()\n> > > > +{\n> > > > +\tif (!isOpen())\n> > > > +\t\treturn;\n> > > > +\n> > > > +\tif (::close(fd_) < 0)\n> > > > +\t\tLOG(V4L2, Error) << \"Failed to close V4L2 device: \"\n> > > > +\t\t\t\t << strerror(errno);\n> > > > +\tfd_ = -1;\n> > > > +}\n> > > > +\n> > > > +/**\n> > > > + * \\fn V4L2Device::isOpen()\n> > > > + * \\brief Check if the V4L2 device node is open\n> > > > + * \\return True if the V4L2 device node is open, false otherwise\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\fn V4L2Device::deviceNode()\n> > > > + * \\brief Retrieve the device node path\n> > > > + * \\return The device node path\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\brief Construct a V4L2Device\n> > > > + * \\param[in] deviceNode The device node filesystem path\n> > > > + * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > > > + * the device in the log output\n> > > > + *\n> > > > + * The V4L2Device constructor is protected and can only be accessed by the\n> > > > + * V4L2VideoDevice and V4L2Subdevice derived classes.\n> > >\n> > > I would drop this sentence.\n> > >\n> > > > + *\n> > > > + * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> > > > + * at open() time, and the \\a logTag to prefix log messages with.\n> > > > + */\n> > > > +V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > > > +\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > > > +{\n> > > > +}\n> > > > +\n> > > > +/**\n> > > > + * \\fn V4L2Device::fd()\n> > > > + * \\brief Retrieve the V4L2 device file descriptor\n> > > > + * \\return The V4L2 device file descriptor, -1 if the device node is not open\n> > > > + */\n> > > > +\n> > > > +/**\n> > > > + * \\brief Open a V4L2 device node\n> > > > + * \\param[in] flags Access mode flags\n> > > > + *\n> > > > + * Open the device node path with the provided access mode \\a flags and\n> > > > + * initialize the file descriptor, which was initially set to -1.\n> > > > + *\n> > > > + * \\return 0 on success or a negative error code otherwise\n> > > > + */\n> > > > +int V4L2Device::open(unsigned int flags)\n> > > > +{\n> > > > +\tif (isOpen()) {\n> > > > +\t\tLOG(V4L2, Error) << \"Device already open\";\n> > > > +\t\treturn -EBUSY;\n> > > > +\t}\n> > > > +\n> > > > +\tint ret = ::open(deviceNode_.c_str(), flags);\n> > > > +\tif (ret < 0) {\n> > > > +\t\tret = -errno;\n> > > > +\t\tLOG(V4L2, Error) << \"Failed to open V4L2 device: \"\n> > > > +\t\t\t\t << strerror(-ret);\n> > > > +\t\treturn ret;\n> > > > +\t}\n> > > > +\n> > > > +\tfd_ = ret;\n> > > > +\n> > > > +\treturn 0;\n> > > > +}\n> > > > +\n> > > > +/**\n> > > > + * \\brief Perform an IOCTL system call on the device node\n> > > > + * \\param[in] request The IOCTL request code\n> > > > + * \\param[in] argp A pointer to the IOCTL argument\n> > > > + * \\return 0 on success or a negative error code otherwise\n> > > > + */\n> > > > +int V4L2Device::ioctl(unsigned long request, void *argp)\n> > > > +{\n> > > > +\t/*\n> > > > +\t * Printing out an error message is usually better performed\n> > > > +\t * in the caller, which can provide more context.\n> > > > +\t */\n> > > > +\tif (::ioctl(fd_, request, argp) < 0)\n> > > > +\t\treturn -errno;\n> > > > +\n> > > > +\treturn 0;\n> > > > +}\n> > > > +\n> > > > +/**\n> > > > + * \\fn V4L2Device::logPrefix()\n> > > > + * \\brief Retrieve the tag to prefix log messages with\n> > > > + * \\return The tag to prefix log messages with\n> > > > + */\n> > > > +\n> > > > +} /* namespace libcamera */\n> > > > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > > > index 48f1fcb30ed4..d0e1d717b26c 100644\n> > > > --- a/src/libcamera/v4l2_subdevice.cpp\n> > > > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > > > @@ -29,7 +29,7 @@\n> > > >\n> > > >  namespace libcamera {\n> > > >\n> > > > -LOG_DEFINE_CATEGORY(V4L2Subdev)\n> > > > +LOG_DECLARE_CATEGORY(V4L2)\n> > > >\n> > > >  /**\n> > > >   * \\struct V4L2SubdeviceFormat\n> > > > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> > > >   * path\n> > > >   */\n> > > >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > > > -\t: entity_(entity), fd_(-1)\n> > > > +\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> > > >  {\n> > > >  }\n> > > >\n> > > > @@ -117,45 +117,7 @@ V4L2Subdevice::~V4L2Subdevice()\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(entity_->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 '\"\n> > > > -\t\t\t<< entity_->deviceNode() << \"': \" << 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> > > > +\treturn V4L2Device::open(O_RDWR);\n> > > >  }\n> > > >\n> > > >  /**\n> > > > @@ -200,7 +162,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> > > >  \tImageFormats formats;\n> > > >\n> > > >  \tif (pad >= entity_->pads().size()) {\n> > > > -\t\tLOG(V4L2Subdev, Error) << \"Invalid pad: \" << pad;\n> > > > +\t\tLOG(V4L2, Error) << \"Invalid pad: \" << pad;\n> > > >  \t\treturn {};\n> > > >  \t}\n> > > >\n> > > > @@ -210,7 +172,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> > > >  \t\t\treturn {};\n> > > >\n> > > >  \t\tif (formats.addFormat(code, sizes)) {\n> > > > -\t\t\tLOG(V4L2Subdev, Error)\n> > > > +\t\t\tLOG(V4L2, Error)\n> > > >  \t\t\t\t<< \"Could not add sizes for media bus code \"\n> > > >  \t\t\t\t<< code << \" on pad \" << pad;\n> > > >  \t\t\treturn {};\n> > > > @@ -232,10 +194,9 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> > > >  \tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > > >  \tsubdevFmt.pad = pad;\n> > > >\n> > > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > > > +\tint ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > > -\t\tLOG(V4L2Subdev, Error)\n> > > > +\t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to get format on pad \" << pad\n> > > >  \t\t\t<< \": \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > > @@ -268,10 +229,9 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> > > >  \tsubdevFmt.format.height = format->size.height;\n> > > >  \tsubdevFmt.format.code = format->mbus_code;\n> > > >\n> > > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > > > +\tint ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > > -\t\tLOG(V4L2Subdev, Error)\n> > > > +\t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to set format on pad \" << pad\n> > > >  \t\t\t<< \": \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > > @@ -305,11 +265,6 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> > > >  \treturn new V4L2Subdevice(mediaEntity);\n> > > >  }\n> > > >\n> > > > -std::string V4L2Subdevice::logPrefix() const\n> > > > -{\n> > > > -\treturn \"'\" + entity_->name() + \"'\";\n> > > > -}\n> > > > -\n> > > >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > > >  {\n> > > >  \tstd::vector<unsigned int> codes;\n> > > > @@ -321,18 +276,17 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > > >  \t\tmbusEnum.index = index;\n> > > >  \t\tmbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > > >\n> > > > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > > > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > > >  \t\tif (ret)\n> > > >  \t\t\tbreak;\n> > > >\n> > > >  \t\tcodes.push_back(mbusEnum.code);\n> > > >  \t}\n> > > >\n> > > > -\tif (ret && errno != EINVAL) {\n> > > > -\t\tret = errno;\n> > > > -\t\tLOG(V4L2Subdev, Error)\n> > > > +\tif (ret < 0 && ret != -EINVAL) {\n> > > > +\t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to enumerate formats on pad \" << pad\n> > > > -\t\t\t<< \": \" << strerror(ret);\n> > > > +\t\t\t<< \": \" << strerror(-ret);\n> > > >  \t\treturn {};\n> > > >  \t}\n> > > >\n> > > > @@ -352,7 +306,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> > > >  \t\tsizeEnum.code = code;\n> > > >  \t\tsizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > > >\n> > > > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > > > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > > >  \t\tif (ret)\n> > > >  \t\t\tbreak;\n> > > >\n> > > > @@ -360,9 +314,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> > > >  \t\t\t\t   sizeEnum.max_width, sizeEnum.max_height);\n> > > >  \t}\n> > > >\n> > > > -\tif (ret && (errno != EINVAL && errno != ENOTTY)) {\n> > > > -\t\tret = -errno;\n> > > > -\t\tLOG(V4L2Subdev, Error)\n> > > > +\tif (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {\n> > > > +\t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to enumerate sizes on pad \" << pad\n> > > >  \t\t\t<< \": \" << strerror(-ret);\n> > > >  \t\treturn {};\n> > > > @@ -386,10 +339,9 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,\n> > > >  \tsel.r.width = rect->w;\n> > > >  \tsel.r.height = rect->h;\n> > > >\n> > > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > > > +\tint ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > > -\t\tLOG(V4L2Subdev, Error)\n> > > > +\t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to set rectangle \" << target << \" on pad \"\n> > > >  \t\t\t<< pad << \": \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > > index 0e70498c8bb1..403e0b2e0653 100644\n> > > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > > @@ -30,7 +30,7 @@\n> > > >   */\n> > > >  namespace libcamera {\n> > > >\n> > > > -LOG_DEFINE_CATEGORY(V4L2)\n> > > > +LOG_DECLARE_CATEGORY(V4L2)\n> > > >\n> > > >  /**\n> > > >   * \\struct V4L2Capability\n> > > > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> > > >   * \\param[in] deviceNode The file-system path to the video device node\n> > > >   */\n> > > >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > > > -\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n> > > > +\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> > > >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> > > >  {\n> > > >  \t/*\n> > > > @@ -305,23 +305,12 @@ int V4L2VideoDevice::open()\n> > > >  {\n> > > >  \tint ret;\n> > > >\n> > > > -\tif (isOpen()) {\n> > > > -\t\tLOG(V4L2, Error) << \"Device already open\";\n> > > > -\t\treturn -EBUSY;\n> > > > -\t}\n> > > > -\n> > > > -\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n> > > > -\tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > > -\t\tLOG(V4L2, Error)\n> > > > -\t\t\t<< \"Failed to open V4L2 device: \" << strerror(-ret);\n> > > > +\tret = V4L2Device::open(O_RDWR | O_NONBLOCK);\n> > > > +\tif (ret < 0)\n> > > >  \t\treturn ret;\n> > > > -\t}\n> > > > -\tfd_ = ret;\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);\n> > > > +\tret = ioctl(VIDIOC_QUERYCAP, &caps_);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to query device capabilities: \"\n> > > >  \t\t\t<< strerror(-ret);\n> > > > @@ -342,20 +331,20 @@ int V4L2VideoDevice::open()\n> > > >  \t * (POLLIN), and write notifications for OUTPUT devices (POLLOUT).\n> > > >  \t */\n> > > >  \tif (caps_.isVideoCapture()) {\n> > > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> > > >  \t\tbufferType_ = caps_.isMultiplanar()\n> > > >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE\n> > > >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> > > >  \t} else if (caps_.isVideoOutput()) {\n> > > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> > > >  \t\tbufferType_ = caps_.isMultiplanar()\n> > > >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n> > > >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n> > > >  \t} else if (caps_.isMetaCapture()) {\n> > > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> > > >  \t\tbufferType_ = V4L2_BUF_TYPE_META_CAPTURE;\n> > > >  \t} else if (caps_.isMetaOutput()) {\n> > > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> > > >  \t\tbufferType_ = V4L2_BUF_TYPE_META_OUTPUT;\n> > > >  \t} else {\n> > > >  \t\tLOG(V4L2, Error) << \"Device is not a supported type\";\n> > > > @@ -368,28 +357,18 @@ int V4L2VideoDevice::open()\n> > > >  \treturn 0;\n> > > >  }\n> > > >\n> > > > -/**\n> > > > - * \\brief Check if device is successfully opened\n> > > > - * \\return True if the device is open, false otherwise\n> > > > - */\n> > > > -bool V4L2VideoDevice::isOpen() const\n> > > > -{\n> > > > -\treturn fd_ != -1;\n> > > > -}\n> > > > -\n> > > >  /**\n> > > >   * \\brief Close the device, releasing any resources acquired by open()\n> > > >   */\n> > > >  void V4L2VideoDevice::close()\n> > > >  {\n> > > > -\tif (fd_ < 0)\n> > > > +\tif (!isOpen())\n> > > >  \t\treturn;\n> > > >\n> > > >  \treleaseBuffers();\n> > > >  \tdelete fdEvent_;\n> > > >\n> > > > -\t::close(fd_);\n> > > > -\tfd_ = -1;\n> > > > +\tV4L2Device::close();\n> > > >  }\n> > > >\n> > > >  /**\n> > > > @@ -410,17 +389,6 @@ void V4L2VideoDevice::close()\n> > > >   * \\return The string containing the device location\n> > > >   */\n> > > >\n> > > > -/**\n> > > > - * \\fn V4L2VideoDevice::deviceNode()\n> > > > - * \\brief Retrieve the video device node path\n> > > > - * \\return The video device device node path\n> > > > - */\n> > > > -\n> > > > -std::string V4L2VideoDevice::logPrefix() const\n> > > > -{\n> > > > -\treturn deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > > > -}\n> > > > -\n> > > >  /**\n> > > >   * \\brief Retrieve the image format set on the V4L2 device\n> > > >   * \\param[out] format The image format applied on the device\n> > > > @@ -462,9 +430,8 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)\n> > > >  \tint ret;\n> > > >\n> > > >  \tv4l2Format.type = bufferType_;\n> > > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > >  \t}\n> > > > @@ -488,9 +455,8 @@ int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)\n> > > >  \tv4l2Format.type = bufferType_;\n> > > >  \tpix->dataformat = format->fourcc;\n> > > >  \tpix->buffersize = format->planes[0].size;\n> > > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > >  \t}\n> > > > @@ -516,9 +482,8 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)\n> > > >  \tint ret;\n> > > >\n> > > >  \tv4l2Format.type = bufferType_;\n> > > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > >  \t}\n> > > > @@ -554,9 +519,8 @@ int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)\n> > > >  \t\tpix->plane_fmt[i].sizeimage = format->planes[i].size;\n> > > >  \t}\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > >  \t}\n> > > > @@ -584,9 +548,8 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)\n> > > >  \tint ret;\n> > > >\n> > > >  \tv4l2Format.type = bufferType_;\n> > > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > >  \t}\n> > > > @@ -613,9 +576,8 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)\n> > > >  \tpix->pixelformat = format->fourcc;\n> > > >  \tpix->bytesperline = format->planes[0].bpl;\n> > > >  \tpix->field = V4L2_FIELD_NONE;\n> > > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > > >  \tif (ret) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > >  \t}\n> > > > @@ -670,9 +632,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n> > > >  \trb.type = bufferType_;\n> > > >  \trb.memory = memoryType_;\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_REQBUFS, &rb);\n> > > > +\tret = ioctl(VIDIOC_REQBUFS, &rb);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to request \" << count << \" buffers: \"\n> > > >  \t\t\t<< strerror(-ret);\n> > > > @@ -722,9 +683,8 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n> > > >  \t\tbuf.length = VIDEO_MAX_PLANES;\n> > > >  \t\tbuf.m.planes = planes;\n> > > >\n> > > > -\t\tret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);\n> > > > +\t\tret = ioctl(VIDIOC_QUERYBUF, &buf);\n> > > >  \t\tif (ret < 0) {\n> > > > -\t\t\tret = -errno;\n> > > >  \t\t\tLOG(V4L2, Error)\n> > > >  \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n> > > >  \t\t\t\t<< strerror(-ret);\n> > > > @@ -775,9 +735,8 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex,\n> > > >  \texpbuf.plane = planeIndex;\n> > > >  \texpbuf.flags = O_RDWR;\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);\n> > > > +\tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to export buffer: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > > @@ -801,15 +760,14 @@ std::vector<unsigned int> V4L2VideoDevice::enumPixelformats()\n> > > >  \t\tpixelformatEnum.index = index;\n> > > >  \t\tpixelformatEnum.type = bufferType_;\n> > > >\n> > > > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > > > +\t\tret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > > >  \t\tif (ret)\n> > > >  \t\t\tbreak;\n> > > >\n> > > >  \t\tformats.push_back(pixelformatEnum.pixelformat);\n> > > >  \t}\n> > > >\n> > > > -\tif (ret && errno != EINVAL) {\n> > > > -\t\tret = -errno;\n> > > > +\tif (ret && ret != -EINVAL) {\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to enumerate pixel formats: \"\n> > > >  \t\t\t<< strerror(-ret);\n> > > > @@ -829,7 +787,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> > > >  \t\tframeSize.index = index;\n> > > >  \t\tframeSize.pixel_format = pixelFormat;\n> > > >\n> > > > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > > > +\t\tret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > > >  \t\tif (ret)\n> > > >  \t\t\tbreak;\n> > > >\n> > > > @@ -867,8 +825,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> > > >  \t\t}\n> > > >  \t}\n> > > >\n> > > > -\tif (ret && errno != EINVAL) {\n> > > > -\t\tret = -errno;\n> > > > +\tif (ret && ret != -EINVAL) {\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Unable to enumerate frame sizes: \"\n> > > >  \t\t\t<< strerror(-ret);\n> > > > @@ -969,9 +926,8 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n> > > >\n> > > >  \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n> > > > +\tret = ioctl(VIDIOC_QBUF, &buf);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n> > > >  \t\t\t<< strerror(-ret);\n> > > > @@ -1006,9 +962,8 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > > >  \t\tbuf.m.planes = planes;\n> > > >  \t}\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n> > > > +\tret = ioctl(VIDIOC_DQBUF, &buf);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> > > >  \t\treturn nullptr;\n> > > > @@ -1066,9 +1021,8 @@ int V4L2VideoDevice::streamOn()\n> > > >  {\n> > > >  \tint ret;\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);\n> > > > +\tret = ioctl(VIDIOC_STREAMON, &bufferType_);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to start streaming: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > > > @@ -1089,9 +1043,8 @@ int V4L2VideoDevice::streamOff()\n> > > >  {\n> > > >  \tint ret;\n> > > >\n> > > > -\tret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);\n> > > > +\tret = ioctl(VIDIOC_STREAMOFF, &bufferType_);\n> > > >  \tif (ret < 0) {\n> > > > -\t\tret = -errno;\n> > > >  \t\tLOG(V4L2, Error)\n> > > >  \t\t\t<< \"Failed to stop streaming: \" << strerror(-ret);\n> > > >  \t\treturn ret;\n> > >\n> > > --\n> > > Regards,\n> > >\n> > > Laurent Pinchart\n>\n>\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay10.mail.gandi.net (relay10.mail.gandi.net\n\t[217.70.178.230])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9457E61A15\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Jun 2019 14:37:26 +0200 (CEST)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay10.mail.gandi.net (Postfix) with ESMTPSA id E9D48240007;\n\tWed, 19 Jun 2019 12:37:25 +0000 (UTC)"],"Date":"Wed, 19 Jun 2019 14:38:40 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190619123840.32wfzfjaq6anemxq@uno.localdomain>","References":"<20190619110548.20742-1-jacopo@jmondi.org>\n\t<20190619110548.20742-3-jacopo@jmondi.org>\n\t<20190619122029.GE5045@pendragon.ideasonboard.com>\n\t<20190619122654.nbffm2d7mrya3f73@uno.localdomain>\n\t<20190619122908.GG5045@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"qzxlhvip7cditdk5\"","Content-Disposition":"inline","In-Reply-To":"<20190619122908.GG5045@pendragon.ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","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":"Wed, 19 Jun 2019 12:37:26 -0000"}},{"id":1942,"web_url":"https://patchwork.libcamera.org/comment/1942/","msgid":"<20190619130727.3kbaz6vu3ort4ur7@uno.localdomain>","date":"2019-06-19T13:07:27","subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi\nLaurent,\nOn Wed, Jun 19, 2019 at 03:20:29PM +0300, Laurent Pinchart wrote:\n> Hi Jacopo,\n>\n> Thank you for the patch.\n>\n> On Wed, Jun 19, 2019 at 01:05:46PM +0200, Jacopo Mondi wrote:\n> > The V4L2 devices and subdevices share a few common operations,like\n> > opening and closing a device node, and perform IOCTLs on the device.\n> >\n> > With the forthcoming introduction of support for V4L2 controls, the\n> > quantity of shared code will increase, as the control support\n> > implementation is identical for the two derived classes.\n> >\n> > To maximize code re-use and avoid duplications, provide a V4L2Device\n> > base class which groups the common operations and members.\n> >\n> > The newly introduced base class provides methods to open/close a device\n> > node, access the file descriptor, and perform IOCTLs on the device.\n> >\n\nWith your patch squashed on top and the other comment addressed, can I\nhave your tag (and push) or would you like another iteration?\n\n> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > ---\n> >  src/libcamera/include/v4l2_device.h      |  43 +++++++\n> >  src/libcamera/include/v4l2_subdevice.h   |   9 +-\n> >  src/libcamera/include/v4l2_videodevice.h |  10 +-\n> >  src/libcamera/meson.build                |   2 +\n> >  src/libcamera/v4l2_device.cpp            | 144 +++++++++++++++++++++++\n> >  src/libcamera/v4l2_subdevice.cpp         |  84 +++----------\n> >  src/libcamera/v4l2_videodevice.cpp       | 103 +++++-----------\n> >  7 files changed, 239 insertions(+), 156 deletions(-)\n> >  create mode 100644 src/libcamera/include/v4l2_device.h\n> >  create mode 100644 src/libcamera/v4l2_device.cpp\n> >\n> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > new file mode 100644\n> > index 000000000000..2c26f3eae2b4\n> > --- /dev/null\n> > +++ b/src/libcamera/include/v4l2_device.h\n> > @@ -0,0 +1,43 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2019, Google Inc.\n> > + *\n> > + * v4l2_device.h - Common base for V4L2 video devices and subdevices\n> > + */\n> > +#ifndef __LIBCAMERA_V4L2_DEVICE_H__\n> > +#define __LIBCAMERA_V4L2_DEVICE_H__\n> > +\n> > +#include <string>\n> > +\n> > +#include \"log.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +class V4L2Device : protected Loggable\n> > +{\n> > +public:\n> > +\tvoid close();\n> > +\tbool isOpen() const { return fd_ != -1; }\n> > +\tconst std::string &deviceNode() const { return deviceNode_; }\n> > +\n> > +protected:\n> > +\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > +\t~V4L2Device() {}\n> > +\n> > +\tint fd() { return fd_; }\n> > +\n> > +\tint open(unsigned int flags);\n> > +\n> > +\tint ioctl(unsigned long request, void *argp);\n> > +\n> > +\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n>\n> Let's try not to inline methods that are more complex than just\n> returning a member of the class.\n>\n> I didn't notice in my previous review that V4L2Subdevice was using the\n> entity name as a prefix. I'm afraid my comment about moving logPrefix()\n> to V4L2Device was a bad one :-/ However, V4L2Device should still inherit\n> from Loggable. It will thus have a pure virtual logPrefix() inherited\n> from Loggable, requiring the derived classes to implement that method,\n> but allowing LOG() statements in V4L2Device to use the log prefix.\n>\n> Here's the diff I came up with, it can be squashed with this patch if\n> you agree on the comments.\n>\n> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> index 2c26f3eae2b4..8c83adab4d43 100644\n> --- a/src/libcamera/include/v4l2_device.h\n> +++ b/src/libcamera/include/v4l2_device.h\n> @@ -21,7 +21,7 @@ public:\n>  \tconst std::string &deviceNode() const { return deviceNode_; }\n>\n>  protected:\n> -\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> +\tV4L2Device(const std::string &deviceNode);\n>  \t~V4L2Device() {}\n>\n>  \tint fd() { return fd_; }\n> @@ -30,11 +30,8 @@ protected:\n>\n>  \tint ioctl(unsigned long request, void *argp);\n>\n> -\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> -\n>  private:\n>  \tstd::string deviceNode_;\n> -\tstd::string logTag_;\n>  \tint fd_;\n>  };\n>\n> diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> index b1da4a87c553..9c077674f997 100644\n> --- a/src/libcamera/include/v4l2_subdevice.h\n> +++ b/src/libcamera/include/v4l2_subdevice.h\n> @@ -52,6 +52,9 @@ public:\n>  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n>  \t\t\t\t\t     const std::string &entity);\n>\n> +protected:\n> +\tstd::string logPrefix() const;\n> +\n>  private:\n>  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n>  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> index e73dec13f847..734b34f1dc53 100644\n> --- a/src/libcamera/include/v4l2_videodevice.h\n> +++ b/src/libcamera/include/v4l2_videodevice.h\n> @@ -147,6 +147,9 @@ public:\n>  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n>  \t\t\t\t\t       const std::string &entity);\n>\n> +protected:\n> +\tstd::string logPrefix() const;\n> +\n>  private:\n>  \tint getFormatMeta(V4L2DeviceFormat *format);\n>  \tint setFormatMeta(V4L2DeviceFormat *format);\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> index eeed0a70fb0e..1f113780ba45 100644\n> --- a/src/libcamera/v4l2_device.cpp\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -68,17 +68,15 @@ void V4L2Device::close()\n>  /**\n>   * \\brief Construct a V4L2Device\n>   * \\param[in] deviceNode The device node filesystem path\n> - * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> - * the device in the log output\n>   *\n>   * The V4L2Device constructor is protected and can only be accessed by the\n>   * V4L2VideoDevice and V4L2Subdevice derived classes.\n>   *\n>   * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n>   * at open() time, and the \\a logTag to prefix log messages with.\n>   */\n> -V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> -\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> +V4L2Device::V4L2Device(const std::string &deviceNode)\n> +\t: deviceNode_(deviceNode), fd_(-1)\n>  {\n>  }\n>\n> @@ -99,6 +94,7 @@ V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n>   */\n>  int V4L2Device::open(unsigned int flags)\n>  {\n> +\tLOG(V4L2, Info) << \"Opening device\";\n>  \tif (isOpen()) {\n>  \t\tLOG(V4L2, Error) << \"Device already open\";\n>  \t\treturn -EBUSY;\n> @@ -135,10 +131,4 @@ int V4L2Device::ioctl(unsigned long request, void *argp)\n>  \treturn 0;\n>  }\n>\n> -/**\n> - * \\fn V4L2Device::logPrefix()\n> - * \\brief Retrieve the tag to prefix log messages with\n> - * \\return The tag to prefix log messages with\n> - */\n> -\n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> index d0e1d717b26c..a188298de34c 100644\n> --- a/src/libcamera/v4l2_subdevice.cpp\n> +++ b/src/libcamera/v4l2_subdevice.cpp\n> @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n>   * path\n>   */\n>  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> -\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> +\t: V4L2Device(entity->deviceNode()), entity_(entity)\n>  {\n>  }\n>\n> @@ -265,6 +265,11 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n>  \treturn new V4L2Subdevice(mediaEntity);\n>  }\n>\n> +std::string V4L2Subdevice::logPrefix() const\n> +{\n> +\treturn \"'\" + entity_->name() + \"'\";\n> +}\n> +\n>  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n>  {\n>  \tstd::vector<unsigned int> codes;\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 403e0b2e0653..2de3751f467e 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n>   * \\param[in] deviceNode The file-system path to the video device node\n>   */\n>  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> -\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> +\t: V4L2Device(deviceNode), bufferPool_(nullptr),\n>  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n>  {\n>  \t/*\n> @@ -389,6 +389,11 @@ void V4L2VideoDevice::close()\n>   * \\return The string containing the device location\n>   */\n>\n> +std::string V4L2VideoDevice::logPrefix() const\n> +{\n> +\treturn deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> +}\n> +\n>  /**\n>   * \\brief Retrieve the image format set on the V4L2 device\n>   * \\param[out] format The image format applied on the device\n>\n> > +\n> > +private:\n> > +\tstd::string deviceNode_;\n> > +\tstd::string logTag_;\n> > +\tint fd_;\n> > +};\n> > +\n> > +} /* namespace libcamera */\n> > +\n> > +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */\n> > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > index 9afd28b6db02..b1da4a87c553 100644\n> > --- a/src/libcamera/include/v4l2_subdevice.h\n> > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > @@ -16,6 +16,7 @@\n> >  #include \"formats.h\"\n> >  #include \"log.h\"\n> >  #include \"media_object.h\"\n> > +#include \"v4l2_device.h\"\n> >\n> >  namespace libcamera {\n> >\n> > @@ -28,7 +29,7 @@ struct V4L2SubdeviceFormat {\n> >  \tconst std::string toString() const;\n> >  };\n> >\n> > -class V4L2Subdevice : protected Loggable\n> > +class V4L2Subdevice : public V4L2Device\n> >  {\n> >  public:\n> >  \texplicit V4L2Subdevice(const MediaEntity *entity);\n> > @@ -37,8 +38,6 @@ public:\n> >  \t~V4L2Subdevice();\n> >\n> >  \tint open();\n> > -\tbool isOpen() const;\n> > -\tvoid close();\n> >\n> >  \tconst MediaEntity *entity() const { return entity_; }\n> >\n> > @@ -53,9 +52,6 @@ public:\n> >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t     const std::string &entity);\n> >\n> > -protected:\n> > -\tstd::string logPrefix() const;\n> > -\n> >  private:\n> >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > @@ -65,7 +61,6 @@ private:\n> >  \t\t\t Rectangle *rect);\n> >\n> >  \tconst MediaEntity *entity_;\n> > -\tint fd_;\n> >  };\n> >\n> >  } /* namespace libcamera */\n> > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > index 895658805b47..e73dec13f847 100644\n> > --- a/src/libcamera/include/v4l2_videodevice.h\n> > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > @@ -18,6 +18,7 @@\n> >\n> >  #include \"formats.h\"\n> >  #include \"log.h\"\n> > +#include \"v4l2_device.h\"\n> >\n> >  namespace libcamera {\n> >\n> > @@ -112,7 +113,7 @@ public:\n> >  \tconst std::string toString() const;\n> >  };\n> >\n> > -class V4L2VideoDevice : protected Loggable\n> > +class V4L2VideoDevice : public V4L2Device\n> >  {\n> >  public:\n> >  \texplicit V4L2VideoDevice(const std::string &deviceNode);\n> > @@ -123,13 +124,11 @@ public:\n> >  \tV4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;\n> >\n> >  \tint open();\n> > -\tbool isOpen() const;\n> >  \tvoid close();\n> >\n> >  \tconst char *driverName() const { return caps_.driver(); }\n> >  \tconst char *deviceName() const { return caps_.card(); }\n> >  \tconst char *busName() const { return caps_.bus_info(); }\n> > -\tconst std::string &deviceNode() const { return deviceNode_; }\n> >\n> >  \tint getFormat(V4L2DeviceFormat *format);\n> >  \tint setFormat(V4L2DeviceFormat *format);\n> > @@ -148,9 +147,6 @@ public:\n> >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t       const std::string &entity);\n> >\n> > -protected:\n> > -\tstd::string logPrefix() const;\n> > -\n> >  private:\n> >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > @@ -171,8 +167,6 @@ private:\n> >  \tBuffer *dequeueBuffer();\n> >  \tvoid bufferAvailable(EventNotifier *notifier);\n> >\n> > -\tstd::string deviceNode_;\n> > -\tint fd_;\n> >  \tV4L2Capability caps_;\n> >\n> >  \tenum v4l2_buf_type bufferType_;\n> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > index 15ab53b1abbe..f26ad5b2dc57 100644\n> > --- a/src/libcamera/meson.build\n> > +++ b/src/libcamera/meson.build\n> > @@ -23,6 +23,7 @@ libcamera_sources = files([\n> >      'stream.cpp',\n> >      'timer.cpp',\n> >      'utils.cpp',\n> > +    'v4l2_device.cpp',\n> >      'v4l2_subdevice.cpp',\n> >      'v4l2_videodevice.cpp',\n> >  ])\n> > @@ -41,6 +42,7 @@ libcamera_headers = files([\n> >      'include/media_object.h',\n> >      'include/pipeline_handler.h',\n> >      'include/utils.h',\n> > +    'include/v4l2_device.h',\n> >      'include/v4l2_subdevice.h',\n> >      'include/v4l2_videodevice.h',\n> >  ])\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > new file mode 100644\n> > index 000000000000..eeed0a70fb0e\n> > --- /dev/null\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -0,0 +1,144 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2019, Google Inc.\n> > + *\n> > + * v4l2_device.cpp - Common base for V4L2 video devices and subdevices\n> > + */\n> > +\n> > +#include \"v4l2_device.h\"\n> > +\n> > +#include <fcntl.h>\n> > +#include <string.h>\n> > +#include <sys/ioctl.h>\n> > +#include <unistd.h>\n> > +\n> > +#include \"log.h\"\n> > +\n> > +/**\n> > + * \\file v4l2_device.h\n> > + * \\brief Common base for V4L2 devices and subdevices\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DEFINE_CATEGORY(V4L2)\n> > +\n> > +/**\n> > + * \\class V4L2Device\n> > + * \\brief Base class for V4L2VideoDevice and V4L2Subdevice\n> > + *\n> > + * The V4L2Device class groups together the methods and fields common to\n> > + * both the V4L2VideoDevice and V4L2Subdevice classes, and provides a base\n> > + * class whith methods to open and close the device node associated with the\n> > + * device and to perform IOCTL system calls on it.\n> > + *\n> > + * The V4L2Device class cannot be instantiated directly, as its constructor\n> > + * is protected. Users should instead create instances of one the derived\n> > + * classes to model either a V4L2 video device or a V4L2 subdevice.\n> > + */\n> > +\n> > +/**\n> > + * \\brief Close the device node\n> > + *\n> > + * Reset the file descriptor to -1\n> > + */\n> > +void V4L2Device::close()\n> > +{\n> > +\tif (!isOpen())\n> > +\t\treturn;\n> > +\n> > +\tif (::close(fd_) < 0)\n> > +\t\tLOG(V4L2, Error) << \"Failed to close V4L2 device: \"\n> > +\t\t\t\t << strerror(errno);\n> > +\tfd_ = -1;\n> > +}\n> > +\n> > +/**\n> > + * \\fn V4L2Device::isOpen()\n> > + * \\brief Check if the V4L2 device node is open\n> > + * \\return True if the V4L2 device node is open, false otherwise\n> > + */\n> > +\n> > +/**\n> > + * \\fn V4L2Device::deviceNode()\n> > + * \\brief Retrieve the device node path\n> > + * \\return The device node path\n> > + */\n> > +\n> > +/**\n> > + * \\brief Construct a V4L2Device\n> > + * \\param[in] deviceNode The device node filesystem path\n> > + * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > + * the device in the log output\n> > + *\n> > + * The V4L2Device constructor is protected and can only be accessed by the\n> > + * V4L2VideoDevice and V4L2Subdevice derived classes.\n>\n> I would drop this sentence.\n>\n> > + *\n> > + * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> > + * at open() time, and the \\a logTag to prefix log messages with.\n> > + */\n> > +V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > +\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\fn V4L2Device::fd()\n> > + * \\brief Retrieve the V4L2 device file descriptor\n> > + * \\return The V4L2 device file descriptor, -1 if the device node is not open\n> > + */\n> > +\n> > +/**\n> > + * \\brief Open a V4L2 device node\n> > + * \\param[in] flags Access mode flags\n> > + *\n> > + * Open the device node path with the provided access mode \\a flags and\n> > + * initialize the file descriptor, which was initially set to -1.\n> > + *\n> > + * \\return 0 on success or a negative error code otherwise\n> > + */\n> > +int V4L2Device::open(unsigned int flags)\n> > +{\n> > +\tif (isOpen()) {\n> > +\t\tLOG(V4L2, Error) << \"Device already open\";\n> > +\t\treturn -EBUSY;\n> > +\t}\n> > +\n> > +\tint ret = ::open(deviceNode_.c_str(), flags);\n> > +\tif (ret < 0) {\n> > +\t\tret = -errno;\n> > +\t\tLOG(V4L2, Error) << \"Failed to open V4L2 device: \"\n> > +\t\t\t\t << strerror(-ret);\n> > +\t\treturn ret;\n> > +\t}\n> > +\n> > +\tfd_ = ret;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Perform an IOCTL system call on the device node\n> > + * \\param[in] request The IOCTL request code\n> > + * \\param[in] argp A pointer to the IOCTL argument\n> > + * \\return 0 on success or a negative error code otherwise\n> > + */\n> > +int V4L2Device::ioctl(unsigned long request, void *argp)\n> > +{\n> > +\t/*\n> > +\t * Printing out an error message is usually better performed\n> > +\t * in the caller, which can provide more context.\n> > +\t */\n> > +\tif (::ioctl(fd_, request, argp) < 0)\n> > +\t\treturn -errno;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +/**\n> > + * \\fn V4L2Device::logPrefix()\n> > + * \\brief Retrieve the tag to prefix log messages with\n> > + * \\return The tag to prefix log messages with\n> > + */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > index 48f1fcb30ed4..d0e1d717b26c 100644\n> > --- a/src/libcamera/v4l2_subdevice.cpp\n> > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > @@ -29,7 +29,7 @@\n> >\n> >  namespace libcamera {\n> >\n> > -LOG_DEFINE_CATEGORY(V4L2Subdev)\n> > +LOG_DECLARE_CATEGORY(V4L2)\n> >\n> >  /**\n> >   * \\struct V4L2SubdeviceFormat\n> > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> >   * path\n> >   */\n> >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > -\t: entity_(entity), fd_(-1)\n> > +\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> >  {\n> >  }\n> >\n> > @@ -117,45 +117,7 @@ V4L2Subdevice::~V4L2Subdevice()\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(entity_->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 '\"\n> > -\t\t\t<< entity_->deviceNode() << \"': \" << 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> > +\treturn V4L2Device::open(O_RDWR);\n> >  }\n> >\n> >  /**\n> > @@ -200,7 +162,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> >  \tImageFormats formats;\n> >\n> >  \tif (pad >= entity_->pads().size()) {\n> > -\t\tLOG(V4L2Subdev, Error) << \"Invalid pad: \" << pad;\n> > +\t\tLOG(V4L2, Error) << \"Invalid pad: \" << pad;\n> >  \t\treturn {};\n> >  \t}\n> >\n> > @@ -210,7 +172,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> >  \t\t\treturn {};\n> >\n> >  \t\tif (formats.addFormat(code, sizes)) {\n> > -\t\t\tLOG(V4L2Subdev, Error)\n> > +\t\t\tLOG(V4L2, Error)\n> >  \t\t\t\t<< \"Could not add sizes for media bus code \"\n> >  \t\t\t\t<< code << \" on pad \" << pad;\n> >  \t\t\treturn {};\n> > @@ -232,10 +194,9 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> >  \tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> >  \tsubdevFmt.pad = pad;\n> >\n> > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > +\tint ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to get format on pad \" << pad\n> >  \t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -268,10 +229,9 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> >  \tsubdevFmt.format.height = format->size.height;\n> >  \tsubdevFmt.format.code = format->mbus_code;\n> >\n> > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > +\tint ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to set format on pad \" << pad\n> >  \t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -305,11 +265,6 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> >  \treturn new V4L2Subdevice(mediaEntity);\n> >  }\n> >\n> > -std::string V4L2Subdevice::logPrefix() const\n> > -{\n> > -\treturn \"'\" + entity_->name() + \"'\";\n> > -}\n> > -\n> >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> >  {\n> >  \tstd::vector<unsigned int> codes;\n> > @@ -321,18 +276,17 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> >  \t\tmbusEnum.index = index;\n> >  \t\tmbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> >  \t\tcodes.push_back(mbusEnum.code);\n> >  \t}\n> >\n> > -\tif (ret && errno != EINVAL) {\n> > -\t\tret = errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\tif (ret < 0 && ret != -EINVAL) {\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate formats on pad \" << pad\n> > -\t\t\t<< \": \" << strerror(ret);\n> > +\t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn {};\n> >  \t}\n> >\n> > @@ -352,7 +306,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> >  \t\tsizeEnum.code = code;\n> >  \t\tsizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> > @@ -360,9 +314,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> >  \t\t\t\t   sizeEnum.max_width, sizeEnum.max_height);\n> >  \t}\n> >\n> > -\tif (ret && (errno != EINVAL && errno != ENOTTY)) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\tif (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate sizes on pad \" << pad\n> >  \t\t\t<< \": \" << strerror(-ret);\n> >  \t\treturn {};\n> > @@ -386,10 +339,9 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,\n> >  \tsel.r.width = rect->w;\n> >  \tsel.r.height = rect->h;\n> >\n> > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > +\tint ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2Subdev, Error)\n> > +\t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to set rectangle \" << target << \" on pad \"\n> >  \t\t\t<< pad << \": \" << strerror(-ret);\n> >  \t\treturn ret;\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 0e70498c8bb1..403e0b2e0653 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -30,7 +30,7 @@\n> >   */\n> >  namespace libcamera {\n> >\n> > -LOG_DEFINE_CATEGORY(V4L2)\n> > +LOG_DECLARE_CATEGORY(V4L2)\n> >\n> >  /**\n> >   * \\struct V4L2Capability\n> > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> >   * \\param[in] deviceNode The file-system path to the video device node\n> >   */\n> >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > -\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n> > +\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> >  {\n> >  \t/*\n> > @@ -305,23 +305,12 @@ int V4L2VideoDevice::open()\n> >  {\n> >  \tint ret;\n> >\n> > -\tif (isOpen()) {\n> > -\t\tLOG(V4L2, Error) << \"Device already open\";\n> > -\t\treturn -EBUSY;\n> > -\t}\n> > -\n> > -\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n> > -\tif (ret < 0) {\n> > -\t\tret = -errno;\n> > -\t\tLOG(V4L2, Error)\n> > -\t\t\t<< \"Failed to open V4L2 device: \" << strerror(-ret);\n> > +\tret = V4L2Device::open(O_RDWR | O_NONBLOCK);\n> > +\tif (ret < 0)\n> >  \t\treturn ret;\n> > -\t}\n> > -\tfd_ = ret;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);\n> > +\tret = ioctl(VIDIOC_QUERYCAP, &caps_);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to query device capabilities: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -342,20 +331,20 @@ int V4L2VideoDevice::open()\n> >  \t * (POLLIN), and write notifications for OUTPUT devices (POLLOUT).\n> >  \t */\n> >  \tif (caps_.isVideoCapture()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> >  \t\tbufferType_ = caps_.isMultiplanar()\n> >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE\n> >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> >  \t} else if (caps_.isVideoOutput()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> >  \t\tbufferType_ = caps_.isMultiplanar()\n> >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n> >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n> >  \t} else if (caps_.isMetaCapture()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> >  \t\tbufferType_ = V4L2_BUF_TYPE_META_CAPTURE;\n> >  \t} else if (caps_.isMetaOutput()) {\n> > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> >  \t\tbufferType_ = V4L2_BUF_TYPE_META_OUTPUT;\n> >  \t} else {\n> >  \t\tLOG(V4L2, Error) << \"Device is not a supported type\";\n> > @@ -368,28 +357,18 @@ int V4L2VideoDevice::open()\n> >  \treturn 0;\n> >  }\n> >\n> > -/**\n> > - * \\brief Check if device is successfully opened\n> > - * \\return True if the device is open, false otherwise\n> > - */\n> > -bool V4L2VideoDevice::isOpen() const\n> > -{\n> > -\treturn fd_ != -1;\n> > -}\n> > -\n> >  /**\n> >   * \\brief Close the device, releasing any resources acquired by open()\n> >   */\n> >  void V4L2VideoDevice::close()\n> >  {\n> > -\tif (fd_ < 0)\n> > +\tif (!isOpen())\n> >  \t\treturn;\n> >\n> >  \treleaseBuffers();\n> >  \tdelete fdEvent_;\n> >\n> > -\t::close(fd_);\n> > -\tfd_ = -1;\n> > +\tV4L2Device::close();\n> >  }\n> >\n> >  /**\n> > @@ -410,17 +389,6 @@ void V4L2VideoDevice::close()\n> >   * \\return The string containing the device location\n> >   */\n> >\n> > -/**\n> > - * \\fn V4L2VideoDevice::deviceNode()\n> > - * \\brief Retrieve the video device node path\n> > - * \\return The video device device node path\n> > - */\n> > -\n> > -std::string V4L2VideoDevice::logPrefix() const\n> > -{\n> > -\treturn deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > -}\n> > -\n> >  /**\n> >   * \\brief Retrieve the image format set on the V4L2 device\n> >   * \\param[out] format The image format applied on the device\n> > @@ -462,9 +430,8 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)\n> >  \tint ret;\n> >\n> >  \tv4l2Format.type = bufferType_;\n> > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -488,9 +455,8 @@ int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)\n> >  \tv4l2Format.type = bufferType_;\n> >  \tpix->dataformat = format->fourcc;\n> >  \tpix->buffersize = format->planes[0].size;\n> > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -516,9 +482,8 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)\n> >  \tint ret;\n> >\n> >  \tv4l2Format.type = bufferType_;\n> > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -554,9 +519,8 @@ int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)\n> >  \t\tpix->plane_fmt[i].sizeimage = format->planes[i].size;\n> >  \t}\n> >\n> > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -584,9 +548,8 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)\n> >  \tint ret;\n> >\n> >  \tv4l2Format.type = bufferType_;\n> > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -613,9 +576,8 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)\n> >  \tpix->pixelformat = format->fourcc;\n> >  \tpix->bytesperline = format->planes[0].bpl;\n> >  \tpix->field = V4L2_FIELD_NONE;\n> > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> >  \tif (ret) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> >  \t\treturn ret;\n> >  \t}\n> > @@ -670,9 +632,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n> >  \trb.type = bufferType_;\n> >  \trb.memory = memoryType_;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_REQBUFS, &rb);\n> > +\tret = ioctl(VIDIOC_REQBUFS, &rb);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to request \" << count << \" buffers: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -722,9 +683,8 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n> >  \t\tbuf.length = VIDEO_MAX_PLANES;\n> >  \t\tbuf.m.planes = planes;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);\n> > +\t\tret = ioctl(VIDIOC_QUERYBUF, &buf);\n> >  \t\tif (ret < 0) {\n> > -\t\t\tret = -errno;\n> >  \t\t\tLOG(V4L2, Error)\n> >  \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n> >  \t\t\t\t<< strerror(-ret);\n> > @@ -775,9 +735,8 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex,\n> >  \texpbuf.plane = planeIndex;\n> >  \texpbuf.flags = O_RDWR;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);\n> > +\tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to export buffer: \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -801,15 +760,14 @@ std::vector<unsigned int> V4L2VideoDevice::enumPixelformats()\n> >  \t\tpixelformatEnum.index = index;\n> >  \t\tpixelformatEnum.type = bufferType_;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > +\t\tret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> >  \t\tformats.push_back(pixelformatEnum.pixelformat);\n> >  \t}\n> >\n> > -\tif (ret && errno != EINVAL) {\n> > -\t\tret = -errno;\n> > +\tif (ret && ret != -EINVAL) {\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate pixel formats: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -829,7 +787,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> >  \t\tframeSize.index = index;\n> >  \t\tframeSize.pixel_format = pixelFormat;\n> >\n> > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > +\t\tret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> >  \t\tif (ret)\n> >  \t\t\tbreak;\n> >\n> > @@ -867,8 +825,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> >  \t\t}\n> >  \t}\n> >\n> > -\tif (ret && errno != EINVAL) {\n> > -\t\tret = -errno;\n> > +\tif (ret && ret != -EINVAL) {\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Unable to enumerate frame sizes: \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -969,9 +926,8 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n> >\n> >  \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n> > +\tret = ioctl(VIDIOC_QBUF, &buf);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n> >  \t\t\t<< strerror(-ret);\n> > @@ -1006,9 +962,8 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> >  \t\tbuf.m.planes = planes;\n> >  \t}\n> >\n> > -\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n> > +\tret = ioctl(VIDIOC_DQBUF, &buf);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> >  \t\treturn nullptr;\n> > @@ -1066,9 +1021,8 @@ int V4L2VideoDevice::streamOn()\n> >  {\n> >  \tint ret;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);\n> > +\tret = ioctl(VIDIOC_STREAMON, &bufferType_);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to start streaming: \" << strerror(-ret);\n> >  \t\treturn ret;\n> > @@ -1089,9 +1043,8 @@ int V4L2VideoDevice::streamOff()\n> >  {\n> >  \tint ret;\n> >\n> > -\tret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);\n> > +\tret = ioctl(VIDIOC_STREAMOFF, &bufferType_);\n> >  \tif (ret < 0) {\n> > -\t\tret = -errno;\n> >  \t\tLOG(V4L2, Error)\n> >  \t\t\t<< \"Failed to stop streaming: \" << strerror(-ret);\n> >  \t\treturn ret;\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay12.mail.gandi.net (relay12.mail.gandi.net\n\t[217.70.178.232])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EBBC861913\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Jun 2019 15:06:13 +0200 (CEST)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay12.mail.gandi.net (Postfix) with ESMTPSA id 4D973200018;\n\tWed, 19 Jun 2019 13:06:13 +0000 (UTC)"],"Date":"Wed, 19 Jun 2019 15:07:27 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190619130727.3kbaz6vu3ort4ur7@uno.localdomain>","References":"<20190619110548.20742-1-jacopo@jmondi.org>\n\t<20190619110548.20742-3-jacopo@jmondi.org>\n\t<20190619122029.GE5045@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"wc2ebs6ayg7drqom\"","Content-Disposition":"inline","In-Reply-To":"<20190619122029.GE5045@pendragon.ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","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":"Wed, 19 Jun 2019 13:06:14 -0000"}},{"id":1944,"web_url":"https://patchwork.libcamera.org/comment/1944/","msgid":"<20190619130744.GE21753@pendragon.ideasonboard.com>","date":"2019-06-19T13:07:44","subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Wed, Jun 19, 2019 at 03:07:27PM +0200, Jacopo Mondi wrote:\n> On Wed, Jun 19, 2019 at 03:20:29PM +0300, Laurent Pinchart wrote:\n> > On Wed, Jun 19, 2019 at 01:05:46PM +0200, Jacopo Mondi wrote:\n> > > The V4L2 devices and subdevices share a few common operations,like\n> > > opening and closing a device node, and perform IOCTLs on the device.\n> > >\n> > > With the forthcoming introduction of support for V4L2 controls, the\n> > > quantity of shared code will increase, as the control support\n> > > implementation is identical for the two derived classes.\n> > >\n> > > To maximize code re-use and avoid duplications, provide a V4L2Device\n> > > base class which groups the common operations and members.\n> > >\n> > > The newly introduced base class provides methods to open/close a device\n> > > node, access the file descriptor, and perform IOCTLs on the device.\n> > >\n> \n> With your patch squashed on top and the other comment addressed, can I\n> have your tag (and push) or would you like another iteration?\n\nI was thinking on commenting about the order of methods in the\nv4l2_device.cpp file, but that can be addressed later, so\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>\n> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > ---\n> > >  src/libcamera/include/v4l2_device.h      |  43 +++++++\n> > >  src/libcamera/include/v4l2_subdevice.h   |   9 +-\n> > >  src/libcamera/include/v4l2_videodevice.h |  10 +-\n> > >  src/libcamera/meson.build                |   2 +\n> > >  src/libcamera/v4l2_device.cpp            | 144 +++++++++++++++++++++++\n> > >  src/libcamera/v4l2_subdevice.cpp         |  84 +++----------\n> > >  src/libcamera/v4l2_videodevice.cpp       | 103 +++++-----------\n> > >  7 files changed, 239 insertions(+), 156 deletions(-)\n> > >  create mode 100644 src/libcamera/include/v4l2_device.h\n> > >  create mode 100644 src/libcamera/v4l2_device.cpp\n> > >\n> > > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > > new file mode 100644\n> > > index 000000000000..2c26f3eae2b4\n> > > --- /dev/null\n> > > +++ b/src/libcamera/include/v4l2_device.h\n> > > @@ -0,0 +1,43 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2019, Google Inc.\n> > > + *\n> > > + * v4l2_device.h - Common base for V4L2 video devices and subdevices\n> > > + */\n> > > +#ifndef __LIBCAMERA_V4L2_DEVICE_H__\n> > > +#define __LIBCAMERA_V4L2_DEVICE_H__\n> > > +\n> > > +#include <string>\n> > > +\n> > > +#include \"log.h\"\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +class V4L2Device : protected Loggable\n> > > +{\n> > > +public:\n> > > +\tvoid close();\n> > > +\tbool isOpen() const { return fd_ != -1; }\n> > > +\tconst std::string &deviceNode() const { return deviceNode_; }\n> > > +\n> > > +protected:\n> > > +\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > > +\t~V4L2Device() {}\n> > > +\n> > > +\tint fd() { return fd_; }\n> > > +\n> > > +\tint open(unsigned int flags);\n> > > +\n> > > +\tint ioctl(unsigned long request, void *argp);\n> > > +\n> > > +\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> >\n> > Let's try not to inline methods that are more complex than just\n> > returning a member of the class.\n> >\n> > I didn't notice in my previous review that V4L2Subdevice was using the\n> > entity name as a prefix. I'm afraid my comment about moving logPrefix()\n> > to V4L2Device was a bad one :-/ However, V4L2Device should still inherit\n> > from Loggable. It will thus have a pure virtual logPrefix() inherited\n> > from Loggable, requiring the derived classes to implement that method,\n> > but allowing LOG() statements in V4L2Device to use the log prefix.\n> >\n> > Here's the diff I came up with, it can be squashed with this patch if\n> > you agree on the comments.\n> >\n> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h\n> > index 2c26f3eae2b4..8c83adab4d43 100644\n> > --- a/src/libcamera/include/v4l2_device.h\n> > +++ b/src/libcamera/include/v4l2_device.h\n> > @@ -21,7 +21,7 @@ public:\n> >  \tconst std::string &deviceNode() const { return deviceNode_; }\n> >\n> >  protected:\n> > -\tV4L2Device(const std::string &deviceNode, const std::string &logTag);\n> > +\tV4L2Device(const std::string &deviceNode);\n> >  \t~V4L2Device() {}\n> >\n> >  \tint fd() { return fd_; }\n> > @@ -30,11 +30,8 @@ protected:\n> >\n> >  \tint ioctl(unsigned long request, void *argp);\n> >\n> > -\tstd::string logPrefix() const { return \"'\" + logTag_ + \"'\"; }\n> > -\n> >  private:\n> >  \tstd::string deviceNode_;\n> > -\tstd::string logTag_;\n> >  \tint fd_;\n> >  };\n> >\n> > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > index b1da4a87c553..9c077674f997 100644\n> > --- a/src/libcamera/include/v4l2_subdevice.h\n> > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > @@ -52,6 +52,9 @@ public:\n> >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t     const std::string &entity);\n> >\n> > +protected:\n> > +\tstd::string logPrefix() const;\n> > +\n> >  private:\n> >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > index e73dec13f847..734b34f1dc53 100644\n> > --- a/src/libcamera/include/v4l2_videodevice.h\n> > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > @@ -147,6 +147,9 @@ public:\n> >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> >  \t\t\t\t\t       const std::string &entity);\n> >\n> > +protected:\n> > +\tstd::string logPrefix() const;\n> > +\n> >  private:\n> >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > index eeed0a70fb0e..1f113780ba45 100644\n> > --- a/src/libcamera/v4l2_device.cpp\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -68,17 +68,15 @@ void V4L2Device::close()\n> >  /**\n> >   * \\brief Construct a V4L2Device\n> >   * \\param[in] deviceNode The device node filesystem path\n> > - * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > - * the device in the log output\n> >   *\n> >   * The V4L2Device constructor is protected and can only be accessed by the\n> >   * V4L2VideoDevice and V4L2Subdevice derived classes.\n> >   *\n> >   * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> >   * at open() time, and the \\a logTag to prefix log messages with.\n> >   */\n> > -V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > -\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > +V4L2Device::V4L2Device(const std::string &deviceNode)\n> > +\t: deviceNode_(deviceNode), fd_(-1)\n> >  {\n> >  }\n> >\n> > @@ -99,6 +94,7 @@ V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> >   */\n> >  int V4L2Device::open(unsigned int flags)\n> >  {\n> > +\tLOG(V4L2, Info) << \"Opening device\";\n> >  \tif (isOpen()) {\n> >  \t\tLOG(V4L2, Error) << \"Device already open\";\n> >  \t\treturn -EBUSY;\n> > @@ -135,10 +131,4 @@ int V4L2Device::ioctl(unsigned long request, void *argp)\n> >  \treturn 0;\n> >  }\n> >\n> > -/**\n> > - * \\fn V4L2Device::logPrefix()\n> > - * \\brief Retrieve the tag to prefix log messages with\n> > - * \\return The tag to prefix log messages with\n> > - */\n> > -\n> >  } /* namespace libcamera */\n> > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > index d0e1d717b26c..a188298de34c 100644\n> > --- a/src/libcamera/v4l2_subdevice.cpp\n> > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> >   * path\n> >   */\n> >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > -\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> > +\t: V4L2Device(entity->deviceNode()), entity_(entity)\n> >  {\n> >  }\n> >\n> > @@ -265,6 +265,11 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> >  \treturn new V4L2Subdevice(mediaEntity);\n> >  }\n> >\n> > +std::string V4L2Subdevice::logPrefix() const\n> > +{\n> > +\treturn \"'\" + entity_->name() + \"'\";\n> > +}\n> > +\n> >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> >  {\n> >  \tstd::vector<unsigned int> codes;\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 403e0b2e0653..2de3751f467e 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> >   * \\param[in] deviceNode The file-system path to the video device node\n> >   */\n> >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > -\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> > +\t: V4L2Device(deviceNode), bufferPool_(nullptr),\n> >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> >  {\n> >  \t/*\n> > @@ -389,6 +389,11 @@ void V4L2VideoDevice::close()\n> >   * \\return The string containing the device location\n> >   */\n> >\n> > +std::string V4L2VideoDevice::logPrefix() const\n> > +{\n> > +\treturn deviceNode() + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > +}\n> > +\n> >  /**\n> >   * \\brief Retrieve the image format set on the V4L2 device\n> >   * \\param[out] format The image format applied on the device\n> >\n> > > +\n> > > +private:\n> > > +\tstd::string deviceNode_;\n> > > +\tstd::string logTag_;\n> > > +\tint fd_;\n> > > +};\n> > > +\n> > > +} /* namespace libcamera */\n> > > +\n> > > +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */\n> > > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h\n> > > index 9afd28b6db02..b1da4a87c553 100644\n> > > --- a/src/libcamera/include/v4l2_subdevice.h\n> > > +++ b/src/libcamera/include/v4l2_subdevice.h\n> > > @@ -16,6 +16,7 @@\n> > >  #include \"formats.h\"\n> > >  #include \"log.h\"\n> > >  #include \"media_object.h\"\n> > > +#include \"v4l2_device.h\"\n> > >\n> > >  namespace libcamera {\n> > >\n> > > @@ -28,7 +29,7 @@ struct V4L2SubdeviceFormat {\n> > >  \tconst std::string toString() const;\n> > >  };\n> > >\n> > > -class V4L2Subdevice : protected Loggable\n> > > +class V4L2Subdevice : public V4L2Device\n> > >  {\n> > >  public:\n> > >  \texplicit V4L2Subdevice(const MediaEntity *entity);\n> > > @@ -37,8 +38,6 @@ public:\n> > >  \t~V4L2Subdevice();\n> > >\n> > >  \tint open();\n> > > -\tbool isOpen() const;\n> > > -\tvoid close();\n> > >\n> > >  \tconst MediaEntity *entity() const { return entity_; }\n> > >\n> > > @@ -53,9 +52,6 @@ public:\n> > >  \tstatic V4L2Subdevice *fromEntityName(const MediaDevice *media,\n> > >  \t\t\t\t\t     const std::string &entity);\n> > >\n> > > -protected:\n> > > -\tstd::string logPrefix() const;\n> > > -\n> > >  private:\n> > >  \tstd::vector<unsigned int> enumPadCodes(unsigned int pad);\n> > >  \tstd::vector<SizeRange> enumPadSizes(unsigned int pad,\n> > > @@ -65,7 +61,6 @@ private:\n> > >  \t\t\t Rectangle *rect);\n> > >\n> > >  \tconst MediaEntity *entity_;\n> > > -\tint fd_;\n> > >  };\n> > >\n> > >  } /* namespace libcamera */\n> > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\n> > > index 895658805b47..e73dec13f847 100644\n> > > --- a/src/libcamera/include/v4l2_videodevice.h\n> > > +++ b/src/libcamera/include/v4l2_videodevice.h\n> > > @@ -18,6 +18,7 @@\n> > >\n> > >  #include \"formats.h\"\n> > >  #include \"log.h\"\n> > > +#include \"v4l2_device.h\"\n> > >\n> > >  namespace libcamera {\n> > >\n> > > @@ -112,7 +113,7 @@ public:\n> > >  \tconst std::string toString() const;\n> > >  };\n> > >\n> > > -class V4L2VideoDevice : protected Loggable\n> > > +class V4L2VideoDevice : public V4L2Device\n> > >  {\n> > >  public:\n> > >  \texplicit V4L2VideoDevice(const std::string &deviceNode);\n> > > @@ -123,13 +124,11 @@ public:\n> > >  \tV4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;\n> > >\n> > >  \tint open();\n> > > -\tbool isOpen() const;\n> > >  \tvoid close();\n> > >\n> > >  \tconst char *driverName() const { return caps_.driver(); }\n> > >  \tconst char *deviceName() const { return caps_.card(); }\n> > >  \tconst char *busName() const { return caps_.bus_info(); }\n> > > -\tconst std::string &deviceNode() const { return deviceNode_; }\n> > >\n> > >  \tint getFormat(V4L2DeviceFormat *format);\n> > >  \tint setFormat(V4L2DeviceFormat *format);\n> > > @@ -148,9 +147,6 @@ public:\n> > >  \tstatic V4L2VideoDevice *fromEntityName(const MediaDevice *media,\n> > >  \t\t\t\t\t       const std::string &entity);\n> > >\n> > > -protected:\n> > > -\tstd::string logPrefix() const;\n> > > -\n> > >  private:\n> > >  \tint getFormatMeta(V4L2DeviceFormat *format);\n> > >  \tint setFormatMeta(V4L2DeviceFormat *format);\n> > > @@ -171,8 +167,6 @@ private:\n> > >  \tBuffer *dequeueBuffer();\n> > >  \tvoid bufferAvailable(EventNotifier *notifier);\n> > >\n> > > -\tstd::string deviceNode_;\n> > > -\tint fd_;\n> > >  \tV4L2Capability caps_;\n> > >\n> > >  \tenum v4l2_buf_type bufferType_;\n> > > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\n> > > index 15ab53b1abbe..f26ad5b2dc57 100644\n> > > --- a/src/libcamera/meson.build\n> > > +++ b/src/libcamera/meson.build\n> > > @@ -23,6 +23,7 @@ libcamera_sources = files([\n> > >      'stream.cpp',\n> > >      'timer.cpp',\n> > >      'utils.cpp',\n> > > +    'v4l2_device.cpp',\n> > >      'v4l2_subdevice.cpp',\n> > >      'v4l2_videodevice.cpp',\n> > >  ])\n> > > @@ -41,6 +42,7 @@ libcamera_headers = files([\n> > >      'include/media_object.h',\n> > >      'include/pipeline_handler.h',\n> > >      'include/utils.h',\n> > > +    'include/v4l2_device.h',\n> > >      'include/v4l2_subdevice.h',\n> > >      'include/v4l2_videodevice.h',\n> > >  ])\n> > > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > > new file mode 100644\n> > > index 000000000000..eeed0a70fb0e\n> > > --- /dev/null\n> > > +++ b/src/libcamera/v4l2_device.cpp\n> > > @@ -0,0 +1,144 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2019, Google Inc.\n> > > + *\n> > > + * v4l2_device.cpp - Common base for V4L2 video devices and subdevices\n> > > + */\n> > > +\n> > > +#include \"v4l2_device.h\"\n> > > +\n> > > +#include <fcntl.h>\n> > > +#include <string.h>\n> > > +#include <sys/ioctl.h>\n> > > +#include <unistd.h>\n> > > +\n> > > +#include \"log.h\"\n> > > +\n> > > +/**\n> > > + * \\file v4l2_device.h\n> > > + * \\brief Common base for V4L2 devices and subdevices\n> > > + */\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DEFINE_CATEGORY(V4L2)\n> > > +\n> > > +/**\n> > > + * \\class V4L2Device\n> > > + * \\brief Base class for V4L2VideoDevice and V4L2Subdevice\n> > > + *\n> > > + * The V4L2Device class groups together the methods and fields common to\n> > > + * both the V4L2VideoDevice and V4L2Subdevice classes, and provides a base\n> > > + * class whith methods to open and close the device node associated with the\n> > > + * device and to perform IOCTL system calls on it.\n> > > + *\n> > > + * The V4L2Device class cannot be instantiated directly, as its constructor\n> > > + * is protected. Users should instead create instances of one the derived\n> > > + * classes to model either a V4L2 video device or a V4L2 subdevice.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Close the device node\n> > > + *\n> > > + * Reset the file descriptor to -1\n> > > + */\n> > > +void V4L2Device::close()\n> > > +{\n> > > +\tif (!isOpen())\n> > > +\t\treturn;\n> > > +\n> > > +\tif (::close(fd_) < 0)\n> > > +\t\tLOG(V4L2, Error) << \"Failed to close V4L2 device: \"\n> > > +\t\t\t\t << strerror(errno);\n> > > +\tfd_ = -1;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::isOpen()\n> > > + * \\brief Check if the V4L2 device node is open\n> > > + * \\return True if the V4L2 device node is open, false otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::deviceNode()\n> > > + * \\brief Retrieve the device node path\n> > > + * \\return The device node path\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Construct a V4L2Device\n> > > + * \\param[in] deviceNode The device node filesystem path\n> > > + * \\param[in] logTag The tag to prefix log messages with. Helpful to identify\n> > > + * the device in the log output\n> > > + *\n> > > + * The V4L2Device constructor is protected and can only be accessed by the\n> > > + * V4L2VideoDevice and V4L2Subdevice derived classes.\n> >\n> > I would drop this sentence.\n> >\n> > > + *\n> > > + * Initialize the file descriptor to -1 and store the \\a deviceNode to be used\n> > > + * at open() time, and the \\a logTag to prefix log messages with.\n> > > + */\n> > > +V4L2Device::V4L2Device(const std::string &deviceNode, const std::string &logTag)\n> > > +\t: deviceNode_(deviceNode), logTag_(logTag), fd_(-1)\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::fd()\n> > > + * \\brief Retrieve the V4L2 device file descriptor\n> > > + * \\return The V4L2 device file descriptor, -1 if the device node is not open\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Open a V4L2 device node\n> > > + * \\param[in] flags Access mode flags\n> > > + *\n> > > + * Open the device node path with the provided access mode \\a flags and\n> > > + * initialize the file descriptor, which was initially set to -1.\n> > > + *\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +int V4L2Device::open(unsigned int flags)\n> > > +{\n> > > +\tif (isOpen()) {\n> > > +\t\tLOG(V4L2, Error) << \"Device already open\";\n> > > +\t\treturn -EBUSY;\n> > > +\t}\n> > > +\n> > > +\tint ret = ::open(deviceNode_.c_str(), flags);\n> > > +\tif (ret < 0) {\n> > > +\t\tret = -errno;\n> > > +\t\tLOG(V4L2, Error) << \"Failed to open V4L2 device: \"\n> > > +\t\t\t\t << strerror(-ret);\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\tfd_ = ret;\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Perform an IOCTL system call on the device node\n> > > + * \\param[in] request The IOCTL request code\n> > > + * \\param[in] argp A pointer to the IOCTL argument\n> > > + * \\return 0 on success or a negative error code otherwise\n> > > + */\n> > > +int V4L2Device::ioctl(unsigned long request, void *argp)\n> > > +{\n> > > +\t/*\n> > > +\t * Printing out an error message is usually better performed\n> > > +\t * in the caller, which can provide more context.\n> > > +\t */\n> > > +\tif (::ioctl(fd_, request, argp) < 0)\n> > > +\t\treturn -errno;\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn V4L2Device::logPrefix()\n> > > + * \\brief Retrieve the tag to prefix log messages with\n> > > + * \\return The tag to prefix log messages with\n> > > + */\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp\n> > > index 48f1fcb30ed4..d0e1d717b26c 100644\n> > > --- a/src/libcamera/v4l2_subdevice.cpp\n> > > +++ b/src/libcamera/v4l2_subdevice.cpp\n> > > @@ -29,7 +29,7 @@\n> > >\n> > >  namespace libcamera {\n> > >\n> > > -LOG_DEFINE_CATEGORY(V4L2Subdev)\n> > > +LOG_DECLARE_CATEGORY(V4L2)\n> > >\n> > >  /**\n> > >   * \\struct V4L2SubdeviceFormat\n> > > @@ -102,7 +102,7 @@ const std::string V4L2SubdeviceFormat::toString() const\n> > >   * path\n> > >   */\n> > >  V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)\n> > > -\t: entity_(entity), fd_(-1)\n> > > +\t: V4L2Device(entity->deviceNode(), entity->name()), entity_(entity)\n> > >  {\n> > >  }\n> > >\n> > > @@ -117,45 +117,7 @@ V4L2Subdevice::~V4L2Subdevice()\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(entity_->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 '\"\n> > > -\t\t\t<< entity_->deviceNode() << \"': \" << 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> > > +\treturn V4L2Device::open(O_RDWR);\n> > >  }\n> > >\n> > >  /**\n> > > @@ -200,7 +162,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> > >  \tImageFormats formats;\n> > >\n> > >  \tif (pad >= entity_->pads().size()) {\n> > > -\t\tLOG(V4L2Subdev, Error) << \"Invalid pad: \" << pad;\n> > > +\t\tLOG(V4L2, Error) << \"Invalid pad: \" << pad;\n> > >  \t\treturn {};\n> > >  \t}\n> > >\n> > > @@ -210,7 +172,7 @@ ImageFormats V4L2Subdevice::formats(unsigned int pad)\n> > >  \t\t\treturn {};\n> > >\n> > >  \t\tif (formats.addFormat(code, sizes)) {\n> > > -\t\t\tLOG(V4L2Subdev, Error)\n> > > +\t\t\tLOG(V4L2, Error)\n> > >  \t\t\t\t<< \"Could not add sizes for media bus code \"\n> > >  \t\t\t\t<< code << \" on pad \" << pad;\n> > >  \t\t\treturn {};\n> > > @@ -232,10 +194,9 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> > >  \tsubdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > >  \tsubdevFmt.pad = pad;\n> > >\n> > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > > +\tint ret = ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to get format on pad \" << pad\n> > >  \t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -268,10 +229,9 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)\n> > >  \tsubdevFmt.format.height = format->size.height;\n> > >  \tsubdevFmt.format.code = format->mbus_code;\n> > >\n> > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > > +\tint ret = ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to set format on pad \" << pad\n> > >  \t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -305,11 +265,6 @@ V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,\n> > >  \treturn new V4L2Subdevice(mediaEntity);\n> > >  }\n> > >\n> > > -std::string V4L2Subdevice::logPrefix() const\n> > > -{\n> > > -\treturn \"'\" + entity_->name() + \"'\";\n> > > -}\n> > > -\n> > >  std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > >  {\n> > >  \tstd::vector<unsigned int> codes;\n> > > @@ -321,18 +276,17 @@ std::vector<unsigned int> V4L2Subdevice::enumPadCodes(unsigned int pad)\n> > >  \t\tmbusEnum.index = index;\n> > >  \t\tmbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > >  \t\tcodes.push_back(mbusEnum.code);\n> > >  \t}\n> > >\n> > > -\tif (ret && errno != EINVAL) {\n> > > -\t\tret = errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\tif (ret < 0 && ret != -EINVAL) {\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate formats on pad \" << pad\n> > > -\t\t\t<< \": \" << strerror(ret);\n> > > +\t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn {};\n> > >  \t}\n> > >\n> > > @@ -352,7 +306,7 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> > >  \t\tsizeEnum.code = code;\n> > >  \t\tsizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > > +\t\tret = ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > > @@ -360,9 +314,8 @@ std::vector<SizeRange> V4L2Subdevice::enumPadSizes(unsigned int pad,\n> > >  \t\t\t\t   sizeEnum.max_width, sizeEnum.max_height);\n> > >  \t}\n> > >\n> > > -\tif (ret && (errno != EINVAL && errno != ENOTTY)) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\tif (ret < 0 && ret != -EINVAL && ret != -ENOTTY) {\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate sizes on pad \" << pad\n> > >  \t\t\t<< \": \" << strerror(-ret);\n> > >  \t\treturn {};\n> > > @@ -386,10 +339,9 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,\n> > >  \tsel.r.width = rect->w;\n> > >  \tsel.r.height = rect->h;\n> > >\n> > > -\tint ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > > +\tint ret = ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2Subdev, Error)\n> > > +\t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to set rectangle \" << target << \" on pad \"\n> > >  \t\t\t<< pad << \": \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > index 0e70498c8bb1..403e0b2e0653 100644\n> > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > @@ -30,7 +30,7 @@\n> > >   */\n> > >  namespace libcamera {\n> > >\n> > > -LOG_DEFINE_CATEGORY(V4L2)\n> > > +LOG_DECLARE_CATEGORY(V4L2)\n> > >\n> > >  /**\n> > >   * \\struct V4L2Capability\n> > > @@ -270,7 +270,7 @@ const std::string V4L2DeviceFormat::toString() const\n> > >   * \\param[in] deviceNode The file-system path to the video device node\n> > >   */\n> > >  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n> > > -\t: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),\n> > > +\t: V4L2Device(deviceNode, deviceNode), bufferPool_(nullptr),\n> > >  \t  queuedBuffersCount_(0), fdEvent_(nullptr)\n> > >  {\n> > >  \t/*\n> > > @@ -305,23 +305,12 @@ int V4L2VideoDevice::open()\n> > >  {\n> > >  \tint ret;\n> > >\n> > > -\tif (isOpen()) {\n> > > -\t\tLOG(V4L2, Error) << \"Device already open\";\n> > > -\t\treturn -EBUSY;\n> > > -\t}\n> > > -\n> > > -\tret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);\n> > > -\tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > > -\t\tLOG(V4L2, Error)\n> > > -\t\t\t<< \"Failed to open V4L2 device: \" << strerror(-ret);\n> > > +\tret = V4L2Device::open(O_RDWR | O_NONBLOCK);\n> > > +\tif (ret < 0)\n> > >  \t\treturn ret;\n> > > -\t}\n> > > -\tfd_ = ret;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);\n> > > +\tret = ioctl(VIDIOC_QUERYCAP, &caps_);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to query device capabilities: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -342,20 +331,20 @@ int V4L2VideoDevice::open()\n> > >  \t * (POLLIN), and write notifications for OUTPUT devices (POLLOUT).\n> > >  \t */\n> > >  \tif (caps_.isVideoCapture()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> > >  \t\tbufferType_ = caps_.isMultiplanar()\n> > >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE\n> > >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> > >  \t} else if (caps_.isVideoOutput()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> > >  \t\tbufferType_ = caps_.isMultiplanar()\n> > >  \t\t\t    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE\n> > >  \t\t\t    : V4L2_BUF_TYPE_VIDEO_OUTPUT;\n> > >  \t} else if (caps_.isMetaCapture()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Read);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Read);\n> > >  \t\tbufferType_ = V4L2_BUF_TYPE_META_CAPTURE;\n> > >  \t} else if (caps_.isMetaOutput()) {\n> > > -\t\tfdEvent_ = new EventNotifier(fd_, EventNotifier::Write);\n> > > +\t\tfdEvent_ = new EventNotifier(fd(), EventNotifier::Write);\n> > >  \t\tbufferType_ = V4L2_BUF_TYPE_META_OUTPUT;\n> > >  \t} else {\n> > >  \t\tLOG(V4L2, Error) << \"Device is not a supported type\";\n> > > @@ -368,28 +357,18 @@ int V4L2VideoDevice::open()\n> > >  \treturn 0;\n> > >  }\n> > >\n> > > -/**\n> > > - * \\brief Check if device is successfully opened\n> > > - * \\return True if the device is open, false otherwise\n> > > - */\n> > > -bool V4L2VideoDevice::isOpen() const\n> > > -{\n> > > -\treturn fd_ != -1;\n> > > -}\n> > > -\n> > >  /**\n> > >   * \\brief Close the device, releasing any resources acquired by open()\n> > >   */\n> > >  void V4L2VideoDevice::close()\n> > >  {\n> > > -\tif (fd_ < 0)\n> > > +\tif (!isOpen())\n> > >  \t\treturn;\n> > >\n> > >  \treleaseBuffers();\n> > >  \tdelete fdEvent_;\n> > >\n> > > -\t::close(fd_);\n> > > -\tfd_ = -1;\n> > > +\tV4L2Device::close();\n> > >  }\n> > >\n> > >  /**\n> > > @@ -410,17 +389,6 @@ void V4L2VideoDevice::close()\n> > >   * \\return The string containing the device location\n> > >   */\n> > >\n> > > -/**\n> > > - * \\fn V4L2VideoDevice::deviceNode()\n> > > - * \\brief Retrieve the video device node path\n> > > - * \\return The video device device node path\n> > > - */\n> > > -\n> > > -std::string V4L2VideoDevice::logPrefix() const\n> > > -{\n> > > -\treturn deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? \"[out]\" : \"[cap]\");\n> > > -}\n> > > -\n> > >  /**\n> > >   * \\brief Retrieve the image format set on the V4L2 device\n> > >   * \\param[out] format The image format applied on the device\n> > > @@ -462,9 +430,8 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)\n> > >  \tint ret;\n> > >\n> > >  \tv4l2Format.type = bufferType_;\n> > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -488,9 +455,8 @@ int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)\n> > >  \tv4l2Format.type = bufferType_;\n> > >  \tpix->dataformat = format->fourcc;\n> > >  \tpix->buffersize = format->planes[0].size;\n> > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -516,9 +482,8 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)\n> > >  \tint ret;\n> > >\n> > >  \tv4l2Format.type = bufferType_;\n> > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -554,9 +519,8 @@ int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)\n> > >  \t\tpix->plane_fmt[i].sizeimage = format->planes[i].size;\n> > >  \t}\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -584,9 +548,8 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)\n> > >  \tint ret;\n> > >\n> > >  \tv4l2Format.type = bufferType_;\n> > > -\tret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_G_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to get format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -613,9 +576,8 @@ int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)\n> > >  \tpix->pixelformat = format->fourcc;\n> > >  \tpix->bytesperline = format->planes[0].bpl;\n> > >  \tpix->field = V4L2_FIELD_NONE;\n> > > -\tret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);\n> > > +\tret = ioctl(VIDIOC_S_FMT, &v4l2Format);\n> > >  \tif (ret) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error) << \"Unable to set format: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > >  \t}\n> > > @@ -670,9 +632,8 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n> > >  \trb.type = bufferType_;\n> > >  \trb.memory = memoryType_;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_REQBUFS, &rb);\n> > > +\tret = ioctl(VIDIOC_REQBUFS, &rb);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to request \" << count << \" buffers: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -722,9 +683,8 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n> > >  \t\tbuf.length = VIDEO_MAX_PLANES;\n> > >  \t\tbuf.m.planes = planes;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);\n> > > +\t\tret = ioctl(VIDIOC_QUERYBUF, &buf);\n> > >  \t\tif (ret < 0) {\n> > > -\t\t\tret = -errno;\n> > >  \t\t\tLOG(V4L2, Error)\n> > >  \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n> > >  \t\t\t\t<< strerror(-ret);\n> > > @@ -775,9 +735,8 @@ int V4L2VideoDevice::createPlane(Buffer *buffer, unsigned int planeIndex,\n> > >  \texpbuf.plane = planeIndex;\n> > >  \texpbuf.flags = O_RDWR;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);\n> > > +\tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to export buffer: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -801,15 +760,14 @@ std::vector<unsigned int> V4L2VideoDevice::enumPixelformats()\n> > >  \t\tpixelformatEnum.index = index;\n> > >  \t\tpixelformatEnum.type = bufferType_;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > > +\t\tret = ioctl(VIDIOC_ENUM_FMT, &pixelformatEnum);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > >  \t\tformats.push_back(pixelformatEnum.pixelformat);\n> > >  \t}\n> > >\n> > > -\tif (ret && errno != EINVAL) {\n> > > -\t\tret = -errno;\n> > > +\tif (ret && ret != -EINVAL) {\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate pixel formats: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -829,7 +787,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> > >  \t\tframeSize.index = index;\n> > >  \t\tframeSize.pixel_format = pixelFormat;\n> > >\n> > > -\t\tret = ioctl(fd_, VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > > +\t\tret = ioctl(VIDIOC_ENUM_FRAMESIZES, &frameSize);\n> > >  \t\tif (ret)\n> > >  \t\t\tbreak;\n> > >\n> > > @@ -867,8 +825,7 @@ std::vector<SizeRange> V4L2VideoDevice::enumSizes(unsigned int pixelFormat)\n> > >  \t\t}\n> > >  \t}\n> > >\n> > > -\tif (ret && errno != EINVAL) {\n> > > -\t\tret = -errno;\n> > > +\tif (ret && ret != -EINVAL) {\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Unable to enumerate frame sizes: \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -969,9 +926,8 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n> > >\n> > >  \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_QBUF, &buf);\n> > > +\tret = ioctl(VIDIOC_QBUF, &buf);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to queue buffer \" << buf.index << \": \"\n> > >  \t\t\t<< strerror(-ret);\n> > > @@ -1006,9 +962,8 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n> > >  \t\tbuf.m.planes = planes;\n> > >  \t}\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_DQBUF, &buf);\n> > > +\tret = ioctl(VIDIOC_DQBUF, &buf);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to dequeue buffer: \" << strerror(-ret);\n> > >  \t\treturn nullptr;\n> > > @@ -1066,9 +1021,8 @@ int V4L2VideoDevice::streamOn()\n> > >  {\n> > >  \tint ret;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);\n> > > +\tret = ioctl(VIDIOC_STREAMON, &bufferType_);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to start streaming: \" << strerror(-ret);\n> > >  \t\treturn ret;\n> > > @@ -1089,9 +1043,8 @@ int V4L2VideoDevice::streamOff()\n> > >  {\n> > >  \tint ret;\n> > >\n> > > -\tret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);\n> > > +\tret = ioctl(VIDIOC_STREAMOFF, &bufferType_);\n> > >  \tif (ret < 0) {\n> > > -\t\tret = -errno;\n> > >  \t\tLOG(V4L2, Error)\n> > >  \t\t\t<< \"Failed to stop streaming: \" << strerror(-ret);\n> > >  \t\treturn ret;","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AE2D261913\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Jun 2019 15:08:01 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 20F29333;\n\tWed, 19 Jun 2019 15:08:01 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1560949681;\n\tbh=Zy4idP9XE5hYn+JwclYjT/fI4/TclxZSOOHgjtMKYcQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ARmnOntLCs+Dv1SNWhh5NKhDHlIramJO7Bpeo7diahByiZHLosXkkbc0uZWhx8b4V\n\tQ8qBGeBuUsYILGuBQQph9SPlc4MSV7xF7dFLvDEBnzom2PcqFSOhjykY/l6xrC3npU\n\tLqQwO66UobTVGEMagHC/9dz5qxwXOovizIZjIaj8=","Date":"Wed, 19 Jun 2019 16:07:44 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190619130744.GE21753@pendragon.ideasonboard.com>","References":"<20190619110548.20742-1-jacopo@jmondi.org>\n\t<20190619110548.20742-3-jacopo@jmondi.org>\n\t<20190619122029.GE5045@pendragon.ideasonboard.com>\n\t<20190619130727.3kbaz6vu3ort4ur7@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20190619130727.3kbaz6vu3ort4ur7@uno.localdomain>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v5 2/4] libcamera: Introduce\n\tV4L2Device base class","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":"Wed, 19 Jun 2019 13:08:01 -0000"}}]