{"id":2495,"url":"https://patchwork.libcamera.org/api/1.1/patches/2495/?format=json","web_url":"https://patchwork.libcamera.org/patch/2495/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200104001414.12755-6-paul.elder@ideasonboard.com>","date":"2020-01-04T00:14:13","name":"[libcamera-devel,v6,5/5] v4l2: v4l2_compat: Add V4L2 compatibility layer","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"80bdab7044effc1d82c732e0d9e6d34dbee2881a","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/1.1/people/17/?format=json","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/2495/mbox/","series":[{"id":599,"url":"https://patchwork.libcamera.org/api/1.1/series/599/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=599","date":"2020-01-04T00:14:08","name":"V4L2 compatibility layer","version":6,"mbox":"https://patchwork.libcamera.org/series/599/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/2495/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/2495/checks/","tags":{},"headers":{"Return-Path":"<paul.elder@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 3F528605F8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat,  4 Jan 2020 01:14:26 +0100 (CET)","from neptunite.amanokami.net (unknown [96.44.9.94])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 02EAEA50;\n\tSat,  4 Jan 2020 01:14:23 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1578096864;\n\tbh=EtvsQKDq3Z36zWA1P7oteZY4AsDw555wHbj5x/Qe/G0=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=S8XwND3JcT1LVoDtGxjOJ4P9ky+EJidQHHLLcd7M/0+brPXui9yxX3i7eTCpTcy9+\n\t3EE66V7WvyfSJ9Vs/aX6b+CFpOkCCGFK1E9VjDRrZ/8G9DpgIDCEI4Xd2gYN3IPk6C\n\tcabuF6G+CG16Vfem7cpbzqdba4FyZR0b0bP3sUwg=","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Fri,  3 Jan 2020 19:14:13 -0500","Message-Id":"<20200104001414.12755-6-paul.elder@ideasonboard.com>","X-Mailer":"git-send-email 2.24.1","In-Reply-To":"<20200104001414.12755-1-paul.elder@ideasonboard.com>","References":"<20200104001414.12755-1-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v6 5/5] v4l2: v4l2_compat: Add V4L2\n\tcompatibility layer","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","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":"Sat, 04 Jan 2020 00:14:26 -0000"},"content":"Add libcamera V4L2 compatibility layer.\n\nThis initial implementation supports the minimal set of V4L2 operations,\nwhich allows getting, setting, and enumerating formats, and streaming\nframes from a video device. Some data about the wrapped V4L2 video\ndevice are hardcoded.\n\nAdd a build option named 'v4l2' and adjust the build system to\nselectively compile the V4L2 compatibility layer.\n\nFor now we match the V4L2 device node to a libcamera camera based on a\ndevnum that a pipeline handler may optionally map to a libcamera\ncamera.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n---\nChanges in v6:\n- use strlcpy instead of memcpy in V4L2CameraProxy\n- fix fourcc conversion in V4L2CameraProxy::vidioc_try_fmt\n- add aliases for __open_2 and __openat_2\n- cosmetic changes and other little fixes\n\nChanges in v5:\n- cosmetic changes and little fixes\n\nMajor changes in v4:\n- squash buffer state tracking in V4L2CameraProxy (from 6/6 in v3)\n- separated out V4L2CameraProxy::freeBuffers() (since it was cluttering\n  up V4L2CameraProxy::vidioc_reqbufs)\n\nLess major changes in v4:\n- check that addresses passed to V4L2CameraProxy::munmap() have been\n  mmaped\n- other cosmetic changes\n\nMajor changes in v3:\n- V4L2CompatManager::openat() verify video node and match to camera via\n  devnum instead of path\n- add FrameMetadata class to encapsulate portions of the libcamera\n  Buffer that needs to be extracted to v4l2_buffer, and to prepare for\n  the Buffer rework\n- V4L2CameraProxy refcount for tracking dups and closes (and open)\n- add V4L2CompatManager::initialized_ to deal with race of waiting on\n  init cv after the cv is notified\n\nLess major changes in v3:\n- change the list of pending Reqeusts (v4l2 buffers queued before\n  streamon) to unique pointers\n- V4L2Camera::updateSizeImage() -> V4L2CameraProxy::calculateSizeImage()\n- change V4L2CompatManager list of V4L2CameraProxy to unique_ptr, with\n  maps of fd/mmap addr to V4L2CameraProxy *\n- removed cross-thread validation methods from V4L2CameraProxy\n  (validate[Stream|Memory]Type)\n- removed hasPixelFormat() and hasSize() from V4L2CameraProxy\n- moved mmap logic out of V4L2Camera and into V4L2CameraProxy\n- moved nonblock logic out of V4L2Camera and into V4L2CameraProxy\n- add todos\n- oter cosmetic changes\n\nChanges in v2:\n- move all errno acrobatics to V4L2CameraProxy\n- remove all mentions of dmabuf\n- make V4L2CompatManager::getCamera return pointer rather than\n  shared_ptr\n- match V4L2 device nodes to libcamera cameras using Camera::name()\n  compared to /sys/dev/char/maj:min/name (only works for UVC cameras)\n  - in V4L2CompatManager::getCameraIndex()\n- add -fvisibility=hidden to v4l2 compat\n- cache the results of V4L2CompatManager::imageSize() within V4L2Camera\n  (where V4L2Camera is interested)\n- remove V4L2CompatManager::valid[fd|mmap], and where those methods were\n  used, check V4L2CompatManager::getCamera() != nullptr instead\n- fixed V4L2CompatManager::drmToV4L2() mappings for DRM_FORMAT_BGR888\n  and DRM_FORMAT_RGB888\n- other cosmetic changes\n---\n meson_options.txt                |   5 +\n src/meson.build                  |   4 +\n src/v4l2/meson.build             |  31 ++\n src/v4l2/v4l2_camera.cpp         | 224 +++++++++++\n src/v4l2/v4l2_camera.h           |  85 ++++\n src/v4l2/v4l2_camera_proxy.cpp   | 644 +++++++++++++++++++++++++++++++\n src/v4l2/v4l2_camera_proxy.h     |  81 ++++\n src/v4l2/v4l2_compat.cpp         |  85 ++++\n src/v4l2/v4l2_compat_manager.cpp | 250 ++++++++++++\n src/v4l2/v4l2_compat_manager.h   |  77 ++++\n 10 files changed, 1486 insertions(+)\n create mode 100644 src/v4l2/meson.build\n create mode 100644 src/v4l2/v4l2_camera.cpp\n create mode 100644 src/v4l2/v4l2_camera.h\n create mode 100644 src/v4l2/v4l2_camera_proxy.cpp\n create mode 100644 src/v4l2/v4l2_camera_proxy.h\n create mode 100644 src/v4l2/v4l2_compat.cpp\n create mode 100644 src/v4l2/v4l2_compat_manager.cpp\n create mode 100644 src/v4l2/v4l2_compat_manager.h","diff":"diff --git a/meson_options.txt b/meson_options.txt\nindex 1a328045..79ee4de6 100644\n--- a/meson_options.txt\n+++ b/meson_options.txt\n@@ -10,3 +10,8 @@ option('documentation',\n option('test',\n         type : 'boolean',\n         description: 'Compile and include the tests')\n+\n+option('v4l2',\n+        type : 'boolean',\n+        value : false,\n+        description : 'Compile the V4L2 compatibility layer')\ndiff --git a/src/meson.build b/src/meson.build\nindex 67ad20aa..5adcd61f 100644\n--- a/src/meson.build\n+++ b/src/meson.build\n@@ -6,3 +6,7 @@ subdir('libcamera')\n subdir('ipa')\n subdir('cam')\n subdir('qcam')\n+\n+if get_option('v4l2')\n+    subdir('v4l2')\n+endif\ndiff --git a/src/v4l2/meson.build b/src/v4l2/meson.build\nnew file mode 100644\nindex 00000000..14ee3594\n--- /dev/null\n+++ b/src/v4l2/meson.build\n@@ -0,0 +1,31 @@\n+v4l2_compat_sources = files([\n+    'v4l2_camera.cpp',\n+    'v4l2_camera_proxy.cpp',\n+    'v4l2_compat.cpp',\n+    'v4l2_compat_manager.cpp',\n+])\n+\n+v4l2_compat_includes = [\n+    libcamera_includes,\n+    libcamera_internal_includes,\n+]\n+\n+v4l2_compat_cpp_args = [\n+    # Meson enables large file support unconditionally, which redirect file\n+    # operations to 64-bit versions. This results in some symbols being\n+    # renamed, for instance open() being renamed to open64(). As the V4L2\n+    # adaptation wrapper needs to provide both 32-bit and 64-bit versions of\n+    # file operations, disable transparent large file support.\n+    '-U_FILE_OFFSET_BITS',\n+    '-D_FILE_OFFSET_BITS=32',\n+    '-fvisibility=hidden',\n+]\n+\n+v4l2_compat = shared_library('v4l2-compat',\n+                             v4l2_compat_sources,\n+                             name_prefix : '',\n+                             install : true,\n+                             link_with : libcamera,\n+                             include_directories : v4l2_compat_includes,\n+                             dependencies : libcamera_deps,\n+                             cpp_args : v4l2_compat_cpp_args)\ndiff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\nnew file mode 100644\nindex 00000000..3590730f\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera.cpp\n@@ -0,0 +1,224 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_camera.cpp - V4L2 compatibility camera\n+ */\n+\n+#include \"v4l2_camera.h\"\n+\n+#include <errno.h>\n+\n+#include \"log.h\"\n+#include \"utils.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(V4L2Compat);\n+\n+FrameMetadata::FrameMetadata(Buffer *buffer)\n+\t: index_(buffer->index()), bytesused_(buffer->bytesused()),\n+\t  timestamp_(buffer->timestamp()), sequence_(buffer->sequence()),\n+\t  status_(buffer->status())\n+{\n+}\n+\n+V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)\n+\t: camera_(camera), isRunning_(false)\n+{\n+\tcamera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);\n+}\n+\n+V4L2Camera::~V4L2Camera()\n+{\n+\tcamera_->release();\n+}\n+\n+void V4L2Camera::open(int *ret)\n+{\n+\t/* \\todo Support multiple open. */\n+\tif (camera_->acquire() < 0) {\n+\t\tLOG(V4L2Compat, Error) << \"Failed to acquire camera\";\n+\t\t*ret = -EINVAL;\n+\t\treturn;\n+\t}\n+\n+\tconfig_ = camera_->generateConfiguration({ StreamRole::Viewfinder });\n+\tif (!config_) {\n+\t\tcamera_->release();\n+\t\t*ret = -EINVAL;\n+\t\treturn;\n+\t}\n+\n+\t*ret = 0;\n+}\n+\n+void V4L2Camera::close()\n+{\n+\tcamera_->release();\n+}\n+\n+void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)\n+{\n+\t*streamConfig = config_->at(0);\n+}\n+\n+std::vector<FrameMetadata> V4L2Camera::completedBuffers()\n+{\n+\tstd::vector<FrameMetadata> v;\n+\n+\tbufferLock_.lock();\n+\tfor (std::unique_ptr<FrameMetadata> &metadata : completedBuffers_)\n+\t\tv.push_back(*metadata.get());\n+\tcompletedBuffers_.clear();\n+\tbufferLock_.unlock();\n+\n+\treturn v;\n+}\n+\n+void V4L2Camera::requestComplete(Request *request)\n+{\n+\tif (request->status() == Request::RequestCancelled)\n+\t\treturn;\n+\n+\t/* We only have one stream at the moment. */\n+\tbufferLock_.lock();\n+\tBuffer *buffer = request->buffers().begin()->second;\n+\tstd::unique_ptr<FrameMetadata> metadata =\n+\t\tutils::make_unique<FrameMetadata>(buffer);\n+\tcompletedBuffers_.push_back(std::move(metadata));\n+\tbufferLock_.unlock();\n+\n+\tbufferSema_.release();\n+}\n+\n+void V4L2Camera::configure(int *ret, StreamConfiguration *streamConfigOut,\n+\t\t\t   const Size &size, PixelFormat pixelformat,\n+\t\t\t   unsigned int bufferCount)\n+{\n+\tStreamConfiguration &streamConfig = config_->at(0);\n+\tstreamConfig.size.width = size.width;\n+\tstreamConfig.size.height = size.height;\n+\tstreamConfig.pixelFormat = pixelformat;\n+\tstreamConfig.bufferCount = bufferCount;\n+\t/* \\todo memoryType (interval vs external) */\n+\n+\tCameraConfiguration::Status validation = config_->validate();\n+\tif (validation == CameraConfiguration::Invalid) {\n+\t\tLOG(V4L2Compat, Debug) << \"Configuration invalid\";\n+\t\t*ret = -EINVAL;\n+\t\treturn;\n+\t}\n+\tif (validation == CameraConfiguration::Adjusted)\n+\t\tLOG(V4L2Compat, Debug) << \"Configuration adjusted\";\n+\n+\tLOG(V4L2Compat, Debug) << \"Validated configuration is: \"\n+\t\t\t      << streamConfig.toString();\n+\n+\t*ret = camera_->configure(config_.get());\n+\tif (*ret < 0)\n+\t\treturn;\n+\n+\t*streamConfigOut = config_->at(0);\n+}\n+\n+void V4L2Camera::mmap(void **ret, unsigned int index)\n+{\n+\tStream *stream = *camera_->streams().begin();\n+\t*ret = stream->buffers()[index].planes()[0].mem();\n+}\n+\n+void V4L2Camera::allocBuffers(int *ret, unsigned int count)\n+{\n+\t*ret = camera_->allocateBuffers();\n+\tif (*ret == -EACCES)\n+\t\t*ret = -EBUSY;\n+}\n+\n+void V4L2Camera::freeBuffers()\n+{\n+\tcamera_->freeBuffers();\n+}\n+\n+void V4L2Camera::streamOn(int *ret)\n+{\n+\t*ret = 0;\n+\n+\tif (isRunning_)\n+\t\treturn;\n+\n+\t*ret = camera_->start();\n+\tif (*ret < 0) {\n+\t\tif (*ret == -EACCES)\n+\t\t\t*ret = -EBUSY;\n+\t\treturn;\n+\t}\n+\tisRunning_ = true;\n+\n+\tfor (std::unique_ptr<Request> &req : pendingRequests_) {\n+\t\t/* \\todo What should we do if this returns -EINVAL? */\n+\t\t*ret = camera_->queueRequest(req.release());\n+\t\tif (*ret < 0) {\n+\t\t\tif (*ret == -EACCES)\n+\t\t\t\t*ret = -EBUSY;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tpendingRequests_.clear();\n+}\n+\n+void V4L2Camera::streamOff(int *ret)\n+{\n+\t*ret = 0;\n+\n+\t/* \\todo Restore buffers to reqbufs state? */\n+\tif (!isRunning_)\n+\t\treturn;\n+\n+\t*ret = camera_->stop();\n+\tif (*ret < 0) {\n+\t\tif (*ret == -EACCES)\n+\t\t\t*ret = -EBUSY;\n+\t\treturn;\n+\t}\n+\tisRunning_ = false;\n+}\n+\n+void V4L2Camera::qbuf(int *ret, unsigned int index)\n+{\n+\tStream *stream = config_->at(0).stream();\n+\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(index);\n+\tif (!buffer) {\n+\t\tLOG(V4L2Compat, Error) << \"Can't create buffer\";\n+\t\t*ret = -ENOMEM;\n+\t\treturn;\n+\t}\n+\n+\tstd::unique_ptr<Request> request =\n+\t\tstd::unique_ptr<Request>(camera_->createRequest());\n+\tif (!request) {\n+\t\tLOG(V4L2Compat, Error) << \"Can't create request\";\n+\t\t*ret = -ENOMEM;\n+\t\treturn;\n+\t}\n+\n+\t*ret = request->addBuffer(std::move(buffer));\n+\tif (*ret < 0) {\n+\t\tLOG(V4L2Compat, Error) << \"Can't set buffer for request\";\n+\t\t*ret = -ENOMEM;\n+\t\treturn;\n+\t}\n+\n+\tif (!isRunning_) {\n+\t\tpendingRequests_.push_back(std::move(request));\n+\t\treturn;\n+\t}\n+\n+\t*ret = camera_->queueRequest(request.release());\n+\tif (*ret < 0) {\n+\t\tLOG(V4L2Compat, Error) << \"Can't queue request\";\n+\t\tif (*ret == -EACCES)\n+\t\t\t*ret = -EBUSY;\n+\t}\n+}\ndiff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\nnew file mode 100644\nindex 00000000..d24dbca6\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera.h\n@@ -0,0 +1,85 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_camera.h - V4L2 compatibility camera\n+ */\n+\n+#ifndef __V4L2_CAMERA_H__\n+#define __V4L2_CAMERA_H__\n+\n+#include <deque>\n+#include <linux/videodev2.h>\n+#include <mutex>\n+\n+#include <libcamera/buffer.h>\n+#include <libcamera/camera.h>\n+\n+#include \"semaphore.h\"\n+\n+using namespace libcamera;\n+\n+class FrameMetadata\n+{\n+public:\n+\tFrameMetadata(Buffer *buffer);\n+\n+\tint index() const { return index_; }\n+\n+\tunsigned int bytesused() const { return bytesused_; }\n+\tuint64_t timestamp() const { return timestamp_; }\n+\tunsigned int sequence() const { return sequence_; }\n+\n+\tBuffer::Status status() const { return status_; }\n+\n+private:\n+\tint index_;\n+\n+\tunsigned int bytesused_;\n+\tuint64_t timestamp_;\n+\tunsigned int sequence_;\n+\n+\tBuffer::Status status_;\n+};\n+\n+class V4L2Camera : public Object\n+{\n+public:\n+\tV4L2Camera(std::shared_ptr<Camera> camera);\n+\t~V4L2Camera();\n+\n+\tvoid open(int *ret);\n+\tvoid close();\n+\tvoid getStreamConfig(StreamConfiguration *streamConfig);\n+\tstd::vector<FrameMetadata> completedBuffers();\n+\n+\tvoid mmap(void **ret, unsigned int index);\n+\n+\tvoid configure(int *ret, StreamConfiguration *streamConfigOut,\n+\t\t       const Size &size, PixelFormat pixelformat,\n+\t\t       unsigned int bufferCount);\n+\n+\tvoid allocBuffers(int *ret, unsigned int count);\n+\tvoid freeBuffers();\n+\tvoid streamOn(int *ret);\n+\tvoid streamOff(int *ret);\n+\n+\tvoid qbuf(int *ret, unsigned int index);\n+\n+\tSemaphore bufferSema_;\n+\n+private:\n+\tvoid requestComplete(Request *request);\n+\n+\tstd::shared_ptr<Camera> camera_;\n+\tstd::unique_ptr<CameraConfiguration> config_;\n+\n+\tbool isRunning_;\n+\n+\tstd::mutex bufferLock_;\n+\n+\tstd::deque<std::unique_ptr<Request>> pendingRequests_;\n+\tstd::deque<std::unique_ptr<FrameMetadata>> completedBuffers_;\n+};\n+\n+#endif /* __V4L2_CAMERA_H__ */\ndiff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\nnew file mode 100644\nindex 00000000..c9df7156\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera_proxy.cpp\n@@ -0,0 +1,644 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_camera_proxy.cpp - Proxy to V4L2 compatibility camera\n+ */\n+\n+#include \"v4l2_camera_proxy.h\"\n+\n+#include <algorithm>\n+#include <errno.h>\n+#include <linux/drm_fourcc.h>\n+#include <linux/videodev2.h>\n+#include <string.h>\n+#include <sys/mman.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/object.h>\n+\n+#include \"log.h\"\n+#include \"utils.h\"\n+#include \"v4l2_camera.h\"\n+#include \"v4l2_compat_manager.h\"\n+\n+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(V4L2Compat);\n+\n+V4L2CameraProxy::V4L2CameraProxy(unsigned int index,\n+\t\t\t\t std::shared_ptr<Camera> camera)\n+\t: refcount_(0), index_(index), bufferCount_(0), currentBuf_(0),\n+\t  vcam_(utils::make_unique<V4L2Camera>(camera))\n+{\n+\tquerycap(camera);\n+}\n+\n+int V4L2CameraProxy::open(bool nonBlocking)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing open\";\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::open, ConnectionTypeBlocking,\n+\t\t\t    &ret);\n+\tif (ret < 0) {\n+\t\terrno = -ret;\n+\t\treturn -1;\n+\t}\n+\n+\tnonBlocking_ = nonBlocking;\n+\n+\tvcam_->invokeMethod(&V4L2Camera::getStreamConfig,\n+\t\t\t    ConnectionTypeBlocking, &streamConfig_);\n+\tsetFmtFromConfig(streamConfig_);\n+\tsizeimage_ = calculateSizeImage(streamConfig_);\n+\n+\trefcount_++;\n+\n+\treturn 0;\n+}\n+\n+void V4L2CameraProxy::dup()\n+{\n+\trefcount_++;\n+}\n+\n+void V4L2CameraProxy::close()\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing close\";\n+\n+\tif (--refcount_ > 0)\n+\t\treturn;\n+\n+\tvcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking);\n+}\n+\n+void *V4L2CameraProxy::mmap(size_t length, int prot, int flags, off_t offset)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing mmap\";\n+\n+\t/* \\todo Validate prot and flags properly. */\n+\tif (prot != (PROT_READ | PROT_WRITE)) {\n+\t\terrno = EINVAL;\n+\t\treturn MAP_FAILED;\n+\t}\n+\n+\tunsigned int index = offset / sizeimage_;\n+\tif (index * sizeimage_ != offset || length != sizeimage_) {\n+\t\terrno = EINVAL;\n+\t\treturn MAP_FAILED;\n+\t}\n+\n+\tvoid *val;\n+\tvcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking,\n+\t\t\t    &val, index);\n+\n+\tbuffers_[index].flags |= V4L2_BUF_FLAG_MAPPED;\n+\tmmaps_[val] = index;\n+\n+\treturn val;\n+}\n+\n+int V4L2CameraProxy::munmap(void *addr, size_t length)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing munmap\";\n+\n+\tauto iter = mmaps_.find(addr);\n+\tif (iter == mmaps_.end() || length != sizeimage_) {\n+\t\terrno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\n+\tbuffers_[iter->second].flags &= ~V4L2_BUF_FLAG_MAPPED;\n+\tmmaps_.erase(iter);\n+\n+\treturn 0;\n+}\n+\n+bool V4L2CameraProxy::validateBufferType(uint32_t type)\n+{\n+\treturn type == V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+}\n+\n+bool V4L2CameraProxy::validateMemoryType(uint32_t memory)\n+{\n+\treturn memory == V4L2_MEMORY_MMAP;\n+}\n+\n+void V4L2CameraProxy::setFmtFromConfig(StreamConfiguration &streamConfig)\n+{\n+\tcurV4L2Format_.fmt.pix.width = streamConfig.size.width;\n+\tcurV4L2Format_.fmt.pix.height = streamConfig.size.height;\n+\tcurV4L2Format_.fmt.pix.pixelformat = drmToV4L2(streamConfig.pixelFormat);\n+\tcurV4L2Format_.fmt.pix.field = V4L2_FIELD_NONE;\n+\tcurV4L2Format_.fmt.pix.bytesperline =\n+\t\tbplMultiplier(curV4L2Format_.fmt.pix.pixelformat) *\n+\t\tcurV4L2Format_.fmt.pix.width;\n+\tcurV4L2Format_.fmt.pix.sizeimage =\n+\t\timageSize(curV4L2Format_.fmt.pix.pixelformat,\n+\t\t\t  curV4L2Format_.fmt.pix.width,\n+\t\t\t  curV4L2Format_.fmt.pix.height);\n+\tcurV4L2Format_.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;\n+}\n+\n+unsigned int V4L2CameraProxy::calculateSizeImage(StreamConfiguration &streamConfig)\n+{\n+\t/*\n+\t * \\todo Merge this method with setFmtFromConfig (need imageSize to\n+\t * support all libcamera formats first, or filter out MJPEG for now).\n+\t */\n+\treturn imageSize(drmToV4L2(streamConfig.pixelFormat),\n+\t\t\t streamConfig.size.width,\n+\t\t\t streamConfig.size.height);\n+}\n+\n+void V4L2CameraProxy::querycap(std::shared_ptr<Camera> camera)\n+{\n+\tstd::string driver = \"libcamera\";\n+\tstd::string bus_info = driver + \":\" + std::to_string(index_);\n+\n+\tutils::strlcpy((char *)capabilities_.driver, driver.c_str(),\n+\t\t       sizeof(capabilities_.driver));\n+\tutils::strlcpy((char *)capabilities_.card, camera->name().c_str(),\n+\t\t       sizeof(capabilities_.card));\n+\tutils::strlcpy((char *)capabilities_.bus_info, bus_info.c_str(),\n+\t\t       sizeof(capabilities_.bus_info));\n+\t/* \\todo Put this in a header/config somewhere. */\n+\tcapabilities_.version = KERNEL_VERSION(5, 2, 0);\n+\tcapabilities_.device_caps = V4L2_CAP_VIDEO_CAPTURE;\n+\tcapabilities_.capabilities = capabilities_.device_caps\n+\t\t\t\t   | V4L2_CAP_DEVICE_CAPS;\n+\tmemset(capabilities_.reserved, 0, sizeof(capabilities_.reserved));\n+}\n+\n+void V4L2CameraProxy::updateBuffers()\n+{\n+\tstd::vector<FrameMetadata> completedBuffers = vcam_->completedBuffers();\n+\tfor (FrameMetadata &fmd : completedBuffers) {\n+\t\tstruct v4l2_buffer &buf = buffers_[fmd.index()];\n+\n+\t\tswitch (fmd.status()) {\n+\t\tcase Buffer::Status::BufferSuccess:\n+\t\t\tbuf.bytesused = fmd.bytesused();\n+\t\t\tbuf.field = V4L2_FIELD_NONE;\n+\t\t\tbuf.timestamp.tv_sec = fmd.timestamp() / 1000000000;\n+\t\t\tbuf.timestamp.tv_usec = fmd.timestamp() % 1000000;\n+\t\t\tbuf.sequence = fmd.sequence();\n+\n+\t\t\tbuf.flags |= V4L2_BUF_FLAG_DONE;\n+\t\t\tbreak;\n+\t\tcase Buffer::Status::BufferError:\n+\t\t\tbuf.flags |= V4L2_BUF_FLAG_ERROR;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\n+\n+int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querycap\";\n+\n+\t*arg = capabilities_;\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_enum_fmt\";\n+\n+\tif (!validateBufferType(arg->type) ||\n+\t    arg->index > streamConfig_.formats().pixelformats().size())\n+\t\treturn -EINVAL;\n+\n+\t/* \\todo Add map from format to description. */\n+\tutils::strlcpy((char *)arg->description, \"Video Format Description\",\n+\t\t       sizeof(arg->description));\n+\targ->pixelformat = drmToV4L2(streamConfig_.formats().pixelformats()[arg->index]);\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_g_fmt(struct v4l2_format *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_g_fmt\";\n+\n+\tif (!validateBufferType(arg->type))\n+\t\treturn -EINVAL;\n+\n+\tmemset(&arg->fmt, 0, sizeof(arg->fmt));\n+\targ->fmt.pix = curV4L2Format_.fmt.pix;\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_s_fmt\";\n+\n+\tint ret = vidioc_try_fmt(arg);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tSize size(arg->fmt.pix.width, arg->fmt.pix.height);\n+\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking,\n+\t\t\t    &ret, &streamConfig_, size,\n+\t\t\t    v4l2ToDrm(arg->fmt.pix.pixelformat), bufferCount_);\n+\tif (ret < 0)\n+\t\treturn -EINVAL;\n+\n+\tunsigned int sizeimage = calculateSizeImage(streamConfig_);\n+\tif (sizeimage == 0)\n+\t\treturn -EINVAL;\n+\n+\tsizeimage_ = sizeimage;\n+\n+\tsetFmtFromConfig(streamConfig_);\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_try_fmt(struct v4l2_format *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_try_fmt\";\n+\tif (!validateBufferType(arg->type))\n+\t\treturn -EINVAL;\n+\n+\tPixelFormat format = v4l2ToDrm(arg->fmt.pix.pixelformat);\n+\tconst std::vector<PixelFormat> &formats =\n+\t\tstreamConfig_.formats().pixelformats();\n+\tif (std::find(formats.begin(), formats.end(), format) == formats.end())\n+\t\tformat = streamConfig_.formats().pixelformats()[0];\n+\n+\tSize size(arg->fmt.pix.width, arg->fmt.pix.height);\n+\tconst std::vector<Size> &sizes = streamConfig_.formats().sizes(format);\n+\tif (std::find(sizes.begin(), sizes.end(), size) == sizes.end())\n+\t\tsize = streamConfig_.formats().sizes(format)[0];\n+\n+\targ->fmt.pix.width        = size.width;\n+\targ->fmt.pix.height       = size.height;\n+\targ->fmt.pix.pixelformat  = v4l2ToDrm(format);\n+\targ->fmt.pix.field        = V4L2_FIELD_NONE;\n+\targ->fmt.pix.bytesperline = bplMultiplier(drmToV4L2(format)) *\n+\t\t\t\t    arg->fmt.pix.width;\n+\targ->fmt.pix.sizeimage    = imageSize(drmToV4L2(format),\n+\t\t\t\t\t      arg->fmt.pix.width,\n+\t\t\t\t\t      arg->fmt.pix.height);\n+\targ->fmt.pix.colorspace   = V4L2_COLORSPACE_SRGB;\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::freeBuffers()\n+{\n+\tLOG(V4L2Compat, Debug) << \"Freeing libcamera bufs\";\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n+\t\t\t    ConnectionTypeBlocking, &ret);\n+\tif (ret < 0) {\n+\t\tLOG(V4L2Compat, Error) << \"Failed to stop stream\";\n+\t\treturn ret;\n+\t}\n+\tvcam_->invokeMethod(&V4L2Camera::freeBuffers, ConnectionTypeBlocking);\n+\tbufferCount_ = 0;\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)\n+{\n+\tint ret;\n+\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_reqbufs\";\n+\tif (!validateBufferType(arg->type) ||\n+\t    !validateMemoryType(arg->memory))\n+\t\treturn -EINVAL;\n+\n+\tLOG(V4L2Compat, Debug) << arg->count << \" buffers requested \";\n+\n+\targ->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;\n+\n+\tif (arg->count == 0)\n+\t\treturn freeBuffers();\n+\n+\tSize size(curV4L2Format_.fmt.pix.width, curV4L2Format_.fmt.pix.height);\n+\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking,\n+\t\t\t    &ret, &streamConfig_, size,\n+\t\t\t    v4l2ToDrm(curV4L2Format_.fmt.pix.pixelformat),\n+\t\t\t    arg->count);\n+\tif (ret < 0)\n+\t\treturn -EINVAL;\n+\n+\tsizeimage_ = calculateSizeImage(streamConfig_);\n+\tif (sizeimage_ == 0)\n+\t\treturn -EINVAL;\n+\n+\tsetFmtFromConfig(streamConfig_);\n+\n+\targ->count = streamConfig_.bufferCount;\n+\tbufferCount_ = arg->count;\n+\n+\tvcam_->invokeMethod(&V4L2Camera::allocBuffers,\n+\t\t\t    ConnectionTypeBlocking, &ret, arg->count);\n+\tif (ret < 0) {\n+\t\targ->count = 0;\n+\t\treturn ret;\n+\t}\n+\n+\tbuffers_.resize(arg->count);\n+\tfor (unsigned int i = 0; i < arg->count; i++) {\n+\t\tstruct v4l2_buffer buf = {};\n+\t\tbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\t\tbuf.length = curV4L2Format_.fmt.pix.sizeimage;\n+\t\tbuf.memory = V4L2_MEMORY_MMAP;\n+\t\tbuf.m.offset = i * curV4L2Format_.fmt.pix.sizeimage;\n+\t\tbuf.index = i;\n+\n+\t\tbuffers_[i] = buf;\n+\t}\n+\n+\tLOG(V4L2Compat, Debug) << \"Allocated \" << arg->count << \" buffers\";\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querybuf\";\n+\n+\tif (!validateBufferType(arg->type) ||\n+\t    arg->index >= bufferCount_)\n+\t\treturn -EINVAL;\n+\n+\tupdateBuffers();\n+\n+\t*arg = buffers_[arg->index];\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_qbuf, index = \"\n+\t\t\t       << arg->index;\n+\n+\tif (!validateBufferType(arg->type) ||\n+\t    !validateMemoryType(arg->memory) ||\n+\t    arg->index >= bufferCount_)\n+\t\treturn -EINVAL;\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::qbuf, ConnectionTypeBlocking,\n+\t\t\t    &ret, arg->index);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\targ->flags |= V4L2_BUF_FLAG_QUEUED;\n+\targ->flags &= ~V4L2_BUF_FLAG_DONE;\n+\n+\treturn ret;\n+}\n+\n+int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_dqbuf\";\n+\n+\tif (!validateBufferType(arg->type) ||\n+\t    !validateMemoryType(arg->memory))\n+\t\treturn -EINVAL;\n+\n+\tif (nonBlocking_ && !vcam_->bufferSema_.tryAcquire())\n+\t\treturn -EAGAIN;\n+\telse\n+\t\tvcam_->bufferSema_.acquire();\n+\n+\tupdateBuffers();\n+\n+\tstruct v4l2_buffer &buf = buffers_[currentBuf_];\n+\n+\tbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;\n+\tbuf.length = sizeimage_;\n+\t*arg = buf;\n+\n+\tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::vidioc_streamon(int *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamon\";\n+\n+\tif (!validateBufferType(*arg))\n+\t\treturn -EINVAL;\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::streamOn,\n+\t\t\t    ConnectionTypeBlocking, &ret);\n+\n+\treturn ret;\n+}\n+\n+int V4L2CameraProxy::vidioc_streamoff(int *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamoff\";\n+\n+\tif (!validateBufferType(*arg))\n+\t\treturn -EINVAL;\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n+\t\t\t    ConnectionTypeBlocking, &ret);\n+\n+\tfor (struct v4l2_buffer &buf : buffers_)\n+\t\tbuf.flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE);\n+\n+\treturn ret;\n+}\n+\n+int V4L2CameraProxy::ioctl(unsigned long request, void *arg)\n+{\n+\tint ret;\n+\tswitch (request) {\n+\tcase VIDIOC_QUERYCAP:\n+\t\tret = vidioc_querycap(static_cast<struct v4l2_capability *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_ENUM_FMT:\n+\t\tret = vidioc_enum_fmt(static_cast<struct v4l2_fmtdesc *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_G_FMT:\n+\t\tret = vidioc_g_fmt(static_cast<struct v4l2_format *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_S_FMT:\n+\t\tret = vidioc_s_fmt(static_cast<struct v4l2_format *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_TRY_FMT:\n+\t\tret = vidioc_try_fmt(static_cast<struct v4l2_format *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_REQBUFS:\n+\t\tret = vidioc_reqbufs(static_cast<struct v4l2_requestbuffers *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_QUERYBUF:\n+\t\tret = vidioc_querybuf(static_cast<struct v4l2_buffer *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_QBUF:\n+\t\tret = vidioc_qbuf(static_cast<struct v4l2_buffer *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_DQBUF:\n+\t\tret = vidioc_dqbuf(static_cast<struct v4l2_buffer *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_STREAMON:\n+\t\tret = vidioc_streamon(static_cast<int *>(arg));\n+\t\tbreak;\n+\tcase VIDIOC_STREAMOFF:\n+\t\tret = vidioc_streamoff(static_cast<int *>(arg));\n+\t\tbreak;\n+\tdefault:\n+\t\tret = -ENOTTY;\n+\t\tbreak;\n+\t}\n+\n+\tif (ret < 0) {\n+\t\terrno = -ret;\n+\t\treturn -1;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/* \\todo make libcamera export these */\n+int V4L2CameraProxy::bplMultiplier(unsigned int format)\n+{\n+\tswitch (format) {\n+\tcase V4L2_PIX_FMT_NV12:\n+\tcase V4L2_PIX_FMT_NV21:\n+\tcase V4L2_PIX_FMT_NV16:\n+\tcase V4L2_PIX_FMT_NV61:\n+\tcase V4L2_PIX_FMT_NV24:\n+\tcase V4L2_PIX_FMT_NV42:\n+\t\treturn 1;\n+\tcase V4L2_PIX_FMT_BGR24:\n+\tcase V4L2_PIX_FMT_RGB24:\n+\t\treturn 3;\n+\tcase V4L2_PIX_FMT_ARGB32:\n+\t\treturn 4;\n+\tcase V4L2_PIX_FMT_VYUY:\n+\tcase V4L2_PIX_FMT_YVYU:\n+\tcase V4L2_PIX_FMT_UYVY:\n+\tcase V4L2_PIX_FMT_YUYV:\n+\t\treturn 2;\n+\tdefault:\n+\t\treturn 0;\n+\t};\n+}\n+\n+int V4L2CameraProxy::imageSize(unsigned int format,\n+\t\t\t       unsigned int width, unsigned int height)\n+{\n+\tswitch (format) {\n+\tcase V4L2_PIX_FMT_NV12:\n+\tcase V4L2_PIX_FMT_NV21:\n+\t\treturn width * height * 3 / 2;\n+\tcase V4L2_PIX_FMT_NV16:\n+\tcase V4L2_PIX_FMT_NV61:\n+\t\treturn width * height * 2;\n+\tcase V4L2_PIX_FMT_NV24:\n+\tcase V4L2_PIX_FMT_NV42:\n+\t\treturn width * height * 3;\n+\tcase V4L2_PIX_FMT_BGR24:\n+\tcase V4L2_PIX_FMT_RGB24:\n+\t\treturn width * height * 3;\n+\tcase V4L2_PIX_FMT_ARGB32:\n+\t\treturn width * height * 4;\n+\tcase V4L2_PIX_FMT_VYUY:\n+\tcase V4L2_PIX_FMT_YVYU:\n+\tcase V4L2_PIX_FMT_UYVY:\n+\tcase V4L2_PIX_FMT_YUYV:\n+\t\treturn width * height * 2;\n+\tdefault:\n+\t\treturn 0;\n+\t};\n+}\n+\n+unsigned int V4L2CameraProxy::v4l2ToDrm(unsigned int pixelformat)\n+{\n+\tswitch (pixelformat) {\n+\t/* RGB formats. */\n+\tcase V4L2_PIX_FMT_RGB24:\n+\t\treturn DRM_FORMAT_BGR888;\n+\tcase V4L2_PIX_FMT_BGR24:\n+\t\treturn DRM_FORMAT_RGB888;\n+\tcase V4L2_PIX_FMT_ARGB32:\n+\t\treturn DRM_FORMAT_BGRA8888;\n+\n+\t/* YUV packed formats. */\n+\tcase V4L2_PIX_FMT_YUYV:\n+\t\treturn DRM_FORMAT_YUYV;\n+\tcase V4L2_PIX_FMT_YVYU:\n+\t\treturn DRM_FORMAT_YVYU;\n+\tcase V4L2_PIX_FMT_UYVY:\n+\t\treturn DRM_FORMAT_UYVY;\n+\tcase V4L2_PIX_FMT_VYUY:\n+\t\treturn DRM_FORMAT_VYUY;\n+\n+\t/* YUY planar formats. */\n+\tcase V4L2_PIX_FMT_NV16:\n+\t\treturn DRM_FORMAT_NV16;\n+\tcase V4L2_PIX_FMT_NV61:\n+\t\treturn DRM_FORMAT_NV61;\n+\tcase V4L2_PIX_FMT_NV12:\n+\t\treturn DRM_FORMAT_NV12;\n+\tcase V4L2_PIX_FMT_NV21:\n+\t\treturn DRM_FORMAT_NV21;\n+\tcase V4L2_PIX_FMT_NV24:\n+\t\treturn DRM_FORMAT_NV24;\n+\tcase V4L2_PIX_FMT_NV42:\n+\t\treturn DRM_FORMAT_NV42;\n+\tdefault:\n+\t\treturn pixelformat;\n+\t};\n+}\n+\n+unsigned int V4L2CameraProxy::drmToV4L2(unsigned int pixelformat)\n+{\n+\tswitch (pixelformat) {\n+\t/* RGB formats. */\n+\tcase DRM_FORMAT_BGR888:\n+\t\treturn V4L2_PIX_FMT_RGB24;\n+\tcase DRM_FORMAT_RGB888:\n+\t\treturn V4L2_PIX_FMT_BGR24;\n+\tcase DRM_FORMAT_BGRA8888:\n+\t\treturn V4L2_PIX_FMT_ARGB32;\n+\n+\t/* YUV packed formats. */\n+\tcase DRM_FORMAT_YUYV:\n+\t\treturn V4L2_PIX_FMT_YUYV;\n+\tcase DRM_FORMAT_YVYU:\n+\t\treturn V4L2_PIX_FMT_YVYU;\n+\tcase DRM_FORMAT_UYVY:\n+\t\treturn V4L2_PIX_FMT_UYVY;\n+\tcase DRM_FORMAT_VYUY:\n+\t\treturn V4L2_PIX_FMT_VYUY;\n+\n+\t/* YUY planar formats. */\n+\tcase DRM_FORMAT_NV16:\n+\t\treturn V4L2_PIX_FMT_NV16;\n+\tcase DRM_FORMAT_NV61:\n+\t\treturn V4L2_PIX_FMT_NV61;\n+\tcase DRM_FORMAT_NV12:\n+\t\treturn V4L2_PIX_FMT_NV12;\n+\tcase DRM_FORMAT_NV21:\n+\t\treturn V4L2_PIX_FMT_NV21;\n+\tcase DRM_FORMAT_NV24:\n+\t\treturn V4L2_PIX_FMT_NV24;\n+\tcase DRM_FORMAT_NV42:\n+\t\treturn V4L2_PIX_FMT_NV42;\n+\tdefault:\n+\t\treturn pixelformat;\n+\t}\n+}\ndiff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h\nnew file mode 100644\nindex 00000000..bef0f0af\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera_proxy.h\n@@ -0,0 +1,81 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_camera_proxy.h - Proxy to V4L2 compatibility camera\n+ */\n+\n+#ifndef __V4L2_CAMERA_PROXY_H__\n+#define __V4L2_CAMERA_PROXY_H__\n+\n+#include <linux/videodev2.h>\n+#include <map>\n+#include <memory>\n+#include <sys/types.h>\n+#include <vector>\n+\n+#include <libcamera/camera.h>\n+\n+#include \"v4l2_camera.h\"\n+\n+using namespace libcamera;\n+\n+class V4L2CameraProxy\n+{\n+public:\n+\tV4L2CameraProxy(unsigned int index, std::shared_ptr<Camera> camera);\n+\n+\tint open(bool nonBlocking);\n+\tvoid dup();\n+\tvoid close();\n+\tvoid *mmap(size_t length, int prot, int flags, off_t offset);\n+\tint munmap(void *addr, size_t length);\n+\n+\tint ioctl(unsigned long request, void *arg);\n+\n+private:\n+\tbool validateBufferType(uint32_t type);\n+\tbool validateMemoryType(uint32_t memory);\n+\tvoid setFmtFromConfig(StreamConfiguration &streamConfig);\n+\tunsigned int calculateSizeImage(StreamConfiguration &streamConfig);\n+\tvoid querycap(std::shared_ptr<Camera> camera);\n+\tvoid updateBuffers();\n+\tint freeBuffers();\n+\n+\tint vidioc_querycap(struct v4l2_capability *arg);\n+\tint vidioc_enum_fmt(struct v4l2_fmtdesc *arg);\n+\tint vidioc_g_fmt(struct v4l2_format *arg);\n+\tint vidioc_s_fmt(struct v4l2_format *arg);\n+\tint vidioc_try_fmt(struct v4l2_format *arg);\n+\tint vidioc_reqbufs(struct v4l2_requestbuffers *arg);\n+\tint vidioc_querybuf(struct v4l2_buffer *arg);\n+\tint vidioc_qbuf(struct v4l2_buffer *arg);\n+\tint vidioc_dqbuf(struct v4l2_buffer *arg);\n+\tint vidioc_streamon(int *arg);\n+\tint vidioc_streamoff(int *arg);\n+\n+\tstatic int bplMultiplier(unsigned int format);\n+\tstatic int imageSize(unsigned int format, unsigned int width,\n+\t\t\t     unsigned int height);\n+\n+\tstatic unsigned int v4l2ToDrm(unsigned int pixelformat);\n+\tstatic unsigned int drmToV4L2(unsigned int pixelformat);\n+\n+\tunsigned int refcount_;\n+\tunsigned int index_;\n+\tbool nonBlocking_;\n+\n+\tstruct v4l2_format curV4L2Format_;\n+\tStreamConfiguration streamConfig_;\n+\tstruct v4l2_capability capabilities_;\n+\tunsigned int bufferCount_;\n+\tunsigned int currentBuf_;\n+\tunsigned int sizeimage_;\n+\n+\tstd::vector<struct v4l2_buffer> buffers_;\n+\tstd::map<void *, unsigned int> mmaps_;\n+\n+\tstd::unique_ptr<V4L2Camera> vcam_;\n+};\n+\n+#endif /* __V4L2_CAMERA_PROXY_H__ */\ndiff --git a/src/v4l2/v4l2_compat.cpp b/src/v4l2/v4l2_compat.cpp\nnew file mode 100644\nindex 00000000..171ebed6\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat.cpp\n@@ -0,0 +1,85 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_compat.cpp - V4L2 compatibility layer\n+ */\n+\n+#include \"v4l2_compat_manager.h\"\n+\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <stdarg.h>\n+#include <sys/mman.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+\n+#define LIBCAMERA_PUBLIC __attribute__((visibility(\"default\")))\n+\n+using namespace libcamera;\n+\n+#define extract_va_arg(type, arg, last)\t\\\n+{\t\t\t\t\t\\\n+\tva_list ap;\t\t\t\\\n+\tva_start(ap, last);\t\t\\\n+\targ = va_arg(ap, type);\t\t\\\n+\tva_end(ap);\t\t\t\\\n+}\n+\n+extern \"C\" {\n+\n+LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...)\n+{\n+\tmode_t mode = 0;\n+\tif (oflag & O_CREAT || oflag & O_TMPFILE)\n+\t\textract_va_arg(mode_t, mode, oflag);\n+\n+\treturn V4L2CompatManager::instance()->openat(AT_FDCWD, path,\n+\t\t\t\t\t\t     oflag, mode);\n+}\n+\n+/* _FORTIFY_SOURCE redirects open to __open_2 */\n+LIBCAMERA_PUBLIC extern __typeof(open) __open_2 __attribute__ ((alias(\"open\")));\n+\n+LIBCAMERA_PUBLIC int openat(int dirfd, const char *path, int oflag, ...)\n+{\n+\tmode_t mode = 0;\n+\tif (oflag & O_CREAT || oflag & O_TMPFILE)\n+\t\textract_va_arg(mode_t, mode, oflag);\n+\n+\treturn V4L2CompatManager::instance()->openat(dirfd, path, oflag, mode);\n+}\n+\n+LIBCAMERA_PUBLIC extern __typeof(openat) __openat_2 __attribute__ ((alias(\"openat\")));\n+\n+LIBCAMERA_PUBLIC int dup(int oldfd)\n+{\n+\treturn V4L2CompatManager::instance()->dup(oldfd);\n+}\n+\n+LIBCAMERA_PUBLIC int close(int fd)\n+{\n+\treturn V4L2CompatManager::instance()->close(fd);\n+}\n+\n+LIBCAMERA_PUBLIC void *mmap(void *addr, size_t length, int prot, int flags,\n+\t\t\t    int fd, off_t offset)\n+{\n+\treturn V4L2CompatManager::instance()->mmap(addr, length, prot, flags,\n+\t\t\t\t\t\t   fd, offset);\n+}\n+\n+LIBCAMERA_PUBLIC int munmap(void *addr, size_t length)\n+{\n+\treturn V4L2CompatManager::instance()->munmap(addr, length);\n+}\n+\n+LIBCAMERA_PUBLIC int ioctl(int fd, unsigned long request, ...)\n+{\n+\tvoid *arg;\n+\textract_va_arg(void *, arg, request);\n+\n+\treturn V4L2CompatManager::instance()->ioctl(fd, request, arg);\n+}\n+\n+}\ndiff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp\nnew file mode 100644\nindex 00000000..a3d69374\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat_manager.cpp\n@@ -0,0 +1,250 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_compat_manager.cpp - V4L2 compatibility manager\n+ */\n+\n+#include \"v4l2_compat_manager.h\"\n+\n+#include <dlfcn.h>\n+#include <fcntl.h>\n+#include <map>\n+#include <stdarg.h>\n+#include <string.h>\n+#include <sys/eventfd.h>\n+#include <sys/mman.h>\n+#include <sys/stat.h>\n+#include <sys/sysmacros.h>\n+#include <sys/types.h>\n+#include <unistd.h>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/camera_manager.h>\n+\n+#include \"log.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(V4L2Compat)\n+\n+V4L2CompatManager::V4L2CompatManager()\n+\t: cm_(nullptr), initialized_(false)\n+{\n+\topenat_func_ = (openat_func_t)dlsym(RTLD_NEXT, \"openat\");\n+\tdup_func_    = (dup_func_t   )dlsym(RTLD_NEXT, \"dup\");\n+\tclose_func_  = (close_func_t )dlsym(RTLD_NEXT, \"close\");\n+\tioctl_func_  = (ioctl_func_t )dlsym(RTLD_NEXT, \"ioctl\");\n+\tmmap_func_   = (mmap_func_t  )dlsym(RTLD_NEXT, \"mmap\");\n+\tmunmap_func_ = (munmap_func_t)dlsym(RTLD_NEXT, \"munmap\");\n+}\n+\n+V4L2CompatManager::~V4L2CompatManager()\n+{\n+\tdevices_.clear();\n+\tmmaps_.clear();\n+\n+\tif (isRunning()) {\n+\t\texit(0);\n+\t\t/* \\todo Wait with a timeout, just in case. */\n+\t\twait();\n+\t}\n+}\n+\n+int V4L2CompatManager::init()\n+{\n+\tstart();\n+\n+\tMutexLocker locker(mutex_);\n+\tcv_.wait(locker, [&] { return initialized_; });\n+\n+\treturn 0;\n+}\n+\n+void V4L2CompatManager::run()\n+{\n+\tcm_ = new CameraManager();\n+\n+\tint ret = cm_->start();\n+\tif (ret) {\n+\t\tLOG(V4L2Compat, Error) << \"Failed to start camera manager: \"\n+\t\t\t\t       << strerror(-ret);\n+\t\treturn;\n+\t}\n+\n+\tLOG(V4L2Compat, Debug) << \"Started camera manager\";\n+\n+\t/*\n+\t * For each Camera registered in the system, a V4L2CameraProxy gets\n+\t * created here to wrap a camera device.\n+\t */\n+\tunsigned int index = 0;\n+\tfor (auto &camera : cm_->cameras()) {\n+\t\tV4L2CameraProxy *proxy = new V4L2CameraProxy(index, camera);\n+\t\tproxies_.emplace_back(proxy);\n+\t\t++index;\n+\t}\n+\n+\t/*\n+\t * libcamera has been initialized. Unlock the init() caller as we're\n+\t * now ready to handle calls from the framework.\n+\t */\n+\tmutex_.lock();\n+\tinitialized_ = true;\n+\tmutex_.unlock();\n+\tcv_.notify_one();\n+\n+\t/* Now start processing events and messages. */\n+\texec();\n+\n+\tproxies_.clear();\n+\tcm_->stop();\n+\tdelete cm_;\n+\tcm_ = nullptr;\n+}\n+\n+V4L2CompatManager *V4L2CompatManager::instance()\n+{\n+\tstatic V4L2CompatManager instance;\n+\treturn &instance;\n+}\n+\n+V4L2CameraProxy *V4L2CompatManager::getProxy(int fd)\n+{\n+\tauto device = devices_.find(fd);\n+\tif (device == devices_.end())\n+\t\treturn nullptr;\n+\n+\treturn device->second;\n+}\n+\n+int V4L2CompatManager::getCameraIndex(int fd)\n+{\n+\tstruct stat statbuf;\n+\tint ret = fstat(fd, &statbuf);\n+\tif (ret < 0)\n+\t\treturn -1;\n+\n+\tstd::shared_ptr<Camera> target = cm_->get(statbuf.st_rdev);\n+\tif (!target)\n+\t\treturn -1;\n+\n+\tunsigned int index = 0;\n+\tfor (auto &camera : cm_->cameras()) {\n+\t\tif (camera == target)\n+\t\t\treturn index;\n+\t\t++index;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mode)\n+{\n+\tint fd = openat_func_(dirfd, path, oflag, mode);\n+\tif (fd < 0)\n+\t\treturn fd;\n+\n+\tstruct stat statbuf;\n+\tint ret = fstat(fd, &statbuf);\n+\tif (ret < 0 || (statbuf.st_mode & S_IFMT) != S_IFCHR ||\n+\t    major(statbuf.st_rdev) != 81)\n+\t\treturn fd;\n+\n+\tif (!isRunning())\n+\t\tinit();\n+\n+\tret = getCameraIndex(fd);\n+\tif (ret < 0) {\n+\t\tLOG(V4L2Compat, Info) << \"No camera found for \" << path;\n+\t\treturn fd;\n+\t}\n+\n+\tclose_func_(fd);\n+\n+\tunsigned int camera_index = static_cast<unsigned int>(ret);\n+\n+\tV4L2CameraProxy *proxy = proxies_[camera_index].get();\n+\tret = proxy->open(oflag & O_NONBLOCK);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tint efd = eventfd(0, oflag & (O_CLOEXEC | O_NONBLOCK));\n+\tif (efd < 0) {\n+\t\tproxy->close();\n+\t\treturn efd;\n+\t}\n+\n+\tdevices_.emplace(efd, proxy);\n+\n+\treturn efd;\n+}\n+\n+int V4L2CompatManager::dup(int oldfd)\n+{\n+\tint newfd = dup_func_(oldfd);\n+\tif (newfd < 0)\n+\t\treturn newfd;\n+\n+\tauto device = devices_.find(oldfd);\n+\tif (device != devices_.end()) {\n+\t\tV4L2CameraProxy *proxy = device->second;\n+\t\tdevices_[newfd] = proxy;\n+\t\tproxy->dup();\n+\t}\n+\n+\treturn newfd;\n+}\n+\n+int V4L2CompatManager::close(int fd)\n+{\n+\tV4L2CameraProxy *proxy = getProxy(fd);\n+\tif (proxy) {\n+\t\tproxy->close();\n+\t\tdevices_.erase(fd);\n+\t\treturn 0;\n+\t}\n+\n+\treturn close_func_(fd);\n+}\n+\n+void *V4L2CompatManager::mmap(void *addr, size_t length, int prot, int flags,\n+\t\t\t      int fd, off_t offset)\n+{\n+\tV4L2CameraProxy *proxy = getProxy(fd);\n+\tif (!proxy)\n+\t\treturn mmap_func_(addr, length, prot, flags, fd, offset);\n+\n+\tvoid *map = proxy->mmap(length, prot, flags, offset);\n+\tif (map == MAP_FAILED)\n+\t\treturn map;\n+\n+\tmmaps_[map] = proxy;\n+\treturn map;\n+}\n+\n+int V4L2CompatManager::munmap(void *addr, size_t length)\n+{\n+\tauto device = mmaps_.find(addr);\n+\tif (device == mmaps_.end())\n+\t\treturn munmap_func_(addr, length);\n+\n+\tV4L2CameraProxy *proxy = device->second;\n+\n+\tint ret = proxy->munmap(addr, length);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tmmaps_.erase(device);\n+\n+\treturn 0;\n+}\n+\n+int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg)\n+{\n+\tV4L2CameraProxy *proxy = getProxy(fd);\n+\tif (!proxy)\n+\t\treturn ioctl_func_(fd, request, arg);\n+\n+\treturn proxy->ioctl(request, arg);\n+}\ndiff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h\nnew file mode 100644\nindex 00000000..dd9d3210\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat_manager.h\n@@ -0,0 +1,77 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * v4l2_compat_manager.h - V4L2 compatibility manager\n+ */\n+\n+#ifndef __V4L2_COMPAT_MANAGER_H__\n+#define __V4L2_COMPAT_MANAGER_H__\n+\n+#include <condition_variable>\n+#include <fcntl.h>\n+#include <map>\n+#include <memory>\n+#include <mutex>\n+#include <sys/types.h>\n+#include <vector>\n+\n+#include <libcamera/camera_manager.h>\n+\n+#include \"thread.h\"\n+#include \"v4l2_camera_proxy.h\"\n+\n+using namespace libcamera;\n+\n+class V4L2CompatManager : public Thread\n+{\n+public:\n+\tstatic V4L2CompatManager *instance();\n+\n+\tint init();\n+\n+\tV4L2CameraProxy *getProxy(int fd);\n+\n+\tint openat(int dirfd, const char *path, int oflag, mode_t mode);\n+\n+\tint dup(int oldfd);\n+\tint close(int fd);\n+\tvoid *mmap(void *addr, size_t length, int prot, int flags,\n+\t\t   int fd, off_t offset);\n+\tint munmap(void *addr, size_t length);\n+\tint ioctl(int fd, unsigned long request, void *arg);\n+\n+private:\n+\tV4L2CompatManager();\n+\t~V4L2CompatManager();\n+\n+\tvoid run() override;\n+\tint getCameraIndex(int fd);\n+\n+\ttypedef int (*openat_func_t)(int dirfd, const char *path, int oflag, ...);\n+\ttypedef int (*dup_func_t)(int oldfd);\n+\ttypedef int (*close_func_t)(int fd);\n+\ttypedef int (*ioctl_func_t)(int fd, unsigned long request, ...);\n+\ttypedef void *(*mmap_func_t)(void *addr, size_t length, int prot,\n+\t\t\t\t     int flags, int fd, off_t offset);\n+\ttypedef int (*munmap_func_t)(void *addr, size_t length);\n+\n+\topenat_func_t openat_func_;\n+\tdup_func_t    dup_func_;\n+\tclose_func_t  close_func_;\n+\tioctl_func_t  ioctl_func_;\n+\tmmap_func_t   mmap_func_;\n+\tmunmap_func_t munmap_func_;\n+\n+\tCameraManager *cm_;\n+\n+\tstd::mutex mutex_;\n+\tstd::condition_variable cv_;\n+\tbool initialized_;\n+\n+\tstd::vector<std::unique_ptr<V4L2CameraProxy>> proxies_;\n+\tstd::map<int, V4L2CameraProxy *> devices_;\n+\tstd::map<void *, V4L2CameraProxy *> mmaps_;\n+};\n+\n+#endif /* __V4L2_COMPAT_MANAGER_H__ */\n","prefixes":["libcamera-devel","v6","5/5"]}