[{"id":3318,"web_url":"https://patchwork.libcamera.org/comment/3318/","msgid":"<20200104003514.GI4847@pendragon.ideasonboard.com>","date":"2020-01-04T00:35:14","subject":"Re: [libcamera-devel] [PATCH v6 5/5] v4l2: v4l2_compat: Add V4L2\n\tcompatibility layer","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Fri, Jan 03, 2020 at 07:14:13PM -0500, Paul Elder wrote:\n> Add libcamera V4L2 compatibility layer.\n> \n> This initial implementation supports the minimal set of V4L2 operations,\n> which allows getting, setting, and enumerating formats, and streaming\n> frames from a video device. Some data about the wrapped V4L2 video\n> device are hardcoded.\n> \n> Add a build option named 'v4l2' and adjust the build system to\n> selectively compile the V4L2 compatibility layer.\n> \n> For now we match the V4L2 device node to a libcamera camera based on a\n> devnum that a pipeline handler may optionally map to a libcamera\n> camera.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n> ---\n> Changes in v6:\n> - use strlcpy instead of memcpy in V4L2CameraProxy\n> - fix fourcc conversion in V4L2CameraProxy::vidioc_try_fmt\n> - add aliases for __open_2 and __openat_2\n> - cosmetic changes and other little fixes\n> \n> Changes in v5:\n> - cosmetic changes and little fixes\n> \n> Major changes in v4:\n> - squash buffer state tracking in V4L2CameraProxy (from 6/6 in v3)\n> - separated out V4L2CameraProxy::freeBuffers() (since it was cluttering\n>   up V4L2CameraProxy::vidioc_reqbufs)\n> \n> Less major changes in v4:\n> - check that addresses passed to V4L2CameraProxy::munmap() have been\n>   mmaped\n> - other cosmetic changes\n> \n> Major changes in v3:\n> - V4L2CompatManager::openat() verify video node and match to camera via\n>   devnum instead of path\n> - add FrameMetadata class to encapsulate portions of the libcamera\n>   Buffer that needs to be extracted to v4l2_buffer, and to prepare for\n>   the Buffer rework\n> - V4L2CameraProxy refcount for tracking dups and closes (and open)\n> - add V4L2CompatManager::initialized_ to deal with race of waiting on\n>   init cv after the cv is notified\n> \n> Less major changes in v3:\n> - change the list of pending Reqeusts (v4l2 buffers queued before\n>   streamon) to unique pointers\n> - V4L2Camera::updateSizeImage() -> V4L2CameraProxy::calculateSizeImage()\n> - change V4L2CompatManager list of V4L2CameraProxy to unique_ptr, with\n>   maps of fd/mmap addr to V4L2CameraProxy *\n> - removed cross-thread validation methods from V4L2CameraProxy\n>   (validate[Stream|Memory]Type)\n> - removed hasPixelFormat() and hasSize() from V4L2CameraProxy\n> - moved mmap logic out of V4L2Camera and into V4L2CameraProxy\n> - moved nonblock logic out of V4L2Camera and into V4L2CameraProxy\n> - add todos\n> - oter cosmetic changes\n> \n> Changes in v2:\n> - move all errno acrobatics to V4L2CameraProxy\n> - remove all mentions of dmabuf\n> - make V4L2CompatManager::getCamera return pointer rather than\n>   shared_ptr\n> - match V4L2 device nodes to libcamera cameras using Camera::name()\n>   compared to /sys/dev/char/maj:min/name (only works for UVC cameras)\n>   - in V4L2CompatManager::getCameraIndex()\n> - add -fvisibility=hidden to v4l2 compat\n> - cache the results of V4L2CompatManager::imageSize() within V4L2Camera\n>   (where V4L2Camera is interested)\n> - remove V4L2CompatManager::valid[fd|mmap], and where those methods were\n>   used, check V4L2CompatManager::getCamera() != nullptr instead\n> - fixed V4L2CompatManager::drmToV4L2() mappings for DRM_FORMAT_BGR888\n>   and DRM_FORMAT_RGB888\n> - other cosmetic changes\n> ---\n>  meson_options.txt                |   5 +\n>  src/meson.build                  |   4 +\n>  src/v4l2/meson.build             |  31 ++\n>  src/v4l2/v4l2_camera.cpp         | 224 +++++++++++\n>  src/v4l2/v4l2_camera.h           |  85 ++++\n>  src/v4l2/v4l2_camera_proxy.cpp   | 644 +++++++++++++++++++++++++++++++\n>  src/v4l2/v4l2_camera_proxy.h     |  81 ++++\n>  src/v4l2/v4l2_compat.cpp         |  85 ++++\n>  src/v4l2/v4l2_compat_manager.cpp | 250 ++++++++++++\n>  src/v4l2/v4l2_compat_manager.h   |  77 ++++\n>  10 files changed, 1486 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\n> \n> diff --git a/meson_options.txt b/meson_options.txt\n> index 1a328045..79ee4de6 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 the V4L2 compatibility layer')\n> diff --git a/src/meson.build b/src/meson.build\n> index 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\n> diff --git a/src/v4l2/meson.build b/src/v4l2/meson.build\n> new file mode 100644\n> index 00000000..14ee3594\n> --- /dev/null\n> +++ b/src/v4l2/meson.build\n> @@ -0,0 +1,31 @@\n> +v4l2_compat_sources = files([\n> +    'v4l2_camera.cpp',\n> +    'v4l2_camera_proxy.cpp',\n> +    'v4l2_compat.cpp',\n> +    'v4l2_compat_manager.cpp',\n> +])\n> +\n> +v4l2_compat_includes = [\n> +    libcamera_includes,\n> +    libcamera_internal_includes,\n> +]\n> +\n> +v4l2_compat_cpp_args = [\n> +    # Meson enables large file support unconditionally, which redirect file\n> +    # operations to 64-bit versions. This results in some symbols being\n> +    # renamed, for instance open() being renamed to open64(). As the V4L2\n> +    # adaptation wrapper needs to provide both 32-bit and 64-bit versions of\n> +    # file operations, disable transparent large file support.\n> +    '-U_FILE_OFFSET_BITS',\n> +    '-D_FILE_OFFSET_BITS=32',\n> +    '-fvisibility=hidden',\n> +]\n> +\n> +v4l2_compat = shared_library('v4l2-compat',\n> +                             v4l2_compat_sources,\n> +                             name_prefix : '',\n> +                             install : true,\n> +                             link_with : libcamera,\n> +                             include_directories : v4l2_compat_includes,\n> +                             dependencies : libcamera_deps,\n> +                             cpp_args : v4l2_compat_cpp_args)\n> diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\n> new file mode 100644\n> index 00000000..3590730f\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_camera.cpp\n> @@ -0,0 +1,224 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_camera.cpp - V4L2 compatibility camera\n> + */\n> +\n> +#include \"v4l2_camera.h\"\n> +\n> +#include <errno.h>\n> +\n> +#include \"log.h\"\n> +#include \"utils.h\"\n> +\n> +using namespace libcamera;\n> +\n> +LOG_DECLARE_CATEGORY(V4L2Compat);\n> +\n> +FrameMetadata::FrameMetadata(Buffer *buffer)\n> +\t: index_(buffer->index()), bytesused_(buffer->bytesused()),\n> +\t  timestamp_(buffer->timestamp()), sequence_(buffer->sequence()),\n> +\t  status_(buffer->status())\n> +{\n> +}\n> +\n> +V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)\n> +\t: camera_(camera), isRunning_(false)\n> +{\n> +\tcamera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);\n> +}\n> +\n> +V4L2Camera::~V4L2Camera()\n> +{\n> +\tcamera_->release();\n> +}\n> +\n> +void V4L2Camera::open(int *ret)\n> +{\n> +\t/* \\todo Support multiple open. */\n> +\tif (camera_->acquire() < 0) {\n> +\t\tLOG(V4L2Compat, Error) << \"Failed to acquire camera\";\n> +\t\t*ret = -EINVAL;\n> +\t\treturn;\n> +\t}\n> +\n> +\tconfig_ = camera_->generateConfiguration({ StreamRole::Viewfinder });\n> +\tif (!config_) {\n> +\t\tcamera_->release();\n> +\t\t*ret = -EINVAL;\n> +\t\treturn;\n> +\t}\n> +\n> +\t*ret = 0;\n> +}\n> +\n> +void V4L2Camera::close()\n> +{\n> +\tcamera_->release();\n> +}\n> +\n> +void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)\n> +{\n> +\t*streamConfig = config_->at(0);\n> +}\n> +\n> +std::vector<FrameMetadata> V4L2Camera::completedBuffers()\n> +{\n> +\tstd::vector<FrameMetadata> v;\n> +\n> +\tbufferLock_.lock();\n> +\tfor (std::unique_ptr<FrameMetadata> &metadata : completedBuffers_)\n> +\t\tv.push_back(*metadata.get());\n> +\tcompletedBuffers_.clear();\n> +\tbufferLock_.unlock();\n> +\n> +\treturn v;\n> +}\n> +\n> +void V4L2Camera::requestComplete(Request *request)\n> +{\n> +\tif (request->status() == Request::RequestCancelled)\n> +\t\treturn;\n> +\n> +\t/* We only have one stream at the moment. */\n> +\tbufferLock_.lock();\n> +\tBuffer *buffer = request->buffers().begin()->second;\n> +\tstd::unique_ptr<FrameMetadata> metadata =\n> +\t\tutils::make_unique<FrameMetadata>(buffer);\n> +\tcompletedBuffers_.push_back(std::move(metadata));\n> +\tbufferLock_.unlock();\n> +\n> +\tbufferSema_.release();\n> +}\n> +\n> +void V4L2Camera::configure(int *ret, StreamConfiguration *streamConfigOut,\n> +\t\t\t   const Size &size, PixelFormat pixelformat,\n> +\t\t\t   unsigned int bufferCount)\n> +{\n> +\tStreamConfiguration &streamConfig = config_->at(0);\n> +\tstreamConfig.size.width = size.width;\n> +\tstreamConfig.size.height = size.height;\n> +\tstreamConfig.pixelFormat = pixelformat;\n> +\tstreamConfig.bufferCount = bufferCount;\n> +\t/* \\todo memoryType (interval vs external) */\n> +\n> +\tCameraConfiguration::Status validation = config_->validate();\n> +\tif (validation == CameraConfiguration::Invalid) {\n> +\t\tLOG(V4L2Compat, Debug) << \"Configuration invalid\";\n> +\t\t*ret = -EINVAL;\n> +\t\treturn;\n> +\t}\n> +\tif (validation == CameraConfiguration::Adjusted)\n> +\t\tLOG(V4L2Compat, Debug) << \"Configuration adjusted\";\n> +\n> +\tLOG(V4L2Compat, Debug) << \"Validated configuration is: \"\n> +\t\t\t      << streamConfig.toString();\n> +\n> +\t*ret = camera_->configure(config_.get());\n> +\tif (*ret < 0)\n> +\t\treturn;\n> +\n> +\t*streamConfigOut = config_->at(0);\n> +}\n> +\n> +void V4L2Camera::mmap(void **ret, unsigned int index)\n> +{\n> +\tStream *stream = *camera_->streams().begin();\n> +\t*ret = stream->buffers()[index].planes()[0].mem();\n> +}\n> +\n> +void V4L2Camera::allocBuffers(int *ret, unsigned int count)\n> +{\n> +\t*ret = camera_->allocateBuffers();\n> +\tif (*ret == -EACCES)\n> +\t\t*ret = -EBUSY;\n> +}\n> +\n> +void V4L2Camera::freeBuffers()\n> +{\n> +\tcamera_->freeBuffers();\n> +}\n> +\n> +void V4L2Camera::streamOn(int *ret)\n> +{\n> +\t*ret = 0;\n> +\n> +\tif (isRunning_)\n> +\t\treturn;\n> +\n> +\t*ret = camera_->start();\n> +\tif (*ret < 0) {\n> +\t\tif (*ret == -EACCES)\n> +\t\t\t*ret = -EBUSY;\n> +\t\treturn;\n> +\t}\n> +\tisRunning_ = true;\n> +\n> +\tfor (std::unique_ptr<Request> &req : pendingRequests_) {\n> +\t\t/* \\todo What should we do if this returns -EINVAL? */\n> +\t\t*ret = camera_->queueRequest(req.release());\n> +\t\tif (*ret < 0) {\n> +\t\t\tif (*ret == -EACCES)\n> +\t\t\t\t*ret = -EBUSY;\n> +\t\t\treturn;\n> +\t\t}\n> +\t}\n> +\n> +\tpendingRequests_.clear();\n> +}\n> +\n> +void V4L2Camera::streamOff(int *ret)\n> +{\n> +\t*ret = 0;\n> +\n> +\t/* \\todo Restore buffers to reqbufs state? */\n> +\tif (!isRunning_)\n> +\t\treturn;\n> +\n> +\t*ret = camera_->stop();\n> +\tif (*ret < 0) {\n> +\t\tif (*ret == -EACCES)\n> +\t\t\t*ret = -EBUSY;\n> +\t\treturn;\n> +\t}\n> +\tisRunning_ = false;\n> +}\n> +\n> +void V4L2Camera::qbuf(int *ret, unsigned int index)\n> +{\n> +\tStream *stream = config_->at(0).stream();\n> +\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(index);\n> +\tif (!buffer) {\n> +\t\tLOG(V4L2Compat, Error) << \"Can't create buffer\";\n> +\t\t*ret = -ENOMEM;\n> +\t\treturn;\n> +\t}\n> +\n> +\tstd::unique_ptr<Request> request =\n> +\t\tstd::unique_ptr<Request>(camera_->createRequest());\n> +\tif (!request) {\n> +\t\tLOG(V4L2Compat, Error) << \"Can't create request\";\n> +\t\t*ret = -ENOMEM;\n> +\t\treturn;\n> +\t}\n> +\n> +\t*ret = request->addBuffer(std::move(buffer));\n> +\tif (*ret < 0) {\n> +\t\tLOG(V4L2Compat, Error) << \"Can't set buffer for request\";\n> +\t\t*ret = -ENOMEM;\n> +\t\treturn;\n> +\t}\n> +\n> +\tif (!isRunning_) {\n> +\t\tpendingRequests_.push_back(std::move(request));\n> +\t\treturn;\n> +\t}\n> +\n> +\t*ret = camera_->queueRequest(request.release());\n> +\tif (*ret < 0) {\n> +\t\tLOG(V4L2Compat, Error) << \"Can't queue request\";\n> +\t\tif (*ret == -EACCES)\n> +\t\t\t*ret = -EBUSY;\n> +\t}\n> +}\n> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> new file mode 100644\n> index 00000000..d24dbca6\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_camera.h\n> @@ -0,0 +1,85 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_camera.h - V4L2 compatibility camera\n> + */\n> +\n> +#ifndef __V4L2_CAMERA_H__\n> +#define __V4L2_CAMERA_H__\n> +\n> +#include <deque>\n> +#include <linux/videodev2.h>\n> +#include <mutex>\n> +\n> +#include <libcamera/buffer.h>\n> +#include <libcamera/camera.h>\n> +\n> +#include \"semaphore.h\"\n> +\n> +using namespace libcamera;\n> +\n> +class FrameMetadata\n> +{\n> +public:\n> +\tFrameMetadata(Buffer *buffer);\n> +\n> +\tint index() const { return index_; }\n> +\n> +\tunsigned int bytesused() const { return bytesused_; }\n> +\tuint64_t timestamp() const { return timestamp_; }\n> +\tunsigned int sequence() const { return sequence_; }\n> +\n> +\tBuffer::Status status() const { return status_; }\n> +\n> +private:\n> +\tint index_;\n> +\n> +\tunsigned int bytesused_;\n> +\tuint64_t timestamp_;\n> +\tunsigned int sequence_;\n> +\n> +\tBuffer::Status status_;\n> +};\n> +\n> +class V4L2Camera : public Object\n> +{\n> +public:\n> +\tV4L2Camera(std::shared_ptr<Camera> camera);\n> +\t~V4L2Camera();\n> +\n> +\tvoid open(int *ret);\n> +\tvoid close();\n> +\tvoid getStreamConfig(StreamConfiguration *streamConfig);\n> +\tstd::vector<FrameMetadata> completedBuffers();\n> +\n> +\tvoid mmap(void **ret, unsigned int index);\n> +\n> +\tvoid configure(int *ret, StreamConfiguration *streamConfigOut,\n> +\t\t       const Size &size, PixelFormat pixelformat,\n> +\t\t       unsigned int bufferCount);\n> +\n> +\tvoid allocBuffers(int *ret, unsigned int count);\n> +\tvoid freeBuffers();\n> +\tvoid streamOn(int *ret);\n> +\tvoid streamOff(int *ret);\n> +\n> +\tvoid qbuf(int *ret, unsigned int index);\n> +\n> +\tSemaphore bufferSema_;\n> +\n> +private:\n> +\tvoid requestComplete(Request *request);\n> +\n> +\tstd::shared_ptr<Camera> camera_;\n> +\tstd::unique_ptr<CameraConfiguration> config_;\n> +\n> +\tbool isRunning_;\n> +\n> +\tstd::mutex bufferLock_;\n> +\n> +\tstd::deque<std::unique_ptr<Request>> pendingRequests_;\n> +\tstd::deque<std::unique_ptr<FrameMetadata>> completedBuffers_;\n> +};\n> +\n> +#endif /* __V4L2_CAMERA_H__ */\n> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\n> new file mode 100644\n> index 00000000..c9df7156\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_camera_proxy.cpp\n> @@ -0,0 +1,644 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_camera_proxy.cpp - Proxy to V4L2 compatibility camera\n> + */\n> +\n> +#include \"v4l2_camera_proxy.h\"\n> +\n> +#include <algorithm>\n> +#include <errno.h>\n> +#include <linux/drm_fourcc.h>\n> +#include <linux/videodev2.h>\n> +#include <string.h>\n> +#include <sys/mman.h>\n> +\n> +#include <libcamera/camera.h>\n> +#include <libcamera/object.h>\n> +\n> +#include \"log.h\"\n> +#include \"utils.h\"\n> +#include \"v4l2_camera.h\"\n> +#include \"v4l2_compat_manager.h\"\n> +\n> +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))\n> +\n> +using namespace libcamera;\n> +\n> +LOG_DECLARE_CATEGORY(V4L2Compat);\n> +\n> +V4L2CameraProxy::V4L2CameraProxy(unsigned int index,\n> +\t\t\t\t std::shared_ptr<Camera> camera)\n> +\t: refcount_(0), index_(index), bufferCount_(0), currentBuf_(0),\n> +\t  vcam_(utils::make_unique<V4L2Camera>(camera))\n> +{\n> +\tquerycap(camera);\n> +}\n> +\n> +int V4L2CameraProxy::open(bool nonBlocking)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing open\";\n> +\n> +\tint ret;\n> +\tvcam_->invokeMethod(&V4L2Camera::open, ConnectionTypeBlocking,\n> +\t\t\t    &ret);\n> +\tif (ret < 0) {\n> +\t\terrno = -ret;\n> +\t\treturn -1;\n> +\t}\n> +\n> +\tnonBlocking_ = nonBlocking;\n> +\n> +\tvcam_->invokeMethod(&V4L2Camera::getStreamConfig,\n> +\t\t\t    ConnectionTypeBlocking, &streamConfig_);\n> +\tsetFmtFromConfig(streamConfig_);\n> +\tsizeimage_ = calculateSizeImage(streamConfig_);\n> +\n> +\trefcount_++;\n> +\n> +\treturn 0;\n> +}\n> +\n> +void V4L2CameraProxy::dup()\n> +{\n> +\trefcount_++;\n> +}\n> +\n> +void V4L2CameraProxy::close()\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing close\";\n> +\n> +\tif (--refcount_ > 0)\n> +\t\treturn;\n> +\n> +\tvcam_->invokeMethod(&V4L2Camera::close, ConnectionTypeBlocking);\n> +}\n> +\n> +void *V4L2CameraProxy::mmap(size_t length, int prot, int flags, off_t offset)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing mmap\";\n> +\n> +\t/* \\todo Validate prot and flags properly. */\n> +\tif (prot != (PROT_READ | PROT_WRITE)) {\n> +\t\terrno = EINVAL;\n> +\t\treturn MAP_FAILED;\n> +\t}\n> +\n> +\tunsigned int index = offset / sizeimage_;\n> +\tif (index * sizeimage_ != offset || length != sizeimage_) {\n> +\t\terrno = EINVAL;\n> +\t\treturn MAP_FAILED;\n> +\t}\n> +\n> +\tvoid *val;\n> +\tvcam_->invokeMethod(&V4L2Camera::mmap, ConnectionTypeBlocking,\n> +\t\t\t    &val, index);\n> +\n> +\tbuffers_[index].flags |= V4L2_BUF_FLAG_MAPPED;\n> +\tmmaps_[val] = index;\n> +\n> +\treturn val;\n> +}\n> +\n> +int V4L2CameraProxy::munmap(void *addr, size_t length)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing munmap\";\n> +\n> +\tauto iter = mmaps_.find(addr);\n> +\tif (iter == mmaps_.end() || length != sizeimage_) {\n> +\t\terrno = EINVAL;\n> +\t\treturn -1;\n> +\t}\n> +\n> +\tbuffers_[iter->second].flags &= ~V4L2_BUF_FLAG_MAPPED;\n> +\tmmaps_.erase(iter);\n> +\n> +\treturn 0;\n> +}\n> +\n> +bool V4L2CameraProxy::validateBufferType(uint32_t type)\n> +{\n> +\treturn type == V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> +}\n> +\n> +bool V4L2CameraProxy::validateMemoryType(uint32_t memory)\n> +{\n> +\treturn memory == V4L2_MEMORY_MMAP;\n> +}\n> +\n> +void V4L2CameraProxy::setFmtFromConfig(StreamConfiguration &streamConfig)\n> +{\n> +\tcurV4L2Format_.fmt.pix.width = streamConfig.size.width;\n> +\tcurV4L2Format_.fmt.pix.height = streamConfig.size.height;\n> +\tcurV4L2Format_.fmt.pix.pixelformat = drmToV4L2(streamConfig.pixelFormat);\n> +\tcurV4L2Format_.fmt.pix.field = V4L2_FIELD_NONE;\n> +\tcurV4L2Format_.fmt.pix.bytesperline =\n> +\t\tbplMultiplier(curV4L2Format_.fmt.pix.pixelformat) *\n> +\t\tcurV4L2Format_.fmt.pix.width;\n> +\tcurV4L2Format_.fmt.pix.sizeimage =\n> +\t\timageSize(curV4L2Format_.fmt.pix.pixelformat,\n> +\t\t\t  curV4L2Format_.fmt.pix.width,\n> +\t\t\t  curV4L2Format_.fmt.pix.height);\n> +\tcurV4L2Format_.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;\n> +}\n> +\n> +unsigned int V4L2CameraProxy::calculateSizeImage(StreamConfiguration &streamConfig)\n> +{\n> +\t/*\n> +\t * \\todo Merge this method with setFmtFromConfig (need imageSize to\n> +\t * support all libcamera formats first, or filter out MJPEG for now).\n> +\t */\n> +\treturn imageSize(drmToV4L2(streamConfig.pixelFormat),\n> +\t\t\t streamConfig.size.width,\n> +\t\t\t streamConfig.size.height);\n> +}\n> +\n> +void V4L2CameraProxy::querycap(std::shared_ptr<Camera> camera)\n> +{\n> +\tstd::string driver = \"libcamera\";\n> +\tstd::string bus_info = driver + \":\" + std::to_string(index_);\n> +\n> +\tutils::strlcpy((char *)capabilities_.driver, driver.c_str(),\n> +\t\t       sizeof(capabilities_.driver));\n\nstatic_cast<char *>(...) instead of (char *).\n\n> +\tutils::strlcpy((char *)capabilities_.card, camera->name().c_str(),\n> +\t\t       sizeof(capabilities_.card));\n> +\tutils::strlcpy((char *)capabilities_.bus_info, bus_info.c_str(),\n> +\t\t       sizeof(capabilities_.bus_info));\n> +\t/* \\todo Put this in a header/config somewhere. */\n> +\tcapabilities_.version = KERNEL_VERSION(5, 2, 0);\n> +\tcapabilities_.device_caps = V4L2_CAP_VIDEO_CAPTURE;\n> +\tcapabilities_.capabilities = capabilities_.device_caps\n> +\t\t\t\t   | V4L2_CAP_DEVICE_CAPS;\n> +\tmemset(capabilities_.reserved, 0, sizeof(capabilities_.reserved));\n> +}\n> +\n> +void V4L2CameraProxy::updateBuffers()\n> +{\n> +\tstd::vector<FrameMetadata> completedBuffers = vcam_->completedBuffers();\n> +\tfor (FrameMetadata &fmd : completedBuffers) {\n> +\t\tstruct v4l2_buffer &buf = buffers_[fmd.index()];\n> +\n> +\t\tswitch (fmd.status()) {\n> +\t\tcase Buffer::Status::BufferSuccess:\n> +\t\t\tbuf.bytesused = fmd.bytesused();\n> +\t\t\tbuf.field = V4L2_FIELD_NONE;\n> +\t\t\tbuf.timestamp.tv_sec = fmd.timestamp() / 1000000000;\n> +\t\t\tbuf.timestamp.tv_usec = fmd.timestamp() % 1000000;\n> +\t\t\tbuf.sequence = fmd.sequence();\n> +\n> +\t\t\tbuf.flags |= V4L2_BUF_FLAG_DONE;\n> +\t\t\tbreak;\n> +\t\tcase Buffer::Status::BufferError:\n> +\t\t\tbuf.flags |= V4L2_BUF_FLAG_ERROR;\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +}\n> +\n> +int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_querycap\";\n> +\n> +\t*arg = capabilities_;\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 (!validateBufferType(arg->type) ||\n> +\t    arg->index > streamConfig_.formats().pixelformats().size())\n> +\t\treturn -EINVAL;\n> +\n> +\t/* \\todo Add map from format to description. */\n> +\tutils::strlcpy((char *)arg->description, \"Video Format Description\",\n> +\t\t       sizeof(arg->description));\n\nSame here.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nand feel free to push :-)\n\n> +\targ->pixelformat = drmToV4L2(streamConfig_.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 (!validateBufferType(arg->type))\n> +\t\treturn -EINVAL;\n> +\n> +\tmemset(&arg->fmt, 0, sizeof(arg->fmt));\n> +\targ->fmt.pix = curV4L2Format_.fmt.pix;\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> +\tSize size(arg->fmt.pix.width, arg->fmt.pix.height);\n> +\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking,\n> +\t\t\t    &ret, &streamConfig_, size,\n> +\t\t\t    v4l2ToDrm(arg->fmt.pix.pixelformat), bufferCount_);\n> +\tif (ret < 0)\n> +\t\treturn -EINVAL;\n> +\n> +\tunsigned int sizeimage = calculateSizeImage(streamConfig_);\n> +\tif (sizeimage == 0)\n> +\t\treturn -EINVAL;\n> +\n> +\tsizeimage_ = sizeimage;\n> +\n> +\tsetFmtFromConfig(streamConfig_);\n> +\n> +\treturn 0;\n> +}\n> +\n> +int V4L2CameraProxy::vidioc_try_fmt(struct v4l2_format *arg)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_try_fmt\";\n> +\tif (!validateBufferType(arg->type))\n> +\t\treturn -EINVAL;\n> +\n> +\tPixelFormat format = v4l2ToDrm(arg->fmt.pix.pixelformat);\n> +\tconst std::vector<PixelFormat> &formats =\n> +\t\tstreamConfig_.formats().pixelformats();\n> +\tif (std::find(formats.begin(), formats.end(), format) == formats.end())\n> +\t\tformat = streamConfig_.formats().pixelformats()[0];\n> +\n> +\tSize size(arg->fmt.pix.width, arg->fmt.pix.height);\n> +\tconst std::vector<Size> &sizes = streamConfig_.formats().sizes(format);\n> +\tif (std::find(sizes.begin(), sizes.end(), size) == sizes.end())\n> +\t\tsize = streamConfig_.formats().sizes(format)[0];\n> +\n> +\targ->fmt.pix.width        = size.width;\n> +\targ->fmt.pix.height       = size.height;\n> +\targ->fmt.pix.pixelformat  = v4l2ToDrm(format);\n> +\targ->fmt.pix.field        = V4L2_FIELD_NONE;\n> +\targ->fmt.pix.bytesperline = bplMultiplier(drmToV4L2(format)) *\n> +\t\t\t\t    arg->fmt.pix.width;\n> +\targ->fmt.pix.sizeimage    = imageSize(drmToV4L2(format),\n> +\t\t\t\t\t      arg->fmt.pix.width,\n> +\t\t\t\t\t      arg->fmt.pix.height);\n> +\targ->fmt.pix.colorspace   = V4L2_COLORSPACE_SRGB;\n> +\n> +\treturn 0;\n> +}\n> +\n> +int V4L2CameraProxy::freeBuffers()\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Freeing libcamera bufs\";\n> +\n> +\tint ret;\n> +\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n> +\t\t\t    ConnectionTypeBlocking, &ret);\n> +\tif (ret < 0) {\n> +\t\tLOG(V4L2Compat, Error) << \"Failed to stop stream\";\n> +\t\treturn ret;\n> +\t}\n> +\tvcam_->invokeMethod(&V4L2Camera::freeBuffers, ConnectionTypeBlocking);\n> +\tbufferCount_ = 0;\n> +\n> +\treturn 0;\n> +}\n> +\n> +int V4L2CameraProxy::vidioc_reqbufs(struct v4l2_requestbuffers *arg)\n> +{\n> +\tint ret;\n> +\n> +\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_reqbufs\";\n> +\tif (!validateBufferType(arg->type) ||\n> +\t    !validateMemoryType(arg->memory))\n> +\t\treturn -EINVAL;\n> +\n> +\tLOG(V4L2Compat, Debug) << arg->count << \" buffers requested \";\n> +\n> +\targ->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;\n> +\n> +\tif (arg->count == 0)\n> +\t\treturn freeBuffers();\n> +\n> +\tSize size(curV4L2Format_.fmt.pix.width, curV4L2Format_.fmt.pix.height);\n> +\tvcam_->invokeMethod(&V4L2Camera::configure, ConnectionTypeBlocking,\n> +\t\t\t    &ret, &streamConfig_, size,\n> +\t\t\t    v4l2ToDrm(curV4L2Format_.fmt.pix.pixelformat),\n> +\t\t\t    arg->count);\n> +\tif (ret < 0)\n> +\t\treturn -EINVAL;\n> +\n> +\tsizeimage_ = calculateSizeImage(streamConfig_);\n> +\tif (sizeimage_ == 0)\n> +\t\treturn -EINVAL;\n> +\n> +\tsetFmtFromConfig(streamConfig_);\n> +\n> +\targ->count = streamConfig_.bufferCount;\n> +\tbufferCount_ = arg->count;\n> +\n> +\tvcam_->invokeMethod(&V4L2Camera::allocBuffers,\n> +\t\t\t    ConnectionTypeBlocking, &ret, arg->count);\n> +\tif (ret < 0) {\n> +\t\targ->count = 0;\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tbuffers_.resize(arg->count);\n> +\tfor (unsigned int i = 0; i < arg->count; i++) {\n> +\t\tstruct v4l2_buffer buf = {};\n> +\t\tbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;\n> +\t\tbuf.length = curV4L2Format_.fmt.pix.sizeimage;\n> +\t\tbuf.memory = V4L2_MEMORY_MMAP;\n> +\t\tbuf.m.offset = i * curV4L2Format_.fmt.pix.sizeimage;\n> +\t\tbuf.index = i;\n> +\n> +\t\tbuffers_[i] = buf;\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> +\n> +\tif (!validateBufferType(arg->type) ||\n> +\t    arg->index >= bufferCount_)\n> +\t\treturn -EINVAL;\n> +\n> +\tupdateBuffers();\n> +\n> +\t*arg = buffers_[arg->index];\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> +\tif (!validateBufferType(arg->type) ||\n> +\t    !validateMemoryType(arg->memory) ||\n> +\t    arg->index >= bufferCount_)\n> +\t\treturn -EINVAL;\n> +\n> +\tint ret;\n> +\tvcam_->invokeMethod(&V4L2Camera::qbuf, ConnectionTypeBlocking,\n> +\t\t\t    &ret, arg->index);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\targ->flags |= V4L2_BUF_FLAG_QUEUED;\n> +\targ->flags &= ~V4L2_BUF_FLAG_DONE;\n> +\n> +\treturn ret;\n> +}\n> +\n> +int V4L2CameraProxy::vidioc_dqbuf(struct v4l2_buffer *arg)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_dqbuf\";\n> +\n> +\tif (!validateBufferType(arg->type) ||\n> +\t    !validateMemoryType(arg->memory))\n> +\t\treturn -EINVAL;\n> +\n> +\tif (nonBlocking_ && !vcam_->bufferSema_.tryAcquire())\n> +\t\treturn -EAGAIN;\n> +\telse\n> +\t\tvcam_->bufferSema_.acquire();\n> +\n> +\tupdateBuffers();\n> +\n> +\tstruct v4l2_buffer &buf = buffers_[currentBuf_];\n> +\n> +\tbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;\n> +\tbuf.length = sizeimage_;\n> +\t*arg = buf;\n> +\n> +\tcurrentBuf_ = (currentBuf_ + 1) % bufferCount_;\n> +\n> +\treturn 0;\n> +}\n> +\n> +int V4L2CameraProxy::vidioc_streamon(int *arg)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamon\";\n> +\n> +\tif (!validateBufferType(*arg))\n> +\t\treturn -EINVAL;\n> +\n> +\tint ret;\n> +\tvcam_->invokeMethod(&V4L2Camera::streamOn,\n> +\t\t\t    ConnectionTypeBlocking, &ret);\n> +\n> +\treturn ret;\n> +}\n> +\n> +int V4L2CameraProxy::vidioc_streamoff(int *arg)\n> +{\n> +\tLOG(V4L2Compat, Debug) << \"Servicing vidioc_streamoff\";\n> +\n> +\tif (!validateBufferType(*arg))\n> +\t\treturn -EINVAL;\n> +\n> +\tint ret;\n> +\tvcam_->invokeMethod(&V4L2Camera::streamOff,\n> +\t\t\t    ConnectionTypeBlocking, &ret);\n> +\n> +\tfor (struct v4l2_buffer &buf : buffers_)\n> +\t\tbuf.flags &= ~(V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE);\n> +\n> +\treturn ret;\n> +}\n> +\n> +int V4L2CameraProxy::ioctl(unsigned long request, void *arg)\n> +{\n> +\tint ret;\n> +\tswitch (request) {\n> +\tcase VIDIOC_QUERYCAP:\n> +\t\tret = vidioc_querycap(static_cast<struct v4l2_capability *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_ENUM_FMT:\n> +\t\tret = vidioc_enum_fmt(static_cast<struct v4l2_fmtdesc *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_G_FMT:\n> +\t\tret = vidioc_g_fmt(static_cast<struct v4l2_format *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_S_FMT:\n> +\t\tret = vidioc_s_fmt(static_cast<struct v4l2_format *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_TRY_FMT:\n> +\t\tret = vidioc_try_fmt(static_cast<struct v4l2_format *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_REQBUFS:\n> +\t\tret = vidioc_reqbufs(static_cast<struct v4l2_requestbuffers *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_QUERYBUF:\n> +\t\tret = vidioc_querybuf(static_cast<struct v4l2_buffer *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_QBUF:\n> +\t\tret = vidioc_qbuf(static_cast<struct v4l2_buffer *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_DQBUF:\n> +\t\tret = vidioc_dqbuf(static_cast<struct v4l2_buffer *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_STREAMON:\n> +\t\tret = vidioc_streamon(static_cast<int *>(arg));\n> +\t\tbreak;\n> +\tcase VIDIOC_STREAMOFF:\n> +\t\tret = vidioc_streamoff(static_cast<int *>(arg));\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tret = -ENOTTY;\n> +\t\tbreak;\n> +\t}\n> +\n> +\tif (ret < 0) {\n> +\t\terrno = -ret;\n> +\t\treturn -1;\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n> +/* \\todo make libcamera export these */\n> +int V4L2CameraProxy::bplMultiplier(unsigned int format)\n> +{\n> +\tswitch (format) {\n> +\tcase V4L2_PIX_FMT_NV12:\n> +\tcase V4L2_PIX_FMT_NV21:\n> +\tcase V4L2_PIX_FMT_NV16:\n> +\tcase V4L2_PIX_FMT_NV61:\n> +\tcase V4L2_PIX_FMT_NV24:\n> +\tcase V4L2_PIX_FMT_NV42:\n> +\t\treturn 1;\n> +\tcase V4L2_PIX_FMT_BGR24:\n> +\tcase V4L2_PIX_FMT_RGB24:\n> +\t\treturn 3;\n> +\tcase V4L2_PIX_FMT_ARGB32:\n> +\t\treturn 4;\n> +\tcase V4L2_PIX_FMT_VYUY:\n> +\tcase V4L2_PIX_FMT_YVYU:\n> +\tcase V4L2_PIX_FMT_UYVY:\n> +\tcase V4L2_PIX_FMT_YUYV:\n> +\t\treturn 2;\n> +\tdefault:\n> +\t\treturn 0;\n> +\t};\n> +}\n> +\n> +int V4L2CameraProxy::imageSize(unsigned int format,\n> +\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 * 3 / 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 V4L2CameraProxy::v4l2ToDrm(unsigned int pixelformat)\n> +{\n> +\tswitch (pixelformat) {\n> +\t/* RGB formats. */\n> +\tcase V4L2_PIX_FMT_RGB24:\n> +\t\treturn DRM_FORMAT_BGR888;\n> +\tcase V4L2_PIX_FMT_BGR24:\n> +\t\treturn DRM_FORMAT_RGB888;\n> +\tcase V4L2_PIX_FMT_ARGB32:\n> +\t\treturn DRM_FORMAT_BGRA8888;\n> +\n> +\t/* YUV packed formats. */\n> +\tcase V4L2_PIX_FMT_YUYV:\n> +\t\treturn DRM_FORMAT_YUYV;\n> +\tcase V4L2_PIX_FMT_YVYU:\n> +\t\treturn DRM_FORMAT_YVYU;\n> +\tcase V4L2_PIX_FMT_UYVY:\n> +\t\treturn DRM_FORMAT_UYVY;\n> +\tcase V4L2_PIX_FMT_VYUY:\n> +\t\treturn DRM_FORMAT_VYUY;\n> +\n> +\t/* YUY planar formats. */\n> +\tcase V4L2_PIX_FMT_NV16:\n> +\t\treturn DRM_FORMAT_NV16;\n> +\tcase V4L2_PIX_FMT_NV61:\n> +\t\treturn DRM_FORMAT_NV61;\n> +\tcase V4L2_PIX_FMT_NV12:\n> +\t\treturn DRM_FORMAT_NV12;\n> +\tcase V4L2_PIX_FMT_NV21:\n> +\t\treturn DRM_FORMAT_NV21;\n> +\tcase V4L2_PIX_FMT_NV24:\n> +\t\treturn DRM_FORMAT_NV24;\n> +\tcase V4L2_PIX_FMT_NV42:\n> +\t\treturn DRM_FORMAT_NV42;\n> +\tdefault:\n> +\t\treturn pixelformat;\n> +\t};\n> +}\n> +\n> +unsigned int V4L2CameraProxy::drmToV4L2(unsigned int pixelformat)\n> +{\n> +\tswitch (pixelformat) {\n> +\t/* RGB formats. */\n> +\tcase DRM_FORMAT_BGR888:\n> +\t\treturn V4L2_PIX_FMT_RGB24;\n> +\tcase DRM_FORMAT_RGB888:\n> +\t\treturn V4L2_PIX_FMT_BGR24;\n> +\tcase DRM_FORMAT_BGRA8888:\n> +\t\treturn V4L2_PIX_FMT_ARGB32;\n> +\n> +\t/* YUV packed formats. */\n> +\tcase DRM_FORMAT_YUYV:\n> +\t\treturn V4L2_PIX_FMT_YUYV;\n> +\tcase DRM_FORMAT_YVYU:\n> +\t\treturn V4L2_PIX_FMT_YVYU;\n> +\tcase DRM_FORMAT_UYVY:\n> +\t\treturn V4L2_PIX_FMT_UYVY;\n> +\tcase DRM_FORMAT_VYUY:\n> +\t\treturn V4L2_PIX_FMT_VYUY;\n> +\n> +\t/* YUY planar formats. */\n> +\tcase DRM_FORMAT_NV16:\n> +\t\treturn V4L2_PIX_FMT_NV16;\n> +\tcase DRM_FORMAT_NV61:\n> +\t\treturn V4L2_PIX_FMT_NV61;\n> +\tcase DRM_FORMAT_NV12:\n> +\t\treturn V4L2_PIX_FMT_NV12;\n> +\tcase DRM_FORMAT_NV21:\n> +\t\treturn V4L2_PIX_FMT_NV21;\n> +\tcase DRM_FORMAT_NV24:\n> +\t\treturn V4L2_PIX_FMT_NV24;\n> +\tcase DRM_FORMAT_NV42:\n> +\t\treturn V4L2_PIX_FMT_NV42;\n> +\tdefault:\n> +\t\treturn pixelformat;\n> +\t}\n> +}\n> diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h\n> new file mode 100644\n> index 00000000..bef0f0af\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_camera_proxy.h\n> @@ -0,0 +1,81 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_camera_proxy.h - Proxy to V4L2 compatibility camera\n> + */\n> +\n> +#ifndef __V4L2_CAMERA_PROXY_H__\n> +#define __V4L2_CAMERA_PROXY_H__\n> +\n> +#include <linux/videodev2.h>\n> +#include <map>\n> +#include <memory>\n> +#include <sys/types.h>\n> +#include <vector>\n> +\n> +#include <libcamera/camera.h>\n> +\n> +#include \"v4l2_camera.h\"\n> +\n> +using namespace libcamera;\n> +\n> +class V4L2CameraProxy\n> +{\n> +public:\n> +\tV4L2CameraProxy(unsigned int index, std::shared_ptr<Camera> camera);\n> +\n> +\tint open(bool nonBlocking);\n> +\tvoid dup();\n> +\tvoid close();\n> +\tvoid *mmap(size_t length, int prot, int flags, 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 validateBufferType(uint32_t type);\n> +\tbool validateMemoryType(uint32_t memory);\n> +\tvoid setFmtFromConfig(StreamConfiguration &streamConfig);\n> +\tunsigned int calculateSizeImage(StreamConfiguration &streamConfig);\n> +\tvoid querycap(std::shared_ptr<Camera> camera);\n> +\tvoid updateBuffers();\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> +\n> +\tstatic int bplMultiplier(unsigned int format);\n> +\tstatic int imageSize(unsigned int format, unsigned int width,\n> +\t\t\t     unsigned int height);\n> +\n> +\tstatic unsigned int v4l2ToDrm(unsigned int pixelformat);\n> +\tstatic unsigned int drmToV4L2(unsigned int pixelformat);\n> +\n> +\tunsigned int refcount_;\n> +\tunsigned int index_;\n> +\tbool nonBlocking_;\n> +\n> +\tstruct v4l2_format curV4L2Format_;\n> +\tStreamConfiguration streamConfig_;\n> +\tstruct v4l2_capability capabilities_;\n> +\tunsigned int bufferCount_;\n> +\tunsigned int currentBuf_;\n> +\tunsigned int sizeimage_;\n> +\n> +\tstd::vector<struct v4l2_buffer> buffers_;\n> +\tstd::map<void *, unsigned int> mmaps_;\n> +\n> +\tstd::unique_ptr<V4L2Camera> vcam_;\n> +};\n> +\n> +#endif /* __V4L2_CAMERA_PROXY_H__ */\n> diff --git a/src/v4l2/v4l2_compat.cpp b/src/v4l2/v4l2_compat.cpp\n> new file mode 100644\n> index 00000000..171ebed6\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_compat.cpp\n> @@ -0,0 +1,85 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_compat.cpp - V4L2 compatibility layer\n> + */\n> +\n> +#include \"v4l2_compat_manager.h\"\n> +\n> +#include <errno.h>\n> +#include <fcntl.h>\n> +#include <stdarg.h>\n> +#include <sys/mman.h>\n> +#include <sys/stat.h>\n> +#include <sys/types.h>\n> +\n> +#define LIBCAMERA_PUBLIC __attribute__((visibility(\"default\")))\n> +\n> +using namespace libcamera;\n> +\n> +#define extract_va_arg(type, arg, last)\t\\\n> +{\t\t\t\t\t\\\n> +\tva_list ap;\t\t\t\\\n> +\tva_start(ap, last);\t\t\\\n> +\targ = va_arg(ap, type);\t\t\\\n> +\tva_end(ap);\t\t\t\\\n> +}\n> +\n> +extern \"C\" {\n> +\n> +LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...)\n> +{\n> +\tmode_t mode = 0;\n> +\tif (oflag & O_CREAT || oflag & O_TMPFILE)\n> +\t\textract_va_arg(mode_t, mode, oflag);\n> +\n> +\treturn V4L2CompatManager::instance()->openat(AT_FDCWD, path,\n> +\t\t\t\t\t\t     oflag, mode);\n> +}\n> +\n> +/* _FORTIFY_SOURCE redirects open to __open_2 */\n> +LIBCAMERA_PUBLIC extern __typeof(open) __open_2 __attribute__ ((alias(\"open\")));\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 extern __typeof(openat) __openat_2 __attribute__ ((alias(\"openat\")));\n> +\n> +LIBCAMERA_PUBLIC int dup(int oldfd)\n> +{\n> +\treturn V4L2CompatManager::instance()->dup(oldfd);\n> +}\n> +\n> +LIBCAMERA_PUBLIC int close(int fd)\n> +{\n> +\treturn V4L2CompatManager::instance()->close(fd);\n> +}\n> +\n> +LIBCAMERA_PUBLIC void *mmap(void *addr, size_t length, int prot, int flags,\n> +\t\t\t    int fd, off_t offset)\n> +{\n> +\treturn V4L2CompatManager::instance()->mmap(addr, length, prot, flags,\n> +\t\t\t\t\t\t   fd, offset);\n> +}\n> +\n> +LIBCAMERA_PUBLIC int munmap(void *addr, size_t length)\n> +{\n> +\treturn V4L2CompatManager::instance()->munmap(addr, length);\n> +}\n> +\n> +LIBCAMERA_PUBLIC int ioctl(int fd, unsigned long request, ...)\n> +{\n> +\tvoid *arg;\n> +\textract_va_arg(void *, arg, request);\n> +\n> +\treturn V4L2CompatManager::instance()->ioctl(fd, request, arg);\n> +}\n> +\n> +}\n> diff --git a/src/v4l2/v4l2_compat_manager.cpp b/src/v4l2/v4l2_compat_manager.cpp\n> new file mode 100644\n> index 00000000..a3d69374\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_compat_manager.cpp\n> @@ -0,0 +1,250 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_compat_manager.cpp - V4L2 compatibility manager\n> + */\n> +\n> +#include \"v4l2_compat_manager.h\"\n> +\n> +#include <dlfcn.h>\n> +#include <fcntl.h>\n> +#include <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> +\n> +#include \"log.h\"\n> +\n> +using namespace libcamera;\n> +\n> +LOG_DEFINE_CATEGORY(V4L2Compat)\n> +\n> +V4L2CompatManager::V4L2CompatManager()\n> +\t: cm_(nullptr), initialized_(false)\n> +{\n> +\topenat_func_ = (openat_func_t)dlsym(RTLD_NEXT, \"openat\");\n> +\tdup_func_    = (dup_func_t   )dlsym(RTLD_NEXT, \"dup\");\n> +\tclose_func_  = (close_func_t )dlsym(RTLD_NEXT, \"close\");\n> +\tioctl_func_  = (ioctl_func_t )dlsym(RTLD_NEXT, \"ioctl\");\n> +\tmmap_func_   = (mmap_func_t  )dlsym(RTLD_NEXT, \"mmap\");\n> +\tmunmap_func_ = (munmap_func_t)dlsym(RTLD_NEXT, \"munmap\");\n> +}\n> +\n> +V4L2CompatManager::~V4L2CompatManager()\n> +{\n> +\tdevices_.clear();\n> +\tmmaps_.clear();\n> +\n> +\tif (isRunning()) {\n> +\t\texit(0);\n> +\t\t/* \\todo Wait with a timeout, just in case. */\n> +\t\twait();\n> +\t}\n> +}\n> +\n> +int V4L2CompatManager::init()\n> +{\n> +\tstart();\n> +\n> +\tMutexLocker locker(mutex_);\n> +\tcv_.wait(locker, [&] { return initialized_; });\n> +\n> +\treturn 0;\n> +}\n> +\n> +void V4L2CompatManager::run()\n> +{\n> +\tcm_ = new CameraManager();\n> +\n> +\tint ret = cm_->start();\n> +\tif (ret) {\n> +\t\tLOG(V4L2Compat, Error) << \"Failed to start camera manager: \"\n> +\t\t\t\t       << strerror(-ret);\n> +\t\treturn;\n> +\t}\n> +\n> +\tLOG(V4L2Compat, Debug) << \"Started camera manager\";\n> +\n> +\t/*\n> +\t * For each Camera registered in the system, a V4L2CameraProxy gets\n> +\t * created here to wrap a camera device.\n> +\t */\n> +\tunsigned int index = 0;\n> +\tfor (auto &camera : cm_->cameras()) {\n> +\t\tV4L2CameraProxy *proxy = new V4L2CameraProxy(index, camera);\n> +\t\tproxies_.emplace_back(proxy);\n> +\t\t++index;\n> +\t}\n> +\n> +\t/*\n> +\t * libcamera has been initialized. Unlock the init() caller as we're\n> +\t * now ready to handle calls from the framework.\n> +\t */\n> +\tmutex_.lock();\n> +\tinitialized_ = true;\n> +\tmutex_.unlock();\n> +\tcv_.notify_one();\n> +\n> +\t/* Now start processing events and messages. */\n> +\texec();\n> +\n> +\tproxies_.clear();\n> +\tcm_->stop();\n> +\tdelete cm_;\n> +\tcm_ = nullptr;\n> +}\n> +\n> +V4L2CompatManager *V4L2CompatManager::instance()\n> +{\n> +\tstatic V4L2CompatManager instance;\n> +\treturn &instance;\n> +}\n> +\n> +V4L2CameraProxy *V4L2CompatManager::getProxy(int fd)\n> +{\n> +\tauto device = devices_.find(fd);\n> +\tif (device == devices_.end())\n> +\t\treturn nullptr;\n> +\n> +\treturn device->second;\n> +}\n> +\n> +int V4L2CompatManager::getCameraIndex(int fd)\n> +{\n> +\tstruct stat statbuf;\n> +\tint ret = fstat(fd, &statbuf);\n> +\tif (ret < 0)\n> +\t\treturn -1;\n> +\n> +\tstd::shared_ptr<Camera> target = cm_->get(statbuf.st_rdev);\n> +\tif (!target)\n> +\t\treturn -1;\n> +\n> +\tunsigned int index = 0;\n> +\tfor (auto &camera : cm_->cameras()) {\n> +\t\tif (camera == target)\n> +\t\t\treturn index;\n> +\t\t++index;\n> +\t}\n> +\n> +\treturn -1;\n> +}\n> +\n> +int V4L2CompatManager::openat(int dirfd, const char *path, int oflag, mode_t mode)\n> +{\n> +\tint fd = openat_func_(dirfd, path, oflag, mode);\n> +\tif (fd < 0)\n> +\t\treturn fd;\n> +\n> +\tstruct stat statbuf;\n> +\tint ret = fstat(fd, &statbuf);\n> +\tif (ret < 0 || (statbuf.st_mode & S_IFMT) != S_IFCHR ||\n> +\t    major(statbuf.st_rdev) != 81)\n> +\t\treturn fd;\n> +\n> +\tif (!isRunning())\n> +\t\tinit();\n> +\n> +\tret = getCameraIndex(fd);\n> +\tif (ret < 0) {\n> +\t\tLOG(V4L2Compat, Info) << \"No camera found for \" << path;\n> +\t\treturn fd;\n> +\t}\n> +\n> +\tclose_func_(fd);\n> +\n> +\tunsigned int camera_index = static_cast<unsigned int>(ret);\n> +\n> +\tV4L2CameraProxy *proxy = proxies_[camera_index].get();\n> +\tret = proxy->open(oflag & O_NONBLOCK);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tint efd = eventfd(0, oflag & (O_CLOEXEC | O_NONBLOCK));\n> +\tif (efd < 0) {\n> +\t\tproxy->close();\n> +\t\treturn efd;\n> +\t}\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 (newfd < 0)\n> +\t\treturn newfd;\n> +\n> +\tauto device = devices_.find(oldfd);\n> +\tif (device != devices_.end()) {\n> +\t\tV4L2CameraProxy *proxy = device->second;\n> +\t\tdevices_[newfd] = proxy;\n> +\t\tproxy->dup();\n> +\t}\n> +\n> +\treturn newfd;\n> +}\n> +\n> +int V4L2CompatManager::close(int fd)\n> +{\n> +\tV4L2CameraProxy *proxy = getProxy(fd);\n> +\tif (proxy) {\n> +\t\tproxy->close();\n> +\t\tdevices_.erase(fd);\n> +\t\treturn 0;\n> +\t}\n> +\n> +\treturn close_func_(fd);\n> +}\n> +\n> +void *V4L2CompatManager::mmap(void *addr, size_t length, int prot, int flags,\n> +\t\t\t      int fd, off_t offset)\n> +{\n> +\tV4L2CameraProxy *proxy = getProxy(fd);\n> +\tif (!proxy)\n> +\t\treturn mmap_func_(addr, length, prot, flags, fd, offset);\n> +\n> +\tvoid *map = proxy->mmap(length, prot, flags, offset);\n> +\tif (map == MAP_FAILED)\n> +\t\treturn map;\n> +\n> +\tmmaps_[map] = proxy;\n> +\treturn map;\n> +}\n> +\n> +int V4L2CompatManager::munmap(void *addr, size_t length)\n> +{\n> +\tauto device = mmaps_.find(addr);\n> +\tif (device == mmaps_.end())\n> +\t\treturn munmap_func_(addr, length);\n> +\n> +\tV4L2CameraProxy *proxy = device->second;\n> +\n> +\tint ret = proxy->munmap(addr, length);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tmmaps_.erase(device);\n> +\n> +\treturn 0;\n> +}\n> +\n> +int V4L2CompatManager::ioctl(int fd, unsigned long request, void *arg)\n> +{\n> +\tV4L2CameraProxy *proxy = getProxy(fd);\n> +\tif (!proxy)\n> +\t\treturn ioctl_func_(fd, request, arg);\n> +\n> +\treturn proxy->ioctl(request, arg);\n> +}\n> diff --git a/src/v4l2/v4l2_compat_manager.h b/src/v4l2/v4l2_compat_manager.h\n> new file mode 100644\n> index 00000000..dd9d3210\n> --- /dev/null\n> +++ b/src/v4l2/v4l2_compat_manager.h\n> @@ -0,0 +1,77 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * v4l2_compat_manager.h - V4L2 compatibility manager\n> + */\n> +\n> +#ifndef __V4L2_COMPAT_MANAGER_H__\n> +#define __V4L2_COMPAT_MANAGER_H__\n> +\n> +#include <condition_variable>\n> +#include <fcntl.h>\n> +#include <map>\n> +#include <memory>\n> +#include <mutex>\n> +#include <sys/types.h>\n> +#include <vector>\n> +\n> +#include <libcamera/camera_manager.h>\n> +\n> +#include \"thread.h\"\n> +#include \"v4l2_camera_proxy.h\"\n> +\n> +using namespace libcamera;\n> +\n> +class V4L2CompatManager : public Thread\n> +{\n> +public:\n> +\tstatic V4L2CompatManager *instance();\n> +\n> +\tint init();\n> +\n> +\tV4L2CameraProxy *getProxy(int fd);\n> +\n> +\tint openat(int dirfd, const char *path, int oflag, mode_t mode);\n> +\n> +\tint dup(int oldfd);\n> +\tint close(int fd);\n> +\tvoid *mmap(void *addr, size_t length, int prot, int flags,\n> +\t\t   int fd, off_t offset);\n> +\tint munmap(void *addr, size_t length);\n> +\tint ioctl(int fd, unsigned long request, void *arg);\n> +\n> +private:\n> +\tV4L2CompatManager();\n> +\t~V4L2CompatManager();\n> +\n> +\tvoid run() override;\n> +\tint getCameraIndex(int fd);\n> +\n> +\ttypedef int (*openat_func_t)(int dirfd, const char *path, int oflag, ...);\n> +\ttypedef int (*dup_func_t)(int oldfd);\n> +\ttypedef int (*close_func_t)(int fd);\n> +\ttypedef int (*ioctl_func_t)(int fd, unsigned long request, ...);\n> +\ttypedef void *(*mmap_func_t)(void *addr, size_t length, int prot,\n> +\t\t\t\t     int flags, int fd, off_t offset);\n> +\ttypedef int (*munmap_func_t)(void *addr, size_t length);\n> +\n> +\topenat_func_t openat_func_;\n> +\tdup_func_t    dup_func_;\n> +\tclose_func_t  close_func_;\n> +\tioctl_func_t  ioctl_func_;\n> +\tmmap_func_t   mmap_func_;\n> +\tmunmap_func_t munmap_func_;\n> +\n> +\tCameraManager *cm_;\n> +\n> +\tstd::mutex mutex_;\n> +\tstd::condition_variable cv_;\n> +\tbool initialized_;\n> +\n> +\tstd::vector<std::unique_ptr<V4L2CameraProxy>> proxies_;\n> +\tstd::map<int, V4L2CameraProxy *> devices_;\n> +\tstd::map<void *, V4L2CameraProxy *> mmaps_;\n> +};\n> +\n> +#endif /* __V4L2_COMPAT_MANAGER_H__ */","headers":{"Return-Path":"<laurent.pinchart@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 DAFDA605F8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat,  4 Jan 2020 01:35:24 +0100 (CET)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 21DF730F;\n\tSat,  4 Jan 2020 01:35:24 +0100 (CET)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1578098124;\n\tbh=xVITtkqeP98w2YZ7hqXQUzwF7cDqneN+61IItA9LMyI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=wJRGk8X1BZ/fzMwXiqiP0OgmLULZcuqslp4bPmsJRaKw4VjE+q7gVj2HYWPKH1p8J\n\ttZQGvMg8yPhNBUDZ4EAt8UbSKGUeZfIykOvc+nfJRJBKnAU8SaFeygOU16UbY53GlD\n\tz+D5zOMSV71xmXTk5cxrUhFBsLwBhpAagmCn/IB4=","Date":"Sat, 4 Jan 2020 02:35:14 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org, Jacopo Mondi <jacopo@jmondi.org>","Message-ID":"<20200104003514.GI4847@pendragon.ideasonboard.com>","References":"<20200104001414.12755-1-paul.elder@ideasonboard.com>\n\t<20200104001414.12755-6-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20200104001414.12755-6-paul.elder@ideasonboard.com>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH v6 5/5] 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":"Sat, 04 Jan 2020 00:35:25 -0000"}}]