summaryrefslogtreecommitdiff
path: root/src/libcamera
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcamera')
-rw-r--r--src/libcamera/device_enumerator.cpp54
-rw-r--r--src/libcamera/device_enumerator_sysfs.cpp35
-rw-r--r--src/libcamera/device_enumerator_udev.cpp135
-rw-r--r--src/libcamera/include/device_enumerator.h3
-rw-r--r--src/libcamera/include/device_enumerator_sysfs.h6
-rw-r--r--src/libcamera/include/device_enumerator_udev.h16
6 files changed, 216 insertions, 33 deletions
diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp
index 60c918f0..e76438af 100644
--- a/src/libcamera/device_enumerator.cpp
+++ b/src/libcamera/device_enumerator.cpp
@@ -195,16 +195,21 @@ DeviceEnumerator::~DeviceEnumerator()
*/
/**
- * \brief Add a media device to the enumerator
- * \param[in] deviceNode path to the media device to add
+ * \brief Create a media device instance
+ * \param[in] deviceNode path to the media device to create
*
- * Create a media device for the \a deviceNode, open it, populate its media graph,
- * and look up device nodes associated with all entities. Store the media device
- * in the internal list for later matching with pipeline handlers.
+ * Create a media device for the \a deviceNode, open it, and populate its
+ * media graph. The device enumerator shall then populate the media device by
+ * associating device nodes with entities using MediaEntity::setDeviceNode().
+ * This process is specific to each device enumerator, and the device enumerator
+ * shall ensure that device nodes are ready to be used (for instance, if
+ * applicable, by waiting for device nodes to be created and access permissions
+ * to be set by the system). Once done, it shall add the media device to the
+ * system with addDevice().
*
- * \return 0 on success or a negative error code otherwise
+ * \return Created media device instance on success, or nullptr otherwise
*/
-int DeviceEnumerator::addDevice(const std::string &deviceNode)
+std::shared_ptr<MediaDevice> DeviceEnumerator::createDevice(const std::string &deviceNode)
{
std::shared_ptr<MediaDevice> media = std::make_shared<MediaDevice>(deviceNode);
@@ -213,34 +218,31 @@ int DeviceEnumerator::addDevice(const std::string &deviceNode)
LOG(DeviceEnumerator, Info)
<< "Unable to populate media device " << deviceNode
<< " (" << strerror(-ret) << "), skipping";
- return ret;
+ return nullptr;
}
LOG(DeviceEnumerator, Debug)
<< "New media device \"" << media->driver()
<< "\" created from " << deviceNode;
- /* Associate entities to device node paths. */
- for (MediaEntity *entity : media->entities()) {
- if (entity->deviceMajor() == 0 && entity->deviceMinor() == 0)
- continue;
-
- std::string deviceNode = lookupDeviceNode(entity->deviceMajor(),
- entity->deviceMinor());
- if (deviceNode.empty())
- return -EINVAL;
-
- ret = entity->setDeviceNode(deviceNode);
- if (ret)
- return ret;
- }
+ return media;
+}
+/**
+ * \brief Add a media device to the enumerator
+ * \param[in] media media device instance to add
+ *
+ * Store the media device in the internal list for later matching with
+ * pipeline handlers. \a media shall be created with createDevice() first.
+ * This method shall be called after all members of the entities of the
+ * media graph have been confirmed to be initialized.
+ */
+void DeviceEnumerator::addDevice(const std::shared_ptr<MediaDevice> &media)
+{
LOG(DeviceEnumerator, Debug)
- << "Added device " << deviceNode << ": " << media->driver();
-
- devices_.push_back(std::move(media));
+ << "Added device " << media->deviceNode() << ": " << media->driver();
- return 0;
+ devices_.push_back(media);
}
/**
diff --git a/src/libcamera/device_enumerator_sysfs.cpp b/src/libcamera/device_enumerator_sysfs.cpp
index f3054d5e..78a7da8d 100644
--- a/src/libcamera/device_enumerator_sysfs.cpp
+++ b/src/libcamera/device_enumerator_sysfs.cpp
@@ -18,6 +18,7 @@
#include <unistd.h>
#include "log.h"
+#include "media_device.h"
namespace libcamera {
@@ -32,6 +33,7 @@ int DeviceEnumeratorSysfs::enumerate()
{
struct dirent *ent;
DIR *dir;
+ int ret = 0;
static const char * const sysfs_dirs[] = {
"/sys/subsystem/media/devices",
@@ -71,11 +73,42 @@ int DeviceEnumeratorSysfs::enumerate()
continue;
}
- addDevice(devnode);
+ std::shared_ptr<MediaDevice> media = createDevice(devnode);
+ if (!media) {
+ ret = -ENODEV;
+ break;
+ }
+
+ if (populateMediaDevice(media) < 0) {
+ ret = -ENODEV;
+ break;
+ }
+
+ addDevice(media);
}
closedir(dir);
+ return ret;
+}
+
+int DeviceEnumeratorSysfs::populateMediaDevice(const std::shared_ptr<MediaDevice> &media)
+{
+ /* Associate entities to device node paths. */
+ for (MediaEntity *entity : media->entities()) {
+ if (entity->deviceMajor() == 0 && entity->deviceMinor() == 0)
+ continue;
+
+ std::string deviceNode = lookupDeviceNode(entity->deviceMajor(),
+ entity->deviceMinor());
+ if (deviceNode.empty())
+ return -EINVAL;
+
+ int ret = entity->setDeviceNode(deviceNode);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/src/libcamera/device_enumerator_udev.cpp b/src/libcamera/device_enumerator_udev.cpp
index 86f6ca18..40853e77 100644
--- a/src/libcamera/device_enumerator_udev.cpp
+++ b/src/libcamera/device_enumerator_udev.cpp
@@ -7,6 +7,10 @@
#include "device_enumerator_udev.h"
+#include <algorithm>
+#include <list>
+#include <map>
+
#include <fcntl.h>
#include <libudev.h>
#include <string.h>
@@ -17,6 +21,7 @@
#include <libcamera/event_notifier.h>
#include "log.h"
+#include "media_device.h"
namespace libcamera {
@@ -57,9 +62,40 @@ int DeviceEnumeratorUdev::init()
if (ret < 0)
return ret;
+ ret = udev_monitor_filter_add_match_subsystem_devtype(monitor_, "video4linux",
+ nullptr);
+ if (ret < 0)
+ return ret;
+
return 0;
}
+int DeviceEnumeratorUdev::addUdevDevice(struct udev_device *dev)
+{
+ const char *subsystem = udev_device_get_subsystem(dev);
+ if (!subsystem)
+ return -ENODEV;
+
+ if (!strcmp(subsystem, "media")) {
+ std::shared_ptr<MediaDevice> media =
+ createDevice(udev_device_get_devnode(dev));
+ if (!media)
+ return -ENODEV;
+
+ int ret = populateMediaDevice(media);
+ if (ret == 0)
+ addDevice(media);
+ return 0;
+ }
+
+ if (!strcmp(subsystem, "video4linux")) {
+ addV4L2Device(udev_device_get_devnum(dev));
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
int DeviceEnumeratorUdev::enumerate()
{
struct udev_enumerate *udev_enum = nullptr;
@@ -74,6 +110,14 @@ int DeviceEnumeratorUdev::enumerate()
if (ret < 0)
goto done;
+ ret = udev_enumerate_add_match_subsystem(udev_enum, "video4linux");
+ if (ret < 0)
+ goto done;
+
+ ret = udev_enumerate_add_match_is_initialized(udev_enum);
+ if (ret < 0)
+ goto done;
+
ret = udev_enumerate_scan_devices(udev_enum);
if (ret < 0)
goto done;
@@ -102,10 +146,12 @@ int DeviceEnumeratorUdev::enumerate()
goto done;
}
- addDevice(devnode);
-
+ ret = addUdevDevice(dev);
udev_device_unref(dev);
+ if (ret < 0)
+ break;
}
+
done:
udev_enumerate_unref(udev_enum);
if (ret < 0)
@@ -122,6 +168,43 @@ done:
return 0;
}
+int DeviceEnumeratorUdev::populateMediaDevice(const std::shared_ptr<MediaDevice> &media)
+{
+ unsigned int pendingNodes = 0;
+ int ret;
+
+ /* Associate entities to device node paths. */
+ for (MediaEntity *entity : media->entities()) {
+ if (entity->deviceMajor() == 0 && entity->deviceMinor() == 0)
+ continue;
+
+ std::string deviceNode = lookupDeviceNode(entity->deviceMajor(),
+ entity->deviceMinor());
+ dev_t devnum = makedev(entity->deviceMajor(),
+ entity->deviceMinor());
+
+ /* Take device from orphan list first, if it is in the list. */
+ if (std::find(orphans_.begin(), orphans_.end(), devnum) != orphans_.end()) {
+ if (deviceNode.empty())
+ return -EINVAL;
+
+ ret = entity->setDeviceNode(deviceNode);
+ if (ret)
+ return ret;
+
+ orphans_.remove(devnum);
+ continue;
+ }
+
+ deps_[media].push_back(devnum);
+ devnumToDevice_[devnum] = media;
+ devnumToEntity_[devnum] = entity;
+ pendingNodes++;
+ }
+
+ return pendingNodes;
+}
+
std::string DeviceEnumeratorUdev::lookupDeviceNode(int major, int minor)
{
struct udev_device *device;
@@ -143,6 +226,48 @@ std::string DeviceEnumeratorUdev::lookupDeviceNode(int major, int minor)
return deviceNode;
}
+/**
+ * \brief Add a V4L2 device to the media device that it belongs to
+ * \param[in] devnum major:minor number of V4L2 device to add, as a dev_t
+ *
+ * Add V4L2 device identified by \a devnum to the MediaDevice that it belongs
+ * to, if such a MediaDevice has been created. Otherwise add the V4L2 device
+ * to the orphan list. If the V4L2 device is added to a MediaDevice, and it is
+ * the last V4L2 device that the MediaDevice needs, then the MediaDevice is
+ * added to the DeviceEnumerator, where it is available for pipeline handlers.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int DeviceEnumeratorUdev::addV4L2Device(dev_t devnum)
+{
+ MediaEntity *entity = devnumToEntity_[devnum];
+ if (!entity) {
+ orphans_.push_back(devnum);
+ return 0;
+ }
+
+ std::string deviceNode = lookupDeviceNode(entity->deviceMajor(),
+ entity->deviceMinor());
+ if (deviceNode.empty())
+ return -EINVAL;
+
+ int ret = entity->setDeviceNode(deviceNode);
+ if (ret)
+ return ret;
+
+ std::shared_ptr<MediaDevice> media = devnumToDevice_[devnum];
+ deps_[media].remove(devnum);
+ devnumToDevice_.erase(devnum);
+ devnumToEntity_.erase(devnum);
+
+ if (deps_[media].empty()) {
+ addDevice(media);
+ deps_.erase(media);
+ }
+
+ return 0;
+}
+
void DeviceEnumeratorUdev::udevNotify(EventNotifier *notifier)
{
struct udev_device *dev = udev_monitor_receive_device(monitor_);
@@ -153,9 +278,11 @@ void DeviceEnumeratorUdev::udevNotify(EventNotifier *notifier)
<< action << " device " << udev_device_get_devnode(dev);
if (action == "add") {
- addDevice(deviceNode);
+ addUdevDevice(dev);
} else if (action == "remove") {
- removeDevice(deviceNode);
+ const char *subsystem = udev_device_get_subsystem(dev);
+ if (subsystem && !strcmp(subsystem, "media"))
+ removeDevice(deviceNode);
}
udev_device_unref(dev);
diff --git a/src/libcamera/include/device_enumerator.h b/src/libcamera/include/device_enumerator.h
index 02aec3bc..c5d26f1a 100644
--- a/src/libcamera/include/device_enumerator.h
+++ b/src/libcamera/include/device_enumerator.h
@@ -44,7 +44,8 @@ public:
std::shared_ptr<MediaDevice> search(const DeviceMatch &dm);
protected:
- int addDevice(const std::string &deviceNode);
+ std::shared_ptr<MediaDevice> createDevice(const std::string &deviceNode);
+ void addDevice(const std::shared_ptr<MediaDevice> &media);
void removeDevice(const std::string &deviceNode);
private:
diff --git a/src/libcamera/include/device_enumerator_sysfs.h b/src/libcamera/include/device_enumerator_sysfs.h
index 8d3adc91..242b22b2 100644
--- a/src/libcamera/include/device_enumerator_sysfs.h
+++ b/src/libcamera/include/device_enumerator_sysfs.h
@@ -7,10 +7,13 @@
#ifndef __LIBCAMERA_DEVICE_ENUMERATOR_SYSFS_H__
#define __LIBCAMERA_DEVICE_ENUMERATOR_SYSFS_H__
+#include <memory>
#include <string>
#include "device_enumerator.h"
+class MediaDevice;
+
namespace libcamera {
class DeviceEnumeratorSysfs final : public DeviceEnumerator
@@ -20,7 +23,8 @@ public:
int enumerate();
private:
- std::string lookupDeviceNode(int major, int minor);
+ int populateMediaDevice(const std::shared_ptr<MediaDevice> &media);
+ std::string lookupDeviceNode(int major, int minor) final;
};
} /* namespace libcamera */
diff --git a/src/libcamera/include/device_enumerator_udev.h b/src/libcamera/include/device_enumerator_udev.h
index 80f9372b..5bdcdea6 100644
--- a/src/libcamera/include/device_enumerator_udev.h
+++ b/src/libcamera/include/device_enumerator_udev.h
@@ -7,16 +7,23 @@
#ifndef __LIBCAMERA_DEVICE_ENUMERATOR_UDEV_H__
#define __LIBCAMERA_DEVICE_ENUMERATOR_UDEV_H__
+#include <list>
+#include <map>
+#include <memory>
#include <string>
+#include <sys/types.h>
#include "device_enumerator.h"
struct udev;
+struct udev_device;
struct udev_monitor;
namespace libcamera {
class EventNotifier;
+class MediaDevice;
+class MediaEntity;
class DeviceEnumeratorUdev : public DeviceEnumerator
{
@@ -32,8 +39,17 @@ private:
struct udev_monitor *monitor_;
EventNotifier *notifier_;
+ std::map<std::shared_ptr<MediaDevice>, std::list<dev_t>> deps_;
+ std::map<dev_t, std::shared_ptr<MediaDevice>> devnumToDevice_;
+ std::map<dev_t, MediaEntity *> devnumToEntity_;
+
+ std::list<dev_t> orphans_;
+
+ int addUdevDevice(struct udev_device *dev);
+ int populateMediaDevice(const std::shared_ptr<MediaDevice> &media);
std::string lookupDeviceNode(int major, int minor) final;
+ int addV4L2Device(dev_t devnum);
void udevNotify(EventNotifier *notifier);
};