[libcamera-devel,10/10] libcamera: device_enumerator: Add hotplug support

Message ID 20190124101651.9993-11-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Hotplug support and object lifetime management
Related show

Commit Message

Laurent Pinchart Jan. 24, 2019, 10:16 a.m. UTC
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 <laurent.pinchart@ideasonboard.com>
---
 src/libcamera/device_enumerator.cpp       | 83 ++++++++++++++++++++++-
 src/libcamera/include/device_enumerator.h |  6 ++
 2 files changed, 88 insertions(+), 1 deletion(-)

Patch

diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp
index 149ffbf9aea6..703b03dd418a 100644
--- a/src/libcamera/device_enumerator.cpp
+++ b/src/libcamera/device_enumerator.cpp
@@ -11,6 +11,8 @@ 
 #include <sys/ioctl.h>
 #include <unistd.h>
 
+#include <libcamera/event_notifier.h>
+
 #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<MediaDevice> 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 */
diff --git a/src/libcamera/include/device_enumerator.h b/src/libcamera/include/device_enumerator.h
index 3f87a6255303..22ed8dedcb06 100644
--- a/src/libcamera/include/device_enumerator.h
+++ b/src/libcamera/include/device_enumerator.h
@@ -16,6 +16,7 @@ 
 
 namespace libcamera {
 
+class EventNotifier;
 class MediaDevice;
 
 class DeviceMatch
@@ -46,6 +47,7 @@  public:
 
 protected:
 	int addDevice(const std::string &deviceNode);
+	void removeDevice(const std::string &deviceNode);
 
 private:
 	std::vector<std::shared_ptr<MediaDevice>> devices_;
@@ -64,8 +66,12 @@  public:
 
 private:
 	struct udev *udev_;
+	struct udev_monitor *monitor_;
+	EventNotifier *notifier_;
 
 	std::string lookupDeviceNode(int major, int minor) final;
+
+	void udevNotify(EventNotifier *notifier);
 };
 
 } /* namespace libcamera */