1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "device/hid/hid_service_linux.h" 6 7 #include <linux/hidraw.h> 8 #include <sys/ioctl.h> 9 #include <stdint.h> 10 11 #include <string> 12 13 #include "base/bind.h" 14 #include "base/files/file.h" 15 #include "base/files/file_path.h" 16 #include "base/logging.h" 17 #include "base/stl_util.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_piece.h" 20 #include "base/strings/string_split.h" 21 #include "base/threading/thread_restrictions.h" 22 #include "device/hid/hid_connection_linux.h" 23 #include "device/hid/hid_device_info.h" 24 #include "device/hid/hid_report_descriptor.h" 25 #include "device/udev_linux/udev.h" 26 27 #if defined(OS_CHROMEOS) 28 #include "base/sys_info.h" 29 #include "chromeos/dbus/dbus_thread_manager.h" 30 #include "chromeos/dbus/permission_broker_client.h" 31 #endif // defined(OS_CHROMEOS) 32 33 namespace device { 34 35 namespace { 36 37 const char kHidrawSubsystem[] = "hidraw"; 38 const char kHIDID[] = "HID_ID"; 39 const char kHIDName[] = "HID_NAME"; 40 const char kHIDUnique[] = "HID_UNIQ"; 41 42 } // namespace 43 44 HidServiceLinux::HidServiceLinux( 45 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) 46 : ui_task_runner_(ui_task_runner), 47 weak_factory_(this) { 48 base::ThreadRestrictions::AssertIOAllowed(); 49 DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance(); 50 monitor->AddObserver(this); 51 monitor->Enumerate( 52 base::Bind(&HidServiceLinux::OnDeviceAdded, weak_factory_.GetWeakPtr())); 53 } 54 55 scoped_refptr<HidConnection> HidServiceLinux::Connect( 56 const HidDeviceId& device_id) { 57 HidDeviceInfo device_info; 58 if (!GetDeviceInfo(device_id, &device_info)) 59 return NULL; 60 61 ScopedUdevDevicePtr device = 62 DeviceMonitorLinux::GetInstance()->GetDeviceFromPath( 63 device_info.device_id); 64 65 if (device) { 66 std::string dev_node = udev_device_get_devnode(device.get()); 67 return new HidConnectionLinux(device_info, dev_node); 68 } 69 70 return NULL; 71 } 72 73 HidServiceLinux::~HidServiceLinux() { 74 if (DeviceMonitorLinux::HasInstance()) 75 DeviceMonitorLinux::GetInstance()->RemoveObserver(this); 76 } 77 78 void HidServiceLinux::OnDeviceAdded(udev_device* device) { 79 if (!device) 80 return; 81 82 const char* device_path = udev_device_get_syspath(device); 83 if (!device_path) 84 return; 85 const char* subsystem = udev_device_get_subsystem(device); 86 if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0) 87 return; 88 89 scoped_ptr<HidDeviceInfo> device_info(new HidDeviceInfo); 90 device_info->device_id = device_path; 91 92 uint32_t int_property = 0; 93 const char* str_property = NULL; 94 95 udev_device *parent = udev_device_get_parent(device); 96 if (!parent) { 97 return; 98 } 99 100 const char* hid_id = udev_device_get_property_value(parent, kHIDID); 101 if (!hid_id) 102 return; 103 104 std::vector<std::string> parts; 105 base::SplitString(hid_id, ':', &parts); 106 if (parts.size() != 3) { 107 return; 108 } 109 110 if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) { 111 device_info->vendor_id = int_property; 112 } 113 114 if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) { 115 device_info->product_id = int_property; 116 } 117 118 str_property = udev_device_get_property_value(parent, kHIDUnique); 119 if (str_property != NULL) 120 device_info->serial_number = str_property; 121 122 str_property = udev_device_get_property_value(parent, kHIDName); 123 if (str_property != NULL) 124 device_info->product_name = str_property; 125 126 const std::string dev_node = udev_device_get_devnode(device); 127 #if defined(OS_CHROMEOS) 128 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to 129 // use permission broker. 130 if (base::SysInfo::IsRunningOnChromeOS()) { 131 chromeos::PermissionBrokerClient* client = 132 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); 133 DCHECK(client) << "Could not get permission broker client."; 134 if (!client) { 135 return; 136 } 137 ui_task_runner_->PostTask( 138 FROM_HERE, 139 base::Bind(&chromeos::PermissionBrokerClient::RequestPathAccess, 140 base::Unretained(client), 141 dev_node, 142 -1, 143 base::Bind(&HidServiceLinux::OnRequestAccessComplete, 144 weak_factory_.GetWeakPtr(), 145 dev_node, 146 base::Passed(&device_info)))); 147 } else { 148 OnRequestAccessComplete(dev_node, device_info.Pass(), true); 149 } 150 #else 151 OnRequestAccessComplete(dev_node, device_info.Pass(), true); 152 #endif // defined(OS_CHROMEOS) 153 } 154 155 void HidServiceLinux::OnDeviceRemoved(udev_device* device) { 156 const char* device_path = udev_device_get_syspath(device);; 157 if (device_path) 158 RemoveDevice(device_path); 159 } 160 161 void HidServiceLinux::OnRequestAccessComplete( 162 const std::string& path, 163 scoped_ptr<HidDeviceInfo> device_info, 164 bool success) { 165 const int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; 166 base::File device_file(base::FilePath(path), flags); 167 if (!device_file.IsValid()) { 168 LOG(ERROR) << "Cannot open '" << path << "': " 169 << base::File::ErrorToString(device_file.error_details()); 170 return; 171 } 172 173 int desc_size = 0; 174 int res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESCSIZE, &desc_size); 175 if (res < 0) { 176 PLOG(ERROR) << "Failed to get report descriptor size"; 177 device_file.Close(); 178 return; 179 } 180 181 hidraw_report_descriptor rpt_desc; 182 rpt_desc.size = desc_size; 183 184 res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESC, &rpt_desc); 185 if (res < 0) { 186 PLOG(ERROR) << "Failed to get report descriptor"; 187 device_file.Close(); 188 return; 189 } 190 191 device_file.Close(); 192 193 HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size); 194 report_descriptor.GetDetails(&device_info->collections, 195 &device_info->has_report_id, 196 &device_info->max_input_report_size, 197 &device_info->max_output_report_size, 198 &device_info->max_feature_report_size); 199 200 AddDevice(*device_info); 201 } 202 203 } // namespace device 204