Patch Detail
Show a patch.
GET /api/patches/2407/?format=api
{ "id": 2407, "url": "https://patchwork.libcamera.org/api/patches/2407/?format=api", "web_url": "https://patchwork.libcamera.org/patch/2407/", "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": "<20191206092654.24340-2-paul.elder@ideasonboard.com>", "date": "2019-12-06T09:26:54", "name": "[libcamera-devel,2/2] v4l2: v4l2_compat: Add V4L2 compatibility layer", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "1f261009d5a03200ea7597651256d8e34a4e8c94", "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/2407/mbox/", "series": [ { "id": 584, "url": "https://patchwork.libcamera.org/api/series/584/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=584", "date": "2019-12-06T09:26:54", "name": "[libcamera-devel,1/2] libcamera: v4l2_device, v4l2_videodevice: call open system call directly", "version": 1, "mbox": "https://patchwork.libcamera.org/series/584/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/2407/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/2407/checks/", "tags": {}, "headers": { "Return-Path": "<paul.elder@ideasonboard.com>", "Received": [ "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 719D560BFE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 6 Dec 2019 10:27:13 +0100 (CET)", "from neptunite.amanokami.net (unknown [96.44.9.94])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7650399A;\n\tFri, 6 Dec 2019 10:27:12 +0100 (CET)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1575624433;\n\tbh=RQxQfKfQvHQ5/I/O0oKDAB7hG/ZBNS4AoshrMzNWkM0=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=InuXns0L4ubUgb3godYfcVUA5xY6oPELY+zyp+VEftRTU3MZIvMYa8c5U1OxYTXMU\n\tlZmOu55vAtaxryDmaEGq7UutfxAFX0KiPKzBGNAaNgEYJHseHc7DRc6qQMWdKL3W5K\n\tHLhmVVHdmyiMnwdwVuzoNGwtpMQ5t8S087c8Bc3o=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 6 Dec 2019 04:26:54 -0500", "Message-Id": "<20191206092654.24340-2-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.23.0", "In-Reply-To": "<20191206092654.24340-1-paul.elder@ideasonboard.com>", "References": "<20191206092654.24340-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH 2/2] 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": "Fri, 06 Dec 2019 09:27:13 -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\nNote that until we have a way of mapping V4L2 device nodes to libcamera\ncameras, the V4L2 compatibility layer will always selects and use the\nfirst enumerated libcamera camera.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n meson_options.txt | 5 +\n src/meson.build | 4 +\n src/v4l2/meson.build | 30 +++\n src/v4l2/v4l2_camera.cpp | 312 ++++++++++++++++++++++\n src/v4l2/v4l2_camera.h | 68 +++++\n src/v4l2/v4l2_camera_proxy.cpp | 438 +++++++++++++++++++++++++++++++\n src/v4l2/v4l2_camera_proxy.h | 63 +++++\n src/v4l2/v4l2_compat.cpp | 81 ++++++\n src/v4l2/v4l2_compat_manager.cpp | 353 +++++++++++++++++++++++++\n src/v4l2/v4l2_compat_manager.h | 91 +++++++\n 10 files changed, 1445 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..d96db4ff\n--- /dev/null\n+++ b/src/v4l2/meson.build\n@@ -0,0 +1,30 @@\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+]\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..c6c84ef3\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera.cpp\n@@ -0,0 +1,312 @@\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+#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 \"v4l2_compat_manager.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DECLARE_CATEGORY(V4L2Compat);\n+\n+V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)\n+\t: camera_(camera), bufferCount_(0), isRunning_(false), sequence_(0),\n+\t buffer_sema_(new Semaphore())\n+{\n+\tcamera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);\n+};\n+\n+V4L2Camera::~V4L2Camera()\n+{\n+\twhile (!pendingRequests_.empty()) {\n+\t\tdelete pendingRequests_.front();\n+\t\tpendingRequests_.pop();\n+\t}\n+\n+\tbuffer_lock_.lock();\n+\twhile (!completedBuffers_.empty()) {\n+\t\tdelete completedBuffers_.front();\n+\t\tcompletedBuffers_.pop();\n+\t}\n+\tbuffer_lock_.unlock();\n+\n+\tdelete buffer_sema_;\n+\n+\tcamera_->release();\n+}\n+\n+void V4L2Camera::open(int *ret, bool nonblock)\n+{\n+\tnonblock_ = nonblock;\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_ == nullptr) {\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 *ret)\n+{\n+\t*ret = config_->at(0);\n+}\n+\n+void V4L2Camera::requestComplete(Request *request)\n+{\n+\tif (request->status() == Request::RequestCancelled) {\n+\t\tLOG(V4L2Compat, Error) << \"Request not succesfully completed: \"\n+\t\t\t\t << request->status();\n+\t\treturn;\n+\t}\n+\n+\t/* We only have one stream at the moment. */\n+\tbuffer_lock_.lock();\n+\tBuffer *buffer = request->buffers().begin()->second;\n+\tcompletedBuffers_.push(buffer);\n+\tbuffer_lock_.unlock();\n+\n+\tbuffer_sema_->release();\n+}\n+\n+void V4L2Camera::configure(int *ret, struct v4l2_format *arg,\n+\t\t\t unsigned int bufferCount)\n+{\n+\tconfig_->at(0).size.width = arg->fmt.pix.width;\n+\tconfig_->at(0).size.height = arg->fmt.pix.height;\n+\tconfig_->at(0).pixelFormat =\n+\t\tV4L2CompatManager::v4l2_to_drm(arg->fmt.pix.pixelformat);\n+\tbufferCount_ = bufferCount;\n+\tconfig_->at(0).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, Info) << \"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 << config_->at(0).toString();\n+\n+\t*ret = camera_->configure(config_.get());\n+\tif (*ret < 0)\n+\t\treturn;\n+\n+\tbufferCount_ = config_->at(0).bufferCount;\n+\n+\t*ret = 0;\n+}\n+\n+void V4L2Camera::mmap(void **ret, void *addr, size_t length, int prot,\n+\t\t int flags, off_t offset)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing MMAP\";\n+\n+\tif (prot != (PROT_READ | PROT_WRITE)) {\n+\t\terrno = EINVAL;\n+\t\t*ret = MAP_FAILED;\n+\t\treturn;\n+\t}\n+\n+\tStreamConfiguration &streamConfig = config_->at(0);\n+\tunsigned int sizeimage =\n+\t\tV4L2CompatManager::image_size(\n+\t\t\tV4L2CompatManager::drm_to_v4l2(streamConfig.pixelFormat),\n+\t\t\tstreamConfig.size.width,\n+\t\t\tstreamConfig.size.height);\n+\tif (sizeimage == 0) {\n+\t\terrno = EINVAL;\n+\t\t*ret = MAP_FAILED;\n+\t\treturn;\n+\t}\n+\n+\tunsigned int index = offset / sizeimage;\n+\tif (index * sizeimage != offset || length != sizeimage) {\n+\t\terrno = EINVAL;\n+\t\t*ret = MAP_FAILED;\n+\t\treturn;\n+\t}\n+\n+\tStream *stream = *camera_->streams().begin();\n+\t*ret = stream->buffers()[index].planes()[0].mem();\n+}\n+\n+void V4L2Camera::munmap(int *ret, void *addr, size_t length)\n+{\n+\tStreamConfiguration &streamConfig = config_->at(0);\n+\tunsigned int sizeimage =\n+\t\tV4L2CompatManager::image_size(streamConfig.pixelFormat,\n+\t\t\t\t\t streamConfig.size.width,\n+\t\t\t\t\t streamConfig.size.height);\n+\n+\tif (length != sizeimage) {\n+\t\terrno = EINVAL;\n+\t\t*ret = -1;\n+\t\treturn;\n+\t}\n+\n+\t*ret = 0;\n+}\n+\n+void V4L2Camera::validStreamType(bool *ret, uint32_t type)\n+{\n+\t*ret = (type == V4L2_BUF_TYPE_VIDEO_CAPTURE);\n+}\n+\n+void V4L2Camera::validMemoryType(bool *ret, uint32_t memory)\n+{\n+\t*ret = (memory == V4L2_MEMORY_MMAP);\n+}\n+\n+void V4L2Camera::allocBuffers(int *ret, unsigned int count)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Allocating libcamera bufs\";\n+\t*ret = camera_->allocateBuffers();\n+\tif (*ret == -EACCES)\n+\t\t*ret = -EBUSY;\n+}\n+\n+/* \\todo implement allocDMABuffers */\n+void V4L2Camera::allocDMABuffers(int *ret, unsigned int count)\n+{\n+\t*ret = -ENOTTY;\n+}\n+\n+void V4L2Camera::freeBuffers()\n+{\n+\tcamera_->freeBuffers();\n+\tbufferCount_ = 0;\n+}\n+\n+void V4L2Camera::streamOn(int *ret)\n+{\n+\tif (isRunning_) {\n+\t\t*ret = 0;\n+\t\treturn;\n+\t}\n+\n+\tsequence_ = 0;\n+\n+\t*ret = camera_->start();\n+\tif (*ret < 0)\n+\t\treturn;\n+\tisRunning_ = true;\n+\n+\twhile (!pendingRequests_.empty()) {\n+\t\t*ret = camera_->queueRequest(pendingRequests_.front());\n+\t\tpendingRequests_.pop();\n+\t\tif (*ret < 0)\n+\t\t\treturn;\n+\t}\n+\n+\t*ret = 0;\n+}\n+\n+void V4L2Camera::streamOff(int *ret)\n+{\n+\t/* \\todo restore buffers to reqbufs state? */\n+\tif (!isRunning_) {\n+\t\t*ret = 0;\n+\t\treturn;\n+\t}\n+\n+\t*ret = camera_->stop();\n+\tif (*ret < 0)\n+\t\treturn;\n+\tisRunning_ = false;\n+\n+\t*ret = 0;\n+}\n+\n+void V4L2Camera::qbuf(int *ret, struct v4l2_buffer *arg)\n+{\n+\tStream *stream = config_->at(0).stream();\n+\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(arg->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+\tRequest *request = camera_->createRequest();\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\treturn;\n+\t}\n+\n+\tif (!isRunning_) {\n+\t\tpendingRequests_.push(request);\n+\t} else {\n+\t\t*ret = camera_->queueRequest(request);\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+\targ->flags |= V4L2_BUF_FLAG_QUEUED;\n+\targ->flags |= V4L2_BUF_FLAG_MAPPED;\n+\targ->flags &= ~V4L2_BUF_FLAG_DONE;\n+\t*ret = 0;\n+}\n+\n+int V4L2Camera::dqbuf(struct v4l2_buffer *arg)\n+{\n+\tif (nonblock_ && !buffer_sema_->tryAcquire())\n+\t\treturn -EAGAIN;\n+\telse\n+\t\tbuffer_sema_->acquire();\n+\n+\tbuffer_lock_.lock();\n+\tBuffer *buffer = completedBuffers_.front();\n+\tcompletedBuffers_.pop();\n+\tbuffer_lock_.unlock();\n+\n+\targ->bytesused = buffer->bytesused();\n+\targ->field = V4L2_FIELD_NONE;\n+\targ->timestamp.tv_sec = buffer->timestamp() / 1000000000;\n+\targ->timestamp.tv_usec = buffer->timestamp() / 1000;\n+\targ->sequence = sequence_++;\n+\n+\targ->flags &= ~V4L2_BUF_FLAG_QUEUED;\n+\targ->flags &= ~V4L2_BUF_FLAG_DONE;\n+\n+\tStreamConfiguration &streamConfig = config_->at(0);\n+\tunsigned int sizeimage =\n+\t\tV4L2CompatManager::image_size(streamConfig.pixelFormat,\n+\t\t\t\t\t streamConfig.size.width,\n+\t\t\t\t\t streamConfig.size.height);\n+\targ->length = sizeimage;\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..3d3cd8ff\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera.h\n@@ -0,0 +1,68 @@\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+#ifndef __V4L2_CAMERA_H__\n+#define __V4L2_CAMERA_H__\n+\n+#include <linux/videodev2.h>\n+#include <mutex>\n+#include <queue>\n+\n+#include <libcamera/camera.h>\n+#include \"semaphore.h\"\n+\n+using namespace libcamera;\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, bool nonblock);\n+\tvoid close(int *ret);\n+\tvoid getStreamConfig(StreamConfiguration *ret);\n+\tvoid requestComplete(Request *request);\n+\n+\tvoid mmap(void **ret, void *addr, size_t length, int prot, int flags,\n+\t\t off_t offset);\n+\tvoid munmap(int *ret, void *addr, size_t length);\n+\n+\tvoid configure(int *ret, struct v4l2_format *arg,\n+\t\t unsigned int bufferCount);\n+\n+\tvoid validStreamType(bool *ret, uint32_t type);\n+\tvoid validMemoryType(bool *ret, uint32_t memory);\n+\n+\tvoid allocBuffers(int *ret, unsigned int count);\n+\tvoid allocDMABuffers(int *ret, unsigned int count);\n+\tvoid freeBuffers();\n+\tvoid streamOn(int *ret);\n+\tvoid streamOff(int *ret);\n+\n+\tvoid qbuf(int *ret, struct v4l2_buffer *arg);\n+\tint dqbuf(struct v4l2_buffer *arg);\n+\n+private:\n+\tvoid initDefaultV4L2Format();\n+\n+\tstd::shared_ptr<Camera> camera_;\n+\tstd::unique_ptr<CameraConfiguration> config_;\n+\n+\tunsigned int bufferCount_;\n+\tbool isRunning_;\n+\tbool nonblock_;\n+\n+\tunsigned int sequence_;\n+\n+\tSemaphore *buffer_sema_;\n+\tstd::mutex buffer_lock_;\n+\n+\tstd::queue<Request *> pendingRequests_;\n+\tstd::queue<Buffer *> 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..1dd2c363\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera_proxy.cpp\n@@ -0,0 +1,438 @@\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+#include \"v4l2_camera_proxy.h\"\n+\n+#include <algorithm>\n+#include <linux/videodev2.h>\n+#include <string.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+V4L2CameraProxy::~V4L2CameraProxy()\n+{\n+\tvcam_.reset();\n+}\n+\n+int V4L2CameraProxy::open(bool nonblock)\n+{\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::open, ConnectionTypeBlocking,\n+\t\t\t &ret, nonblock);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tvcam_->invokeMethod(&V4L2Camera::getStreamConfig,\n+\t\t\t ConnectionTypeBlocking, &streamConfig_);\n+\tsetFmtFromConfig();\n+\n+\treturn 0;\n+}\n+\n+int V4L2CameraProxy::close()\n+{\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking, &ret);\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+\tvoid *val;\n+\tvcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking,\n+\t\t\t &val, addr, length, prot, flags, offset);\n+\treturn val;\n+}\n+\n+int V4L2CameraProxy::munmap(void *addr, size_t length)\n+{\n+\tLOG(V4L2Compat, Debug) << \"Servicing MUNMAP\";\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::munmap, ConnectionTypeBlocking,\n+\t\t\t &ret, addr, length);\n+\treturn ret;\n+}\n+\n+bool V4L2CameraProxy::hasPixelFormat(unsigned int format)\n+{\n+\tconst std::vector<PixelFormat> &formats =\n+\t\tstreamConfig_.formats().pixelformats();\n+\treturn std::find(formats.begin(), formats.end(), format) != formats.end();\n+}\n+\n+/* \\todo getDeviceCaps? getMemoryCaps? */\n+\n+bool V4L2CameraProxy::hasSize(unsigned int format, Size size)\n+{\n+\tint len = streamConfig_.formats().sizes(format).size();\n+\tfor (int i = 0; i < len; i++)\n+\t\tif (streamConfig_.formats().sizes(format)[i] == size)\n+\t\t\treturn true;\n+\n+\treturn false;\n+}\n+\n+bool V4L2CameraProxy::validateStreamType(uint32_t type)\n+{\n+\tbool valid;\n+\tvcam_->invokeMethod(&V4L2Camera::validStreamType,\n+\t\t\t ConnectionTypeBlocking, &valid, type);\n+\tif (!valid)\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+bool V4L2CameraProxy::validateMemoryType(uint32_t memory)\n+{\n+\tbool valid;\n+\tvcam_->invokeMethod(&V4L2Camera::validMemoryType,\n+\t\t\t ConnectionTypeBlocking, &valid, memory);\n+\tif (!valid)\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+void V4L2CameraProxy::setFmtFromConfig()\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::drm_to_v4l2(streamConfig_.pixelFormat);\n+\tcurV4L2Format_.fmt.pix.field = V4L2_FIELD_NONE;\n+\tcurV4L2Format_.fmt.pix.bytesperline =\n+\t\tV4L2CompatManager::bpl_multiplier(\n+\t\t\tcurV4L2Format_.fmt.pix.pixelformat) *\n+\t\tcurV4L2Format_.fmt.pix.width;\n+\tcurV4L2Format_.fmt.pix.sizeimage =\n+\t\tV4L2CompatManager::image_size(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+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+\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+\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\treturn -EINVAL;\n+\tif (arg->index > streamConfig_.formats().pixelformats().size())\n+\t\treturn -EINVAL;\n+\n+\tmemcpy(arg->description, \"asdf\", 5);\n+\targ->pixelformat =\n+\t\tV4L2CompatManager::drm_to_v4l2(\n+\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,\n+\t\t\t &ret, arg, bufferCount_);\n+\tif (ret < 0)\n+\t\treturn -EINVAL;\n+\n+\tvcam_->invokeMethod(&V4L2Camera::getStreamConfig,\n+\t\t\t ConnectionTypeBlocking, &streamConfig_);\n+\tsetFmtFromConfig();\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+\tif (!hasPixelFormat(format))\n+\t\tformat = streamConfig_.formats().pixelformats()[0];\n+\n+\tSize size(arg->fmt.pix.width, arg->fmt.pix.height);\n+\tif (!hasSize(format, size))\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::bpl_multiplier(\n+\t\t\tV4L2CompatManager::drm_to_v4l2(format)) *\n+\t\targ->fmt.pix.width;\n+\targ->fmt.pix.sizeimage =\n+\t\tV4L2CompatManager::image_size(\n+\t\t\tV4L2CompatManager::drm_to_v4l2(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+\tLOG(V4L2Compat, Debug) << \"Servicing VIDIOC_REQBUFS\";\n+\tif (!validateStreamType(arg->type))\n+\t\treturn -EINVAL;\n+\tif (!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\tint ret;\n+\t\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n+\t\t\t\t ConnectionTypeBlocking, &ret);\n+\t\tvcam_->invokeMethod(&V4L2Camera::freeBuffers,\n+\t\t\t\t ConnectionTypeBlocking);\n+\t\tbufferCount_ = 0;\n+\t\treturn 0;\n+\t}\n+\n+\tint ret;\n+\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking,\n+\t\t\t &ret, &curV4L2Format_, arg->count);\n+\tif (ret < 0)\n+\t\treturn -EINVAL;\n+\targ->count = streamConfig_.bufferCount;\n+\tbufferCount_ = arg->count;\n+\n+\tret = -EINVAL;\n+\tif (arg->memory == V4L2_MEMORY_MMAP)\n+\t\tvcam_->invokeMethod(&V4L2Camera::allocBuffers,\n+\t\t\t\t ConnectionTypeBlocking, &ret, arg->count);\n+\telse if (arg->memory == V4L2_MEMORY_DMABUF)\n+\t\tvcam_->invokeMethod(&V4L2Camera::allocDMABuffers,\n+\t\t\t\t ConnectionTypeBlocking, &ret, arg->count);\n+\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\treturn -EINVAL;\n+\tif (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\treturn -EINVAL;\n+\tif (!validateMemoryType(arg->memory))\n+\t\treturn -EINVAL;\n+\tif (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);\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\treturn -EINVAL;\n+\tif (!validateMemoryType(arg->memory))\n+\t\treturn -EINVAL;\n+\n+\targ->index = currentBuf_;\n+\tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n+\n+\treturn vcam_->dqbuf(arg);\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+\tcase VIDIOC_EXPBUF:\n+\tcase VIDIOC_ENUM_FRAMESIZES:\n+\tdefault:\n+\t\tret = -ENOTTY;\n+\t}\n+\n+\tif (ret < 0) {\n+\t\terrno = ret;\n+\t\treturn -1;\n+\t}\n+\n+\terrno = 0;\n+\treturn ret;\n+\n+}\ndiff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h\nnew file mode 100644\nindex 00000000..64c7aadd\n--- /dev/null\n+++ b/src/v4l2/v4l2_camera_proxy.h\n@@ -0,0 +1,63 @@\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+#ifndef __V4L2_CAMERA_PROXY_H__\n+#define __V4L2_CAMERA_PROXY_H__\n+\n+#include <linux/videodev2.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+\t~V4L2CameraProxy();\n+\n+\tint open(bool nonblock);\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+private:\n+\tbool hasPixelFormat(unsigned int format);\n+\tbool hasSize(unsigned int format, Size size);\n+\tbool validateStreamType(uint32_t type);\n+\tbool validateMemoryType(uint32_t memory);\n+\tvoid setFmtFromConfig();\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 index_;\n+\n+\tstruct v4l2_format curV4L2Format_;\n+\tStreamConfiguration streamConfig_;\n+\tstruct v4l2_capability capabilities_;\n+\tunsigned int bufferCount_;\n+\tunsigned int currentBuf_;\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..3330e7bc\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat.cpp\n@@ -0,0 +1,81 @@\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 <iostream>\n+\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <linux/videodev2.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+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 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+\tvoid *val = V4L2CompatManager::instance()->mmap(addr, length, prot, flags, fd, offset);\n+\treturn val;\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..4eeb714f\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat_manager.cpp\n@@ -0,0 +1,353 @@\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+#include \"v4l2_compat_manager.h\"\n+\n+#include <dlfcn.h>\n+#include <fcntl.h>\n+#include <iostream>\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: cameraManagerRunning_(false), cm_(nullptr)\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+\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);\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\n+\t * gets created here to wraps a camera device.\n+\t */\n+\t// \\todo map v4l2 devices to libcamera cameras; minor device node?\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\n+\t * as we're now ready to handle calls from the framework.\n+\t */\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 v4l2CompatManager;\n+\treturn &v4l2CompatManager;\n+}\n+\n+bool V4L2CompatManager::validfd(int fd)\n+{\n+\treturn devices_.find(fd) != devices_.end();\n+}\n+\n+bool V4L2CompatManager::validmmap(void *addr)\n+{\n+\treturn mmaps_.find(addr) != mmaps_.end();\n+}\n+\n+std::shared_ptr<V4L2CameraProxy> V4L2CompatManager::getCamera(int fd)\n+{\n+\tif (!validfd(fd))\n+\t\treturn nullptr;\n+\n+\treturn devices_.at(fd);\n+}\n+\n+std::shared_ptr<V4L2CameraProxy> V4L2CompatManager::getCamera(void *addr)\n+{\n+\tif (!validmmap(addr))\n+\t\treturn nullptr;\n+\n+\treturn devices_.at(mmaps_.at(addr));\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+\tif (std::string(path).find(\"video\") == std::string::npos)\n+\t\treturn fd;\n+\n+\tif (!isRunning())\n+\t\tinit();\n+\n+\t/* \\todo map v4l2 devnodes to libcamera cameras */\n+\tunsigned int camera_index = 0;\n+\n+\tstd::shared_ptr<V4L2CameraProxy> proxy = proxies_[camera_index];\n+\tint ret = proxy->open(mode & O_NONBLOCK);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tint efd = eventfd(0, (mode & O_CLOEXEC) | (mode & O_NONBLOCK));\n+\tif (efd < 0)\n+\t\treturn errno;\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 (validfd(oldfd))\n+\t\tdevices_[newfd] = devices_[oldfd];\n+\n+\treturn newfd;\n+}\n+\n+int V4L2CompatManager::close(int fd)\n+{\n+\tif (validfd(fd) && devices_[fd]->close() < 0)\n+\t\treturn -EIO;\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+\tif (!validfd(fd))\n+\t\treturn mmap_func_(addr, length, prot, flags, fd, offset);\n+\n+\tvoid *map = getCamera(fd)->mmap(addr, length, prot, flags, offset);\n+\tif (map == MAP_FAILED) {\n+\t\terrno = EINVAL;\n+\t\treturn map;\n+\t}\n+\n+\tmmaps_[addr] = fd;\n+\treturn map;\n+}\n+\n+int V4L2CompatManager::munmap(void *addr, size_t length)\n+{\n+\tif (!validmmap(addr))\n+\t\treturn munmap_func_(addr, length);\n+\n+\tint ret = getCamera(addr)->munmap(addr, length);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tmmaps_.erase(addr);\n+\taddr = nullptr;\n+\n+\treturn 0;\n+}\n+\n+int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg)\n+{\n+\tstd::shared_ptr<V4L2CameraProxy> 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::bpl_multiplier(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::image_size(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::v4l2_to_drm(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::drm_to_v4l2(unsigned int pixelformat)\n+{\n+\tswitch (pixelformat) {\n+\t/* RGB formats. */\n+\tcase DRM_FORMAT_BGR888:\n+\t\treturn V4L2_PIX_FMT_BGR24;\n+\tcase DRM_FORMAT_RGB888:\n+\t\treturn V4L2_PIX_FMT_RGB24;\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..492f97fd\n--- /dev/null\n+++ b/src/v4l2/v4l2_compat_manager.h\n@@ -0,0 +1,91 @@\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+#ifndef __V4L2_COMPAT_MANAGER_H__\n+#define __V4L2_COMPAT_MANAGER_H__\n+\n+#include <condition_variable>\n+#include <linux/videodev2.h>\n+#include <map>\n+#include <mutex>\n+#include <queue>\n+#include <sys/syscall.h>\n+#include <unistd.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+\tint openat(int dirfd, const char *path, int oflag, mode_t mode);\n+\n+\tstd::shared_ptr<V4L2CameraProxy> getCamera(int fd);\n+\tstd::shared_ptr<V4L2CameraProxy> getCamera(void *addr);\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 bpl_multiplier(unsigned int format);\n+\tstatic int image_size(unsigned int format, unsigned int width,\n+\t\t\t unsigned int height);\n+\n+\tstatic unsigned int v4l2_to_drm(unsigned int pixelformat);\n+\tstatic unsigned int drm_to_v4l2(unsigned int pixelformat);\n+\n+private:\n+\tV4L2CompatManager();\n+\t~V4L2CompatManager();\n+\n+\tvoid run() override;\n+\n+\tbool validfd(int fd);\n+\tbool validmmap(void *addr);\n+\n+\tint default_ioctl(int fd, unsigned long request, void *arg);\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+\tbool cameraManagerRunning_;\n+\tCameraManager *cm_;\n+\n+\tstd::mutex mutex_;\n+\tstd::condition_variable cv_;\n+\n+\tstd::vector<std::shared_ptr<V4L2CameraProxy>> proxies_;\n+\tstd::map<int, std::shared_ptr<V4L2CameraProxy>> devices_;\n+\tstd::map<void *, int> mmaps_;\n+};\n+\n+#endif /* __V4L2_COMPAT_MANAGER_H__ */\n", "prefixes": [ "libcamera-devel", "2/2" ] }