[libcamera-devel] [PATCH 4/4] libcamera: device_enumerator_udev: Support entities sharing device nodes
Kieran Bingham
kieran.bingham at ideasonboard.com
Fri Sep 13 11:14:32 CEST 2019
Hi Laurent,
On 12/09/2019 21:03, Laurent Pinchart wrote:
> 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")
This fixes the issue I reported.
Tested-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
And I can't see anything glaring out at me in the code - so LGTM:
Reviewed-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
> 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<MediaDevice> &media)
> {
> - unsigned int pendingNodes = 0;
> - int ret;
> + std::set<dev_t> 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<MediaDevice>
> 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<MediaDevice> 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 <list>
> #include <map>
> #include <memory>
> +#include <set>
> #include <string>
> #include <sys/types.h>
>
> @@ -39,11 +40,27 @@ private:
> struct udev_monitor *monitor_;
> EventNotifier *notifier_;
>
> - std::map<std::shared_ptr<MediaDevice>, std::list<dev_t>> deps_;
> - std::map<dev_t, std::shared_ptr<MediaDevice>> devnumToDevice_;
> - std::map<dev_t, MediaEntity *> devnumToEntity_;
> + using DependencyMap = std::map<dev_t, std::list<MediaEntity *>>;
>
> - std::list<dev_t> orphans_;
> + struct MediaDeviceDeps {
> + MediaDeviceDeps(const std::shared_ptr<MediaDevice> &media,
> + const DependencyMap &deps)
> + : media_(media), deps_(deps)
> + {
> + }
> +
> + bool operator==(const MediaDeviceDeps &other) const
> + {
> + return media_ == other.media_;
> + }
> +
> + std::shared_ptr<MediaDevice> media_;
> + DependencyMap deps_;
> + };
> +
> + std::set<dev_t> orphans_;
> + std::list<MediaDeviceDeps> pending_;
> + std::map<dev_t, MediaDeviceDeps *> devMap_;
>
> int addUdevDevice(struct udev_device *dev);
> int populateMediaDevice(const std::shared_ptr<MediaDevice> &media);
>
--
Regards
--
Kieran
More information about the libcamera-devel
mailing list