diff options
Diffstat (limited to 'src/libcamera/device_enumerator_udev.cpp')
-rw-r--r-- | src/libcamera/device_enumerator_udev.cpp | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp new file mode 100644 index 00000000..cb2d21b9 --- /dev/null +++ b/src/libcamera/device_enumerator_udev.cpp @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018-2019, Google Inc. + * + * device_enumerator_udev.cpp - udev-based device enumerator + */ + +#include "device_enumerator_udev.h" + +#include <fcntl.h> +#include <libudev.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include <libcamera/event_notifier.h> + +#include "log.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(DeviceEnumerator) + +DeviceEnumeratorUdev::DeviceEnumeratorUdev() + : udev_(nullptr) +{ +} + +DeviceEnumeratorUdev::~DeviceEnumeratorUdev() +{ + delete notifier_; + + if (monitor_) + udev_monitor_unref(monitor_); + if (udev_) + udev_unref(udev_); +} + +int DeviceEnumeratorUdev::init() +{ + int ret; + + if (udev_) + return -EBUSY; + + udev_ = udev_new(); + 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; +} + +int DeviceEnumeratorUdev::enumerate() +{ + struct udev_enumerate *udev_enum = nullptr; + struct udev_list_entry *ents, *ent; + int ret; + + udev_enum = udev_enumerate_new(udev_); + if (!udev_enum) + return -ENOMEM; + + ret = udev_enumerate_add_match_subsystem(udev_enum, "media"); + if (ret < 0) + goto done; + + ret = udev_enumerate_scan_devices(udev_enum); + if (ret < 0) + goto done; + + ents = udev_enumerate_get_list_entry(udev_enum); + if (!ents) + goto done; + + udev_list_entry_foreach(ent, ents) { + struct udev_device *dev; + const char *devnode; + const char *syspath = udev_list_entry_get_name(ent); + + dev = udev_device_new_from_syspath(udev_, syspath); + if (!dev) { + LOG(DeviceEnumerator, Warning) + << "Failed to get device for '" + << syspath << "', skipping"; + continue; + } + + devnode = udev_device_get_devnode(dev); + if (!devnode) { + udev_device_unref(dev); + ret = -ENODEV; + goto done; + } + + addDevice(devnode); + + udev_device_unref(dev); + } +done: + udev_enumerate_unref(udev_enum); + 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) +{ + struct udev_device *device; + const char *name; + dev_t devnum; + std::string deviceNode = std::string(); + + devnum = makedev(major, minor); + device = udev_device_new_from_devnum(udev_, 'c', devnum); + if (!device) + return std::string(); + + name = udev_device_get_devnode(device); + if (name) + deviceNode = name; + + udev_device_unref(device); + + 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(DeviceEnumerator, 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 */ |