From patchwork Wed Aug 20 13:23:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 24166 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 CFA32BD87C for ; Wed, 20 Aug 2025 13:23:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 5D0C6692E5; Wed, 20 Aug 2025 15:23:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="sCBZBU9m"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 048BF6142C for ; Wed, 20 Aug 2025 15:23:37 +0200 (CEST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3FA24B0B; Wed, 20 Aug 2025 15:22:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1755696158; bh=QsJiDoh1KBHgRuOEwcExXG5glWOhBMzyXXI5IhXstns=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sCBZBU9mF5Rvx+8vbO10mWj8rIghQ/lI9zH/oM11XrpCDabMnmNlT0QZ6SIMwksMO BHhpl9YwXpAvQ6AXnWLrsvK4Z9nL+FRp5gXGejUuqnqsFVkGQ1qKdg4WYHVRG6Ibj5 0bzYHWwFWkvT/7A0Gb/ZkH2yE/gmNuYB0h0IvrHs= From: Daniel Scally To: libcamera-devel@lists.libcamera.org Cc: dan.scally@ideasonboard.com Subject: [PATCH 1/3] libcamera: media_device: Expand return values for populateEntities() Date: Wed, 20 Aug 2025 14:23:14 +0100 Message-ID: <20250820132316.1033443-2-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250820132316.1033443-1-dan.scally@ideasonboard.com> References: <20250820132316.1033443-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 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" At the moment populateEntities() just returns true or false to indicate success or failure. Although the populate() function assumes that the topology is settled once the version number stops incrementing, that's not guaranteed to be the case. One indicator that it's likely to increase again in the future is the absence of interfaces for an entity, which would only be created for V4L2 subdevices once the v4l2-async framework had run to completion. Update populateEntities() to return -EAGAIN in the event an entity has no associated interface yet and pass that error out from populate to indicate to callers that they should re-try the population at a later time. Signed-off-by: Daniel Scally --- include/libcamera/internal/media_device.h | 2 +- src/libcamera/media_device.cpp | 27 ++++++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/libcamera/internal/media_device.h b/include/libcamera/internal/media_device.h index b3a48b98d..d3c5eaa02 100644 --- a/include/libcamera/internal/media_device.h +++ b/include/libcamera/internal/media_device.h @@ -70,7 +70,7 @@ private: struct media_v2_interface *findInterface(const struct media_v2_topology &topology, unsigned int entityId); - bool populateEntities(const struct media_v2_topology &topology); + int populateEntities(const struct media_v2_topology &topology); bool populatePads(const struct media_v2_topology &topology); bool populateLinks(const struct media_v2_topology &topology); void fixupEntityFlags(struct media_v2_entity *entity); diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp index 353f34a81..6d58f09eb 100644 --- a/src/libcamera/media_device.cpp +++ b/src/libcamera/media_device.cpp @@ -257,12 +257,13 @@ int MediaDevice::populate() } /* Populate entities, pads and links. */ - if (populateEntities(topology) && - populatePads(topology) && - populateLinks(topology)) - valid_ = true; + if (!(ret = populateEntities(topology))) { + if (populatePads(topology) && populateLinks(topology)) + valid_ = true; + else + ret = -EINVAL; + } - ret = 0; done: close(); @@ -271,10 +272,8 @@ done: delete[] pads; delete[] links; - if (!valid_) { + if (!valid_) clear(); - return -EINVAL; - } return ret; } @@ -618,7 +617,7 @@ struct media_v2_interface *MediaDevice::findInterface(const struct media_v2_topo * For each entity in the media graph create a MediaEntity and store a * reference in the media device objects map and entities list. */ -bool MediaDevice::populateEntities(const struct media_v2_topology &topology) +int MediaDevice::populateEntities(const struct media_v2_topology &topology) { struct media_v2_entity *mediaEntities = reinterpret_cast (topology.ptr_entities); @@ -639,17 +638,23 @@ bool MediaDevice::populateEntities(const struct media_v2_topology &topology) */ struct media_v2_interface *iface = findInterface(topology, ent->id); + if (!iface) { + LOG(MediaDevice, Debug) + << "Entity " << ent->name << " has no interface yet"; + return -EAGAIN; + } + MediaEntity *entity = new MediaEntity(this, ent, iface); if (!addObject(entity)) { delete entity; - return false; + return -EINVAL; } entities_.push_back(entity); } - return true; + return 0; } bool MediaDevice::populatePads(const struct media_v2_topology &topology) From patchwork Wed Aug 20 13:23:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 24167 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 3371CC32BB for ; Wed, 20 Aug 2025 13:23:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 37A13692D9; Wed, 20 Aug 2025 15:23:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="EBk/ZxFY"; 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 3C1DF6924B for ; Wed, 20 Aug 2025 15:23:37 +0200 (CEST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7DDD31737; Wed, 20 Aug 2025 15:22:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1755696158; bh=m2QHDUqBxfNJb0lR2NwG9UP5ZK+3RlPaAs2vQu5hcoE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EBk/ZxFYdjP2h5qQ91kUUpmrl6vasLNsV08rcSmweI7rptfiPQc6hcLWp1JGAMIdy q0gO3uhGAZjguLusJJ7w4YBb9I6BgpBnnB8VNmn+X+eV/h8saCF9R2vTndwLV9QIXc djvcV+rgUKhzzFmJlmfDg3QMoVNCBZkvMqj2i/2E= From: Daniel Scally To: libcamera-devel@lists.libcamera.org Cc: dan.scally@ideasonboard.com Subject: [PATCH 2/3] libcamera: device_enumerator_udev: Add initMediaDevice() Date: Wed, 20 Aug 2025 14:23:15 +0100 Message-ID: <20250820132316.1033443-3-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250820132316.1033443-1-dan.scally@ideasonboard.com> References: <20250820132316.1033443-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 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" We will shortly be processing MediaDevices created by the device enumerator in two places if they're initially deferreed. Move the steps to a dedicated function so it can be called from multiple places. Signed-off-by: Daniel Scally --- .../internal/device_enumerator_udev.h | 1 + src/libcamera/device_enumerator_udev.cpp | 58 ++++++++++--------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/include/libcamera/internal/device_enumerator_udev.h b/include/libcamera/internal/device_enumerator_udev.h index 1378c1906..a5e5e6efe 100644 --- a/include/libcamera/internal/device_enumerator_udev.h +++ b/include/libcamera/internal/device_enumerator_udev.h @@ -56,6 +56,7 @@ private: int addUdevDevice(struct udev_device *dev); int populateMediaDevice(MediaDevice *media, DependencyMap *deps); + int initMediaDevice(std::unique_ptr media); std::string lookupDeviceNode(dev_t devnum); int addV4L2Device(dev_t devnum); diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp index 4e20a3cc0..53b4fac80 100644 --- a/src/libcamera/device_enumerator_udev.cpp +++ b/src/libcamera/device_enumerator_udev.cpp @@ -82,32 +82,7 @@ int DeviceEnumeratorUdev::addUdevDevice(struct udev_device *dev) if (!media) return -ENODEV; - DependencyMap deps; - int ret = populateMediaDevice(media.get(), &deps); - if (ret < 0) { - LOG(DeviceEnumerator, Warning) - << "Failed to populate media device " - << media->deviceNode() - << " (" << media->driver() << "), skipping"; - return ret; - } - - if (!deps.empty()) { - LOG(DeviceEnumerator, Debug) - << "Defer media device " << media->deviceNode() - << " due to " << deps.size() - << " missing dependencies"; - - pending_.emplace_back(std::move(media), std::move(deps)); - MediaDeviceDeps *mediaDeps = &pending_.back(); - for (const auto &dep : mediaDeps->deps_) - devMap_[dep.first] = mediaDeps; - - return 0; - } - - addDevice(std::move(media)); - return 0; + return initMediaDevice(std::move(media)); } if (!strcmp(subsystem, "video4linux")) { @@ -194,6 +169,37 @@ done: return 0; } +int DeviceEnumeratorUdev::initMediaDevice(std::unique_ptr media) +{ + DependencyMap deps; + + int ret = populateMediaDevice(media.get(), &deps); + if (ret < 0) { + LOG(DeviceEnumerator, Warning) + << "Failed to populate media device " + << media->deviceNode() + << " (" << media->driver() << "), skipping"; + return ret; + } + + if (!deps.empty()) { + LOG(DeviceEnumerator, Debug) + << "Defer media device " << media->deviceNode() + << " due to " << deps.size() + << " missing dependencies"; + + pending_.emplace_back(std::move(media), std::move(deps)); + MediaDeviceDeps *mediaDeps = &pending_.back(); + for (const auto &dep : mediaDeps->deps_) + devMap_[dep.first] = mediaDeps; + + return 0; + } + + addDevice(std::move(media)); + return 0; +} + int DeviceEnumeratorUdev::populateMediaDevice(MediaDevice *media, DependencyMap *deps) { std::set children; From patchwork Wed Aug 20 13:23:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Scally X-Patchwork-Id: 24168 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 DFBDEBD87C for ; Wed, 20 Aug 2025 13:23:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1B35C692DD; Wed, 20 Aug 2025 15:23:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aTrKZx6i"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5EEDD6925D for ; Wed, 20 Aug 2025 15:23:37 +0200 (CEST) Received: from mail.ideasonboard.com (cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BF45D6AF; Wed, 20 Aug 2025 15:22:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1755696158; bh=SfG9M9QxXbOfLKLfb9ULC3+cwJExQNZdq4HRuBotRFI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aTrKZx6iCy7WBAHZVkECWZbFblFYYsord2Po9iQ3v7DWqcgSaue28D6IyUOSnmEk9 duBXF2jXMe7+CPe//jtOe7LtPM8tlnEjjlcEvf8Oo3XxZ3651T/J/9vzIBLtTvdbr2 U+7C15Z1LQkp14NtuEGpA2JkyG6prrLgWM3zQfeA= From: Daniel Scally To: libcamera-devel@lists.libcamera.org Cc: dan.scally@ideasonboard.com Subject: [PATCH 3/3] libcamera: device_enumerator_udev: Defer invalid media devices Date: Wed, 20 Aug 2025 14:23:16 +0100 Message-ID: <20250820132316.1033443-4-dan.scally@ideasonboard.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250820132316.1033443-1-dan.scally@ideasonboard.com> References: <20250820132316.1033443-1-dan.scally@ideasonboard.com> MIME-Version: 1.0 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" A MediaDevice created by DeviceEnumeratorUdev may have an invalid topology if the device nodes for some of its entities have not yet been created. If that's the case, defer initialisation of the MediaDevice. Re-try all deferred media devices whenever a new device is added to see if they're ready to be initialised yet. Signed-off-by: Daniel Scally --- .../internal/device_enumerator_udev.h | 2 + src/libcamera/device_enumerator.cpp | 2 +- src/libcamera/device_enumerator_udev.cpp | 37 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/libcamera/internal/device_enumerator_udev.h b/include/libcamera/internal/device_enumerator_udev.h index a5e5e6efe..b76315c63 100644 --- a/include/libcamera/internal/device_enumerator_udev.h +++ b/include/libcamera/internal/device_enumerator_udev.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "libcamera/internal/device_enumerator.h" @@ -67,6 +68,7 @@ private: EventNotifier *notifier_; std::set orphans_; + std::vector> topologyPending_; std::list pending_; std::map devMap_; }; diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index ae17862f6..ab7bceec4 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -220,7 +220,7 @@ std::unique_ptr DeviceEnumerator::createDevice(const std::string &d std::unique_ptr media = std::make_unique(deviceNode); int ret = media->populate(); - if (ret < 0) { + if (ret < 0 && ret != -EAGAIN) { LOG(DeviceEnumerator, Info) << "Unable to populate media device " << deviceNode << " (" << strerror(-ret) << "), skipping"; diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp index 53b4fac80..631e583e7 100644 --- a/src/libcamera/device_enumerator_udev.cpp +++ b/src/libcamera/device_enumerator_udev.cpp @@ -82,6 +82,14 @@ int DeviceEnumeratorUdev::addUdevDevice(struct udev_device *dev) if (!media) return -ENODEV; + if (!media->isValid()) { + LOG(DeviceEnumerator, Debug) + << "Defer media device " << media->deviceNode() + << " due to an invalid topology"; + topologyPending_.emplace_back(std::move(media)); + return 0; + } + return initMediaDevice(std::move(media)); } @@ -353,6 +361,35 @@ void DeviceEnumeratorUdev::udevNotify() << action << " device " << deviceNode; if (action == "add") { + /* + * The addition of a new device may signal that a previously + * deferred media device has had its topology updated to the + * extent that it's now valid - retry devices deferred due to + * invalid topology to see if they're now ready. + */ + + LOG(DeviceEnumerator, Debug) + << "Re-evaluating " << topologyPending_.size() + << " deferred media devices"; + + for (auto media = topologyPending_.begin(); + media != topologyPending_.end(); ++media) { + LOG(DeviceEnumerator, Debug) + << "Evaluating media device " << (*media)->deviceNode() + << " again..."; + + int ret = (*media)->populate(); + if (ret == -EAGAIN) { + LOG(DeviceEnumerator, Debug) + << "Media device " << (*media)->deviceNode() + << " still has invalid topology"; + continue; + } + + initMediaDevice(std::move(*media)); + topologyPending_.erase(media); + } + addUdevDevice(dev); } else if (action == "remove") { const char *subsystem = udev_device_get_subsystem(dev);