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 <linux/hidraw.h> 6 #include <sys/ioctl.h> 7 8 #include <stdint.h> 9 10 #include <string> 11 12 #include "base/bind.h" 13 #include "base/files/file_path.h" 14 #include "base/logging.h" 15 #include "base/platform_file.h" 16 #include "base/stl_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_piece.h" 19 #include "base/strings/string_split.h" 20 #include "device/hid/hid_connection_linux.h" 21 #include "device/hid/hid_device_info.h" 22 #include "device/hid/hid_report_descriptor.h" 23 #include "device/hid/hid_service_linux.h" 24 #include "device/udev_linux/udev.h" 25 26 namespace device { 27 28 namespace { 29 30 const char kHIDSubSystem[] = "hid"; 31 const char kHidrawSubsystem[] = "hidraw"; 32 const char kHIDID[] = "HID_ID"; 33 const char kHIDName[] = "HID_NAME"; 34 const char kHIDUnique[] = "HID_UNIQ"; 35 36 } // namespace 37 38 HidServiceLinux::HidServiceLinux() { 39 DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance(); 40 monitor->AddObserver(this); 41 monitor->Enumerate( 42 base::Bind(&HidServiceLinux::OnDeviceAdded, base::Unretained(this))); 43 } 44 45 scoped_refptr<HidConnection> HidServiceLinux::Connect( 46 const HidDeviceId& device_id) { 47 HidDeviceInfo device_info; 48 if (!GetDeviceInfo(device_id, &device_info)) 49 return NULL; 50 51 ScopedUdevDevicePtr device = 52 DeviceMonitorLinux::GetInstance()->GetDeviceFromPath( 53 device_info.device_id); 54 55 if (device) { 56 std::string dev_node; 57 if (!FindHidrawDevNode(device.get(), &dev_node)) { 58 LOG(ERROR) << "Cannot open HID device as hidraw device."; 59 return NULL; 60 } 61 return new HidConnectionLinux(device_info, dev_node); 62 } 63 64 return NULL; 65 } 66 67 HidServiceLinux::~HidServiceLinux() { 68 if (DeviceMonitorLinux::HasInstance()) 69 DeviceMonitorLinux::GetInstance()->RemoveObserver(this); 70 } 71 72 void HidServiceLinux::OnDeviceAdded(udev_device* device) { 73 if (!device) 74 return; 75 76 const char* device_path = udev_device_get_syspath(device); 77 if (!device_path) 78 return; 79 const char* subsystem = udev_device_get_subsystem(device); 80 if (!subsystem || strcmp(subsystem, kHIDSubSystem) != 0) 81 return; 82 83 HidDeviceInfo device_info; 84 device_info.device_id = device_path; 85 86 uint32_t int_property = 0; 87 const char* str_property = NULL; 88 89 const char* hid_id = udev_device_get_property_value(device, kHIDID); 90 if (!hid_id) 91 return; 92 93 std::vector<std::string> parts; 94 base::SplitString(hid_id, ':', &parts); 95 if (parts.size() != 3) { 96 return; 97 } 98 99 if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) { 100 device_info.vendor_id = int_property; 101 } 102 103 if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) { 104 device_info.product_id = int_property; 105 } 106 107 str_property = udev_device_get_property_value(device, kHIDUnique); 108 if (str_property != NULL) 109 device_info.serial_number = str_property; 110 111 str_property = udev_device_get_property_value(device, kHIDName); 112 if (str_property != NULL) 113 device_info.product_name = str_property; 114 115 std::string dev_node; 116 if (!FindHidrawDevNode(device, &dev_node)) { 117 LOG(ERROR) << "Cannot find device node for HID device."; 118 return; 119 } 120 121 int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; 122 123 base::File device_file(base::FilePath(dev_node), flags); 124 if (!device_file.IsValid()) { 125 LOG(ERROR) << "Cannot open '" << dev_node << "': " 126 << base::File::ErrorToString(device_file.error_details()); 127 return; 128 } 129 130 int desc_size = 0; 131 int res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESCSIZE, &desc_size); 132 if (res < 0) { 133 LOG(ERROR) << "HIDIOCGRDESCSIZE failed."; 134 device_file.Close(); 135 return; 136 } 137 138 hidraw_report_descriptor rpt_desc; 139 rpt_desc.size = desc_size; 140 141 res = ioctl(device_file.GetPlatformFile(), HIDIOCGRDESC, &rpt_desc); 142 if (res < 0) { 143 LOG(ERROR) << "HIDIOCGRDESC failed."; 144 device_file.Close(); 145 return; 146 } 147 148 device_file.Close(); 149 150 HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size); 151 report_descriptor.GetTopLevelCollections(&device_info.usages); 152 153 AddDevice(device_info); 154 } 155 156 void HidServiceLinux::OnDeviceRemoved(udev_device* device) { 157 const char* device_path = udev_device_get_syspath(device);; 158 if (device_path) 159 RemoveDevice(device_path); 160 } 161 162 bool HidServiceLinux::FindHidrawDevNode(udev_device* parent, 163 std::string* result) { 164 udev* udev = udev_device_get_udev(parent); 165 if (!udev) { 166 return false; 167 } 168 ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev)); 169 if (!enumerate) { 170 return false; 171 } 172 if (udev_enumerate_add_match_subsystem(enumerate.get(), kHidrawSubsystem)) { 173 return false; 174 } 175 if (udev_enumerate_scan_devices(enumerate.get())) { 176 return false; 177 } 178 std::string parent_path(udev_device_get_devpath(parent)); 179 if (parent_path.length() == 0 || *parent_path.rbegin() != '/') 180 parent_path += '/'; 181 udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); 182 for (udev_list_entry* i = devices; i != NULL; 183 i = udev_list_entry_get_next(i)) { 184 ScopedUdevDevicePtr hid_dev( 185 udev_device_new_from_syspath(udev, udev_list_entry_get_name(i))); 186 const char* raw_path = udev_device_get_devnode(hid_dev.get()); 187 std::string device_path = udev_device_get_devpath(hid_dev.get()); 188 if (raw_path && 189 !device_path.compare(0, parent_path.length(), parent_path)) { 190 std::string sub_path = device_path.substr(parent_path.length()); 191 if (sub_path.substr(0, sizeof(kHidrawSubsystem) - 1) == 192 kHidrawSubsystem) { 193 *result = raw_path; 194 return true; 195 } 196 } 197 } 198 199 return false; 200 } 201 202 } // namespace device 203