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>
This commit is contained in:
Laurent Pinchart 2019-01-06 12:19:24 +02:00
parent 4d84fa4fee
commit 0052aaa40e
2 changed files with 88 additions and 1 deletions

View file

@ -11,6 +11,8 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <libcamera/event_notifier.h>
#include "device_enumerator.h" #include "device_enumerator.h"
#include "log.h" #include "log.h"
#include "media_device.h" #include "media_device.h"
@ -243,11 +245,47 @@ int DeviceEnumerator::addDevice(const std::string &deviceNode)
media->close(); media->close();
LOG(DeviceEnumerator, Debug)
<< "Added device " << deviceNode << ": " << media->driver();
devices_.push_back(std::move(media)); devices_.push_back(std::move(media));
return 0; 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 * \brief Search available media devices for a pattern match
* \param[in] dm Search pattern * \param[in] dm Search pattern
@ -301,12 +339,18 @@ DeviceEnumeratorUdev::DeviceEnumeratorUdev()
DeviceEnumeratorUdev::~DeviceEnumeratorUdev() DeviceEnumeratorUdev::~DeviceEnumeratorUdev()
{ {
delete notifier_;
if (monitor_)
udev_monitor_unref(monitor_);
if (udev_) if (udev_)
udev_unref(udev_); udev_unref(udev_);
} }
int DeviceEnumeratorUdev::init() int DeviceEnumeratorUdev::init()
{ {
int ret;
if (udev_) if (udev_)
return -EBUSY; return -EBUSY;
@ -314,6 +358,15 @@ int DeviceEnumeratorUdev::init()
if (!udev_) if (!udev_)
return -ENODEV; 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; return 0;
} }
@ -365,7 +418,18 @@ int DeviceEnumeratorUdev::enumerate()
} }
done: done:
udev_enumerate_unref(udev_enum); 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) std::string DeviceEnumeratorUdev::lookupDeviceNode(int major, int minor)
@ -389,4 +453,21 @@ std::string DeviceEnumeratorUdev::lookupDeviceNode(int major, int minor)
return deviceNode; 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 */ } /* namespace libcamera */

View file

@ -16,6 +16,7 @@
namespace libcamera { namespace libcamera {
class EventNotifier;
class MediaDevice; class MediaDevice;
class DeviceMatch class DeviceMatch
@ -46,6 +47,7 @@ public:
protected: protected:
int addDevice(const std::string &deviceNode); int addDevice(const std::string &deviceNode);
void removeDevice(const std::string &deviceNode);
private: private:
std::vector<std::shared_ptr<MediaDevice>> devices_; std::vector<std::shared_ptr<MediaDevice>> devices_;
@ -64,8 +66,12 @@ public:
private: private:
struct udev *udev_; struct udev *udev_;
struct udev_monitor *monitor_;
EventNotifier *notifier_;
std::string lookupDeviceNode(int major, int minor) final; std::string lookupDeviceNode(int major, int minor) final;
void udevNotify(EventNotifier *notifier);
}; };
} /* namespace libcamera */ } /* namespace libcamera */