diff options
Diffstat (limited to 'chromium/services/device/hid')
-rw-r--r-- | chromium/services/device/hid/hid_service.h | 2 | ||||
-rw-r--r-- | chromium/services/device/hid/hid_service_linux.cc | 157 | ||||
-rw-r--r-- | chromium/services/device/hid/hid_service_mac.cc | 6 | ||||
-rw-r--r-- | chromium/services/device/hid/hid_service_win.cc | 187 | ||||
-rw-r--r-- | chromium/services/device/hid/hid_service_win.h | 3 |
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, |