From patchwork Fri Jul 25 10:33:52 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 23961 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 1738BC3323 for ; Fri, 25 Jul 2025 10:34:16 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7AAAE690BC; Fri, 25 Jul 2025 12:34:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="lVHvyY3U"; dkim-atps=neutral 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 8CC6F6909F for ; Fri, 25 Jul 2025 12:34:00 +0200 (CEST) Received: from [192.168.0.172] (mob-5-90-139-29.net.vodafone.it [5.90.139.29]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 52054C73; Fri, 25 Jul 2025 12:33:20 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1753439600; bh=h6fVQQRDCDzLgTYQD7LIce+Sr45tSyEOBhr9buH9vYA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=lVHvyY3UpPvD1g4BKix2rto9FLdcYD+vwIx06q0jiq6VcyCE2tu8UMxB3fIsdgMdn 1b9gXAhbD/LIiuCAyBGozWhjWOhM22YcfpYkn3bhe0T/7LMDjrIIoGsOOE/8mbd/i+ z5JYAUGUHtPq/upBGb7Y8tqbJlRkRKBqaLeMPNv8= From: Jacopo Mondi Date: Fri, 25 Jul 2025 12:33:52 +0200 Subject: [PATCH 7/9] libcamera: media-device: Introduce MediaContext MIME-Version: 1.0 Message-Id: <20250725-multicontext-v1-7-ea558291e101@ideasonboard.com> References: <20250725-multicontext-v1-0-ea558291e101@ideasonboard.com> In-Reply-To: <20250725-multicontext-v1-0-ea558291e101@ideasonboard.com> To: libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=8005; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=h6fVQQRDCDzLgTYQD7LIce+Sr45tSyEOBhr9buH9vYA=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBog12UvJmBPXTa4+aDCplmm6WscOZgcTD3zCvnh XEJJZ8XRBiJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCaINdlAAKCRByNAaPFqFW PES0D/wP9pW8NmeR9R1mTbxSwTOgZDgsmBG44X23brpOuRz3GKduV2AA+Yr29aPG2x7OmrKmGEL m7PkeqyJaMhvn8Wp/J8neJh9YrwPhZJ8540XBWRBma7KnsfSPOMLXOrQMfv2rOIWCPmyt1kwa26 vVuBUXtUarMfHx/Pe68SetXEDLF90bj+VoCdQ8qPq8/K5N/yyfm4tfMo7Fc8nuxTPOwwKJNwgpF qVmc9c4DXsYCZA4VjcmEH2XQkthD0GOdvGZnsbpxqU+rexKr4A0Xo4X7SN9w7NOJf0o9DoMQ40k nA29b2ccCkB/mFaGZ52OUWZtRfkt7m/BVtTAUHvhEfEgELFRZ6YdMJhTbP+ZgJiJbthCEX1bXxn TXqfrdcKLxfPbrEQZsMY/Go2AL7WJ2VB6lt65Mw4QcMRGtLEXt4JxIhwOce6R94pYmCrI54r8Gn uD3VEzTzZ4zXPtBH/SzExJylc7lexO/CYlkn/XfR93Bb2tUfUt+1mGzhLSmpBJ5Nvk7Ix+hxRxn r8gsII4gl4aXCyR7gDelSknoVmFkx67zR0p7QuLXcgq+XoF9ZH7nmvhmsl6BuNDTJOGVd+VNg+V 9hEl2dJOFWWXiuQqj9kH3rEc/sEooF/8uh9w8a0tbOItPCw63tm1MMvSRj256z8pNKbwZV0BIuL GG+AQKkssHCLKrg== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B 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: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Introduce the MediaContext class that represents a media device context to which video devices and subdevices can be bound to. A MediaContext can be created from a MediaDevice (which does not support multi-context) and from SharedMediaDevice (which supports multi-context). Binding devices to a MediaContext created from MediaDevice is a nop, while MediaContext created from SharedMediaDevice perform proper bindings. Signed-off-by: Jacopo Mondi Reviewed-by: Isaac Scott --- include/libcamera/internal/media_device.h | 23 ++++- src/libcamera/media_device.cpp | 135 ++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 1 deletion(-) diff --git a/include/libcamera/internal/media_device.h b/include/libcamera/internal/media_device.h index ea0e9d66009933bbb9613d27423d2f951520c887..2dbfcc447d84bb6a1eee4ed598d22b5cc45adbe9 100644 --- a/include/libcamera/internal/media_device.h +++ b/include/libcamera/internal/media_device.h @@ -21,6 +21,9 @@ namespace libcamera { +class V4L2VideoDevice; +class V4L2Subdevice; + class MediaDeviceFactory { public: @@ -28,6 +31,19 @@ public: createMediaDevice(const std::string &deviceNode); }; +class MediaContext +{ +public: + MediaContext(); + MediaContext(UniqueFD &&fd); + + int bindDevice(V4L2VideoDevice *dev); + int bindDevice(V4L2Subdevice *dev); + +private: + UniqueFD fd_; +}; + class MediaDevice : protected Loggable { public: @@ -63,10 +79,14 @@ public: std::vector locateEntities(unsigned int function); + virtual std::unique_ptr createContext(); + protected: MediaDevice(const std::string &deviceNode); std::string logPrefix() const override; + std::string deviceNode_; + private: friend class MediaDeviceFactory; @@ -88,7 +108,6 @@ private: int setupLink(const MediaLink *link, unsigned int flags); std::string driver_; - std::string deviceNode_; std::string model_; unsigned int version_; unsigned int hwRevision_; @@ -111,6 +130,8 @@ public: bool lock() override; void unlock() override; + std::unique_ptr createContext() override; + private: friend class MediaDeviceFactory; diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp index 70d073f964bde0236585de083baca4cd9c30d193..2b7df346ad9916cf22641ea770e0a1507921d89d 100644 --- a/src/libcamera/media_device.cpp +++ b/src/libcamera/media_device.cpp @@ -21,6 +21,9 @@ #include +#include "libcamera/internal/v4l2_subdevice.h" +#include "libcamera/internal/v4l2_videodevice.h" + /** * \file media_device.h * \brief Provide a representation of a Linux kernel Media Controller device @@ -81,6 +84,99 @@ MediaDeviceFactory::createMediaDevice(const std::string &deviceNode) return std::unique_ptr(new MediaDevice(deviceNode)); } +/** + * \class MediaContext + * \brief Represents a media device context + * + * A media device context is obtained from a MediaDevice or a SharedMediaDevice + * instance with the MediaDevice::createContext() and + * SharedMediaDevice::createContext() functions. + * + * MediaContext instances obtained from a MediaDevice that does not support + * multi-context operations will not have a valid file descriptor associated as + * the media device they are created from doesn't support multiple open. For + * this reason binding a device to a context that is obtained from a MediaDevice + * is a nop. + * + * MediaContext instances obtained from a SharedMediaDevice that supports + * multi-context operations owns the file descriptor that identifies the media + * context, obtained by opening the media device multiple times and provided to + * the context at creation time. Binding devices to a context obtained from a + * SharedMediaDevice associates the devices to an execution context which + * remains valid until the last bound device stays valid. + * + * Users of this class can bind devices on valid and invalid MultiContext + * instances alike. If the kernel driver supports multi-context operations + * then the device can be opened multiple times and multiple contexts can be + * created to which devices can be bound to. If the kernel driver doesn't + * support multi-context operations then it can be opened a single time only + * and a creating contexts and bindings devices to it is effectively a nop. + */ + +/** + * \brief Constructs a MediaContext not associated to any media device context + * + * Constructs a media device context not associated to any valid media device + * context file descriptor. Binding devices to such media device context + * is effectively a nop. + */ +MediaContext::MediaContext() +{ +} + +/** + * \brief Construct a MediaContext associated to a valid media device context + * \param[in] fd The file descriptor that identifies a media device context + * + * Construct a media device context to which devices can be bound to. + * + * Constructing a MediaContext with an associated valid media device context + * allows to create isolated execution contexts by binding devices to it. + * + * The file descriptor \a fd obtained by opening the media device and that + * identifies the media device context is moved in the newly constructed + * MediaContext which maintains its ownership and automatically closes it at + * destruction time. + */ +MediaContext::MediaContext(UniqueFD &&fd) + : fd_(std::move(fd)) +{ +} + +/** + * \brief Bind a V4L2 video device to a media device context + * \param[in] dev The video device to bind + * + * If the MediaContext has been created by a MediaDevice that does not + * support multi-context operations, this function is effectively a nop. + * + * \return 0 on success or a negative error code otherwise + */ +int MediaContext::bindDevice(V4L2VideoDevice *dev) +{ + if (fd_.get() == -1) + return 0; + + return dev->bindContext(fd_.get()); +} + +/** + * \brief Bind a V4L2 subdevice to a media device context + * \param[in] dev The subdevice to bind + * + * If the MediaContext has been created by a MediaDevice that does not + * support multi-context operations, this function is effectively a nop. + * + * \return 0 on success or a negative error code otherwise + */ +int MediaContext::bindDevice(V4L2Subdevice *dev) +{ + if (fd_.get() == -1) + return 0; + + return dev->bindContext(fd_.get()); +} + /** * \class MediaDevice * \brief The MediaDevice represents a Media Controller device with its full @@ -603,6 +699,45 @@ int MediaDevice::disableLinks() * driver unloading for most devices. The media device is passed as a parameter. */ +/** + * \brief Create a MediaContext not associated with a media device context + * \return A MediaContext to which bindings devices is a nop + */ +std::unique_ptr MediaDevice::createContext() +{ + return std::make_unique(); +} + +/** + * \brief Create a MediaContext associated with a media device context + * + * Create a MediaContext by opening the media device and create a media + * device context using the obtained file descriptor. + * + * The open file descriptor is moved to the MediaContext whose ownership is + * moved to the caller of this function. + * + * \return A MediaContext to which is possible to bind devices to + */ +std::unique_ptr SharedMediaDevice::createContext() +{ + auto fd = UniqueFD(::open(deviceNode_.c_str(), O_RDWR | O_CLOEXEC)); + if (!fd.isValid()) { + int ret = -errno; + LOG(MediaDevice, Error) + << "Failed to open media device at " + << deviceNode_ << ": " << strerror(-ret); + return {}; + } + + return std::make_unique(std::move(fd)); +} + +/** + * \var MediaDevice::deviceNode_ + * \brief The media device device node path + */ + /** * \brief Open the media device *