Patch Detail
Show a patch.
GET /api/patches/2448/?format=api
{ "id": 2448, "url": "https://patchwork.libcamera.org/api/patches/2448/?format=api", "web_url": "https://patchwork.libcamera.org/patch/2448/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/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/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/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" ] }