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:
parent
4d84fa4fee
commit
0052aaa40e
2 changed files with 88 additions and 1 deletions
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue