summaryrefslogtreecommitdiff
path: root/chromium/services/device/hid
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/services/device/hid')
-rw-r--r--chromium/services/device/hid/hid_service.h2
-rw-r--r--chromium/services/device/hid/hid_service_linux.cc157
-rw-r--r--chromium/services/device/hid/hid_service_mac.cc6
-rw-r--r--chromium/services/device/hid/hid_service_win.cc187
-rw-r--r--chromium/services/device/hid/hid_service_win.h3
5 files changed, 301 insertions, 54 deletions
diff --git a/chromium/services/device/hid/hid_service.h b/chromium/services/device/hid/hid_service.h
index 1db1f5fbe51..49a49dd7ae9 100644
--- a/chromium/services/device/hid/hid_service.h
+++ b/chromium/services/device/hid/hid_service.h
@@ -11,7 +11,7 @@
#include <vector>
#include "base/bind_helpers.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
diff --git a/chromium/services/device/hid/hid_service_linux.cc b/chromium/services/device/hid/hid_service_linux.cc
index 55c636faf1e..28bf7f8641f 100644
--- a/chromium/services/device/hid/hid_service_linux.cc
+++ b/chromium/services/device/hid/hid_service_linux.cc
@@ -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
@@ -82,7 +205,8 @@ class HidServiceLinux::BlockingTaskRunnerHelper : public UdevWatcher::Observer {
void Start() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- watcher_ = UdevWatcher::StartWatching(this);
+ watcher_ = UdevWatcher::StartWatching(
+ this, {UdevWatcher::Filter(kSubsystemHidraw, "")});
watcher_->EnumerateExistingDevices();
task_runner_->PostTask(
FROM_HERE,
@@ -101,9 +225,11 @@ class HidServiceLinux::BlockingTaskRunnerHelper : public UdevWatcher::Observer {
return;
HidPlatformDeviceId platform_device_id = device_path;
+#if DCHECK_IS_ON()
const char* subsystem = udev_device_get_subsystem(device.get());
- if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0)
- return;
+ DCHECK(subsystem);
+ DCHECK_EQ(base::StringPiece(subsystem), kSubsystemHidraw);
+#endif
const char* str_property = udev_device_get_devnode(device.get());
if (!str_property)
@@ -155,14 +281,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,
diff --git a/chromium/services/device/hid/hid_service_mac.cc b/chromium/services/device/hid/hid_service_mac.cc
index f65c8afda57..5ef38b1e0ba 100644
--- a/chromium/services/device/hid/hid_service_mac.cc
+++ b/chromium/services/device/hid/hid_service_mac.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/thread_pool.h"
@@ -78,8 +79,11 @@ scoped_refptr<HidDeviceInfo> CreateDeviceInfo(
HID_LOG(DEBUG) << "Device report descriptor not available.";
}
+ int32_t location_id = GetIntProperty(service, CFSTR(kIOHIDLocationIDKey));
+ std::string physical_device_id = base::NumberToString(location_id);
+
return new HidDeviceInfo(
- entry_id, /*physical_device_id=*/"",
+ entry_id, physical_device_id,
GetIntProperty(service, CFSTR(kIOHIDVendorIDKey)),
GetIntProperty(service, CFSTR(kIOHIDProductIDKey)),
GetStringProperty(service, CFSTR(kIOHIDProductKey)),
diff --git a/chromium/services/device/hid/hid_service_win.cc b/chromium/services/device/hid/hid_service_win.cc
index b9a5e7a3ec6..80fff2d39a8 100644
--- a/chromium/services/device/hid/hid_service_win.cc
+++ b/chromium/services/device/hid/hid_service_win.cc
@@ -4,15 +4,16 @@
#include "services/device/hid/hid_service_win.h"
-#include <memory>
-
#define INITGUID
#include <dbt.h>
+#include <devpkey.h>
#include <setupapi.h>
#include <stddef.h>
+#include <wdmguid.h>
#include <winioctl.h>
+#include <memory>
#include <utility>
#include "base/bind.h"
@@ -22,15 +23,112 @@
#include "base/memory/free_deleter.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
-#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/win/scoped_devinfo.h"
+#include "base/win/win_util.h"
#include "components/device_event_log/device_event_log.h"
#include "services/device/hid/hid_connection_win.h"
#include "services/device/hid/hid_device_info.h"
namespace device {
+namespace {
+
+// Looks up the value of a GUID-type device property specified by |property| for
+// the device described by |device_info_data|. On success, returns true and sets
+// |property_buffer| to the property value. Returns false if the property is not
+// present or has a different type.
+bool GetDeviceGuidProperty(HDEVINFO device_info_set,
+ SP_DEVINFO_DATA& device_info_data,
+ const DEVPROPKEY& property,
+ GUID* property_buffer) {
+ DEVPROPTYPE property_type;
+ if (!SetupDiGetDeviceProperty(
+ device_info_set, &device_info_data, &property, &property_type,
+ reinterpret_cast<PBYTE>(property_buffer), sizeof(*property_buffer),
+ /*RequiredSize=*/nullptr, /*Flags=*/0) ||
+ property_type != DEVPROP_TYPE_GUID) {
+ return false;
+ }
+ return true;
+}
+
+// Looks up information about the device described by |device_interface_data|
+// in |device_info_set|. On success, returns true and sets |device_info_data|
+// and |device_path|. Returns false if an error occurred.
+bool GetDeviceInfoAndPathFromInterface(
+ HDEVINFO device_info_set,
+ SP_DEVICE_INTERFACE_DATA& device_interface_data,
+ SP_DEVINFO_DATA* device_info_data,
+ base::string16* device_path) {
+ // Get the required buffer size. When called with
+ // DeviceInterfaceDetailData == nullptr and DeviceInterfaceDetailSize == 0,
+ // SetupDiGetDeviceInterfaceDetail returns the required buffer size at
+ // RequiredSize and fails with GetLastError() == ERROR_INSUFFICIENT_BUFFER.
+ DWORD required_size;
+ if (SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data,
+ /*DeviceInterfaceDetailData=*/nullptr,
+ /*DeviceInterfaceDetailSize=*/0,
+ &required_size,
+ /*DeviceInfoData=*/nullptr) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+
+ std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
+ device_interface_detail_data(
+ static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
+ device_interface_detail_data->cbSize = sizeof(*device_interface_detail_data);
+
+ // Call the function again with the correct buffer size to get the detailed
+ // data for this device.
+ if (!SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data,
+ device_interface_detail_data.get(),
+ required_size, /*RequiredSize=*/nullptr,
+ device_info_data)) {
+ return false;
+ }
+
+ // Windows uses case-insensitive paths and may return paths that differ only
+ // by case. Canonicalize the device path by converting to lowercase.
+ base::string16 path =
+ base::string16(device_interface_detail_data->DevicePath);
+ DCHECK(base::IsStringASCII(path));
+ *device_path = base::ToLowerASCII(path);
+ return true;
+}
+
+// Returns a device info set containing only the device described by
+// |device_path|, or an invalid ScopedDevInfo if there was an error while
+// creating the device set. The device info is returned in |device_info_data|.
+base::win::ScopedDevInfo GetDeviceInfoFromPath(
+ const base::string16& device_path,
+ SP_DEVINFO_DATA* device_info_data) {
+ base::win::ScopedDevInfo device_info_set(SetupDiGetClassDevs(
+ &GUID_DEVINTERFACE_HID, /*Enumerator=*/nullptr,
+ /*hwndParent=*/0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
+ if (!device_info_set.is_valid())
+ return base::win::ScopedDevInfo();
+
+ SP_DEVICE_INTERFACE_DATA device_interface_data;
+ device_interface_data.cbSize = sizeof(device_interface_data);
+ if (!SetupDiOpenDeviceInterface(device_info_set.get(), device_path.c_str(),
+ /*OpenFlags=*/0, &device_interface_data)) {
+ return base::win::ScopedDevInfo();
+ }
+
+ base::string16 intf_device_path;
+ GetDeviceInfoAndPathFromInterface(device_info_set.get(),
+ device_interface_data, device_info_data,
+ &intf_device_path);
+ DCHECK_EQ(intf_device_path, device_path);
+ return device_info_set;
+}
+
+} // namespace
+
HidServiceWin::HidServiceWin()
: task_runner_(base::SequencedTaskRunnerHandle::Get()),
blocking_task_runner_(
@@ -81,41 +179,37 @@ base::WeakPtr<HidService> HidServiceWin::GetWeakPtr() {
void HidServiceWin::EnumerateBlocking(
base::WeakPtr<HidServiceWin> service,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
- HDEVINFO device_info_set =
- SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, NULL,
- DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ base::win::ScopedDevInfo dev_info(SetupDiGetClassDevs(
+ &GUID_DEVINTERFACE_HID, /*Enumerator=*/nullptr,
+ /*hwndParent=*/nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
- if (device_info_set != INVALID_HANDLE_VALUE) {
- SP_DEVICE_INTERFACE_DATA device_interface_data;
- device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ if (dev_info.is_valid()) {
+ SP_DEVICE_INTERFACE_DATA device_interface_data = {0};
+ device_interface_data.cbSize = sizeof(device_interface_data);
for (int device_index = 0; SetupDiEnumDeviceInterfaces(
- device_info_set, NULL, &GUID_DEVINTERFACE_HID, device_index,
- &device_interface_data);
+ dev_info.get(), /*DeviceInfoData=*/nullptr, &GUID_DEVINTERFACE_HID,
+ device_index, &device_interface_data);
++device_index) {
- DWORD required_size = 0;
-
- // Determime the required size of detail struct.
- SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data,
- NULL, 0, &required_size, NULL);
+ SP_DEVINFO_DATA dev_info_data = {0};
+ dev_info_data.cbSize = sizeof(dev_info_data);
+ base::string16 device_path;
+ if (!GetDeviceInfoAndPathFromInterface(dev_info.get(),
+ device_interface_data,
+ &dev_info_data, &device_path)) {
+ continue;
+ }
- std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
- device_interface_detail_data(
- static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
- device_interface_detail_data->cbSize =
- sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
-
- // Get the detailed data for this device.
- BOOL res = SetupDiGetDeviceInterfaceDetail(
- device_info_set, &device_interface_data,
- device_interface_detail_data.get(), required_size, NULL, NULL);
- if (!res) {
+ // Get the container ID for the physical device.
+ GUID container_id;
+ if (!GetDeviceGuidProperty(dev_info.get(), dev_info_data,
+ DEVPKEY_Device_ContainerId, &container_id)) {
continue;
}
+ std::string physical_device_id =
+ base::UTF16ToUTF8(base::win::String16FromGUID(container_id));
- base::string16 device_path(device_interface_detail_data->DevicePath);
- DCHECK(base::IsStringASCII(device_path));
- AddDeviceBlocking(service, task_runner, base::ToLowerASCII(device_path));
+ AddDeviceBlocking(service, task_runner, device_path, physical_device_id);
}
}
@@ -170,14 +264,15 @@ void HidServiceWin::CollectInfoFromValueCaps(
void HidServiceWin::AddDeviceBlocking(
base::WeakPtr<HidServiceWin> service,
scoped_refptr<base::SequencedTaskRunner> task_runner,
- const base::string16& device_path) {
+ const base::string16& device_path,
+ const std::string& physical_device_id) {
base::win::ScopedHandle device_handle(OpenDevice(device_path));
if (!device_handle.IsValid()) {
return;
}
HIDD_ATTRIBUTES attrib = {0};
- attrib.Size = sizeof(HIDD_ATTRIBUTES);
+ attrib.Size = sizeof(attrib);
if (!HidD_GetAttributes(device_handle.Get(), &attrib)) {
HID_LOG(EVENT) << "Failed to get device attributes.";
return;
@@ -237,24 +332,24 @@ void HidServiceWin::AddDeviceBlocking(
// 1023 characters plus NULL terminator is more than enough for a USB string
// descriptor which is limited to 126 characters.
- wchar_t buffer[1024];
+ base::char16 buffer[1024];
std::string product_name;
if (HidD_GetProductString(device_handle.Get(), &buffer[0], sizeof(buffer))) {
// NULL termination guaranteed by the API.
- product_name = base::SysWideToUTF8(buffer);
+ product_name = base::UTF16ToUTF8(buffer);
}
std::string serial_number;
if (HidD_GetSerialNumberString(device_handle.Get(), &buffer[0],
sizeof(buffer))) {
// NULL termination guaranteed by the API.
- serial_number = base::SysWideToUTF8(buffer);
+ serial_number = base::UTF16ToUTF8(buffer);
}
// This populates the HidDeviceInfo instance without a raw report descriptor.
// The descriptor is unavailable on Windows because HID devices are exposed to
// user-space as individual top-level collections.
scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfo(
- device_path, /*physical_device_id=*/"", attrib.VendorID, attrib.ProductID,
+ device_path, physical_device_id, attrib.VendorID, attrib.ProductID,
product_name, serial_number,
// TODO(reillyg): Detect Bluetooth. crbug.com/443335
mojom::HidBusType::kHIDBusTypeUSB, std::move(collection_info),
@@ -267,10 +362,24 @@ void HidServiceWin::AddDeviceBlocking(
void HidServiceWin::OnDeviceAdded(const GUID& class_guid,
const base::string16& device_path) {
+ SP_DEVINFO_DATA device_info_data = {0};
+ device_info_data.cbSize = sizeof(device_info_data);
+ auto device_info_set = GetDeviceInfoFromPath(device_path, &device_info_data);
+ if (!device_info_set.is_valid())
+ return;
+
+ GUID container_id;
+ if (!GetDeviceGuidProperty(device_info_set.get(), device_info_data,
+ DEVPKEY_Device_ContainerId, &container_id)) {
+ return;
+ }
+ std::string physical_device_id =
+ base::UTF16ToUTF8(base::win::String16FromGUID(container_id));
+
blocking_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&HidServiceWin::AddDeviceBlocking,
- weak_factory_.GetWeakPtr(), task_runner_, device_path));
+ FROM_HERE, base::BindOnce(&HidServiceWin::AddDeviceBlocking,
+ weak_factory_.GetWeakPtr(), task_runner_,
+ device_path, physical_device_id));
}
void HidServiceWin::OnDeviceRemoved(const GUID& class_guid,
diff --git a/chromium/services/device/hid/hid_service_win.h b/chromium/services/device/hid/hid_service_win.h
index 4965888e989..ac6745f3aaf 100644
--- a/chromium/services/device/hid/hid_service_win.h
+++ b/chromium/services/device/hid/hid_service_win.h
@@ -59,7 +59,8 @@ class HidServiceWin : public HidService, public DeviceMonitorWin::Observer {
static void AddDeviceBlocking(
base::WeakPtr<HidServiceWin> service,
scoped_refptr<base::SequencedTaskRunner> task_runner,
- const base::string16& device_path);
+ const base::string16& device_path,
+ const std::string& physical_device_id);
// DeviceMonitorWin::Observer implementation:
void OnDeviceAdded(const GUID& class_guid,