summaryrefslogtreecommitdiff
path: root/src/libcamera/device_enumerator.cpp
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-01-06 12:19:24 +0200
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-01-25 01:59:29 +0200
commit0052aaa40e9d9c92f4ada41987f03015adf4953e (patch)
tree683f6bc5b2a3050540dbd19bcd47970f573a884a /src/libcamera/device_enumerator.cpp
parent4d84fa4fee4c138cc672715309de00dfbe4fc711 (diff)
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 <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'src/libcamera/device_enumerator.cpp')
-rw-r--r--src/libcamera/device_enumerator.cpp83
1 files changed, 82 insertions, 1 deletions
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 <sys/ioctl.h>
#include <unistd.h>
+#include <libcamera/event_notifier.h>
+
#include "device_enumerator.h"
#include "log.h"
#include "media_device.h"
@@ -243,12 +245,48 @@ 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 */