@@ -13,6 +13,7 @@
#include <set>
#include <string>
#include <sys/types.h>
+#include <unordered_set>
#include <libcamera/base/class.h>
@@ -59,6 +60,7 @@ private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(DeviceEnumeratorUdev)
int addUdevDevice(struct udev_device *dev);
+ void removeUdevDevice(struct udev_device *dev);
int populateMediaDevice(MediaDevice *media, DependencyMap *deps);
std::string lookupDeviceNode(dev_t devnum);
@@ -70,6 +72,7 @@ private:
EventNotifier *notifier_;
std::set<dev_t> orphans_;
+ std::unordered_set<dev_t> devices_;
std::list<MediaDeviceDeps> pending_;
std::map<dev_t, MediaDeviceDeps *> devMap_;
};
@@ -76,6 +76,21 @@ int DeviceEnumeratorUdev::addUdevDevice(struct udev_device *dev)
if (!subsystem)
return -ENODEV;
+ /*
+ * Record that udev reported the given devnum. And reject if it has
+ * already been seen (e.g. device added between udev monitor creation
+ * in `init()` and `enumerate()`). This record is kept even if later
+ * in this function an error is encountered. Only a "remove" event
+ * from udev should erase it from `devices_`.
+ */
+ const dev_t devnum = udev_device_get_devnum(dev);
+ if (devnum == makedev(0, 0))
+ return -ENODEV;
+
+ const auto [it, inserted] = devices_.insert(devnum);
+ if (!inserted)
+ return -EEXIST;
+
if (!strcmp(subsystem, "media")) {
std::unique_ptr<MediaDevice> media =
createDevice(udev_device_get_devnode(dev));
@@ -111,13 +126,22 @@ int DeviceEnumeratorUdev::addUdevDevice(struct udev_device *dev)
}
if (!strcmp(subsystem, "video4linux")) {
- addV4L2Device(udev_device_get_devnum(dev));
+ addV4L2Device(devnum);
return 0;
}
return -ENODEV;
}
+void DeviceEnumeratorUdev::removeUdevDevice(struct udev_device *dev)
+{
+ const char *subsystem = udev_device_get_subsystem(dev);
+ if (subsystem && !strcmp(subsystem, "media"))
+ removeDevice(udev_device_get_devnode(dev));
+
+ devices_.erase(udev_device_get_devnum(dev));
+}
+
int DeviceEnumeratorUdev::enumerate()
{
struct udev_enumerate *udev_enum = nullptr;
@@ -341,18 +365,14 @@ void DeviceEnumeratorUdev::udevNotify()
}
std::string_view action(udev_device_get_action(dev));
- std::string_view deviceNode(udev_device_get_devnode(dev));
LOG(DeviceEnumerator, Debug)
- << action << " device " << deviceNode;
+ << action << " device " << udev_device_get_devnode(dev);
- if (action == "add") {
+ if (action == "add")
addUdevDevice(dev);
- } else if (action == "remove") {
- const char *subsystem = udev_device_get_subsystem(dev);
- if (subsystem && !strcmp(subsystem, "media"))
- removeDevice(std::string(deviceNode));
- }
+ else if (action == "remove")
+ removeUdevDevice(dev);
udev_device_unref(dev);
}