// Copyright 2018 The Chromium 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 "device/gamepad/udev_gamepad_linux.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "device/udev_linux/udev.h" namespace device { namespace { int DeviceIndexFromDevicePath(const std::string& path, const std::string& prefix) { if (!base::StartsWith(path, prefix, base::CompareCase::SENSITIVE)) return -1; int index = -1; base::StringPiece index_str(&path.c_str()[prefix.length()], path.length() - prefix.length()); if (!base::StringToInt(index_str, &index)) return -1; return index; } } // namespace const char UdevGamepadLinux::kInputSubsystem[] = "input"; const char UdevGamepadLinux::kHidrawSubsystem[] = "hidraw"; UdevGamepadLinux::UdevGamepadLinux(Type type, int index, const std::string& path, const std::string& syspath_prefix) : type(type), index(index), path(path), syspath_prefix(syspath_prefix) {} // static std::unique_ptr UdevGamepadLinux::Create(udev_device* dev) { using DeviceRootPair = std::pair; static const std::vector device_roots = { {Type::EVDEV, "/dev/input/event"}, {Type::JOYDEV, "/dev/input/js"}, {Type::HIDRAW, "/dev/hidraw"}, }; if (!dev) return nullptr; const char* node_path = device::udev_device_get_devnode(dev); if (!node_path) return nullptr; const char* node_syspath = device::udev_device_get_syspath(dev); udev_device* parent_dev = device::udev_device_get_parent_with_subsystem_devtype( dev, kInputSubsystem, nullptr); const char* parent_syspath = parent_dev ? device::udev_device_get_syspath(parent_dev) : ""; for (const auto& entry : device_roots) { Type node_type = entry.first; const char* prefix = entry.second; int index_value = DeviceIndexFromDevicePath(node_path, prefix); if (index_value < 0) continue; // The syspath can be used to associate device nodes that describe the same // gamepad through multiple interfaces. For input nodes (evdev and joydev), // we use the syspath of the parent node, which describes the underlying // physical device. For hidraw nodes, we use the syspath of the node itself. // // The parent syspaths for matching evdev and joydev nodes will be identical // because they share the same parent node. The syspath for hidraw nodes // will also be identical up to the subsystem. For instance, if the syspath // for the input subsystem is: // /sys/devices/[...]/0003:054C:09CC.0026/input/input91 // And the corresponding hidraw syspath is: // /sys/devices/[...]/0003:054C:09CC.0026/hidraw/hidraw3 // Then |syspath_prefix| is the common prefix before "input" or "hidraw": // /sys/devices/[...]/0003:054C:09CC.0026/ std::string syspath; std::string subsystem; if (node_type == Type::EVDEV || node_type == Type::JOYDEV) { // If the device is in the input subsystem but does not have the // ID_INPUT_JOYSTICK property, ignore it. if (!device::udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK")) return nullptr; syspath = parent_syspath; subsystem = kInputSubsystem; } else if (node_type == Type::HIDRAW) { syspath = node_syspath; subsystem = kHidrawSubsystem; } size_t subsystem_start = syspath.find(subsystem); if (subsystem_start == std::string::npos) return nullptr; std::string syspath_prefix = syspath.substr(0, subsystem_start); UdevGamepadLinux* pad_info = new UdevGamepadLinux(node_type, index_value, node_path, syspath_prefix); return std::unique_ptr(pad_info); } return nullptr; } } // namespace device