From patchwork Thu Sep 12 20:03:30 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 1963 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 54E7E6195F for ; Thu, 12 Sep 2019 22:03:47 +0200 (CEST) Received: from pendragon.lan (bl10-204-24.dsl.telepac.pt [85.243.204.24]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 9DA8133A; Thu, 12 Sep 2019 22:03:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1568318627; bh=4rTg6U9ANtXakvxxTZDu6ww9bU4KooB3Mp1nTy3auCA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tj2fEhYPnTl/lJ9y/SSZ62ozzCW4FxxMX9kf+dRkhRuFXG8vN8lk6Ns5L/ScXPRHW f5kxDlkIrG7V9Vcea+odQDgDR4iSqzX3Pq7tIa622KgPcS7LcGVnJ8yzPOXsJWigap oqjj166zHxZMZzXzGNR8AnXc9VVw4z6HGJe6cezc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Thu, 12 Sep 2019 23:03:30 +0300 Message-Id: <20190912200330.19004-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190912200330.19004-1-laurent.pinchart@ideasonboard.com> References: <20190912200330.19004-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/4] libcamera: device_enumerator_udev: Support entities sharing device nodes X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Sep 2019 20:03:47 -0000 Some media devices, such as V4L2 M2M devices, share the same device node for multiple entities. The udev enumerator used to support this, but commit 6e620349009d ("libcamera: device_enumerator: fix udev media graph loading dependency") broke this. To fix the problem, rework the media device to V4L2 devices matching code. A new MediaDeviceDeps internal struct stores unmet device number dependencies for a media device, and is stored in a list of pending media devices. To avoid linear lookups, the dependencies are cached in a reverse map of device number to media device dependencies. Fixes: 6e620349009d ("libcamera: device_enumerator: fix udev media graph loading dependency") Signed-off-by: Laurent Pinchart Tested-by: Kieran Bingham Reviewed-by: Kieran Bingham --- src/libcamera/device_enumerator_udev.cpp | 100 ++++++++++++------ .../include/device_enumerator_udev.h | 25 ++++- 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp index c40770911d3d..ddcd59ea52c1 100644 --- a/src/libcamera/device_enumerator_udev.cpp +++ b/src/libcamera/device_enumerator_udev.cpp @@ -170,8 +170,8 @@ done: int DeviceEnumeratorUdev::populateMediaDevice(const std::shared_ptr &media) { - unsigned int pendingNodes = 0; - int ret; + std::set children; + DependencyMap deps; /* Associate entities to device node paths. */ for (MediaEntity *entity : media->entities()) { @@ -181,28 +181,50 @@ int DeviceEnumeratorUdev::populateMediaDevice(const std::shared_ptr dev_t devnum = makedev(entity->deviceMajor(), entity->deviceMinor()); - /* Take device from orphan list first, if it is in the list. */ - auto orphan = std::find(orphans_.begin(), orphans_.end(), devnum); - if (orphan != orphans_.end()) { - std::string deviceNode = lookupDeviceNode(devnum); - if (deviceNode.empty()) - return -EINVAL; - - ret = entity->setDeviceNode(deviceNode); - if (ret) - return ret; - - orphans_.erase(orphan); + /* + * If the devnum isn't in the orphans list, add it to the unmet + * dependencies. + */ + if (orphans_.find(devnum) == orphans_.end()) { + deps[devnum].push_back(entity); continue; } - deps_[media].push_back(devnum); - devnumToDevice_[devnum] = media; - devnumToEntity_[devnum] = entity; - pendingNodes++; + /* + * Otherwise take it from the orphans list. Don't remove the + * entry from the list yet as other entities in this media + * device may need the same device. + */ + std::string deviceNode = lookupDeviceNode(devnum); + if (deviceNode.empty()) + return -EINVAL; + + int ret = entity->setDeviceNode(deviceNode); + if (ret) + return ret; + + children.insert(devnum); + } + + /* Remove all found children from the orphans list. */ + for (auto it = orphans_.begin(), last = orphans_.end(); it != last;) { + if (children.find(*it) != children.end()) + it = orphans_.erase(it); + else + ++it; + } + + /* + * If the media device has unmet dependencies, add it to the pending + * list and update the devnum map accordingly. + */ + if (!deps.empty()) { + pending_.emplace_back(media, deps); + for (const auto &dep : deps) + devMap_[dep.first] = &pending_.back(); } - return pendingNodes; + return deps.size(); } /** @@ -247,28 +269,42 @@ std::string DeviceEnumeratorUdev::lookupDeviceNode(dev_t devnum) */ int DeviceEnumeratorUdev::addV4L2Device(dev_t devnum) { - MediaEntity *entity = devnumToEntity_[devnum]; - if (!entity) { - orphans_.push_back(devnum); + /* + * If the devnum doesn't belong to any media device, add it to the + * orphans list. + */ + auto it = devMap_.find(devnum); + if (it == devMap_.end()) { + orphans_.insert(devnum); return 0; } + /* + * Set the device node for all entities matching the devnum. Multiple + * entities can share the same device node, for instance for V4L2 M2M + * devices. + */ std::string deviceNode = lookupDeviceNode(devnum); if (deviceNode.empty()) return -EINVAL; - int ret = entity->setDeviceNode(deviceNode); - if (ret) - return ret; + MediaDeviceDeps *deps = it->second; + for (MediaEntity *entity : deps->deps_[devnum]) { + int ret = entity->setDeviceNode(deviceNode); + if (ret) + return ret; + } - std::shared_ptr media = devnumToDevice_[devnum]; - deps_[media].remove(devnum); - devnumToDevice_.erase(devnum); - devnumToEntity_.erase(devnum); + /* + * Remove the devnum from the unmet dependencies for this media device. + * If no more dependency is unmet, add the media device to the + * enumerator. + */ + deps->deps_.erase(devnum); - if (deps_[media].empty()) { - addDevice(media); - deps_.erase(media); + if (deps->deps_.empty()) { + addDevice(deps->media_); + pending_.remove(*deps); } return 0; diff --git a/src/libcamera/include/device_enumerator_udev.h b/src/libcamera/include/device_enumerator_udev.h index fb7cac8011a1..6d8268620185 100644 --- a/src/libcamera/include/device_enumerator_udev.h +++ b/src/libcamera/include/device_enumerator_udev.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -39,11 +40,27 @@ private: struct udev_monitor *monitor_; EventNotifier *notifier_; - std::map, std::list> deps_; - std::map> devnumToDevice_; - std::map devnumToEntity_; + using DependencyMap = std::map>; - std::list orphans_; + struct MediaDeviceDeps { + MediaDeviceDeps(const std::shared_ptr &media, + const DependencyMap &deps) + : media_(media), deps_(deps) + { + } + + bool operator==(const MediaDeviceDeps &other) const + { + return media_ == other.media_; + } + + std::shared_ptr media_; + DependencyMap deps_; + }; + + std::set orphans_; + std::list pending_; + std::map devMap_; int addUdevDevice(struct udev_device *dev); int populateMediaDevice(const std::shared_ptr &media);