/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2018-2019, Google Inc. * * device_enumerator_udev.cpp - udev-based device enumerator */ #include "libcamera/internal/device_enumerator_udev.h" #include #include #include #include #include #include #include #include #include #include #include #include "libcamera/internal/media_device.h" namespace libcamera { LOG_DECLARE_CATEGORY(DeviceEnumerator) DeviceEnumeratorUdev::DeviceEnumeratorUdev() : udev_(nullptr), monitor_(nullptr), notifier_(nullptr) { } DeviceEnumeratorUdev::~DeviceEnumeratorUdev() { delete notifier_; if (monitor_) udev_monitor_unref(monitor_); if (udev_) udev_unref(udev_); } int DeviceEnumeratorUdev::init() { int ret; if (udev_) return -EBUSY; udev_ = udev_new(); if (!udev_) return -ENODEV; monitor_ = udev_monitor_new_from_netlink(udev_, "udev"); if (!monitor_) return -ENODEV; ret = udev_monitor_filter_add_match_subsystem_devtype(monitor_, "media", nullptr); if (ret < 0) return ret; ret = udev_monitor_filter_add_match_subsystem_devtype(monitor_, "video4linux", nullptr); if (ret < 0) return ret; return 0; } int DeviceEnumeratorUdev::addUdevDevice(struct udev_device *dev) { const char *subsystem = udev_device_get_subsystem(dev); if (!subsystem) return -ENODEV; if (!strcmp(subsystem, "media")) { std::unique_ptr media = createDevice(udev_device_get_devnode(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; } if (!strcmp(subsystem, "video4linux")) { addV4L2Device(udev_device_get_devnum(dev)); return 0; } return -ENODEV; } int DeviceEnumeratorUdev::enumerate() { struct udev_enumerate *udev_enum = nullptr; struct udev_list_entry *ents, *ent; int ret; udev_enum = udev_enumerate_new(udev_); if (!udev_enum) return -ENOMEM; ret = udev_enumerate_add_match_subsystem(udev_enum, "media"); if (ret < 0) goto done; ret = udev_enumerate_add_match_subsystem(udev_enum, "video4linux"); if (ret < 0) goto done; ret = udev_enumerate_add_match_is_initialized(udev_enum); if (ret < 0) goto done; ret = udev_enumerate_scan_devices(udev_enum); if (ret < 0) goto done; ents = udev_enumerate_get_list_entry(udev_enum); if (!ents) goto done; udev_list_entry_foreach(ent, ents) { struct udev_device *dev; const char *devnode; const char *syspath = udev_list_entry_get_name(ent); dev = udev_device_new_from_syspath(udev_, syspath); if (!dev) { LOG(DeviceEnumerator, Warning) << "Failed to get device for '" << syspath << "', skipping"; continue; } devnode = udev_device_get_devnode(dev); if (!devnode) { udev_device_unref(dev); LOG(DeviceEnumerator, Warning) << "Failed to get device node for '" << syspath << "', skipping"; continue; } if (addUdevDevice(dev) < 0) LOG(DeviceEnumerator, Warning) << "Failed to add device for '" << syspath << "', skipping"; udev_device_unref(dev); } done: udev_enumerate_unref(udev_enum); if (ret < 0) return ret; ret = udev_monitor_enable_receiving(monitor_); if (ret < 0) return ret; int fd = udev_monitor_get_fd(monitor_); notifier_ = new EventNotifier(fd, EventNotifier::Read); notifier_->activated.connect(this, &DeviceEnumeratorUdev::udevNotify); return 0; } int DeviceEnumeratorUdev::populateMediaDevice(MediaDevice *media, DependencyMap *deps) { std::set children; /* Associate entities to device node paths. */ for (MediaEntity *entity : media->entities()) { if (entity->deviceMajor() == 0 && entity->deviceMinor() == 0) continue; dev_t devnum = makedev /* * Copyright (C) 2019, Raspberry Pi (Trading) Limited * * contrast_algorithm.hpp - contrast (gamma) control algorithm interface */ #pragma once #include "algorithm.hpp" namespace RPiController { class ContrastAlgorithm : public Algorithm { public: ContrastAlgorithm(Controller *controller) : Algorithm(controller) {} // A contrast algorithm must provide the following: virtual void SetBrightness(double brightness) = 0; virtual void SetContrast(double contrast) = 0; }; } // namespace RPiController pipeline handlers. * * \return 0 on success or a negative error code otherwise */ int DeviceEnumeratorUdev::addV4L2Device(dev_t 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; MediaDeviceDeps *deps = it->second; for (MediaEntity *entity : deps->deps_[devnum]) { int ret = entity->setDeviceNode(deviceNode); if (ret) return ret; } /* * 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->deps_.empty()) { LOG(DeviceEnumerator, Debug) << "All dependencies for media device " << deps->media_->deviceNode() << " found"; addDevice(std::move(deps->media_)); pending_.remove(*deps); } return 0; } void DeviceEnumeratorUdev::udevNotify() { struct udev_device *dev = udev_monitor_receive_device(monitor_); std::string action(udev_device_get_action(dev)); std::string deviceNode(udev_device_get_devnode(dev)); LOG(DeviceEnumerator, Debug) << action << " device " << udev_device_get_devnode(dev); if (action == "add") { addUdevDevice(dev); } else if (action == "remove") { const char *subsystem = udev_device_get_subsystem(dev); if (subsystem && !strcmp(subsystem, "media")) removeDevice(deviceNode); } udev_device_unref(dev); } } /* namespace libcamera */