diff options
Diffstat (limited to 'src/libcamera/device_enumerator_udev.cpp')
-rw-r--r-- | src/libcamera/device_enumerator_udev.cpp | 100 |
1 files changed, 68 insertions, 32 deletions
diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp index c4077091..ddcd59ea 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); } - return pendingNodes; + /* 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 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; |