From 0052aaa40e9d9c92f4ada41987f03015adf4953e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 6 Jan 2019 12:19:24 +0200 Subject: libcamera: device_enumerator: Add hotplug support Create a udev_monitor in the udev device enumerator to listen to media device disconnection, and emit the corresponding media device's disconnect signal in response. Signed-off-by: Laurent Pinchart --- src/libcamera/device_enumerator.cpp | 83 ++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) (limited to 'src/libcamera/device_enumerator.cpp') diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index 149ffbf9..703b03dd 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include "device_enumerator.h" #include "log.h" #include "media_device.h" @@ -243,11 +245,47 @@ int DeviceEnumerator::addDevice(const std::string &deviceNode) media->close(); + LOG(DeviceEnumerator, Debug) + << "Added device " << deviceNode << ": " << media->driver(); + devices_.push_back(std::move(media)); return 0; } +/** + * \brief Remove a media device from the enumerator + * \param[in] deviceNode Path to the media device to remove + * + * Remove the media device identified by \a deviceNode previously added to the + * enumerator with addDevice(). The media device's MediaDevice::disconnected + * signal is emitted. + */ +void DeviceEnumerator::removeDevice(const std::string &deviceNode) +{ + std::shared_ptr media; + + for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) { + if ((*iter)->deviceNode() == deviceNode) { + media = std::move(*iter); + devices_.erase(iter); + break; + } + } + + if (!media) { + LOG(DeviceEnumerator, Warning) + << "Media device for node " << deviceNode + << " not found"; + return; + } + + LOG(DeviceEnumerator, Debug) + << "Media device for node " << deviceNode << " removed."; + + media->disconnected.emit(media.get()); +} + /** * \brief Search available media devices for a pattern match * \param[in] dm Search pattern @@ -301,12 +339,18 @@ DeviceEnumeratorUdev::DeviceEnumeratorUdev() DeviceEnumeratorUdev::~DeviceEnumeratorUdev() { + delete notifier_; + + if (monitor_) + udev_monitor_unref(monitor_); if (udev_) udev_unref(udev_); } int DeviceEnumeratorUdev::init() { + int ret; + if (udev_) return -EBUSY; @@ -314,6 +358,15 @@ int DeviceEnumeratorUdev::init() 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; + return 0; } @@ -365,7 +418,18 @@ int DeviceEnumeratorUdev::enumerate() } done: udev_enumerate_unref(udev_enum); - return ret >= 0 ? 0 : ret; + 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; } std::string DeviceEnumeratorUdev::lookupDeviceNode(int major, int minor) @@ -389,4 +453,21 @@ std::string DeviceEnumeratorUdev::lookupDeviceNode(int major, int minor) return deviceNode; } +void DeviceEnumeratorUdev::udevNotify(EventNotifier *notifier) +{ + 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(Debug) << action << " device " << udev_device_get_devnode(dev); + + if (action == "add") { + addDevice(deviceNode); + } else if (action == "remove") { + removeDevice(deviceNode); + } + + udev_device_unref(dev); +} + } /* namespace libcamera */ -- cgit v1.2.1