| /* Copyright 2018 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <fcntl.h> |
| #include <linux/media.h> |
| #include <sys/ioctl.h> |
| |
| #include <algorithm> |
| #include <iomanip> |
| #include <iostream> |
| #include <sstream> |
| #include <vector> |
| |
| #include <base/at_exit.h> |
| #include <base/command_line.h> |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/strings/string_util.h> |
| #include <brillo/syslog_logging.h> |
| |
| namespace { |
| |
| const char kSysfsV4lClassRoot[] = "/sys/class/video4linux"; |
| const char kVendorIdPath[] = "device/vendor_id"; |
| const std::vector<std::string> kArgsPattern = {"modules", "list"}; |
| |
| struct camera { |
| std::string name; |
| std::string vendor_id; |
| |
| camera(const std::string& name_, const std::string& vendor_id_) |
| : name(name_), vendor_id(vendor_id_) {} |
| }; |
| |
| class CameraTool { |
| typedef std::vector<struct camera> CameraVector; |
| |
| public: |
| void PrintCameras(void) { |
| const CameraVector& cameras = GetPlatformCameras(); |
| |
| if (cameras.empty()) { |
| std::cout << "No cameras detected in the system." << std::endl; |
| return; |
| } |
| |
| std::cout << std::setw(16) << "Name" |
| << " | " |
| << "Vendor ID" << std::endl; |
| for (const auto& camera : cameras) |
| std::cout << std::setw(16) << camera.name << " | " << camera.vendor_id |
| << std::endl; |
| } |
| |
| private: |
| void ProbeSensorSubdev(struct media_entity_desc* desc, |
| const base::FilePath& path) { |
| const base::FilePath& vendor_id_path = path.Append(kVendorIdPath); |
| std::string vendor_id = "-1"; |
| |
| if (!base::ReadFileToStringWithMaxSize(vendor_id_path, &vendor_id, 64)) |
| LOG(ERROR) << "Failed to read vendor ID for sensor '" << desc->name |
| << "'"; |
| |
| base::TrimWhitespaceASCII(vendor_id, base::TRIM_ALL, &vendor_id); |
| platform_cameras_.emplace_back(desc->name, vendor_id); |
| } |
| |
| base::FilePath FindSubdevSysfsByDevId(int major, int minor) { |
| base::FileEnumerator dev_enum(base::FilePath(kSysfsV4lClassRoot), false, |
| base::FileEnumerator::DIRECTORIES, |
| "v4l-subdev*"); |
| for (base::FilePath name = dev_enum.Next(); !name.empty(); |
| name = dev_enum.Next()) { |
| base::FilePath dev_path = name.Append("dev"); |
| std::string dev_id("255:255"); |
| if (!base::ReadFileToStringWithMaxSize(dev_path, &dev_id, |
| dev_id.size())) { |
| LOG(ERROR) << "Failed to read device ID of '" << dev_path.value() |
| << "' from sysfs"; |
| continue; |
| } |
| base::TrimWhitespaceASCII(dev_id, base::TRIM_ALL, &dev_id); |
| |
| std::ostringstream stream; |
| stream << major << ":" << minor; |
| if (dev_id == stream.str()) |
| return name; |
| } |
| |
| return base::FilePath(); |
| } |
| |
| void ProbeMediaController(int media_fd) { |
| struct media_entity_desc desc; |
| |
| for (desc.id = MEDIA_ENT_ID_FLAG_NEXT; |
| !ioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &desc); |
| desc.id |= MEDIA_ENT_ID_FLAG_NEXT) { |
| if (desc.type != MEDIA_ENT_T_V4L2_SUBDEV_SENSOR) |
| continue; |
| |
| const base::FilePath& path = |
| FindSubdevSysfsByDevId(desc.dev.major, desc.dev.minor); |
| if (path.empty()) { |
| LOG(ERROR) << "v4l-subdev node for sensor '" << desc.name |
| << "' not found"; |
| continue; |
| } |
| |
| LOG(INFO) << "Probing sensor '" << desc.name << "' (" |
| << path.BaseName().value() << ")"; |
| ProbeSensorSubdev(&desc, path); |
| } |
| } |
| |
| void AddV4l2Cameras(void) { |
| base::FileEnumerator dev_enum(base::FilePath("/dev"), false, |
| base::FileEnumerator::FILES, "media*"); |
| for (base::FilePath name = dev_enum.Next(); !name.empty(); |
| name = dev_enum.Next()) { |
| int fd = open(name.value().c_str(), O_RDWR); |
| if (fd < 0) { |
| LOG(ERROR) << "Failed to open '" << name.value() << "'"; |
| continue; |
| } |
| |
| LOG(INFO) << "Probing media device '" << name.value() << "'"; |
| ProbeMediaController(fd); |
| close(fd); |
| } |
| } |
| |
| const CameraVector& GetPlatformCameras() { |
| if (platform_cameras_.empty()) |
| AddV4l2Cameras(); |
| |
| return platform_cameras_; |
| } |
| |
| CameraVector platform_cameras_; |
| }; |
| |
| bool StringEqualsCaseInsensitiveASCII(const std::string& a, |
| const std::string& b) { |
| return base::EqualsCaseInsensitiveASCII(a, b); |
| } |
| |
| } // namespace |
| |
| int main(int argc, char* argv[]) { |
| // Init CommandLine for InitLogging. |
| base::CommandLine::Init(argc, argv); |
| base::AtExitManager at_exit_manager; |
| brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderrIfTty); |
| |
| // FIXME: Currently only "modules list" command is supported |
| base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); |
| const std::vector<std::string>& args = cl->GetArgs(); |
| if (cl->GetArgs().empty() || |
| !std::equal(kArgsPattern.begin(), kArgsPattern.end(), args.begin(), |
| StringEqualsCaseInsensitiveASCII)) { |
| LOG(ERROR) << "Invalid command."; |
| LOG(ERROR) << "Try following supported commands:"; |
| LOG(ERROR) << " modules - operations on camera modules"; |
| LOG(ERROR) << " list - print available modules"; |
| return 1; |
| } |
| |
| CameraTool tool; |
| tool.PrintCameras(); |
| |
| return 0; |
| } |