/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019, Google Inc. * * device_enumerator_sysfs.cpp - sysfs-based device enumerator */ #include "libcamera/internal/device_enumerator_sysfs.h" #include <dirent.h> #include <fcntl.h> #include <fstream> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "libcamera/internal/log.h" #include "libcamera/internal/media_device.h" namespace libcamera { LOG_DECLARE_CATEGORY(DeviceEnumerator) int DeviceEnumeratorSysfs::init() { return 0; } int DeviceEnumeratorSysfs::enumerate() { struct dirent *ent; DIR *dir; static const char * const sysfs_dirs[] = { "/sys/subsystem/media/devices", "/sys/bus/media/devices", "/sys/class/media/devices", }; for (const char *dirname : sysfs_dirs) { dir = opendir(dirname); if (dir) break; } if (!dir) { LOG(DeviceEnumerator, Error) << "No valid sysfs media device directory"; return -ENODEV; } while ((ent = readdir(dir)) != nullptr) { if (strncmp(ent->d_name, "media", 5)) continue; char *end; unsigned int idx = strtoul(ent->d_name + 5, &end, 10); if (*end != '\0') continue; std::string devnode = "/dev/media" + std::to_string(idx); /* Verify that the device node exists. */ struct stat devstat; if (stat(devnode.c_str(), &devstat) < 0) { LOG(DeviceEnumerator, Warning) << "Device node /dev/media" << idx << " should exist but doesn't"; continue; } std::unique_ptr<MediaDevice> media = createDevice(devnode); if (!media) continue; if (populateMediaDevice(media.get()) < 0) { LOG(DeviceEnumerator, Warning) << "Failed to populate media device " << media->deviceNode() << " (" << media->driver() << "), skipping"; continue; } addDevice(std::move(media)); } closedir(dir); return 0; } int DeviceEnumeratorSysfs::populateMediaDevice(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; } /** * \brief Lookup device node path from device number * \param[in] major The device major number * \param[in] minor The device minor number * * Translate a device number given as \a major and \a minor to a device node * path. * * \return The device node path on success, or an empty string if the lookup * fails */ std::string DeviceEnumeratorSysfs::lookupDeviceNode(int major, int minor) { std::string deviceNode; std::string line; std::ifstream ueventFile; ueventFile.open("/sys/dev/char/" + std::to_string(major) + ":" + std::to_string(minor) + "/uevent"); if (!ueventFile) return std::string(); while (ueventFile >> line) { if (line.find("DEVNAME=") == 0) { deviceNode = "/dev/" + line.substr(strlen("DEVNAME=")); break; } } ueventFile.close(); return deviceNode; } } /* namespace libcamera */