Patch Detail
Show a patch.
GET /api/patches/4045/?format=api
{ "id": 4045, "url": "https://patchwork.libcamera.org/api/patches/4045/?format=api", "web_url": "https://patchwork.libcamera.org/patch/4045/", "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": "<20200616131244.70308-2-paul.elder@ideasonboard.com>", "date": "2020-06-16T13:12:30", "name": "[libcamera-devel,01/15] v4l2: v4l2_compat: Support multiple open", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "8765e0832d62757fba360f49f1555b4967d20334", "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/4045/mbox/", "series": [ { "id": 1006, "url": "https://patchwork.libcamera.org/api/series/1006/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1006", "date": "2020-06-16T13:12:29", "name": "Support v4l2-compliance", "version": 1, "mbox": "https://patchwork.libcamera.org/series/1006/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/4045/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/4045/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 1D16C603C1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 16 Jun 2020 15:13:02 +0200 (CEST)", "from jade.flets-east.jp (unknown\n\t[IPv6:2400:4051:61:600:2807:bdfa:f6a:8e53])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 87ECDF9;\n\tTue, 16 Jun 2020 15:13:00 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"co4hZomT\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592313181;\n\tbh=7vjHnnbiDV2VTJ53YxEGoLpKJy4AIIKtV8qdhPYFsfU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=co4hZomTAK/R+HVuMlPBzq1ZYkHRGYz58X+S6qPxsIZeA0aV+iATzuMMtAa9hVKqj\n\tB7fttjp4iY0fI/MOPf7CdmWek2936yoZQvT1wuqjREuX2QKUL/hO+12yyGFyXTz/gm\n\tJs8GQuz1y3a3rmNHbj3m3KapQPrRF1AOAU7YbzWw=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Tue, 16 Jun 2020 22:12:30 +0900", "Message-Id": "<20200616131244.70308-2-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.27.0", "In-Reply-To": "<20200616131244.70308-1-paul.elder@ideasonboard.com>", "References": "<20200616131244.70308-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH 01/15] v4l2: v4l2_compat: Support multiple\n\topen", "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": "Tue, 16 Jun 2020 13:13:02 -0000" }, "content": "Previously, since we acquired the libcamera camera upon open(), it was\nimpossible to support multiple open, as any subsequent opens would\nreturn error because the camera would already be acquired.\n\nTo fix this, we first initialize the camera in the first call to\nV4L2CameraProxy::open(), just to heat up the stream format cache. We\nthen add an exclusive lock on the V4L2Camera. All vidioc ioctls prior to\nreqbufs > 0 (except for s_fmt) are able to access the camera without\nthis exclusive lock. A call to reqbufs > 0 (and s_fmt) will take the\nlock, and the lock will be released at reqbufs = 0. While the lock is\ntaken, the eventfd that should be signaled (and cleared) by V4L2Camera\nand V4L2CameraProxy is set to the fd that has taken the lock, and is\ncleared when the lock is released. In case close() is called without a\nreqbufs = 0 first, the lock is also released on close().\n\nWe also add a mapping of fd to boolean to keep track of which fds opened\nthe camera in non-blocking mode.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n src/v4l2/v4l2_camera.cpp | 9 +-\n src/v4l2/v4l2_camera.h | 1 +\n src/v4l2/v4l2_camera_proxy.cpp | 202 ++++++++++++++++++++++---------\n src/v4l2/v4l2_camera_proxy.h | 47 ++++---\n src/v4l2/v4l2_compat_manager.cpp | 25 ++--\n 5 files changed, 194 insertions(+), 90 deletions(-)", "diff": "diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\nindex 9a1ebc8..2557320 100644\n--- a/src/v4l2/v4l2_camera.cpp\n+++ b/src/v4l2/v4l2_camera.cpp\n@@ -17,7 +17,8 @@ using namespace libcamera;\n LOG_DECLARE_CATEGORY(V4L2Compat);\n \n V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)\n-\t: camera_(camera), isRunning_(false), bufferAllocator_(nullptr)\n+\t: camera_(camera), isRunning_(false), bufferAllocator_(nullptr),\n+\t efd_(-1)\n {\n \tcamera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);\n }\n@@ -29,7 +30,6 @@ V4L2Camera::~V4L2Camera()\n \n int V4L2Camera::open()\n {\n-\t/* \\todo Support multiple open. */\n \tif (camera_->acquire() < 0) {\n \t\tLOG(V4L2Compat, Error) << \"Failed to acquire camera\";\n \t\treturn -EINVAL;\n@@ -59,6 +59,11 @@ void V4L2Camera::bind(int efd)\n \tefd_ = efd;\n }\n \n+void V4L2Camera::unbind()\n+{\n+\tefd_ = -1;\n+}\n+\n void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)\n {\n \t*streamConfig = config_->at(0);\ndiff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\nindex 33f5eb0..30114ed 100644\n--- a/src/v4l2/v4l2_camera.h\n+++ b/src/v4l2/v4l2_camera.h\n@@ -40,6 +40,7 @@ public:\n \tint open();\n \tvoid close();\n \tvoid bind(int efd);\n+\tvoid unbind();\n \tvoid getStreamConfig(StreamConfiguration *streamConfig);\n \tstd::vector<Buffer> completedBuffers();\n \ndiff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\nindex 13db428..594dd13 100644\n--- a/src/v4l2/v4l2_camera_proxy.cpp\n+++ b/src/v4l2/v4l2_camera_proxy.cpp\n@@ -33,45 +33,68 @@ LOG_DECLARE_CATEGORY(V4L2Compat);\n V4L2CameraProxy::V4L2CameraProxy(unsigned int index,\n \t\t\t\t std::shared_ptr<Camera> camera)\n \t: refcount_(0), index_(index), bufferCount_(0), currentBuf_(0),\n-\t vcam_(std::make_unique<V4L2Camera>(camera))\n+\t vcam_(std::make_unique<V4L2Camera>(camera)), efd_(-1),\n+\t acquiredFd_(-1), initialized_(false)\n {\n \tquerycap(camera);\n }\n \n-int V4L2CameraProxy::open(bool nonBlocking)\n+int V4L2CameraProxy::open(int fd, bool nonBlocking)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing open\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing open fd = \" << fd;\n \n-\tint ret = vcam_->open();\n-\tif (ret < 0) {\n-\t\terrno = -ret;\n-\t\treturn -1;\n-\t}\n+\tnonBlockingFds_[fd] = nonBlocking;\n \n-\tnonBlocking_ = nonBlocking;\n+\trefcount_++;\n+\n+\tif (initialized_)\n+\t\treturn 0;\n+\n+\t/*\n+\t * We will open the camera here, once, and keep it open until the last\n+\t * fd is closed. Before any reqbufs with count > 0 is called, anybody\n+\t * can call any ioctl. Once reqbufs is called with count > 0, the\n+\t * exclusive lock will be assigned to that fd, in acquiredFd_. At this\n+\t * point, no other fd can call any ioctl (except for querycap, try_fmt,\n+\t * g/s_priority, and enum/g/s_input), and they will return -EBUSY.\n+\t * After reqbufs is called with count = 0, the exclusive lock will be\n+\t * released.\n+\t */\n+\n+\tinitialized_ = true;\n+\n+\tint ret = vcam_->open();\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tvcam_->getStreamConfig(&streamConfig_);\n \tsetFmtFromConfig(streamConfig_);\n \tsizeimage_ = calculateSizeImage(streamConfig_);\n \n-\trefcount_++;\n-\n \treturn 0;\n }\n \n-void V4L2CameraProxy::dup()\n+void V4L2CameraProxy::dup(int oldfd, int newfd)\n {\n+\tnonBlockingFds_[newfd] = nonBlockingFds_[oldfd];\n \trefcount_++;\n }\n \n-void V4L2CameraProxy::close()\n+void V4L2CameraProxy::close(int fd)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing close\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing close fd = \" << fd;\n+\n+\tnonBlockingFds_.erase(fd);\n+\n+\tif (acquiredFd_ == fd)\n+\t\tacquiredFd_ = -1;\n \n \tif (--refcount_ > 0)\n \t\treturn;\n \n \tvcam_->close();\n+\n+\tinitialized_ = false;\n }\n \n void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags,\n@@ -220,9 +243,10 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)\n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg)\n+int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_enum_fmt\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_enum_fmt fd = \" << fd;\n+\n \n \tif (!validateBufferType(arg->type) ||\n \t arg->index >= streamConfig_.formats().pixelformats().size())\n@@ -236,9 +260,10 @@ int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg)\n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_g_fmt(struct v4l2_format *arg)\n+int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_g_fmt\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_g_fmt fd = \" << fd;\n+\n \n \tif (!validateBufferType(arg->type))\n \t\treturn -EINVAL;\n@@ -274,9 +299,13 @@ void V4L2CameraProxy::tryFormat(struct v4l2_format *arg)\n \targ->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;\n }\n \n-int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg)\n+int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_s_fmt\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_s_fmt fd = \" << fd;\n+\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(arg->type))\n \t\treturn -EINVAL;\n@@ -284,9 +313,9 @@ int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg)\n \ttryFormat(arg);\n \n \tSize size(arg->fmt.pix.width, arg->fmt.pix.height);\n-\tint ret = vcam_->configure(&streamConfig_, size,\n-\t\t\t\t v4l2ToDrm(arg->fmt.pix.pixelformat),\n-\t\t\t\t bufferCount_);\n+\tret = vcam_->configure(&streamConfig_, size,\n+\t\t\t v4l2ToDrm(arg->fmt.pix.pixelformat),\n+\t\t\t bufferCount_);\n \tif (ret < 0)\n \t\treturn -EINVAL;\n \n@@ -301,9 +330,9 @@ int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg)\n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_try_fmt(struct v4l2_format *arg)\n+int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_try_fmt\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_try_fmt fd = \" << fd;\n \n \tif (!validateBufferType(arg->type))\n \t\treturn -EINVAL;\n@@ -328,11 +357,14 @@ int V4L2CameraProxy::freeBuffers()\n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)\n+int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)\n {\n-\tint ret;\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_reqbufs fd = \" << fd;\n+\n \n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_reqbufs\";\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(arg->type) ||\n \t !validateMemoryType(arg->memory))\n@@ -341,9 +373,21 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)\n \tLOG(V4L2Compat, Debug) << arg->count << \" buffers requested \";\n \n \targ->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;\n+\tmemset(arg->reserved, 0, sizeof(arg->reserved));\n \n-\tif (arg->count == 0)\n+\tif (arg->count == 0) {\n+\t\tunlock(fd);\n \t\treturn freeBuffers();\n+\t}\n+\n+\tif (bufferCount_ > 0) {\n+\t\tret = freeBuffers();\n+\t\tif (ret < 0) {\n+\t\t\tLOG(V4L2Compat, Error)\n+\t\t\t\t<< \"Failed to free libcamera buffers for re-reqbufs\";\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n \n \tSize size(curV4L2Format_.fmt.pix.width, curV4L2Format_.fmt.pix.height);\n \tret = vcam_->configure(&streamConfig_, size,\n@@ -386,6 +430,7 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)\n \t\tbuf.memory = V4L2_MEMORY_MMAP;\n \t\tbuf.m.offset = i * curV4L2Format_.fmt.pix.sizeimage;\n \t\tbuf.index = i;\n+\t\tbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;\n \n \t\tbuffers_[i] = buf;\n \t}\n@@ -395,9 +440,13 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)\n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg)\n+int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querybuf\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querybuf fd = \" << fd;\n+\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(arg->type) ||\n \t arg->index >= bufferCount_)\n@@ -410,17 +459,21 @@ int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg)\n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg)\n+int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)\n {\n \tLOG(V4L2Compat, Debug) << \"Servicing vidioc_qbuf, index = \"\n-\t\t\t << arg->index;\n+\t\t\t << arg->index << \" fd = \" << fd;\n+\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(arg->type) ||\n \t !validateMemoryType(arg->memory) ||\n \t arg->index >= bufferCount_)\n \t\treturn -EINVAL;\n \n-\tint ret = vcam_->qbuf(arg->index);\n+\tret = vcam_->qbuf(arg->index);\n \tif (ret < 0)\n \t\treturn ret;\n \n@@ -430,15 +483,19 @@ int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg)\n \treturn ret;\n }\n \n-int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg)\n+int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_dqbuf\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_dqbuf fd = \" << fd;\n+\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(arg->type) ||\n \t !validateMemoryType(arg->memory))\n \t\treturn -EINVAL;\n \n-\tif (!nonBlocking_)\n+\tif (!nonBlockingFds_[fd])\n \t\tvcam_->bufferSema_.acquire();\n \telse if (!vcam_->bufferSema_.tryAcquire())\n \t\treturn -EAGAIN;\n@@ -454,16 +511,20 @@ int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg)\n \tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n \n \tuint64_t data;\n-\tint ret = ::read(efd_, &data, sizeof(data));\n+\tret = ::read(efd_, &data, sizeof(data));\n \tif (ret != sizeof(data))\n \t\tLOG(V4L2Compat, Error) << \"Failed to clear eventfd POLLIN\";\n \n \treturn 0;\n }\n \n-int V4L2CameraProxy::vidioc_streamon(int *arg)\n+int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamon\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamon fd = \" << fd;\n+\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(*arg))\n \t\treturn -EINVAL;\n@@ -473,14 +534,18 @@ int V4L2CameraProxy::vidioc_streamon(int *arg)\n \treturn vcam_->streamOn();\n }\n \n-int V4L2CameraProxy::vidioc_streamoff(int *arg)\n+int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)\n {\n-\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamoff\";\n+\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamoff fd = \" << fd;\n+\n+\tint ret = lock(fd);\n+\tif (ret < 0)\n+\t\treturn ret;\n \n \tif (!validateBufferType(*arg))\n \t\treturn -EINVAL;\n \n-\tint ret = vcam_->streamOff();\n+\tret = vcam_->streamOff();\n \n \tfor (struct v4l2_buffer &buf : buffers_)\n \t\tbuf.flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE);\n@@ -488,7 +553,7 @@ int V4L2CameraProxy::vidioc_streamoff(int *arg)\n \treturn ret;\n }\n \n-int V4L2CameraProxy::ioctl(unsigned long request, void *arg)\n+int V4L2CameraProxy::ioctl(int fd, unsigned long request, void *arg)\n {\n \tint ret;\n \tswitch (request) {\n@@ -496,34 +561,34 @@ int V4L2CameraProxy::ioctl(unsigned long request, void *arg)\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\tret = vidioc_enum_fmt(fd, 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\tret = vidioc_g_fmt(fd, 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\tret = vidioc_s_fmt(fd, 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\tret = vidioc_try_fmt(fd, 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\tret = vidioc_reqbufs(fd, 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\tret = vidioc_querybuf(fd, 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\tret = vidioc_qbuf(fd, 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\tret = vidioc_dqbuf(fd, static_cast<struct v4l2_buffer *>(arg));\n \t\tbreak;\n \tcase VIDIOC_STREAMON:\n-\t\tret = vidioc_streamon(static_cast<int *>(arg));\n+\t\tret = vidioc_streamon(fd, static_cast<int *>(arg));\n \t\tbreak;\n \tcase VIDIOC_STREAMOFF:\n-\t\tret = vidioc_streamoff(static_cast<int *>(arg));\n+\t\tret = vidioc_streamoff(fd, static_cast<int *>(arg));\n \t\tbreak;\n \tdefault:\n \t\tret = -ENOTTY;\n@@ -538,10 +603,37 @@ int V4L2CameraProxy::ioctl(unsigned long request, void *arg)\n \treturn ret;\n }\n \n-void V4L2CameraProxy::bind(int fd)\n+/**\n+ * \\brief Acquire an exclusive lock on the V4L2Camera\n+ *\n+ * \\return Zero on success or if already acquired, and negative error on\n+ * failure.\n+ */\n+int V4L2CameraProxy::lock(int fd)\n {\n+\tif (acquiredFd_ == fd)\n+\t\treturn 0;\n+\n+\tif (acquiredFd_ >= 0)\n+\t\treturn -EBUSY;\n+\n \tefd_ = fd;\n \tvcam_->bind(fd);\n+\n+\tacquiredFd_ = fd;\n+\n+\treturn 0;\n+}\n+\n+void V4L2CameraProxy::unlock(int fd)\n+{\n+\tif (acquiredFd_ != fd)\n+\t\treturn;\n+\n+\tefd_ = -1;\n+\tvcam_->unbind();\n+\n+\tacquiredFd_ = -1;\n }\n \n struct PixelFormatPlaneInfo {\ndiff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h\nindex b5a9801..90e518c 100644\n--- a/src/v4l2/v4l2_camera_proxy.h\n+++ b/src/v4l2/v4l2_camera_proxy.h\n@@ -26,15 +26,13 @@ class V4L2CameraProxy\n public:\n \tV4L2CameraProxy(unsigned int index, std::shared_ptr<Camera> camera);\n \n-\tint open(bool nonBlocking);\n-\tvoid dup();\n-\tvoid close();\n+\tint open(int fd, bool nonBlocking);\n+\tvoid dup(int oldfd, int newfd);\n+\tvoid close(int fd);\n \tvoid *mmap(void *addr, size_t length, int prot, int flags, __off64_t offset);\n \tint munmap(void *addr, size_t length);\n \n-\tint ioctl(unsigned long request, void *arg);\n-\n-\tvoid bind(int fd);\n+\tint ioctl(int fd, unsigned long request, void *arg);\n \n private:\n \tbool validateBufferType(uint32_t type);\n@@ -47,16 +45,19 @@ private:\n \tint freeBuffers();\n \n \tint vidioc_querycap(struct v4l2_capability *arg);\n-\tint vidioc_enum_fmt(struct v4l2_fmtdesc *arg);\n-\tint vidioc_g_fmt(struct v4l2_format *arg);\n-\tint vidioc_s_fmt(struct v4l2_format *arg);\n-\tint vidioc_try_fmt(struct v4l2_format *arg);\n-\tint vidioc_reqbufs(struct v4l2_requestbuffers *arg);\n-\tint vidioc_querybuf(struct v4l2_buffer *arg);\n-\tint vidioc_qbuf(struct v4l2_buffer *arg);\n-\tint vidioc_dqbuf(struct v4l2_buffer *arg);\n-\tint vidioc_streamon(int *arg);\n-\tint vidioc_streamoff(int *arg);\n+\tint vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg);\n+\tint vidioc_g_fmt(int fd, struct v4l2_format *arg);\n+\tint vidioc_s_fmt(int fd, struct v4l2_format *arg);\n+\tint vidioc_try_fmt(int fd, struct v4l2_format *arg);\n+\tint vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg);\n+\tint vidioc_querybuf(int fd, struct v4l2_buffer *arg);\n+\tint vidioc_qbuf(int fd, struct v4l2_buffer *arg);\n+\tint vidioc_dqbuf(int fd, struct v4l2_buffer *arg);\n+\tint vidioc_streamon(int fd, int *arg);\n+\tint vidioc_streamoff(int fd, int *arg);\n+\n+\tint lock(int fd);\n+\tvoid unlock(int fd);\n \n \tstatic unsigned int bplMultiplier(uint32_t format);\n \tstatic unsigned int imageSize(uint32_t format, unsigned int width,\n@@ -67,7 +68,6 @@ private:\n \n \tunsigned int refcount_;\n \tunsigned int index_;\n-\tbool nonBlocking_;\n \n \tstruct v4l2_format curV4L2Format_;\n \tStreamConfiguration streamConfig_;\n@@ -82,6 +82,19 @@ private:\n \tstd::unique_ptr<V4L2Camera> vcam_;\n \n \tint efd_;\n+\n+\tstd::map<int, bool> nonBlockingFds_;\n+\t/*\n+\t * This is an exclusive lock on the camera. When this is not taken,\n+\t * anybody can call any ioctl before reqbufs. reqbufs with count > 0\n+\t * will claim this lock, and reqbufs with count = 0 will release it.\n+\t * Any ioctl is called while the lock is taken will return -EBUSY\n+\t * (if applicable, eg. try_fmt, querycap, g/s_priority, and\n+\t * enum/g/s_input are exempt).\n+\t */\n+\tint acquiredFd_;\n+\n+\tbool initialized_;\n };\n \n #endif /* __V4L2_CAMERA_PROXY_H__ */\ndiff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp\nindex 9298c0f..ce1eace 100644\n--- a/src/v4l2/v4l2_compat_manager.cpp\n+++ b/src/v4l2/v4l2_compat_manager.cpp\n@@ -148,24 +148,17 @@ int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mod\n \n \tfops_.close(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, EFD_SEMAPHORE |\n \t\t\t ((oflag & O_CLOEXEC) ? EFD_CLOEXEC : 0) |\n \t\t\t ((oflag & O_NONBLOCK) ? EFD_NONBLOCK : 0));\n-\tif (efd < 0) {\n-\t\tint err = errno;\n-\t\tproxy->close();\n-\t\terrno = err;\n+\tif (efd < 0)\n \t\treturn efd;\n-\t}\n \n-\tproxy->bind(efd);\n+\tunsigned int camera_index = static_cast<unsigned int>(ret);\n+\n+\tV4L2CameraProxy *proxy = proxies_[camera_index].get();\n+\tproxy->open(efd, oflag & O_NONBLOCK);\n+\n \tdevices_.emplace(efd, proxy);\n \n \treturn efd;\n@@ -181,7 +174,7 @@ int V4L2CompatManager::dup(int oldfd)\n \tif (device != devices_.end()) {\n \t\tV4L2CameraProxy *proxy = device->second;\n \t\tdevices_[newfd] = proxy;\n-\t\tproxy->dup();\n+\t\tproxy->dup(oldfd, newfd);\n \t}\n \n \treturn newfd;\n@@ -191,7 +184,7 @@ int V4L2CompatManager::close(int fd)\n {\n \tV4L2CameraProxy *proxy = getProxy(fd);\n \tif (proxy) {\n-\t\tproxy->close();\n+\t\tproxy->close(fd);\n \t\tdevices_.erase(fd);\n \t\treturn 0;\n \t}\n@@ -237,5 +230,5 @@ int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg)\n \tif (!proxy)\n \t\treturn fops_.ioctl(fd, request, arg);\n \n-\treturn proxy->ioctl(request, arg);\n+\treturn proxy->ioctl(fd, request, arg);\n }\n", "prefixes": [ "libcamera-devel", "01/15" ] }