blob: dd092169ffde50d229def98201f7514e1ad21272 [file] [log] [blame]
// 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> 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 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 =
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)
// 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<UdevGamepadLinux>(pad_info);
return nullptr;
} // namespace device