Show a patch.

GET /api/1.1/patches/2448/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2448,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/2448/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/2448/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20191223072620.13022-6-paul.elder@ideasonboard.com>",
    "date": "2019-12-23T07:26:19",
    "name": "[libcamera-devel,v3,5/6] v4l2: v4l2_compat: Add V4L2 compatibility layer",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "8e4c284b261b82149c5f5066c9b4340f8f7746c2",
    "submitter": {
        "id": 17,
        "url": "https://patchwork.libcamera.org/api/1.1/people/17/?format=api",
        "name": "Paul Elder",
        "email": "paul.elder@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/2448/mbox/",
    "series": [
        {
            "id": 592,
            "url": "https://patchwork.libcamera.org/api/1.1/series/592/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=592",
            "date": "2019-12-23T07:26:14",
            "name": "V4L2 compatibility layer",
            "version": 3,
            "mbox": "https://patchwork.libcamera.org/series/592/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/2448/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/2448/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<paul.elder@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 E9B37605D0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 Dec 2019 08:26:52 +0100 (CET)",
            "from neptunite.amanokami.net (173-16-160-11.client.mchsi.com\n\t[173.16.160.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8D5A5330;\n\tMon, 23 Dec 2019 08:26:51 +0100 (CET)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1577086012;\n\tbh=yaZaNfhlugxX5pGH7T2hPBIW8eTMTwPJEdSMKuof/ag=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=IA54BDrMRUYntqwunGnRNJ4bEwXpqt08hsPz9MsolI0Q0RqovLrUT+3mPoY+tZZz1\n\tTZPu9IMny0wv35iJSLF5Jh2EX36gFXdlBQutpxdVqKd+yKJCoL9w/GfZWWxP7bh7Jh\n\tV1//21mWs38cRzjKc6nKcjdGUfwCGmQPL2WBsmME=",
        "From": "Paul Elder <paul.elder@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Mon, 23 Dec 2019 01:26:19 -0600",
        "Message-Id": "<20191223072620.13022-6-paul.elder@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.23.0",
        "In-Reply-To": "<20191223072620.13022-1-paul.elder@ideasonboard.com>",
        "References": "<20191223072620.13022-1-paul.elder@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v3 5/6] 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": "Mon, 23 Dec 2019 07:26:53 -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 device enumerator may optionally map to a libcamera\ncamera.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\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         | 248 ++++++++++++++++\n src/v4l2/v4l2_camera.h           |  84 ++++++\n src/v4l2/v4l2_camera_proxy.cpp   | 480 +++++++++++++++++++++++++++++++\n src/v4l2/v4l2_camera_proxy.h     |  70 +++++\n src/v4l2/v4l2_compat.cpp         |  78 +++++\n src/v4l2/v4l2_compat_manager.cpp | 379 ++++++++++++++++++++++++\n src/v4l2/v4l2_compat_manager.h   |  86 ++++++\n 10 files changed, 1465 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..b06dd494 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 libcamera with 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..2d33be9f\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera.cpp\n@@ -0,0 +1,248 @@\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+#include <linux/videodev2.h>\n+#include <sys/mman.h>\n+#include <sys/syscall.h>\n+#include <time.h>\n+#include <unistd.h>\n+\n+#include \"log.h\"\n+#include \"utils.h\"\n+#include \"v4l2_compat_manager.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(V4L2Compat);\n+\n+FrameMetadata::FrameMetadata(Buffer *buffer)\n+\t: index_(buffer->index()),\n+\t  bytesused_(buffer->bytesused()),\n+\t  timestamp_(buffer->timestamp()),\n+\t  sequence_(buffer->sequence()),\n+\t  status_(buffer->status())\n+{\n+}\n+\n+V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)\n+\t: camera_(camera), bufferCount_(0), 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+\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(int *ret)\n+{\n+\t*ret = camera_->release();\n+}\n+\n+void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)\n+{\n+\t*streamConfig = config_->at(0);\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> fmd =\n+\t\tutils::make_unique<FrameMetadata>(buffer);\n+\tcompletedBuffers_.push(std::move(fmd));\n+\tbufferLock_.unlock();\n+\n+\tbufferSema_.release();\n+}\n+\n+void V4L2Camera::configure(int *ret, StreamConfiguration *streamConfigOut,\n+\t\t\t   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+\tbufferCount_ = bufferCount;\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, Error) << \"Configuration invalid\";\n+\t\t*ret = -EINVAL;\n+\t\treturn;\n+\t}\n+\tif (validation == CameraConfiguration::Adjusted)\n+\t\tLOG(V4L2Compat, Info) << \"Configuration adjusted\";\n+\n+\tLOG(V4L2Compat, Info) << \"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+\tbufferCount_ = streamConfig.bufferCount;\n+\n+\t*streamConfigOut = config_->at(0);\n+}\n+\n+void V4L2Camera::mmap(void **ret, unsigned int index)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing MMAP\";\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+\tLOG(V4L2Compat, Debug) << \"Allocating libcamera bufs\";\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+\tbufferCount_ = 0;\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\t*ret = (*ret == -EACCES ? -EAGAIN : *ret);\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} else {\n+\t\t*ret = camera_->queueRequest(request.release());\n+\t\tif (*ret < 0) {\n+\t\t\tLOG(V4L2Compat, Error) << \"Can't queue request\";\n+\t\t\tif (*ret == -EACCES)\n+\t\t\t\t*ret = -EBUSY;\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t*ret = 0;\n+}\n+\n+int V4L2Camera::dqbuf(struct v4l2_buffer *arg, bool nonblock)\n+{\n+\tif (nonblock && !bufferSema_.tryAcquire())\n+\t\treturn -EAGAIN;\n+\telse\n+\t\tbufferSema_.acquire();\n+\n+\tbufferLock_.lock();\n+\tFrameMetadata *fmd = completedBuffers_.front().get();\n+\tcompletedBuffers_.pop();\n+\tbufferLock_.unlock();\n+\n+\targ->bytesused = fmd->bytesused();\n+\targ->field = V4L2_FIELD_NONE;\n+\targ->timestamp.tv_sec = fmd->timestamp() / 1000000000;\n+\targ->timestamp.tv_usec = fmd->timestamp() % 1000000;\n+\targ->sequence = fmd->sequence();\n+\n+\treturn 0;\n+}\ndiff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\nnew file mode 100644\nindex 00000000..13418b6b\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera.h\n@@ -0,0 +1,84 @@\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+#include <queue>\n+\n+#include <libcamera/buffer.h>\n+#include <libcamera/camera.h>\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(int *ret);\n+\tvoid getStreamConfig(StreamConfiguration *streamConfig);\n+\n+\tvoid mmap(void **ret, unsigned int index);\n+\n+\tvoid configure(int *ret, StreamConfiguration *streamConfigOut, Size *size,\n+\t\t       PixelFormat pixelformat, 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+\tint dqbuf(struct v4l2_buffer *arg, bool nonblock);\n+\n+private:\n+\tvoid requestComplete(Request *request);\n+\n+\tstd::shared_ptr<Camera> camera_;\n+\tstd::unique_ptr<CameraConfiguration> config_;\n+\n+\tunsigned int bufferCount_;\n+\tbool isRunning_;\n+\n+\tSemaphore bufferSema_;\n+\tstd::mutex bufferLock_;\n+\n+\tstd::deque<std::unique_ptr<Request>> pendingRequests_;\n+\tstd::queue<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..b0acd477\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera_proxy.cpp\n@@ -0,0 +1,480 @@\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 <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: 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+int V4L2CameraProxy::close()\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing close\";\n+\n+\tif (refcount_ > 1) {\n+\t\trefcount_--;\n+\t\treturn 0;\n+\t}\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking, &ret);\n+\tif (ret < 0) {\n+\t\terrno = EIO;\n+\t\treturn -1;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags,\n+\t\t\t    off_t offset)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing mmap\";\n+\n+\tif (prot != (PROT_READ | PROT_WRITE)) {\n+\t\terrno = ENOTSUP;\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+\treturn val;\n+}\n+\n+int V4L2CameraProxy::munmap(void *addr, size_t length)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing munmap\";\n+\n+\tif (length != sizeimage_) {\n+\t\terrno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* \\todo getDeviceCaps? getMemoryCaps? */\n+\n+bool V4L2CameraProxy::validateStreamType(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 =\n+\t\tV4L2CompatManager::drmToV4L2(streamConfig.pixelFormat);\n+\tcurV4L2Format_.fmt.pix.field = V4L2_FIELD_NONE;\n+\tcurV4L2Format_.fmt.pix.bytesperline =\n+\t\tV4L2CompatManager::bplMultiplier(\n+\t\t\tcurV4L2Format_.fmt.pix.pixelformat) *\n+\t\tcurV4L2Format_.fmt.pix.width;\n+\tcurV4L2Format_.fmt.pix.sizeimage =\n+\t\tV4L2CompatManager::imageSize(curV4L2Format_.fmt.pix.pixelformat,\n+\t\t\t\t\t     curV4L2Format_.fmt.pix.width,\n+\t\t\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+\treturn V4L2CompatManager::imageSize(\n+\t\t\tV4L2CompatManager::drmToV4L2(streamConfig.pixelFormat),\n+\t\t\tstreamConfig.size.width,\n+\t\t\tstreamConfig.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+\tmemcpy(capabilities_.driver, driver.c_str(),\n+\t       sizeof(capabilities_.driver));\n+\tmemcpy(capabilities_.card, camera->name().c_str(),\n+\t       sizeof(capabilities_.card));\n+\tmemcpy(capabilities_.bus_info, bus_info.c_str(),\n+\t       sizeof(capabilities_.bus_info));\n+\t/* \\todo Put this is header/config somewhere. */\n+\tcapabilities_.version = KERNEL_VERSION(5, 2, 0);\n+\tcapabilities_.device_caps = V4L2_CAP_VIDEO_CAPTURE;\n+\tcapabilities_.capabilities =\n+\t\tcapabilities_.device_caps | V4L2_CAP_DEVICE_CAPS;\n+\tmemset(capabilities_.reserved, 0, sizeof(capabilities_.reserved));\n+}\n+\n+int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querycap\";\n+\n+\tmemcpy(arg, &capabilities_, sizeof(*arg));\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 (!validateStreamType(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+\tmemcpy(arg->description, \"asdf\", 5);\n+\targ->pixelformat = V4L2CompatManager::drmToV4L2(\n+\t\t\t\tstreamConfig_.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 (!validateStreamType(arg->type))\n+\t\treturn -EINVAL;\n+\n+\targ->fmt.pix.width        = curV4L2Format_.fmt.pix.width;\n+\targ->fmt.pix.height       = curV4L2Format_.fmt.pix.height;\n+\targ->fmt.pix.pixelformat  = curV4L2Format_.fmt.pix.pixelformat;\n+\targ->fmt.pix.field        = curV4L2Format_.fmt.pix.field;\n+\targ->fmt.pix.bytesperline = curV4L2Format_.fmt.pix.bytesperline;\n+\targ->fmt.pix.sizeimage    = curV4L2Format_.fmt.pix.sizeimage;\n+\targ->fmt.pix.colorspace   = curV4L2Format_.fmt.pix.colorspace;\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+\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking, &ret,\n+\t\t\t    &streamConfig_,\n+\t\t\t    new Size(arg->fmt.pix.width, arg->fmt.pix.height),\n+\t\t\t    V4L2CompatManager::v4l2ToDrm(arg->fmt.pix.pixelformat),\n+\t\t\t    bufferCount_);\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+\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 (!validateStreamType(arg->type))\n+\t\treturn -EINVAL;\n+\n+\tunsigned int format = 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  = format;\n+\targ->fmt.pix.field        = V4L2_FIELD_NONE;\n+\targ->fmt.pix.bytesperline =\n+\t\tV4L2CompatManager::bplMultiplier(\n+\t\t\tV4L2CompatManager::drmToV4L2(format)) *\n+\t\targ->fmt.pix.width;\n+\targ->fmt.pix.sizeimage    =\n+\t\tV4L2CompatManager::imageSize(\n+\t\t\tV4L2CompatManager::drmToV4L2(format),\n+\t\t\targ->fmt.pix.width, arg->fmt.pix.height);\n+\targ->fmt.pix.colorspace   = V4L2_COLORSPACE_SRGB;\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 (!validateStreamType(arg->type) ||\n+\t    !validateMemoryType(arg->memory))\n+\t\treturn -EINVAL;\n+\n+\tLOG(V4L2Compat, Debug) << arg->count << \" bufs requested \";\n+\n+\targ->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;\n+\n+\tif (arg->count == 0) {\n+\t\tLOG(V4L2Compat, Debug) << \"Freeing libcamera bufs\";\n+\t\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n+\t\t\t\t    ConnectionTypeBlocking, &ret);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(V4L2Compat, Error) << \"Failed to stop stream\";\n+\t\t\treturn ret;\n+\t\t}\n+\t\tvcam_->invokeMethod(&V4L2Camera::freeBuffers,\n+\t\t\t\t    ConnectionTypeBlocking);\n+\t\tbufferCount_ = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking, &ret,\n+\t\t\t    &streamConfig_,\n+\t\t\t    new Size(curV4L2Format_.fmt.pix.width,\n+\t\t\t\t     curV4L2Format_.fmt.pix.height),\n+\t\t\t    V4L2CompatManager::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+\tif (arg->memory != V4L2_MEMORY_MMAP)\n+\t\treturn -EINVAL;\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 == -EACCES ? -EBUSY : ret;\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+\tStream *stream = streamConfig_.stream();\n+\n+\tif (!validateStreamType(arg->type) ||\n+\t    arg->index >= stream->buffers().size())\n+\t\treturn -EINVAL;\n+\n+\tunsigned int index = arg->index;\n+\tmemset(arg, 0, sizeof(*arg));\n+\targ->index = index;\n+\targ->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;\n+\targ->length = curV4L2Format_.fmt.pix.sizeimage;\n+\targ->memory = V4L2_MEMORY_MMAP;\n+\targ->m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage;\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+\tStream *stream = streamConfig_.stream();\n+\n+\tif (!validateStreamType(arg->type) ||\n+\t    !validateMemoryType(arg->memory) ||\n+\t    arg->index >= stream->buffers().size())\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_MAPPED;\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 (!validateStreamType(arg->type) ||\n+\t    !validateMemoryType(arg->memory))\n+\t\treturn -EINVAL;\n+\n+\targ->index = currentBuf_;\n+\tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n+\n+\tint ret = vcam_->dqbuf(arg, nonBlocking_);\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+\targ->length = sizeimage_;\n+\n+\treturn ret;\n+}\n+\n+int V4L2CameraProxy::vidioc_streamon(int *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamon\";\n+\n+\tif (!validateStreamType(*arg))\n+\t\treturn -EINVAL;\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::streamOn,\n+\t\t\t    ConnectionTypeBlocking, &ret);\n+\treturn ret;\n+}\n+\n+int V4L2CameraProxy::vidioc_streamoff(int *arg)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamoff\";\n+\n+\tif (!validateStreamType(*arg))\n+\t\treturn -EINVAL;\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n+\t\t\t    ConnectionTypeBlocking, &ret);\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+}\ndiff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h\nnew file mode 100644\nindex 00000000..51fdbe19\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera_proxy.h\n@@ -0,0 +1,70 @@\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 <memory>\n+#include <sys/types.h>\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+\tint close();\n+\tvoid *mmap(void *addr, size_t length, int prot, int flags,\n+\t\t   off_t offset);\n+\tint munmap(void *addr, size_t length);\n+\n+\tint ioctl(unsigned long request, void *arg);\n+\n+\tunsigned int refcount() { return refcount_; }\n+\n+private:\n+\tbool validateStreamType(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+\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+\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::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..a6a7aed2\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat.cpp\n@@ -0,0 +1,78 @@\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, oflag, mode);\n+}\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 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, 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..bfe76e82\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat_manager.cpp\n@@ -0,0 +1,379 @@\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 <fstream>\n+#include <linux/drm_fourcc.h>\n+#include <linux/videodev2.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+#include <libcamera/stream.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+\tproxies_.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 V4L2CompatManager::instance()->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+\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::getCamera(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+\tfstat(fd, &statbuf);\n+\tunsigned int devMajor = major(statbuf.st_rdev);\n+\tunsigned int devMinor = minor(statbuf.st_rdev);\n+\n+\tstd::shared_ptr<Camera> target = cm_->get(makedev(devMajor, devMinor));\n+\n+\tunsigned int index = 0;\n+\tfor (auto &camera : cm_->cameras()) {\n+\t\tif (camera == target)\n+\t\t\tbreak;\n+\t\t++index;\n+\t}\n+\n+\tif (index >= cm_->cameras().size())\n+\t\treturn -1;\n+\n+\treturn index;\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+\tfstat(fd, &statbuf);\n+\tif (major(statbuf.st_rdev) != 81)\n+\t\treturn fd;\n+\n+\tif (!isRunning())\n+\t\tinit();\n+\n+\tint ret = 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) | (oflag & O_NONBLOCK));\n+\tif (efd < 0)\n+\t\treturn efd;\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 (getCamera(oldfd)) {\n+\t\tdevices_[oldfd]->dup();\n+\t\tdevices_[newfd] = devices_[oldfd];\n+\t}\n+\n+\treturn newfd;\n+}\n+\n+int V4L2CompatManager::close(int fd)\n+{\n+\tV4L2CameraProxy *proxy = getCamera(fd);\n+\tif (proxy) {\n+\t\tint ret = proxy->close();\n+\t\tdevices_.erase(fd);\n+\t\treturn ret;\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 = getCamera(fd);\n+\tif (!proxy)\n+\t\treturn mmap_func_(addr, length, prot, flags, fd, offset);\n+\n+\tvoid *map = proxy->mmap(addr, 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 = getCamera(fd);\n+\tif (!proxy)\n+\t\treturn ioctl_func_(fd, request, arg);\n+\n+\treturn proxy->ioctl(request, arg);\n+}\n+\n+/* \\todo make libcamera export these */\n+int V4L2CompatManager::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 V4L2CompatManager::imageSize(unsigned int format,\n+\t\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 + width * height / 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 V4L2CompatManager::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 V4L2CompatManager::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_compat_manager.h b/src/v4l2/v4l2_compat_manager.h\nnew file mode 100644\nindex 00000000..b8ae6efe\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat_manager.h\n@@ -0,0 +1,86 @@\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 <mutex>\n+#include <sys/types.h>\n+#include <vector>\n+\n+#include <libcamera/camera.h>\n+#include <libcamera/camera_manager.h>\n+#include <libcamera/stream.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 *getCamera(int fd);\n+\tV4L2CameraProxy *getCamera(void *addr);\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+\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+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",
        "v3",
        "5/6"
    ]
}