Commit 498c6ca7 authored by Matt Reynolds's avatar Matt Reynolds Committed by Commit Bot

[webhid] Populate physical device ID on Linux

On Linux, the physical device ID is the sysfs path of the device
that best represents the physical device connected to the system.
For USB and Bluetooth devices, this is a device node that
represents the entire physical device as opposed to one particular
HID interface exposed by the device.

For some devices, this cannot be determined because the device
driver does not expose the information or the device is not
physical. For these cases, the sysfs path of the HID interface
is used instead.

BUG=1011499

Change-Id: Ia887baa3dd78c0132d6e9fa0d37791941cd17bda
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1966499Reviewed-by: default avatarThomas Anderson <thomasanderson@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#770760}
parent 1125fe1a
......@@ -7,6 +7,7 @@ import("//tools/generate_library_loader/generate_library_loader.gni")
libudev_functions = [
"udev_device_get_action",
"udev_device_get_devnode",
"udev_device_get_devtype",
"udev_device_get_parent",
"udev_device_get_parent_with_subsystem_devtype",
"udev_device_get_property_value",
......
......@@ -74,6 +74,10 @@ const char* FakeUdevLoader::udev_device_get_devnode(udev_device* udev_device) {
return nullptr;
}
const char* FakeUdevLoader::udev_device_get_devtype(udev_device* udev_device) {
return nullptr;
}
udev_device* FakeUdevLoader::udev_device_get_parent(udev_device* udev_device) {
if (!udev_device) {
return nullptr;
......
......@@ -31,6 +31,7 @@ class FakeUdevLoader : public device::UdevLoader {
bool Init() override;
const char* udev_device_get_action(udev_device* udev_device) override;
const char* udev_device_get_devnode(udev_device* udev_device) override;
const char* udev_device_get_devtype(udev_device* udev_device) override;
udev_device* udev_device_get_parent(udev_device* udev_device) override;
udev_device* udev_device_get_parent_with_subsystem_devtype(
udev_device* udev_device,
......
......@@ -27,6 +27,10 @@ const char* udev_device_get_devnode(udev_device* udev_device) {
return UdevLoader::Get()->udev_device_get_devnode(udev_device);
}
const char* udev_device_get_devtype(udev_device* udev_device) {
return UdevLoader::Get()->udev_device_get_devtype(udev_device);
}
udev_device* udev_device_get_parent(udev_device* udev_device) {
return UdevLoader::Get()->udev_device_get_parent(udev_device);
}
......
......@@ -31,6 +31,7 @@ namespace device {
const char* udev_device_get_action(udev_device* udev_device);
const char* udev_device_get_devnode(udev_device* udev_device);
const char* udev_device_get_devtype(udev_device* udev_device);
udev_device* udev_device_get_parent(udev_device* udev_device);
udev_device* udev_device_get_parent_with_subsystem_devtype(
udev_device* udev_device,
......
......@@ -27,6 +27,10 @@ const char* Udev0Loader::udev_device_get_devnode(udev_device* udev_device) {
return lib_loader_->udev_device_get_devnode(udev_device);
}
const char* Udev0Loader::udev_device_get_devtype(udev_device* udev_device) {
return lib_loader_->udev_device_get_devtype(udev_device);
}
udev_device* Udev0Loader::udev_device_get_parent(udev_device* udev_device) {
return lib_loader_->udev_device_get_parent(udev_device);
}
......
......@@ -23,6 +23,7 @@ class Udev0Loader : public UdevLoader {
bool Init() override;
const char* udev_device_get_action(udev_device* udev_device) override;
const char* udev_device_get_devnode(udev_device* udev_device) override;
const char* udev_device_get_devtype(udev_device* udev_device) override;
udev_device* udev_device_get_parent(udev_device* udev_device) override;
udev_device* udev_device_get_parent_with_subsystem_devtype(
udev_device* udev_device,
......
......@@ -27,6 +27,10 @@ const char* Udev1Loader::udev_device_get_devnode(udev_device* udev_device) {
return lib_loader_->udev_device_get_devnode(udev_device);
}
const char* Udev1Loader::udev_device_get_devtype(udev_device* udev_device) {
return lib_loader_->udev_device_get_devtype(udev_device);
}
udev_device* Udev1Loader::udev_device_get_parent(udev_device* udev_device) {
return lib_loader_->udev_device_get_parent(udev_device);
}
......
......@@ -23,6 +23,7 @@ class Udev1Loader : public UdevLoader {
bool Init() override;
const char* udev_device_get_action(udev_device* udev_device) override;
const char* udev_device_get_devnode(udev_device* udev_device) override;
const char* udev_device_get_devtype(udev_device* udev_device) override;
udev_device* udev_device_get_parent(udev_device* udev_device) override;
udev_device* udev_device_get_parent_with_subsystem_devtype(
udev_device* udev_device,
......
......@@ -45,6 +45,7 @@ class UdevLoader {
virtual const char* udev_device_get_action(udev_device* udev_device) = 0;
virtual const char* udev_device_get_devnode(udev_device* udev_device) = 0;
virtual const char* udev_device_get_devtype(udev_device* udev_device) = 0;
virtual udev_device* udev_device_get_parent(udev_device* udev_device) = 0;
virtual udev_device* udev_device_get_parent_with_subsystem_devtype(
udev_device* udev_device,
......
......@@ -5,6 +5,7 @@
#include "services/device/hid/hid_service_linux.h"
#include <fcntl.h>
#include <linux/input.h>
#include <stdint.h>
#include <limits>
......@@ -24,6 +25,7 @@
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/sequenced_task_runner_handle.h"
......@@ -42,11 +44,132 @@ namespace device {
namespace {
const char kHidrawSubsystem[] = "hidraw";
const char kDevtypeUsbDevice[] = "usb_device";
const char kSubsystemBluetooth[] = "bluetooth";
const char kSubsystemHid[] = "hid";
const char kSubsystemHidraw[] = "hidraw";
const char kSubsystemUsb[] = "usb";
const char kHIDID[] = "HID_ID";
const char kHIDName[] = "HID_NAME";
const char kHIDUnique[] = "HID_UNIQ";
const char kSysfsReportDescriptorKey[] = "report_descriptor";
const char kKernelHciPrefix[] = "hci";
// Walks up the sysfs device tree starting at |device| and returns the first
// ancestor in the "hid" subsystem. Returns nullptr on failure.
udev_device* FindFirstHidAncestor(udev_device* device) {
udev_device* ancestor = device;
do {
const char* subsystem = udev_device_get_subsystem(ancestor);
if (!subsystem)
return nullptr;
if (strcmp(subsystem, kSubsystemHid) == 0)
return ancestor;
} while ((ancestor = udev_device_get_parent(ancestor)));
return nullptr;
}
// Walks up the sysfs device tree starting at |device| and returns the first
// ancestor not in the "hid" or "hidraw" subsystems. Returns nullptr on failure.
udev_device* FindFirstNonHidAncestor(udev_device* device) {
udev_device* ancestor = device;
do {
const char* subsystem = udev_device_get_subsystem(ancestor);
if (!subsystem)
return nullptr;
if (strcmp(subsystem, kSubsystemHid) != 0 &&
strcmp(subsystem, kSubsystemHidraw) != 0) {
return ancestor;
}
} while ((ancestor = udev_device_get_parent(ancestor)));
return nullptr;
}
// Returns the sysfs path for a USB device |usb_device|, or nullptr if the sysfs
// path could not be retrieved. |usb_device| must be a device in the "usb"
// subsystem.
//
// Some USB devices expose multiple interfaces. If |usb_device| refers to a
// single USB interface, walk up the device tree to find the ancestor that
// represents the physical device.
const char* GetUsbDeviceSyspath(udev_device* usb_device) {
do {
const char* subsystem = udev_device_get_subsystem(usb_device);
if (!subsystem || strcmp(subsystem, kSubsystemUsb) != 0)
return nullptr;
const char* devtype = udev_device_get_devtype(usb_device);
if (!devtype)
return nullptr;
// Use the syspath of the first ancestor with devtype "usb_device".
if (strcmp(devtype, kDevtypeUsbDevice) == 0)
return udev_device_get_syspath(usb_device);
} while ((usb_device = udev_device_get_parent(usb_device)));
return nullptr;
}
// Returns the sysfs path for a Bluetooth Classic device |bt_device|, or nullptr
// if the sysfs path could not be retrieved. |bt_device| must be a device in the
// "bluetooth" subsystem.
const char* GetBluetoothDeviceSyspath(udev_device* bt_device) {
do {
const char* subsystem = udev_device_get_subsystem(bt_device);
if (!subsystem || strcmp(subsystem, kSubsystemBluetooth) != 0)
return nullptr;
// Look for a sysname like "hci0:123".
const char* sysfs_name = udev_device_get_sysname(bt_device);
if (!sysfs_name)
return nullptr;
std::vector<std::string> parts = base::SplitString(
sysfs_name, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (parts.size() == 2 && base::StartsWith(parts[0], kKernelHciPrefix,
base::CompareCase::SENSITIVE)) {
return udev_device_get_syspath(bt_device);
}
} while ((bt_device = udev_device_get_parent(bt_device)));
return nullptr;
}
// Returns the physical device ID for a device |hidraw_device|. On Linux, the
// physical device ID is the sysfs path to the device node that represents the
// physical device if it is available. When the physical device node is not
// available, the sysfs path of the HID interface is returned instead. Returns
// nullptr on failure.
const char* GetPhysicalDeviceId(udev_device* hidraw_device) {
const char* subsystem = udev_device_get_subsystem(hidraw_device);
if (!subsystem || strcmp(subsystem, kSubsystemHidraw) != 0)
return nullptr;
udev_device* hid_ancestor = FindFirstHidAncestor(hidraw_device);
if (!hid_ancestor)
return nullptr;
const char* hid_sysfs_path = udev_device_get_syspath(hid_ancestor);
udev_device* ancestor = FindFirstNonHidAncestor(hid_ancestor);
if (!ancestor)
return hid_sysfs_path;
const char* ancestor_subsystem = udev_device_get_subsystem(ancestor);
if (!ancestor_subsystem)
return hid_sysfs_path;
if (strcmp(ancestor_subsystem, kSubsystemUsb) == 0) {
const char* usb_sysfs_path = GetUsbDeviceSyspath(ancestor);
if (usb_sysfs_path)
return usb_sysfs_path;
}
if (strcmp(ancestor_subsystem, kSubsystemBluetooth) == 0) {
const char* bt_sysfs_path = GetBluetoothDeviceSyspath(ancestor);
if (bt_sysfs_path)
return bt_sysfs_path;
}
return hid_sysfs_path;
}
} // namespace
......@@ -102,7 +225,7 @@ class HidServiceLinux::BlockingTaskRunnerHelper : public UdevWatcher::Observer {
HidPlatformDeviceId platform_device_id = device_path;
const char* subsystem = udev_device_get_subsystem(device.get());
if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0)
if (!subsystem || strcmp(subsystem, kSubsystemHidraw) != 0)
return;
const char* str_property = udev_device_get_devnode(device.get());
......@@ -155,14 +278,21 @@ class HidServiceLinux::BlockingTaskRunnerHelper : public UdevWatcher::Observer {
if (!base::ReadFileToString(report_descriptor_path, &report_descriptor_str))
return;
scoped_refptr<HidDeviceInfo> device_info(
new HidDeviceInfo(platform_device_id, /*physical_device_id=*/"",
vendor_id, product_id, product_name, serial_number,
// TODO(reillyg): Detect Bluetooth. crbug.com/443335
mojom::HidBusType::kHIDBusTypeUSB,
std::vector<uint8_t>(report_descriptor_str.begin(),
report_descriptor_str.end()),
device_node));
const char* physical_device_id = GetPhysicalDeviceId(device.get());
if (!physical_device_id) {
HID_LOG(EVENT) << "GetPhysicalDeviceId failed for '" << device_path
<< "'";
return;
}
auto device_info = base::MakeRefCounted<HidDeviceInfo>(
platform_device_id, physical_device_id, vendor_id, product_id,
product_name, serial_number,
// TODO(reillyg): Detect Bluetooth. crbug.com/443335
mojom::HidBusType::kHIDBusTypeUSB,
std::vector<uint8_t>(report_descriptor_str.begin(),
report_descriptor_str.end()),
device_node);
task_runner_->PostTask(
FROM_HERE,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment