| // 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 { |
| |
| // Extract the device |index| from a device node |path|. The path must begin |
| // with |prefix| and the remainder of the string must be parsable as an integer. |
| // Returns true if parsing succeeded. |
| bool DeviceIndexFromDevicePath(base::StringPiece path, |
| base::StringPiece prefix, |
| int* index) { |
| DCHECK(index); |
| if (!path.starts_with(prefix)) |
| return false; |
| base::StringPiece index_str = path; |
| index_str.remove_prefix(prefix.length()); |
| return base::StringToInt(index_str, index); |
| } |
| |
| } // namespace |
| |
| const char UdevGamepadLinux::kInputSubsystem[] = "input"; |
| const char UdevGamepadLinux::kHidrawSubsystem[] = "hidraw"; |
| |
| UdevGamepadLinux::UdevGamepadLinux(Type type, |
| int index, |
| base::StringPiece path, |
| base::StringPiece syspath_prefix) |
| : type(type), index(index), path(path), syspath_prefix(syspath_prefix) {} |
| |
| // static |
| std::unique_ptr<UdevGamepadLinux> UdevGamepadLinux::Create(udev_device* dev) { |
| using DeviceRootPair = std::pair<Type, const char*>; |
| static const std::vector<DeviceRootPair> device_roots = { |
| {Type::EVDEV, "/dev/input/event"}, |
| {Type::JOYDEV, "/dev/input/js"}, |
| {Type::HIDRAW, "/dev/hidraw"}, |
| }; |
| |
| if (!dev) |
| return nullptr; |
| |
| const base::StringPiece node_path = device::udev_device_get_devnode(dev); |
| if (node_path.empty()) |
| return nullptr; |
| |
| const base::StringPiece node_syspath = device::udev_device_get_syspath(dev); |
| if (node_syspath.empty()) |
| return nullptr; |
| |
| base::StringPiece parent_syspath; |
| udev_device* parent_dev = |
| device::udev_device_get_parent_with_subsystem_devtype( |
| dev, kInputSubsystem, nullptr); |
| if (parent_dev) |
| parent_syspath = device::udev_device_get_syspath(parent_dev); |
| |
| for (const auto& entry : device_roots) { |
| const Type node_type = entry.first; |
| const base::StringPiece prefix = entry.second; |
| int index_value; |
| if (!DeviceIndexFromDevicePath(node_path, prefix, &index_value)) |
| 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/ |
| base::StringPiece syspath; |
| base::StringPiece 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; |
| } |
| |
| base::StringPiece syspath_prefix; |
| if (!syspath.empty()) { |
| size_t subsystem_start = syspath.find(subsystem); |
| if (subsystem_start == std::string::npos) |
| return nullptr; |
| syspath_prefix = syspath.substr(0, subsystem_start); |
| } |
| |
| return std::make_unique<UdevGamepadLinux>(node_type, index_value, node_path, |
| syspath_prefix); |
| } |
| return nullptr; |
| } |
| |
| } // namespace device |