From patchwork Fri Dec 6 09:26:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2408 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 747AF60C0A for ; Fri, 6 Dec 2019 10:27:13 +0100 (CET) Received: from neptunite.amanokami.net (unknown [96.44.9.94]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C1E8A2E5; Fri, 6 Dec 2019 10:27:11 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1575624432; bh=GrUzwIRZPBoOL5Gq8kItruoDlY0Hd7nZjwdSM4r3BT4=; h=From:To:Cc:Subject:Date:From; b=ksDlSRh0Skrdg7YcfzJoXDttpgOQPCV5SKfSYgLozcXYOnkEZYP5h9O7ZT7oXA7q4 2jysvYdho/TAggdrlpWS8ZlaHp91vhZVtzc0Qy6KmMPx7NAz1fuYHZCVYrrbqg6xQP 8SEPm3Tdi5sF+K0ETWQ4QBzsRl2atQx6qupLJBtc= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 6 Dec 2019 04:26:53 -0500 Message-Id: <20191206092654.24340-1-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.23.0 MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/2] libcamera: v4l2_device, v4l2_videodevice: call open system call directly 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, 06 Dec 2019 09:27:13 -0000 We are preparing to integrate the V4L2 adaptation layer, which will intercept open() calls (among others) via LD_PRELOAD. To prevent libcamera's own open() calls from being intercepted, replace them will a direct syscall. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi --- src/libcamera/v4l2_device.cpp | 3 ++- src/libcamera/v4l2_videodevice.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 0452f801..f16931a3 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "log.h" @@ -75,7 +76,7 @@ int V4L2Device::open(unsigned int flags) return -EBUSY; } - int ret = ::open(deviceNode_.c_str(), flags); + int ret = syscall(SYS_open, deviceNode_.c_str(), flags); if (ret < 0) { ret = -errno; LOG(V4L2, Error) << "Failed to open V4L2 device: " diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 99213075..2dff3d3c 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1427,7 +1428,7 @@ int V4L2M2MDevice::open() * as the V4L2VideoDevice::open() retains a handle by duplicating the * fd passed in. */ - fd = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK); + fd = syscall(SYS_open, deviceNode_.c_str(), O_RDWR | O_NONBLOCK); if (fd < 0) { ret = -errno; LOG(V4L2, Error) From patchwork Fri Dec 6 09:26:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 2407 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 719D560BFE for ; Fri, 6 Dec 2019 10:27:13 +0100 (CET) Received: from neptunite.amanokami.net (unknown [96.44.9.94]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7650399A; Fri, 6 Dec 2019 10:27:12 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1575624433; bh=RQxQfKfQvHQ5/I/O0oKDAB7hG/ZBNS4AoshrMzNWkM0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=InuXns0L4ubUgb3godYfcVUA5xY6oPELY+zyp+VEftRTU3MZIvMYa8c5U1OxYTXMU lZmOu55vAtaxryDmaEGq7UutfxAFX0KiPKzBGNAaNgEYJHseHc7DRc6qQMWdKL3W5K HLhmVVHdmyiMnwdwVuzoNGwtpMQ5t8S087c8Bc3o= From: Paul Elder 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 Subject: [libcamera-devel] [PATCH 2/2] v4l2: v4l2_compat: Add V4L2 compatibility layer 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, 06 Dec 2019 09:27:13 -0000 Add libcamera V4L2 compatibility layer. This initial implementation supports the minimal set of V4L2 operations, which allows getting, setting, and enumerating formats, and streaming frames from a video device. Some data about the wrapped V4L2 video device are hardcoded. Add a build option named 'v4l2' and adjust the build system to selectively compile the V4L2 compatibility layer. Note that until we have a way of mapping V4L2 device nodes to libcamera cameras, the V4L2 compatibility layer will always selects and use the first enumerated libcamera camera. Signed-off-by: Paul Elder --- meson_options.txt | 5 + src/meson.build | 4 + src/v4l2/meson.build | 30 +++ src/v4l2/v4l2_camera.cpp | 312 ++++++++++++++++++++++ src/v4l2/v4l2_camera.h | 68 +++++ src/v4l2/v4l2_camera_proxy.cpp | 438 +++++++++++++++++++++++++++++++ src/v4l2/v4l2_camera_proxy.h | 63 +++++ src/v4l2/v4l2_compat.cpp | 81 ++++++ src/v4l2/v4l2_compat_manager.cpp | 353 +++++++++++++++++++++++++ src/v4l2/v4l2_compat_manager.h | 91 +++++++ 10 files changed, 1445 insertions(+) create mode 100644 src/v4l2/meson.build create mode 100644 src/v4l2/v4l2_camera.cpp create mode 100644 src/v4l2/v4l2_camera.h create mode 100644 src/v4l2/v4l2_camera_proxy.cpp create mode 100644 src/v4l2/v4l2_camera_proxy.h create mode 100644 src/v4l2/v4l2_compat.cpp create mode 100644 src/v4l2/v4l2_compat_manager.cpp create mode 100644 src/v4l2/v4l2_compat_manager.h diff --git a/meson_options.txt b/meson_options.txt index 1a328045..b06dd494 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -10,3 +10,8 @@ option('documentation', option('test', type : 'boolean', description: 'Compile and include the tests') + +option('v4l2', + type : 'boolean', + value : false, + description : 'Compile libcamera with V4L2 compatibility layer') diff --git a/src/meson.build b/src/meson.build index 67ad20aa..5adcd61f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,3 +6,7 @@ subdir('libcamera') subdir('ipa') subdir('cam') subdir('qcam') + +if get_option('v4l2') + subdir('v4l2') +endif diff --git a/src/v4l2/meson.build b/src/v4l2/meson.build new file mode 100644 index 00000000..d96db4ff --- /dev/null +++ b/src/v4l2/meson.build @@ -0,0 +1,30 @@ +v4l2_compat_sources = files([ + 'v4l2_camera.cpp', + 'v4l2_camera_proxy.cpp', + 'v4l2_compat.cpp', + 'v4l2_compat_manager.cpp', +]) + +v4l2_compat_includes = [ + libcamera_includes, + libcamera_internal_includes, +] + +v4l2_compat_cpp_args = [ + # Meson enables large file support unconditionally, which redirect file + # operations to 64-bit versions. This results in some symbols being + # renamed, for instance open() being renamed to open64(). As the V4L2 + # adaptation wrapper needs to provide both 32-bit and 64-bit versions of + # file operations, disable transparent large file support. + '-U_FILE_OFFSET_BITS', + '-D_FILE_OFFSET_BITS=32', +] + +v4l2_compat = shared_library('v4l2-compat', + v4l2_compat_sources, + name_prefix : '', + install : true, + link_with : libcamera, + include_directories : v4l2_compat_includes, + dependencies : libcamera_deps, + cpp_args : v4l2_compat_cpp_args) diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp new file mode 100644 index 00000000..c6c84ef3 --- /dev/null +++ b/src/v4l2/v4l2_camera.cpp @@ -0,0 +1,312 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera.cpp - V4L2 compatibility camera + */ +#include "v4l2_camera.h" + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "v4l2_compat_manager.h" + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(V4L2Compat); + +V4L2Camera::V4L2Camera(std::shared_ptr camera) + : camera_(camera), bufferCount_(0), isRunning_(false), sequence_(0), + buffer_sema_(new Semaphore()) +{ + camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete); +}; + +V4L2Camera::~V4L2Camera() +{ + while (!pendingRequests_.empty()) { + delete pendingRequests_.front(); + pendingRequests_.pop(); + } + + buffer_lock_.lock(); + while (!completedBuffers_.empty()) { + delete completedBuffers_.front(); + completedBuffers_.pop(); + } + buffer_lock_.unlock(); + + delete buffer_sema_; + + camera_->release(); +} + +void V4L2Camera::open(int *ret, bool nonblock) +{ + nonblock_ = nonblock; + + if (camera_->acquire() < 0) { + LOG(V4L2Compat, Error) << "Failed to acquire camera"; + *ret = -EINVAL; + return; + } + + config_ = camera_->generateConfiguration({ StreamRole::Viewfinder }); + if (config_ == nullptr) { + *ret = -EINVAL; + return; + } + + *ret = 0; +} + +void V4L2Camera::close(int *ret) +{ + *ret = camera_->release(); +} + +void V4L2Camera::getStreamConfig(StreamConfiguration *ret) +{ + *ret = config_->at(0); +} + +void V4L2Camera::requestComplete(Request *request) +{ + if (request->status() == Request::RequestCancelled) { + LOG(V4L2Compat, Error) << "Request not succesfully completed: " + << request->status(); + return; + } + + /* We only have one stream at the moment. */ + buffer_lock_.lock(); + Buffer *buffer = request->buffers().begin()->second; + completedBuffers_.push(buffer); + buffer_lock_.unlock(); + + buffer_sema_->release(); +} + +void V4L2Camera::configure(int *ret, struct v4l2_format *arg, + unsigned int bufferCount) +{ + config_->at(0).size.width = arg->fmt.pix.width; + config_->at(0).size.height = arg->fmt.pix.height; + config_->at(0).pixelFormat = + V4L2CompatManager::v4l2_to_drm(arg->fmt.pix.pixelformat); + bufferCount_ = bufferCount; + config_->at(0).bufferCount = bufferCount_; + /* \todo memoryType (interval vs external) */ + + CameraConfiguration::Status validation = config_->validate(); + if (validation == CameraConfiguration::Invalid) { + LOG(V4L2Compat, Info) << "Configuration invalid"; + *ret = -EINVAL; + return; + } + if (validation == CameraConfiguration::Adjusted) + LOG(V4L2Compat, Info) << "Configuration adjusted"; + + LOG(V4L2Compat, Info) << "Validated configuration is: " + << config_->at(0).toString(); + + *ret = camera_->configure(config_.get()); + if (*ret < 0) + return; + + bufferCount_ = config_->at(0).bufferCount; + + *ret = 0; +} + +void V4L2Camera::mmap(void **ret, void *addr, size_t length, int prot, + int flags, off_t offset) +{ + LOG(V4L2Compat, Debug) << "Servicing MMAP"; + + if (prot != (PROT_READ | PROT_WRITE)) { + errno = EINVAL; + *ret = MAP_FAILED; + return; + } + + StreamConfiguration &streamConfig = config_->at(0); + unsigned int sizeimage = + V4L2CompatManager::image_size( + V4L2CompatManager::drm_to_v4l2(streamConfig.pixelFormat), + streamConfig.size.width, + streamConfig.size.height); + if (sizeimage == 0) { + errno = EINVAL; + *ret = MAP_FAILED; + return; + } + + unsigned int index = offset / sizeimage; + if (index * sizeimage != offset || length != sizeimage) { + errno = EINVAL; + *ret = MAP_FAILED; + return; + } + + Stream *stream = *camera_->streams().begin(); + *ret = stream->buffers()[index].planes()[0].mem(); +} + +void V4L2Camera::munmap(int *ret, void *addr, size_t length) +{ + StreamConfiguration &streamConfig = config_->at(0); + unsigned int sizeimage = + V4L2CompatManager::image_size(streamConfig.pixelFormat, + streamConfig.size.width, + streamConfig.size.height); + + if (length != sizeimage) { + errno = EINVAL; + *ret = -1; + return; + } + + *ret = 0; +} + +void V4L2Camera::validStreamType(bool *ret, uint32_t type) +{ + *ret = (type == V4L2_BUF_TYPE_VIDEO_CAPTURE); +} + +void V4L2Camera::validMemoryType(bool *ret, uint32_t memory) +{ + *ret = (memory == V4L2_MEMORY_MMAP); +} + +void V4L2Camera::allocBuffers(int *ret, unsigned int count) +{ + LOG(V4L2Compat, Debug) << "Allocating libcamera bufs"; + *ret = camera_->allocateBuffers(); + if (*ret == -EACCES) + *ret = -EBUSY; +} + +/* \todo implement allocDMABuffers */ +void V4L2Camera::allocDMABuffers(int *ret, unsigned int count) +{ + *ret = -ENOTTY; +} + +void V4L2Camera::freeBuffers() +{ + camera_->freeBuffers(); + bufferCount_ = 0; +} + +void V4L2Camera::streamOn(int *ret) +{ + if (isRunning_) { + *ret = 0; + return; + } + + sequence_ = 0; + + *ret = camera_->start(); + if (*ret < 0) + return; + isRunning_ = true; + + while (!pendingRequests_.empty()) { + *ret = camera_->queueRequest(pendingRequests_.front()); + pendingRequests_.pop(); + if (*ret < 0) + return; + } + + *ret = 0; +} + +void V4L2Camera::streamOff(int *ret) +{ + /* \todo restore buffers to reqbufs state? */ + if (!isRunning_) { + *ret = 0; + return; + } + + *ret = camera_->stop(); + if (*ret < 0) + return; + isRunning_ = false; + + *ret = 0; +} + +void V4L2Camera::qbuf(int *ret, struct v4l2_buffer *arg) +{ + Stream *stream = config_->at(0).stream(); + std::unique_ptr buffer = stream->createBuffer(arg->index); + if (!buffer) { + LOG(V4L2Compat, Error) << "Can't create buffer"; + *ret = -ENOMEM; + return; + } + + Request *request = camera_->createRequest(); + *ret = request->addBuffer(std::move(buffer)); + if (*ret < 0) { + LOG(V4L2Compat, Error) << "Can't set buffer for request"; + return; + } + + if (!isRunning_) { + pendingRequests_.push(request); + } else { + *ret = camera_->queueRequest(request); + if (*ret < 0) { + LOG(V4L2Compat, Error) << "Can't queue request"; + if (*ret == -EACCES) + *ret = -EBUSY; + return; + } + } + + arg->flags |= V4L2_BUF_FLAG_QUEUED; + arg->flags |= V4L2_BUF_FLAG_MAPPED; + arg->flags &= ~V4L2_BUF_FLAG_DONE; + *ret = 0; +} + +int V4L2Camera::dqbuf(struct v4l2_buffer *arg) +{ + if (nonblock_ && !buffer_sema_->tryAcquire()) + return -EAGAIN; + else + buffer_sema_->acquire(); + + buffer_lock_.lock(); + Buffer *buffer = completedBuffers_.front(); + completedBuffers_.pop(); + buffer_lock_.unlock(); + + arg->bytesused = buffer->bytesused(); + arg->field = V4L2_FIELD_NONE; + arg->timestamp.tv_sec = buffer->timestamp() / 1000000000; + arg->timestamp.tv_usec = buffer->timestamp() / 1000; + arg->sequence = sequence_++; + + arg->flags &= ~V4L2_BUF_FLAG_QUEUED; + arg->flags &= ~V4L2_BUF_FLAG_DONE; + + StreamConfiguration &streamConfig = config_->at(0); + unsigned int sizeimage = + V4L2CompatManager::image_size(streamConfig.pixelFormat, + streamConfig.size.width, + streamConfig.size.height); + arg->length = sizeimage; + + return 0; +} diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h new file mode 100644 index 00000000..3d3cd8ff --- /dev/null +++ b/src/v4l2/v4l2_camera.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera.h - V4L2 compatibility camera + */ +#ifndef __V4L2_CAMERA_H__ +#define __V4L2_CAMERA_H__ + +#include +#include +#include + +#include +#include "semaphore.h" + +using namespace libcamera; + +class V4L2Camera : public Object +{ +public: + V4L2Camera(std::shared_ptr camera); + ~V4L2Camera(); + + void open(int *ret, bool nonblock); + void close(int *ret); + void getStreamConfig(StreamConfiguration *ret); + void requestComplete(Request *request); + + void mmap(void **ret, void *addr, size_t length, int prot, int flags, + off_t offset); + void munmap(int *ret, void *addr, size_t length); + + void configure(int *ret, struct v4l2_format *arg, + unsigned int bufferCount); + + void validStreamType(bool *ret, uint32_t type); + void validMemoryType(bool *ret, uint32_t memory); + + void allocBuffers(int *ret, unsigned int count); + void allocDMABuffers(int *ret, unsigned int count); + void freeBuffers(); + void streamOn(int *ret); + void streamOff(int *ret); + + void qbuf(int *ret, struct v4l2_buffer *arg); + int dqbuf(struct v4l2_buffer *arg); + +private: + void initDefaultV4L2Format(); + + std::shared_ptr camera_; + std::unique_ptr config_; + + unsigned int bufferCount_; + bool isRunning_; + bool nonblock_; + + unsigned int sequence_; + + Semaphore *buffer_sema_; + std::mutex buffer_lock_; + + std::queue pendingRequests_; + std::queue completedBuffers_; +}; + +#endif /* __V4L2_CAMERA_H__ */ diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp new file mode 100644 index 00000000..1dd2c363 --- /dev/null +++ b/src/v4l2/v4l2_camera_proxy.cpp @@ -0,0 +1,438 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera_proxy.cpp - Proxy to V4L2 compatibility camera + */ +#include "v4l2_camera_proxy.h" + +#include +#include +#include + +#include +#include + +#include "log.h" +#include "utils.h" +#include "v4l2_camera.h" +#include "v4l2_compat_manager.h" + +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) + +using namespace libcamera; + +LOG_DECLARE_CATEGORY(V4L2Compat); + +V4L2CameraProxy::V4L2CameraProxy(unsigned int index, + std::shared_ptr camera) + : index_(index), bufferCount_(0), currentBuf_(0), + vcam_(utils::make_unique(camera)) +{ + querycap(camera); +} + +V4L2CameraProxy::~V4L2CameraProxy() +{ + vcam_.reset(); +} + +int V4L2CameraProxy::open(bool nonblock) +{ + int ret; + vcam_->invokeMethod(&V4L2Camera::open, ConnectionTypeBlocking, + &ret, nonblock); + if (ret < 0) + return ret; + + vcam_->invokeMethod(&V4L2Camera::getStreamConfig, + ConnectionTypeBlocking, &streamConfig_); + setFmtFromConfig(); + + return 0; +} + +int V4L2CameraProxy::close() +{ + int ret; + vcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking, &ret); + return ret; +} + +void *V4L2CameraProxy::mmap(void *addr, size_t length, int prot, int flags, + off_t offset) +{ + LOG(V4L2Compat, Debug) << "Servicing MMAP"; + + void *val; + vcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking, + &val, addr, length, prot, flags, offset); + return val; +} + +int V4L2CameraProxy::munmap(void *addr, size_t length) +{ + LOG(V4L2Compat, Debug) << "Servicing MUNMAP"; + + int ret; + vcam_->invokeMethod(&V4L2Camera::munmap, ConnectionTypeBlocking, + &ret, addr, length); + return ret; +} + +bool V4L2CameraProxy::hasPixelFormat(unsigned int format) +{ + const std::vector &formats = + streamConfig_.formats().pixelformats(); + return std::find(formats.begin(), formats.end(), format) != formats.end(); +} + +/* \todo getDeviceCaps? getMemoryCaps? */ + +bool V4L2CameraProxy::hasSize(unsigned int format, Size size) +{ + int len = streamConfig_.formats().sizes(format).size(); + for (int i = 0; i < len; i++) + if (streamConfig_.formats().sizes(format)[i] == size) + return true; + + return false; +} + +bool V4L2CameraProxy::validateStreamType(uint32_t type) +{ + bool valid; + vcam_->invokeMethod(&V4L2Camera::validStreamType, + ConnectionTypeBlocking, &valid, type); + if (!valid) + return false; + + return true; +} + +bool V4L2CameraProxy::validateMemoryType(uint32_t memory) +{ + bool valid; + vcam_->invokeMethod(&V4L2Camera::validMemoryType, + ConnectionTypeBlocking, &valid, memory); + if (!valid) + return false; + + return true; +} + +void V4L2CameraProxy::setFmtFromConfig() +{ + curV4L2Format_.fmt.pix.width = streamConfig_.size.width; + curV4L2Format_.fmt.pix.height = streamConfig_.size.height; + curV4L2Format_.fmt.pix.pixelformat = + V4L2CompatManager::drm_to_v4l2(streamConfig_.pixelFormat); + curV4L2Format_.fmt.pix.field = V4L2_FIELD_NONE; + curV4L2Format_.fmt.pix.bytesperline = + V4L2CompatManager::bpl_multiplier( + curV4L2Format_.fmt.pix.pixelformat) * + curV4L2Format_.fmt.pix.width; + curV4L2Format_.fmt.pix.sizeimage = + V4L2CompatManager::image_size(curV4L2Format_.fmt.pix.pixelformat, + curV4L2Format_.fmt.pix.width, + curV4L2Format_.fmt.pix.height); + curV4L2Format_.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; +} + +void V4L2CameraProxy::querycap(std::shared_ptr camera) +{ + std::string driver = "libcamera"; + std::string bus_info = driver + ":" + std::to_string(index_); + + memcpy(capabilities_.driver, driver.c_str(), + sizeof(capabilities_.driver)); + memcpy(capabilities_.card, camera->name().c_str(), + sizeof(capabilities_.card)); + memcpy(capabilities_.bus_info, bus_info.c_str(), + sizeof(capabilities_.bus_info)); + capabilities_.version = KERNEL_VERSION(5, 2, 0); + capabilities_.device_caps = V4L2_CAP_VIDEO_CAPTURE; + capabilities_.capabilities = + capabilities_.device_caps | V4L2_CAP_DEVICE_CAPS; + memset(capabilities_.reserved, 0, sizeof(capabilities_.reserved)); +} + + +int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_QUERYCAP"; + + memcpy(arg, &capabilities_, sizeof(*arg)); + + return 0; +} + +int V4L2CameraProxy::vidioc_enum_fmt(struct v4l2_fmtdesc *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_ENUM_FMT"; + + if (!validateStreamType(arg->type)) + return -EINVAL; + if (arg->index > streamConfig_.formats().pixelformats().size()) + return -EINVAL; + + memcpy(arg->description, "asdf", 5); + arg->pixelformat = + V4L2CompatManager::drm_to_v4l2( + streamConfig_.formats().pixelformats()[arg->index]); + + return 0; +} + +int V4L2CameraProxy::vidioc_g_fmt(struct v4l2_format *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_G_FMT"; + + if (!validateStreamType(arg->type)) + return -EINVAL; + + arg->fmt.pix.width = curV4L2Format_.fmt.pix.width; + arg->fmt.pix.height = curV4L2Format_.fmt.pix.height; + arg->fmt.pix.pixelformat = curV4L2Format_.fmt.pix.pixelformat; + arg->fmt.pix.field = curV4L2Format_.fmt.pix.field; + arg->fmt.pix.bytesperline = curV4L2Format_.fmt.pix.bytesperline; + arg->fmt.pix.sizeimage = curV4L2Format_.fmt.pix.sizeimage; + arg->fmt.pix.colorspace = curV4L2Format_.fmt.pix.colorspace; + + return 0; +} + +int V4L2CameraProxy::vidioc_s_fmt(struct v4l2_format *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_S_FMT"; + + int ret = vidioc_try_fmt(arg); + if (ret < 0) + return ret; + + vcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking, + &ret, arg, bufferCount_); + if (ret < 0) + return -EINVAL; + + vcam_->invokeMethod(&V4L2Camera::getStreamConfig, + ConnectionTypeBlocking, &streamConfig_); + setFmtFromConfig(); + + return 0; +} + +int V4L2CameraProxy::vidioc_try_fmt(struct v4l2_format *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_TRY_FMT"; + if (!validateStreamType(arg->type)) + return -EINVAL; + + unsigned int format = arg->fmt.pix.pixelformat; + if (!hasPixelFormat(format)) + format = streamConfig_.formats().pixelformats()[0]; + + Size size(arg->fmt.pix.width, arg->fmt.pix.height); + if (!hasSize(format, size)) + size = streamConfig_.formats().sizes(format)[0]; + + arg->fmt.pix.width = size.width; + arg->fmt.pix.height = size.height; + arg->fmt.pix.pixelformat = format; + arg->fmt.pix.field = V4L2_FIELD_NONE; + arg->fmt.pix.bytesperline = + V4L2CompatManager::bpl_multiplier( + V4L2CompatManager::drm_to_v4l2(format)) * + arg->fmt.pix.width; + arg->fmt.pix.sizeimage = + V4L2CompatManager::image_size( + V4L2CompatManager::drm_to_v4l2(format), + arg->fmt.pix.width, arg->fmt.pix.height); + arg->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_REQBUFS"; + if (!validateStreamType(arg->type)) + return -EINVAL; + if (!validateMemoryType(arg->memory)) + return -EINVAL; + + LOG(V4L2Compat, Debug) << arg->count << " bufs requested "; + + arg->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP; + + if (arg->count == 0) { + LOG(V4L2Compat, Debug) << "Freeing libcamera bufs"; + int ret; + vcam_->invokeMethod(&V4L2Camera::streamOff, + ConnectionTypeBlocking, &ret); + vcam_->invokeMethod(&V4L2Camera::freeBuffers, + ConnectionTypeBlocking); + bufferCount_ = 0; + return 0; + } + + int ret; + vcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking, + &ret, &curV4L2Format_, arg->count); + if (ret < 0) + return -EINVAL; + arg->count = streamConfig_.bufferCount; + bufferCount_ = arg->count; + + ret = -EINVAL; + if (arg->memory == V4L2_MEMORY_MMAP) + vcam_->invokeMethod(&V4L2Camera::allocBuffers, + ConnectionTypeBlocking, &ret, arg->count); + else if (arg->memory == V4L2_MEMORY_DMABUF) + vcam_->invokeMethod(&V4L2Camera::allocDMABuffers, + ConnectionTypeBlocking, &ret, arg->count); + + if (ret < 0) { + arg->count = 0; + return ret == -EACCES ? -EBUSY : ret; + } + + LOG(V4L2Compat, Debug) << "Allocated " << arg->count << " buffers"; + + return 0; +} + +int V4L2CameraProxy::vidioc_querybuf(struct v4l2_buffer *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_QUERYBUF"; + Stream *stream = streamConfig_.stream(); + + if (!validateStreamType(arg->type)) + return -EINVAL; + if (arg->index >= stream->buffers().size()) + return -EINVAL; + + unsigned int index = arg->index; + memset(arg, 0, sizeof(*arg)); + arg->index = index; + arg->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + arg->length = curV4L2Format_.fmt.pix.sizeimage; + arg->memory = V4L2_MEMORY_MMAP; + arg->m.offset = arg->index * curV4L2Format_.fmt.pix.sizeimage; + + return 0; +} + +int V4L2CameraProxy::vidioc_qbuf(struct v4l2_buffer *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_QBUF, index = " + << arg->index; + + Stream *stream = streamConfig_.stream(); + + if (!validateStreamType(arg->type)) + return -EINVAL; + if (!validateMemoryType(arg->memory)) + return -EINVAL; + if (arg->index >= stream->buffers().size()) + return -EINVAL; + + int ret; + vcam_->invokeMethod(&V4L2Camera::qbuf, ConnectionTypeBlocking, + &ret, arg); + return ret; +} + +int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_DQBUF"; + + if (!validateStreamType(arg->type)) + return -EINVAL; + if (!validateMemoryType(arg->memory)) + return -EINVAL; + + arg->index = currentBuf_; + currentBuf_ = (currentBuf_ + 1) % bufferCount_; + + return vcam_->dqbuf(arg); +} + +int V4L2CameraProxy::vidioc_streamon(int *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_STREAMON"; + + if (!validateStreamType(*arg)) + return -EINVAL; + + int ret; + vcam_->invokeMethod(&V4L2Camera::streamOn, + ConnectionTypeBlocking, &ret); + return ret; +} + +int V4L2CameraProxy::vidioc_streamoff(int *arg) +{ + LOG(V4L2Compat, Debug) << "Servicing VIDIOC_STREAMOFF"; + + if (!validateStreamType(*arg)) + return -EINVAL; + + int ret; + vcam_->invokeMethod(&V4L2Camera::streamOff, + ConnectionTypeBlocking, &ret); + return ret; +} + +int V4L2CameraProxy::ioctl(unsigned long request, void *arg) +{ + int ret; + switch (request) { + case VIDIOC_QUERYCAP: + ret = vidioc_querycap(static_cast(arg)); + break; + case VIDIOC_ENUM_FMT: + ret = vidioc_enum_fmt(static_cast(arg)); + break; + case VIDIOC_G_FMT: + ret = vidioc_g_fmt(static_cast(arg)); + break; + case VIDIOC_S_FMT: + ret = vidioc_s_fmt(static_cast(arg)); + break; + case VIDIOC_TRY_FMT: + ret = vidioc_try_fmt(static_cast(arg)); + break; + case VIDIOC_REQBUFS: + ret = vidioc_reqbufs(static_cast(arg)); + break; + case VIDIOC_QUERYBUF: + ret = vidioc_querybuf(static_cast(arg)); + break; + case VIDIOC_QBUF: + ret = vidioc_qbuf(static_cast(arg)); + break; + case VIDIOC_DQBUF: + ret = vidioc_dqbuf(static_cast(arg)); + break; + case VIDIOC_STREAMON: + ret = vidioc_streamon(static_cast(arg)); + break; + case VIDIOC_STREAMOFF: + ret = vidioc_streamoff(static_cast(arg)); + break; + case VIDIOC_EXPBUF: + case VIDIOC_ENUM_FRAMESIZES: + default: + ret = -ENOTTY; + } + + if (ret < 0) { + errno = ret; + return -1; + } + + errno = 0; + return ret; + +} diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h new file mode 100644 index 00000000..64c7aadd --- /dev/null +++ b/src/v4l2/v4l2_camera_proxy.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_camera_proxy.h - Proxy to V4L2 compatibility camera + */ +#ifndef __V4L2_CAMERA_PROXY_H__ +#define __V4L2_CAMERA_PROXY_H__ + +#include + +#include + +#include "v4l2_camera.h" + +using namespace libcamera; + +class V4L2CameraProxy +{ +public: + V4L2CameraProxy(unsigned int index, std::shared_ptr camera); + ~V4L2CameraProxy(); + + int open(bool nonblock); + int close(); + void *mmap(void *addr, size_t length, int prot, int flags, + off_t offset); + int munmap(void *addr, size_t length); + + int ioctl(unsigned long request, void *arg); + +private: + bool hasPixelFormat(unsigned int format); + bool hasSize(unsigned int format, Size size); + bool validateStreamType(uint32_t type); + bool validateMemoryType(uint32_t memory); + void setFmtFromConfig(); + void querycap(std::shared_ptr camera); + + 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); + + unsigned int index_; + + struct v4l2_format curV4L2Format_; + StreamConfiguration streamConfig_; + struct v4l2_capability capabilities_; + unsigned int bufferCount_; + unsigned int currentBuf_; + + std::unique_ptr vcam_; +}; + +#endif /* __V4L2_CAMERA_PROXY_H__ */ diff --git a/src/v4l2/v4l2_compat.cpp b/src/v4l2/v4l2_compat.cpp new file mode 100644 index 00000000..3330e7bc --- /dev/null +++ b/src/v4l2/v4l2_compat.cpp @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_compat.cpp - V4L2 compatibility layer + */ + +#include "v4l2_compat_manager.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LIBCAMERA_PUBLIC __attribute__((visibility("default"))) + +using namespace libcamera; + +#define extract_va_arg(type, arg, last) \ +{ \ + va_list ap; \ + va_start(ap, last); \ + arg = va_arg(ap, type); \ + va_end(ap); \ +} + +extern "C" { +LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...) +{ + mode_t mode = 0; + if (oflag & O_CREAT || oflag & O_TMPFILE) + extract_va_arg(mode_t, mode, oflag); + + return openat(AT_FDCWD, path, oflag, mode); +} + +LIBCAMERA_PUBLIC int openat(int dirfd, const char *path, int oflag, ...) +{ + mode_t mode = 0; + if (oflag & O_CREAT || oflag & O_TMPFILE) + extract_va_arg(mode_t, mode, oflag); + + return V4L2CompatManager::instance()->openat(dirfd, path, oflag, mode); +} + +LIBCAMERA_PUBLIC int dup(int oldfd) +{ + return V4L2CompatManager::instance()->dup(oldfd); +} + +LIBCAMERA_PUBLIC int close(int fd) +{ + return V4L2CompatManager::instance()->close(fd); +} + +LIBCAMERA_PUBLIC void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset) +{ + void *val = V4L2CompatManager::instance()->mmap(addr, length, prot, flags, fd, offset); + return val; +} + +LIBCAMERA_PUBLIC int munmap(void *addr, size_t length) +{ + return V4L2CompatManager::instance()->munmap(addr, length); +} + +LIBCAMERA_PUBLIC int ioctl(int fd, unsigned long request, ...) +{ + void *arg; + extract_va_arg(void *, arg, request); + + return V4L2CompatManager::instance()->ioctl(fd, request, arg); +} + +} diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp new file mode 100644 index 00000000..4eeb714f --- /dev/null +++ b/src/v4l2/v4l2_compat_manager.cpp @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_compat_manager.cpp - V4L2 compatibility manager + */ +#include "v4l2_compat_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "log.h" + +using namespace libcamera; + +LOG_DEFINE_CATEGORY(V4L2Compat) + +V4L2CompatManager::V4L2CompatManager() + : cameraManagerRunning_(false), cm_(nullptr) +{ + openat_func_ = (openat_func_t )dlsym(RTLD_NEXT, "openat"); + dup_func_ = (dup_func_t )dlsym(RTLD_NEXT, "dup"); + close_func_ = (close_func_t )dlsym(RTLD_NEXT, "close"); + ioctl_func_ = (ioctl_func_t )dlsym(RTLD_NEXT, "ioctl"); + mmap_func_ = (mmap_func_t )dlsym(RTLD_NEXT, "mmap"); + munmap_func_ = (munmap_func_t )dlsym(RTLD_NEXT, "munmap"); +} + +V4L2CompatManager::~V4L2CompatManager() +{ + devices_.clear(); + + if (isRunning()) { + exit(0); + /* \todo Wait with a timeout, just in case. */ + wait(); + } +} + +int V4L2CompatManager::init() +{ + start(); + + MutexLocker locker(mutex_); + cv_.wait(locker); + + return 0; +} + +void V4L2CompatManager::run() +{ + cm_ = new CameraManager(); + + int ret = cm_->start(); + if (ret) { + LOG(V4L2Compat, Error) << "Failed to start camera manager: " + << strerror(-ret); + return; + } + + LOG(V4L2Compat, Debug) << "Started camera manager"; + + /* + * For each Camera registered in the system, a V4L2CameraProxy + * gets created here to wraps a camera device. + */ + // \todo map v4l2 devices to libcamera cameras; minor device node? + unsigned int index = 0; + for (auto &camera : cm_->cameras()) { + V4L2CameraProxy *proxy = new V4L2CameraProxy(index, camera); + proxies_.emplace_back(proxy); + ++index; + } + + /* + * libcamera has been initialized. Unlock the init() caller + * as we're now ready to handle calls from the framework. + */ + cv_.notify_one(); + + /* Now start processing events and messages. */ + exec(); + + cm_->stop(); + delete cm_; + cm_ = nullptr; +} + +V4L2CompatManager *V4L2CompatManager::instance() +{ + static V4L2CompatManager v4l2CompatManager; + return &v4l2CompatManager; +} + +bool V4L2CompatManager::validfd(int fd) +{ + return devices_.find(fd) != devices_.end(); +} + +bool V4L2CompatManager::validmmap(void *addr) +{ + return mmaps_.find(addr) != mmaps_.end(); +} + +std::shared_ptr V4L2CompatManager::getCamera(int fd) +{ + if (!validfd(fd)) + return nullptr; + + return devices_.at(fd); +} + +std::shared_ptr V4L2CompatManager::getCamera(void *addr) +{ + if (!validmmap(addr)) + return nullptr; + + return devices_.at(mmaps_.at(addr)); +} + +int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mode) +{ + int fd = openat_func_(dirfd, path, oflag, mode); + if (fd < 0) + return fd; + + if (std::string(path).find("video") == std::string::npos) + return fd; + + if (!isRunning()) + init(); + + /* \todo map v4l2 devnodes to libcamera cameras */ + unsigned int camera_index = 0; + + std::shared_ptr proxy = proxies_[camera_index]; + int ret = proxy->open(mode & O_NONBLOCK); + if (ret < 0) + return ret; + + int efd = eventfd(0, (mode & O_CLOEXEC) | (mode & O_NONBLOCK)); + if (efd < 0) + return errno; + + devices_.emplace(efd, proxy); + + return efd; +} + +int V4L2CompatManager::dup(int oldfd) +{ + int newfd = dup_func_(oldfd); + if (validfd(oldfd)) + devices_[newfd] = devices_[oldfd]; + + return newfd; +} + +int V4L2CompatManager::close(int fd) +{ + if (validfd(fd) && devices_[fd]->close() < 0) + return -EIO; + + return close_func_(fd); +} + +void *V4L2CompatManager::mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset) +{ + if (!validfd(fd)) + return mmap_func_(addr, length, prot, flags, fd, offset); + + void *map = getCamera(fd)->mmap(addr, length, prot, flags, offset); + if (map == MAP_FAILED) { + errno = EINVAL; + return map; + } + + mmaps_[addr] = fd; + return map; +} + +int V4L2CompatManager::munmap(void *addr, size_t length) +{ + if (!validmmap(addr)) + return munmap_func_(addr, length); + + int ret = getCamera(addr)->munmap(addr, length); + if (ret < 0) + return ret; + + mmaps_.erase(addr); + addr = nullptr; + + return 0; +} + +int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg) +{ + std::shared_ptr proxy = getCamera(fd); + if (!proxy) + return ioctl_func_(fd, request, arg); + + return proxy->ioctl(request, arg); +} + +/* \todo make libcamera export these */ +int V4L2CompatManager::bpl_multiplier(unsigned int format) +{ + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + return 1; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + return 3; + case V4L2_PIX_FMT_ARGB32: + return 4; + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + return 2; + default: + return 0; + }; +} + +int V4L2CompatManager::image_size(unsigned int format, + unsigned int width, unsigned int height) +{ + switch (format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + return width * height + width * height / 2; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + return width * height * 2; + case V4L2_PIX_FMT_NV24: + case V4L2_PIX_FMT_NV42: + return width * height * 3; + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB24: + return width * height * 3; + case V4L2_PIX_FMT_ARGB32: + return width * height * 4; + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + return width * height * 2; + default: + return 0; + }; +} + +unsigned int V4L2CompatManager::v4l2_to_drm(unsigned int pixelformat) +{ + switch (pixelformat) { + /* RGB formats. */ + case V4L2_PIX_FMT_RGB24: + return DRM_FORMAT_BGR888; + case V4L2_PIX_FMT_BGR24: + return DRM_FORMAT_RGB888; + case V4L2_PIX_FMT_ARGB32: + return DRM_FORMAT_BGRA8888; + + /* YUV packed formats. */ + case V4L2_PIX_FMT_YUYV: + return DRM_FORMAT_YUYV; + case V4L2_PIX_FMT_YVYU: + return DRM_FORMAT_YVYU; + case V4L2_PIX_FMT_UYVY: + return DRM_FORMAT_UYVY; + case V4L2_PIX_FMT_VYUY: + return DRM_FORMAT_VYUY; + + /* YUY planar formats. */ + case V4L2_PIX_FMT_NV16: + return DRM_FORMAT_NV16; + case V4L2_PIX_FMT_NV61: + return DRM_FORMAT_NV61; + case V4L2_PIX_FMT_NV12: + return DRM_FORMAT_NV12; + case V4L2_PIX_FMT_NV21: + return DRM_FORMAT_NV21; + case V4L2_PIX_FMT_NV24: + return DRM_FORMAT_NV24; + case V4L2_PIX_FMT_NV42: + return DRM_FORMAT_NV42; + default: + return pixelformat; + }; +} + +unsigned int V4L2CompatManager::drm_to_v4l2(unsigned int pixelformat) +{ + switch (pixelformat) { + /* RGB formats. */ + case DRM_FORMAT_BGR888: + return V4L2_PIX_FMT_BGR24; + case DRM_FORMAT_RGB888: + return V4L2_PIX_FMT_RGB24; + case DRM_FORMAT_BGRA8888: + return V4L2_PIX_FMT_ARGB32; + + /* YUV packed formats. */ + case DRM_FORMAT_YUYV: + return V4L2_PIX_FMT_YUYV; + case DRM_FORMAT_YVYU: + return V4L2_PIX_FMT_YVYU; + case DRM_FORMAT_UYVY: + return V4L2_PIX_FMT_UYVY; + case DRM_FORMAT_VYUY: + return V4L2_PIX_FMT_VYUY; + + /* YUY planar formats. */ + case DRM_FORMAT_NV16: + return V4L2_PIX_FMT_NV16; + case DRM_FORMAT_NV61: + return V4L2_PIX_FMT_NV61; + case DRM_FORMAT_NV12: + return V4L2_PIX_FMT_NV12; + case DRM_FORMAT_NV21: + return V4L2_PIX_FMT_NV21; + case DRM_FORMAT_NV24: + return V4L2_PIX_FMT_NV24; + case DRM_FORMAT_NV42: + return V4L2_PIX_FMT_NV42; + default: + return pixelformat; + } +} diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h new file mode 100644 index 00000000..492f97fd --- /dev/null +++ b/src/v4l2/v4l2_compat_manager.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * v4l2_compat_manager.h - V4L2 compatibility manager + */ +#ifndef __V4L2_COMPAT_MANAGER_H__ +#define __V4L2_COMPAT_MANAGER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "thread.h" +#include "v4l2_camera_proxy.h" + +using namespace libcamera; + +class V4L2CompatManager : public Thread +{ +public: + static V4L2CompatManager *instance(); + + int init(); + + int openat(int dirfd, const char *path, int oflag, mode_t mode); + + std::shared_ptr getCamera(int fd); + std::shared_ptr getCamera(void *addr); + + int dup(int oldfd); + int close(int fd); + void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset); + int munmap(void *addr, size_t length); + int ioctl(int fd, unsigned long request, void *arg); + + static int bpl_multiplier(unsigned int format); + static int image_size(unsigned int format, unsigned int width, + unsigned int height); + + static unsigned int v4l2_to_drm(unsigned int pixelformat); + static unsigned int drm_to_v4l2(unsigned int pixelformat); + +private: + V4L2CompatManager(); + ~V4L2CompatManager(); + + void run() override; + + bool validfd(int fd); + bool validmmap(void *addr); + + int default_ioctl(int fd, unsigned long request, void *arg); + + typedef int (*openat_func_t)(int dirfd, const char *path, int oflag, ...); + typedef int (*dup_func_t)(int oldfd); + typedef int (*close_func_t)(int fd); + typedef int (*ioctl_func_t)(int fd, unsigned long request, ...); + typedef void *(*mmap_func_t)(void *addr, size_t length, int prot, + int flags, int fd, off_t offset); + typedef int (*munmap_func_t)(void *addr, size_t length); + + openat_func_t openat_func_; + dup_func_t dup_func_; + close_func_t close_func_; + ioctl_func_t ioctl_func_; + mmap_func_t mmap_func_; + munmap_func_t munmap_func_; + + bool cameraManagerRunning_; + CameraManager *cm_; + + std::mutex mutex_; + std::condition_variable cv_; + + std::vector> proxies_; + std::map> devices_; + std::map mmaps_; +}; + +#endif /* __V4L2_COMPAT_MANAGER_H__ */