From patchwork Fri Jun 19 05:41:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 4082 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 59F23603BF for ; Fri, 19 Jun 2020 07:41:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="PedOHCHd"; dkim-atps=neutral Received: from jade.flets-east.jp (unknown [IPv6:2400:4051:61:600:e972:d773:e99a:4f79]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 58614560; Fri, 19 Jun 2020 07:41:37 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1592545299; bh=NNsvtSY1y0JWES4pCRY/WP/gNPOjNaCwhM+//iyYG8g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=PedOHCHddPGS+tZzRy5AMvuhg+9iYW0cmnuJrceQ+s2DesMJqgLezII2Ib/NjeguP KGUXihRXYZWim0zeBrNeL8a3NUFUVCWQUqFxwYTNohrfAnWc1KudkVw6VFbQwWAWE3 a01njqfOAeuWBZIh0/TbdN2YWYM8GNP5rWyYNGkE= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 19 Jun 2020 14:41:08 +0900 Message-Id: <20200619054123.19052-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200619054123.19052-1-paul.elder@ideasonboard.com> References: <20200619054123.19052-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 02/17] v4l2: v4l2_compat: Support multiple open X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 Jun 2020 05:41:40 -0000 Previously, since we acquired the libcamera camera upon open(), it was impossible to support multiple open, as any subsequent opens would return error because the camera would already be acquired. To fix this, we first initialize the camera in the first call to V4L2CameraProxy::open(), just to heat up the stream format cache. We then add an exclusive lock on the V4L2Camera. All vidioc ioctls prior to reqbufs > 0 (except for s_fmt) are able to access the camera without this exclusive lock. A call to reqbufs > 0 (and s_fmt) will take the lock, and the lock will be released at reqbufs = 0. While the lock is taken, the eventfd that should be signaled (and cleared) by V4L2Camera and V4L2CameraProxy is set to the fd that has taken the lock, and is cleared when the lock is released. In case close() is called without a reqbufs = 0 first, the lock is also released on close(). We also use the V4L2CameraFile to contain all the information specific to an open instance of the file. This removes the need to keep track of such information within V4L2CameraProxy via multiple maps from int fd to info. We also handle dup in V4L2CompatManager instead, since that is just another int fd pointing to the same V4L2CameraFile instance. V4L2CompatManager is also modified to access the V4L2CameraProxy through the V4L2CameraFile. Signed-off-by: Paul Elder --- Changes in v2: - use V4L2CameraFile in V4L2CompatManager to deal with dup, and in V4L2CameraProxy to deal with information specific to the open file (nonblock and priority) - unlock exclusive camera lock on close() --- src/v4l2/v4l2_camera.cpp | 9 +- src/v4l2/v4l2_camera.h | 1 + src/v4l2/v4l2_camera_file.cpp | 4 +- src/v4l2/v4l2_camera_proxy.cpp | 201 ++++++++++++++++++++++--------- src/v4l2/v4l2_camera_proxy.h | 47 +++++--- src/v4l2/v4l2_compat_manager.cpp | 71 +++++------ src/v4l2/v4l2_compat_manager.h | 4 +- 7 files changed, 215 insertions(+), 122 deletions(-) diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp index 9a1ebc8..2557320 100644 --- a/src/v4l2/v4l2_camera.cpp +++ b/src/v4l2/v4l2_camera.cpp @@ -17,7 +17,8 @@ using namespace libcamera; LOG_DECLARE_CATEGORY(V4L2Compat); V4L2Camera::V4L2Camera(std::shared_ptr camera) - : camera_(camera), isRunning_(false), bufferAllocator_(nullptr) + : camera_(camera), isRunning_(false), bufferAllocator_(nullptr), + efd_(-1) { camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); } @@ -29,7 +30,6 @@ V4L2Camera::~V4L2Camera() int V4L2Camera::open() { - /* \todo Support multiple open. */ if (camera_->acquire() < 0) { LOG(V4L2Compat, Error) << "Failed to acquire camera"; return -EINVAL; @@ -59,6 +59,11 @@ void V4L2Camera::bind(int efd) efd_ = efd; } +void V4L2Camera::unbind() +{ + efd_ = -1; +} + void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig) { *streamConfig = config_->at(0); diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h index 33f5eb0..30114ed 100644 --- a/src/v4l2/v4l2_camera.h +++ b/src/v4l2/v4l2_camera.h @@ -40,6 +40,7 @@ public: int open(); void close(); void bind(int efd); + void unbind(); void getStreamConfig(StreamConfiguration *streamConfig); std::vector completedBuffers(); diff --git a/src/v4l2/v4l2_camera_file.cpp b/src/v4l2/v4l2_camera_file.cpp index 8916729..d07b936 100644 --- a/src/v4l2/v4l2_camera_file.cpp +++ b/src/v4l2/v4l2_camera_file.cpp @@ -21,12 +21,12 @@ V4L2CameraFile::V4L2CameraFile(int efd, bool nonBlocking, V4L2CameraProxy *proxy : priority_(V4L2_PRIORITY_DEFAULT), proxy_(proxy), nonBlocking_(nonBlocking), efd_(efd) { - proxy_->open(nonBlocking); + proxy_->open(this); } V4L2CameraFile::~V4L2CameraFile() { - proxy_->close(); + proxy_->close(this); } V4L2CameraProxy *V4L2CameraFile::proxy() diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp index bf47aa7..f06f58d 100644 --- a/src/v4l2/v4l2_camera_proxy.cpp +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -23,6 +23,7 @@ #include "libcamera/internal/utils.h" #include "v4l2_camera.h" +#include "v4l2_camera_file.h" #include "v4l2_compat_manager.h" #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) @@ -34,45 +35,57 @@ LOG_DECLARE_CATEGORY(V4L2Compat); V4L2CameraProxy::V4L2CameraProxy(unsigned int index, std::shared_ptr camera) : refcount_(0), index_(index), bufferCount_(0), currentBuf_(0), - vcam_(std::make_unique(camera)) + vcam_(std::make_unique(camera)), efd_(-1), + acquiredCf_(nullptr), initialized_(false) { querycap(camera); } -int V4L2CameraProxy::open(bool nonBlocking) +int V4L2CameraProxy::open(V4L2CameraFile *cf) { - LOG(V4L2Compat, Debug) << "Servicing open"; + LOG(V4L2Compat, Debug) << "Servicing open fd = " << cf->efd(); - int ret = vcam_->open(); - if (ret < 0) { - errno = -ret; - return -1; - } + refcount_++; + + if (initialized_) + return 0; - nonBlocking_ = nonBlocking; + /* + * We will open the camera here, once, and keep it open until the last + * V4L2CameraFile is closed. Before any reqbufs with count > 0 is + * called, anybody can call any ioctl. Once reqbufs is called with + * count > 0, the * exclusive lock will be assigned to that + * V4L2CameraFile, in acquiredCf_. At this point, no other fd can call + * any ioctl (except for querycap, try_fmt, g/s_priority, and + * enum/g/s_input), and they will return -EBUSY. After reqbufs is + * called with count = 0, the exclusive lock will be released. + */ + + initialized_ = true; + + int ret = vcam_->open(); + if (ret < 0) + return ret; vcam_->getStreamConfig(&streamConfig_); setFmtFromConfig(streamConfig_); sizeimage_ = calculateSizeImage(streamConfig_); - refcount_++; - return 0; } -void V4L2CameraProxy::dup() +void V4L2CameraProxy::close(V4L2CameraFile *cf) { - refcount_++; -} + LOG(V4L2Compat, Debug) << "Servicing close fd = " << cf->efd(); -void V4L2CameraProxy::close() -{ - LOG(V4L2Compat, Debug) << "Servicing close"; + unlock(cf); if (--refcount_ > 0) return; vcam_->close(); + + initialized_ = false; } void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags, @@ -221,9 +234,10 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg) return 0; } -int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg) +int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *cf, struct v4l2_fmtdesc *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << cf->efd(); + if (!validateBufferType(arg->type) || arg->index >= streamConfig_.formats().pixelformats().size()) @@ -237,9 +251,10 @@ int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg) return 0; } -int V4L2CameraProxy::vidioc_g_fmt(struct v4l2_format *arg) +int V4L2CameraProxy::vidioc_g_fmt(V4L2CameraFile *cf, struct v4l2_format *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << cf->efd(); + if (!validateBufferType(arg->type)) return -EINVAL; @@ -275,9 +290,13 @@ void V4L2CameraProxy::tryFormat(struct v4l2_format *arg) arg->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; } -int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg) +int V4L2CameraProxy::vidioc_s_fmt(V4L2CameraFile *cf, struct v4l2_format *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << cf->efd(); + + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(arg->type)) return -EINVAL; @@ -285,9 +304,9 @@ int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg) tryFormat(arg); Size size(arg->fmt.pix.width, arg->fmt.pix.height); - int ret = vcam_->configure(&streamConfig_, size, - v4l2ToDrm(arg->fmt.pix.pixelformat), - bufferCount_); + ret = vcam_->configure(&streamConfig_, size, + v4l2ToDrm(arg->fmt.pix.pixelformat), + bufferCount_); if (ret < 0) return -EINVAL; @@ -302,9 +321,9 @@ int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg) return 0; } -int V4L2CameraProxy::vidioc_try_fmt(struct v4l2_format *arg) +int V4L2CameraProxy::vidioc_try_fmt(V4L2CameraFile *cf, struct v4l2_format *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << cf->efd(); if (!validateBufferType(arg->type)) return -EINVAL; @@ -329,11 +348,13 @@ int V4L2CameraProxy::freeBuffers() return 0; } -int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg) +int V4L2CameraProxy::vidioc_reqbufs(V4L2CameraFile *cf, struct v4l2_requestbuffers *arg) { - int ret; + LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << cf->efd(); - LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs"; + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(arg->type) || !validateMemoryType(arg->memory)) @@ -342,9 +363,21 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg) LOG(V4L2Compat, Debug) << arg->count << " buffers requested "; arg->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP; + memset(arg->reserved, 0, sizeof(arg->reserved)); - if (arg->count == 0) + if (arg->count == 0) { + unlock(cf); return freeBuffers(); + } + + if (bufferCount_ > 0) { + ret = freeBuffers(); + if (ret < 0) { + LOG(V4L2Compat, Error) + << "Failed to free libcamera buffers for re-reqbufs"; + return ret; + } + } Size size(curV4L2Format_.fmt.pix.width, curV4L2Format_.fmt.pix.height); ret = vcam_->configure(&streamConfig_, size, @@ -387,6 +420,7 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg) buf.memory = V4L2_MEMORY_MMAP; buf.m.offset = i * curV4L2Format_.fmt.pix.sizeimage; buf.index = i; + buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buffers_[i] = buf; } @@ -396,9 +430,13 @@ int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg) return 0; } -int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg) +int V4L2CameraProxy::vidioc_querybuf(V4L2CameraFile *cf, struct v4l2_buffer *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << cf->efd(); + + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(arg->type) || arg->index >= bufferCount_) @@ -411,17 +449,21 @@ int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg) return 0; } -int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg) +int V4L2CameraProxy::vidioc_qbuf(V4L2CameraFile *cf, struct v4l2_buffer *arg) { LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = " - << arg->index; + << arg->index << " fd = " << cf->efd(); + + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(arg->type) || !validateMemoryType(arg->memory) || arg->index >= bufferCount_) return -EINVAL; - int ret = vcam_->qbuf(arg->index); + ret = vcam_->qbuf(arg->index); if (ret < 0) return ret; @@ -431,15 +473,19 @@ int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg) return ret; } -int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg) +int V4L2CameraProxy::vidioc_dqbuf(V4L2CameraFile *cf, struct v4l2_buffer *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << cf->efd(); + + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(arg->type) || !validateMemoryType(arg->memory)) return -EINVAL; - if (!nonBlocking_) + if (!(cf->nonBlocking())) vcam_->bufferSema_.acquire(); else if (!vcam_->bufferSema_.tryAcquire()) return -EAGAIN; @@ -455,16 +501,20 @@ int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg) currentBuf_ = (currentBuf_ + 1) % bufferCount_; uint64_t data; - int ret = ::read(efd_, &data, sizeof(data)); + ret = ::read(efd_, &data, sizeof(data)); if (ret != sizeof(data)) LOG(V4L2Compat, Error) << "Failed to clear eventfd POLLIN"; return 0; } -int V4L2CameraProxy::vidioc_streamon(int *arg) +int V4L2CameraProxy::vidioc_streamon(V4L2CameraFile *cf, int *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << cf->efd(); + + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(*arg)) return -EINVAL; @@ -474,14 +524,18 @@ int V4L2CameraProxy::vidioc_streamon(int *arg) return vcam_->streamOn(); } -int V4L2CameraProxy::vidioc_streamoff(int *arg) +int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *cf, int *arg) { - LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff"; + LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << cf->efd(); + + int ret = lock(cf); + if (ret < 0) + return ret; if (!validateBufferType(*arg)) return -EINVAL; - int ret = vcam_->streamOff(); + ret = vcam_->streamOff(); for (struct v4l2_buffer &buf : buffers_) buf.flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE); @@ -489,7 +543,7 @@ int V4L2CameraProxy::vidioc_streamoff(int *arg) return ret; } -int V4L2CameraProxy::ioctl(unsigned long request, void *arg) +int V4L2CameraProxy::ioctl(V4L2CameraFile *cf, unsigned long request, void *arg) { int ret; switch (request) { @@ -497,34 +551,34 @@ int V4L2CameraProxy::ioctl(unsigned long request, void *arg) ret = vidioc_querycap(static_cast(arg)); break; case VIDIOC_ENUM_FMT: - ret = vidioc_enum_fmt(static_cast(arg)); + ret = vidioc_enum_fmt(cf, static_cast(arg)); break; case VIDIOC_G_FMT: - ret = vidioc_g_fmt(static_cast(arg)); + ret = vidioc_g_fmt(cf, static_cast(arg)); break; case VIDIOC_S_FMT: - ret = vidioc_s_fmt(static_cast(arg)); + ret = vidioc_s_fmt(cf, static_cast(arg)); break; case VIDIOC_TRY_FMT: - ret = vidioc_try_fmt(static_cast(arg)); + ret = vidioc_try_fmt(cf, static_cast(arg)); break; case VIDIOC_REQBUFS: - ret = vidioc_reqbufs(static_cast(arg)); + ret = vidioc_reqbufs(cf, static_cast(arg)); break; case VIDIOC_QUERYBUF: - ret = vidioc_querybuf(static_cast(arg)); + ret = vidioc_querybuf(cf, static_cast(arg)); break; case VIDIOC_QBUF: - ret = vidioc_qbuf(static_cast(arg)); + ret = vidioc_qbuf(cf, static_cast(arg)); break; case VIDIOC_DQBUF: - ret = vidioc_dqbuf(static_cast(arg)); + ret = vidioc_dqbuf(cf, static_cast(arg)); break; case VIDIOC_STREAMON: - ret = vidioc_streamon(static_cast(arg)); + ret = vidioc_streamon(cf, static_cast(arg)); break; case VIDIOC_STREAMOFF: - ret = vidioc_streamoff(static_cast(arg)); + ret = vidioc_streamoff(cf, static_cast(arg)); break; default: ret = -ENOTTY; @@ -539,10 +593,37 @@ int V4L2CameraProxy::ioctl(unsigned long request, void *arg) return ret; } -void V4L2CameraProxy::bind(int fd) +/** + * \brief Acquire an exclusive lock on the V4L2Camera + * + * \return Zero on success or if already acquired, and negative error on + * failure. + */ +int V4L2CameraProxy::lock(V4L2CameraFile *cf) +{ + if (acquiredCf_ == cf) + return 0; + + if (acquiredCf_) + return -EBUSY; + + efd_ = cf->efd(); + vcam_->bind(efd_); + + acquiredCf_ = cf; + + return 0; +} + +void V4L2CameraProxy::unlock(V4L2CameraFile *cf) { - efd_ = fd; - vcam_->bind(fd); + if (acquiredCf_ != cf) + return; + + efd_ = -1; + vcam_->unbind(); + + acquiredCf_ = nullptr; } struct PixelFormatPlaneInfo { diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h index 27d3e50..43290ca 100644 --- a/src/v4l2/v4l2_camera_proxy.h +++ b/src/v4l2/v4l2_camera_proxy.h @@ -21,20 +21,19 @@ using namespace libcamera; +class V4L2CameraFile; + class V4L2CameraProxy { public: V4L2CameraProxy(unsigned int index, std::shared_ptr camera); - int open(bool nonBlocking); - void dup(); - void close(); + int open(V4L2CameraFile *cfile); + void close(V4L2CameraFile *cf); void *mmap(void *addr, size_t length, int prot, int flags, off64_t offset); int munmap(void *addr, size_t length); - int ioctl(unsigned long request, void *arg); - - void bind(int fd); + int ioctl(V4L2CameraFile *cf, unsigned long request, void *arg); private: bool validateBufferType(uint32_t type); @@ -47,16 +46,19 @@ private: int freeBuffers(); int vidioc_querycap(struct v4l2_capability *arg); - int vidioc_enum_fmt(struct v4l2_fmtdesc *arg); - int vidioc_g_fmt(struct v4l2_format *arg); - int vidioc_s_fmt(struct v4l2_format *arg); - int vidioc_try_fmt(struct v4l2_format *arg); - int vidioc_reqbufs(struct v4l2_requestbuffers *arg); - int vidioc_querybuf(struct v4l2_buffer *arg); - int vidioc_qbuf(struct v4l2_buffer *arg); - int vidioc_dqbuf(struct v4l2_buffer *arg); - int vidioc_streamon(int *arg); - int vidioc_streamoff(int *arg); + int vidioc_enum_fmt(V4L2CameraFile *cf, struct v4l2_fmtdesc *arg); + int vidioc_g_fmt(V4L2CameraFile *cf, struct v4l2_format *arg); + int vidioc_s_fmt(V4L2CameraFile *cf, struct v4l2_format *arg); + int vidioc_try_fmt(V4L2CameraFile *cf, struct v4l2_format *arg); + int vidioc_reqbufs(V4L2CameraFile *cf, struct v4l2_requestbuffers *arg); + int vidioc_querybuf(V4L2CameraFile *cf, struct v4l2_buffer *arg); + int vidioc_qbuf(V4L2CameraFile *cf, struct v4l2_buffer *arg); + int vidioc_dqbuf(V4L2CameraFile *cf, struct v4l2_buffer *arg); + int vidioc_streamon(V4L2CameraFile *cf, int *arg); + int vidioc_streamoff(V4L2CameraFile *cf, int *arg); + + int lock(V4L2CameraFile *cf); + void unlock(V4L2CameraFile *cf); static unsigned int bplMultiplier(uint32_t format); static unsigned int imageSize(uint32_t format, unsigned int width, @@ -67,7 +69,6 @@ private: unsigned int refcount_; unsigned int index_; - bool nonBlocking_; struct v4l2_format curV4L2Format_; StreamConfiguration streamConfig_; @@ -82,6 +83,18 @@ private: std::unique_ptr vcam_; int efd_; + + /* + * This is an exclusive lock on the camera. When this is not taken, + * anybody can call any ioctl before reqbufs. reqbufs with count > 0 + * will claim this lock, and reqbufs with count = 0 will release it. + * Any ioctl is called while the lock is taken will return -EBUSY + * (if applicable, eg. try_fmt, querycap, g/s_priority, and + * enum/g/s_input are exempt). + */ + V4L2CameraFile *acquiredCf_; + + bool initialized_; }; #endif /* __V4L2_CAMERA_PROXY_H__ */ diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp index 8da3316..9cede76 100644 --- a/src/v4l2/v4l2_compat_manager.cpp +++ b/src/v4l2/v4l2_compat_manager.cpp @@ -24,6 +24,8 @@ #include "libcamera/internal/log.h" +#include "v4l2_camera_file.h" + using namespace libcamera; LOG_DEFINE_CATEGORY(V4L2Compat) @@ -49,7 +51,7 @@ V4L2CompatManager::V4L2CompatManager() V4L2CompatManager::~V4L2CompatManager() { - devices_.clear(); + cameraFiles_.clear(); mmaps_.clear(); if (cm_) { @@ -95,13 +97,13 @@ V4L2CompatManager *V4L2CompatManager::instance() return &instance; } -V4L2CameraProxy *V4L2CompatManager::getProxy(int fd) +std::shared_ptr V4L2CompatManager::getCameraFile(int fd) { - auto device = devices_.find(fd); - if (device == devices_.end()) - return nullptr; + auto cameraFile = cameraFiles_.find(fd); + if (cameraFile == cameraFiles_.end()) + return std::shared_ptr(); - return device->second; + return cameraFile->second; } int V4L2CompatManager::getCameraIndex(int fd) @@ -148,25 +150,17 @@ int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mod fops_.close(fd); - unsigned int camera_index = static_cast(ret); - - V4L2CameraProxy *proxy = proxies_[camera_index].get(); - ret = proxy->open(oflag & O_NONBLOCK); - if (ret < 0) - return ret; - int efd = eventfd(0, EFD_SEMAPHORE | ((oflag & O_CLOEXEC) ? EFD_CLOEXEC : 0) | ((oflag & O_NONBLOCK) ? EFD_NONBLOCK : 0)); - if (efd < 0) { - int err = errno; - proxy->close(); - errno = err; + if (efd < 0) return efd; - } - proxy->bind(efd); - devices_.emplace(efd, proxy); + unsigned int camera_index = static_cast(ret); + + V4L2CameraProxy *proxy = proxies_[camera_index].get(); + + cameraFiles_.emplace(efd, std::make_shared(efd, oflag & O_NONBLOCK, proxy)); return efd; } @@ -177,40 +171,39 @@ int V4L2CompatManager::dup(int oldfd) if (newfd < 0) return newfd; - auto device = devices_.find(oldfd); - if (device != devices_.end()) { - V4L2CameraProxy *proxy = device->second; - devices_[newfd] = proxy; - proxy->dup(); - } + auto cameraFile = cameraFiles_.find(oldfd); + if (cameraFile != cameraFiles_.end()) + cameraFiles_[newfd] = cameraFile->second; return newfd; } int V4L2CompatManager::close(int fd) { - V4L2CameraProxy *proxy = getProxy(fd); - if (proxy) { - proxy->close(); - devices_.erase(fd); - return 0; - } + std::shared_ptr cameraFile = getCameraFile(fd); + if (cameraFile) + cameraFiles_.erase(fd); + /* We still need to close the eventfd. */ return fops_.close(fd); } void *V4L2CompatManager::mmap(void *addr, size_t length, int prot, int flags, int fd, off64_t offset) { - V4L2CameraProxy *proxy = getProxy(fd); - if (!proxy) + std::shared_ptr cameraFile = getCameraFile(fd); + if (!cameraFile) return fops_.mmap(addr, length, prot, flags, fd, offset); - void *map = proxy->mmap(addr, length, prot, flags, offset); + void *map = cameraFile->proxy()->mmap(addr, length, prot, flags, offset); if (map == MAP_FAILED) return map; - mmaps_[map] = proxy; + /* + * Map to V4L2CameraProxy directly to prevent adding more references + * to V4L2CameraFile. + */ + mmaps_[map] = cameraFile->proxy(); return map; } @@ -233,9 +226,9 @@ int V4L2CompatManager::munmap(void *addr, size_t length) int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg) { - V4L2CameraProxy *proxy = getProxy(fd); - if (!proxy) + std::shared_ptr cameraFile = getCameraFile(fd); + if (!cameraFile) return fops_.ioctl(fd, request, arg); - return proxy->ioctl(request, arg); + return cameraFile->proxy()->ioctl(cameraFile.get(), request, arg); } diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h index 3d4e512..b92f147 100644 --- a/src/v4l2/v4l2_compat_manager.h +++ b/src/v4l2/v4l2_compat_manager.h @@ -44,7 +44,7 @@ public: static V4L2CompatManager *instance(); - V4L2CameraProxy *getProxy(int fd); + std::shared_ptr getCameraFile(int fd); const FileOperations &fops() const { return fops_; } int openat(int dirfd, const char *path, int oflag, mode_t mode); @@ -68,7 +68,7 @@ private: CameraManager *cm_; std::vector> proxies_; - std::map devices_; + std::map> cameraFiles_; std::map mmaps_; };