[email protected] | b550868 | 2014-04-22 17:10:01 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
[email protected] | 1c39a6b | 2012-04-27 16:09:57 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
reillyg | 4591981 | 2015-01-22 02:39:59 | [diff] [blame] | 5 | #include "device/usb/usb_service_impl.h" |
[email protected] | 1c39a6b | 2012-04-27 16:09:57 | [diff] [blame] | 6 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 7 | #include <algorithm> |
[email protected] | d165a644 | 2013-08-08 19:38:11 | [diff] [blame] | 8 | #include <set> |
[email protected] | 22ac24e | 2012-10-03 17:56:05 | [diff] [blame] | 9 | |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 10 | #include "base/bind.h" |
reillyg | 4591981 | 2015-01-22 02:39:59 | [diff] [blame] | 11 | #include "base/location.h" |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 12 | #include "base/memory/weak_ptr.h" |
reillyg | e471fab | 2014-08-29 01:58:43 | [diff] [blame] | 13 | #include "base/single_thread_task_runner.h" |
[email protected] | 1c39a6b | 2012-04-27 16:09:57 | [diff] [blame] | 14 | #include "base/stl_util.h" |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 15 | #include "base/strings/string_number_conversions.h" |
| 16 | #include "base/strings/utf_string_conversions.h" |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 17 | #include "base/thread_task_runner_handle.h" |
reillyg | 0f2dc63 | 2015-02-23 20:02:08 | [diff] [blame] | 18 | #include "components/device_event_log/device_event_log.h" |
reillyg | d77718d | 2014-09-04 00:57:56 | [diff] [blame] | 19 | #include "device/usb/usb_error.h" |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 20 | #include "third_party/libusb/src/libusb/libusb.h" |
[email protected] | 1c39a6b | 2012-04-27 16:09:57 | [diff] [blame] | 21 | |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 22 | #if defined(OS_WIN) |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 23 | #include <setupapi.h> |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 24 | #include <usbiodef.h> |
| 25 | |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 26 | #include "base/strings/string_util.h" |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 27 | #endif // OS_WIN |
| 28 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 29 | #if defined(USE_UDEV) |
| 30 | #include "device/udev_linux/scoped_udev.h" |
| 31 | #endif // USE_UDEV |
| 32 | |
reillyg | d77718d | 2014-09-04 00:57:56 | [diff] [blame] | 33 | namespace device { |
[email protected] | b550868 | 2014-04-22 17:10:01 | [diff] [blame] | 34 | |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 35 | namespace { |
| 36 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 37 | #if defined(USE_UDEV) |
| 38 | |
| 39 | void ReadDeviceStrings(PlatformUsbDevice platform_device, |
| 40 | libusb_device_descriptor* descriptor, |
| 41 | base::string16* manufacturer_string, |
| 42 | base::string16* product_string, |
| 43 | base::string16* serial_number, |
| 44 | std::string* device_node) { |
| 45 | ScopedUdevPtr udev(udev_new()); |
| 46 | ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev.get())); |
| 47 | |
| 48 | udev_enumerate_add_match_subsystem(enumerate.get(), "usb"); |
| 49 | if (udev_enumerate_scan_devices(enumerate.get()) != 0) { |
| 50 | return; |
| 51 | } |
| 52 | std::string bus_number = |
| 53 | base::IntToString(libusb_get_bus_number(platform_device)); |
| 54 | std::string device_address = |
| 55 | base::IntToString(libusb_get_device_address(platform_device)); |
| 56 | udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); |
| 57 | for (udev_list_entry* i = devices; i != NULL; |
| 58 | i = udev_list_entry_get_next(i)) { |
| 59 | ScopedUdevDevicePtr device( |
| 60 | udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i))); |
| 61 | if (device) { |
| 62 | const char* value = udev_device_get_sysattr_value(device.get(), "busnum"); |
| 63 | if (!value || bus_number != value) { |
| 64 | continue; |
| 65 | } |
| 66 | value = udev_device_get_sysattr_value(device.get(), "devnum"); |
| 67 | if (!value || device_address != value) { |
| 68 | continue; |
| 69 | } |
| 70 | |
| 71 | value = udev_device_get_devnode(device.get()); |
| 72 | if (value) { |
| 73 | *device_node = value; |
| 74 | } |
| 75 | value = udev_device_get_sysattr_value(device.get(), "manufacturer"); |
| 76 | if (value) { |
| 77 | *manufacturer_string = base::UTF8ToUTF16(value); |
| 78 | } |
| 79 | value = udev_device_get_sysattr_value(device.get(), "product"); |
| 80 | if (value) { |
| 81 | *product_string = base::UTF8ToUTF16(value); |
| 82 | } |
| 83 | value = udev_device_get_sysattr_value(device.get(), "serial"); |
| 84 | if (value) { |
| 85 | *serial_number = base::UTF8ToUTF16(value); |
| 86 | } |
| 87 | break; |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | #else |
| 93 | |
| 94 | uint16 ReadDeviceLanguage(PlatformUsbDeviceHandle handle) { |
| 95 | uint16 language_id = 0x0409; |
| 96 | uint8 buffer[256]; |
| 97 | int size = |
| 98 | libusb_get_string_descriptor(handle, 0, 0, &buffer[0], sizeof(buffer)); |
| 99 | if (size < 0) { |
| 100 | USB_LOG(EVENT) << "Failed to get supported string languages: " |
| 101 | << ConvertPlatformUsbErrorToString(size); |
| 102 | } else if (size >= 4) { |
| 103 | // Just pick the first supported language. |
| 104 | language_id = buffer[2] | (buffer[3] << 8); |
| 105 | } else { |
| 106 | USB_LOG(EVENT) << "List of available string languages invalid."; |
| 107 | } |
| 108 | |
| 109 | return language_id; |
| 110 | } |
| 111 | |
| 112 | void ReadDeviceString(PlatformUsbDeviceHandle handle, |
| 113 | uint8 string_id, |
| 114 | uint16 language_id, |
| 115 | base::string16* string) { |
| 116 | if (string_id == 0) { |
| 117 | return; |
| 118 | } |
| 119 | |
| 120 | uint8 buffer[256]; |
| 121 | int size = libusb_get_string_descriptor(handle, string_id, language_id, |
| 122 | &buffer[0], sizeof(buffer)); |
| 123 | if (size < 0) { |
| 124 | USB_LOG(EVENT) << "Failed to read string " << (int)string_id |
| 125 | << " from the device: " |
| 126 | << ConvertPlatformUsbErrorToString(size); |
| 127 | } else if (size > 2) { |
| 128 | *string = base::string16(reinterpret_cast<base::char16*>(&buffer[2]), |
| 129 | size / 2 - 1); |
| 130 | } else { |
| 131 | USB_LOG(EVENT) << "String descriptor " << string_id << " is invalid."; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | void ReadDeviceStrings(PlatformUsbDevice platform_device, |
| 136 | libusb_device_descriptor* descriptor, |
| 137 | base::string16* manufacturer_string, |
| 138 | base::string16* product_string, |
| 139 | base::string16* serial_number, |
| 140 | std::string* device_node) { |
| 141 | if (descriptor->iManufacturer == 0 && descriptor->iProduct == 0 && |
| 142 | descriptor->iSerialNumber == 0) { |
| 143 | // Don't bother distrubing the device if it doesn't have any string |
| 144 | // descriptors we care about. |
| 145 | return; |
| 146 | } |
| 147 | |
| 148 | PlatformUsbDeviceHandle handle; |
| 149 | int rv = libusb_open(platform_device, &handle); |
| 150 | if (rv != LIBUSB_SUCCESS) { |
| 151 | USB_LOG(EVENT) << "Failed to open device to read string descriptors: " |
| 152 | << ConvertPlatformUsbErrorToString(rv); |
| 153 | return; |
| 154 | } |
| 155 | |
| 156 | uint16 language_id = ReadDeviceLanguage(handle); |
| 157 | ReadDeviceString(handle, descriptor->iManufacturer, language_id, |
| 158 | manufacturer_string); |
| 159 | ReadDeviceString(handle, descriptor->iProduct, language_id, product_string); |
| 160 | ReadDeviceString(handle, descriptor->iSerialNumber, language_id, |
| 161 | serial_number); |
| 162 | libusb_close(handle); |
| 163 | } |
| 164 | |
| 165 | #endif // USE_UDEV |
| 166 | |
| 167 | #if defined(OS_WIN) |
| 168 | |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 169 | // Wrapper around a HDEVINFO that automatically destroys it. |
| 170 | class ScopedDeviceInfoList { |
| 171 | public: |
| 172 | explicit ScopedDeviceInfoList(HDEVINFO handle) : handle_(handle) {} |
| 173 | |
| 174 | ~ScopedDeviceInfoList() { |
| 175 | if (valid()) { |
| 176 | SetupDiDestroyDeviceInfoList(handle_); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | bool valid() { return handle_ != INVALID_HANDLE_VALUE; } |
| 181 | |
| 182 | HDEVINFO get() { return handle_; } |
| 183 | |
| 184 | private: |
| 185 | HDEVINFO handle_; |
| 186 | |
| 187 | DISALLOW_COPY_AND_ASSIGN(ScopedDeviceInfoList); |
| 188 | }; |
| 189 | |
| 190 | // Wrapper around an SP_DEVINFO_DATA that initializes it properly and |
| 191 | // automatically deletes it. |
| 192 | class ScopedDeviceInfo { |
| 193 | public: |
| 194 | ScopedDeviceInfo() { |
| 195 | memset(&dev_info_data_, 0, sizeof(dev_info_data_)); |
| 196 | dev_info_data_.cbSize = sizeof(dev_info_data_); |
| 197 | } |
| 198 | |
| 199 | ~ScopedDeviceInfo() { |
| 200 | if (dev_info_set_ != INVALID_HANDLE_VALUE) { |
| 201 | SetupDiDeleteDeviceInfo(dev_info_set_, &dev_info_data_); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | // Once the SP_DEVINFO_DATA has been populated it must be freed using the |
| 206 | // HDEVINFO it was created from. |
| 207 | void set_valid(HDEVINFO dev_info_set) { |
| 208 | DCHECK(dev_info_set_ == INVALID_HANDLE_VALUE); |
| 209 | DCHECK(dev_info_set != INVALID_HANDLE_VALUE); |
| 210 | dev_info_set_ = dev_info_set; |
| 211 | } |
| 212 | |
| 213 | PSP_DEVINFO_DATA get() { return &dev_info_data_; } |
| 214 | |
| 215 | private: |
| 216 | HDEVINFO dev_info_set_ = INVALID_HANDLE_VALUE; |
| 217 | SP_DEVINFO_DATA dev_info_data_; |
| 218 | }; |
| 219 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 220 | bool IsWinUsbInterface(const std::string& device_path) { |
| 221 | ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL)); |
| 222 | if (!dev_info_list.valid()) { |
| 223 | USB_PLOG(ERROR) << "Failed to create a device information set"; |
| 224 | return false; |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 225 | } |
| 226 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 227 | // This will add the device to |dev_info_list| so we can query driver info. |
| 228 | if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0, |
| 229 | NULL)) { |
| 230 | USB_PLOG(ERROR) << "Failed to get device interface data for " |
| 231 | << device_path; |
| 232 | return false; |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 233 | } |
| 234 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 235 | ScopedDeviceInfo dev_info; |
| 236 | if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) { |
| 237 | USB_PLOG(ERROR) << "Failed to get device info for " << device_path; |
| 238 | return false; |
| 239 | } |
| 240 | dev_info.set_valid(dev_info_list.get()); |
| 241 | |
| 242 | DWORD reg_data_type; |
| 243 | BYTE buffer[256]; |
| 244 | if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(), |
| 245 | SPDRP_SERVICE, ®_data_type, |
| 246 | &buffer[0], sizeof buffer, NULL)) { |
| 247 | USB_PLOG(ERROR) << "Failed to get device service property"; |
| 248 | return false; |
| 249 | } |
| 250 | if (reg_data_type != REG_SZ) { |
| 251 | USB_LOG(ERROR) << "Unexpected data type for driver service: " |
| 252 | << reg_data_type; |
| 253 | return false; |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 254 | } |
| 255 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 256 | USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << "."; |
| 257 | if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") == |
| 258 | 0) { |
| 259 | return true; |
| 260 | } |
| 261 | return false; |
| 262 | } |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 263 | |
| 264 | #endif // OS_WIN |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 265 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 266 | } // namespace |
| 267 | |
reillyg | 4591981 | 2015-01-22 02:39:59 | [diff] [blame] | 268 | // static |
| 269 | UsbService* UsbServiceImpl::Create( |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 270 | scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { |
reillyg | 4591981 | 2015-01-22 02:39:59 | [diff] [blame] | 271 | PlatformUsbContext context = NULL; |
| 272 | const int rv = libusb_init(&context); |
| 273 | if (rv != LIBUSB_SUCCESS) { |
reillyg | 0f2dc63 | 2015-02-23 20:02:08 | [diff] [blame] | 274 | USB_LOG(ERROR) << "Failed to initialize libusb: " |
| 275 | << ConvertPlatformUsbErrorToString(rv); |
reillyg | 4591981 | 2015-01-22 02:39:59 | [diff] [blame] | 276 | return nullptr; |
| 277 | } |
| 278 | if (!context) { |
| 279 | return nullptr; |
| 280 | } |
| 281 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 282 | return new UsbServiceImpl(context, blocking_task_runner); |
[email protected] | 6226973 | 2013-07-02 19:51:31 | [diff] [blame] | 283 | } |
| 284 | |
reillyg | e471fab | 2014-08-29 01:58:43 | [diff] [blame] | 285 | UsbServiceImpl::UsbServiceImpl( |
| 286 | PlatformUsbContext context, |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 287 | scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) |
reillyg | e471fab | 2014-08-29 01:58:43 | [diff] [blame] | 288 | : context_(new UsbContext(context)), |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 289 | task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 290 | blocking_task_runner_(blocking_task_runner), |
| 291 | #if defined(OS_WIN) |
| 292 | device_observer_(this), |
| 293 | #endif |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 294 | weak_factory_(this) { |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 295 | base::MessageLoop::current()->AddDestructionObserver(this); |
| 296 | |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 297 | int rv = libusb_hotplug_register_callback( |
| 298 | context_->context(), |
| 299 | static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | |
| 300 | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), |
| 301 | LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, |
| 302 | LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, |
| 303 | &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); |
| 304 | if (rv == LIBUSB_SUCCESS) { |
| 305 | hotplug_enabled_ = true; |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 306 | |
| 307 | // libusb will call the hotplug callback for each device currently |
| 308 | // enumerated. Once this is complete enumeration_ready_ can be set to true |
| 309 | // but we must first wait for any tasks posted to blocking_task_runner_ to |
| 310 | // complete. |
| 311 | blocking_task_runner_->PostTaskAndReply( |
| 312 | FROM_HERE, base::Bind(&base::DoNothing), |
| 313 | base::Bind(&UsbServiceImpl::RefreshDevicesComplete, |
| 314 | weak_factory_.GetWeakPtr(), nullptr, 0)); |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 315 | } else { |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 316 | RefreshDevices(""); |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 317 | #if defined(OS_WIN) |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 318 | DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); |
| 319 | if (device_monitor) { |
| 320 | device_observer_.Add(device_monitor); |
| 321 | } |
reillyg | 257f4309 | 2015-01-08 00:48:10 | [diff] [blame] | 322 | #endif // OS_WIN |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 323 | } |
[email protected] | e195487 | 2014-04-13 17:43:29 | [diff] [blame] | 324 | } |
| 325 | |
[email protected] | ad2263f | 2014-05-05 13:57:50 | [diff] [blame] | 326 | UsbServiceImpl::~UsbServiceImpl() { |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 327 | base::MessageLoop::current()->RemoveDestructionObserver(this); |
| 328 | |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 329 | if (hotplug_enabled_) { |
| 330 | libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); |
| 331 | } |
| 332 | for (const auto& map_entry : devices_) { |
| 333 | map_entry.second->OnDisconnect(); |
[email protected] | 22ac24e | 2012-10-03 17:56:05 | [diff] [blame] | 334 | } |
[email protected] | 22ac24e | 2012-10-03 17:56:05 | [diff] [blame] | 335 | } |
| 336 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 337 | scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { |
| 338 | DCHECK(CalledOnValidThread()); |
| 339 | DeviceMap::iterator it = devices_.find(unique_id); |
| 340 | if (it != devices_.end()) { |
| 341 | return it->second; |
| 342 | } |
| 343 | return NULL; |
| 344 | } |
| 345 | |
| 346 | void UsbServiceImpl::GetDevices(const GetDevicesCallback& callback) { |
[email protected] | e195487 | 2014-04-13 17:43:29 | [diff] [blame] | 347 | DCHECK(CalledOnValidThread()); |
[email protected] | d165a644 | 2013-08-08 19:38:11 | [diff] [blame] | 348 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 349 | if (!enumeration_ready_) { |
| 350 | // On startup wait for the first enumeration, |
| 351 | pending_enumerations_.push_back(callback); |
| 352 | } else if (hotplug_enabled_) { |
| 353 | // The device list is updated live when hotplug events are supported. |
| 354 | std::vector<scoped_refptr<UsbDevice>> devices; |
| 355 | for (const auto& map_entry : devices_) { |
| 356 | devices.push_back(map_entry.second); |
[email protected] | d165a644 | 2013-08-08 19:38:11 | [diff] [blame] | 357 | } |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 358 | callback.Run(devices); |
| 359 | } else { |
| 360 | // Only post one re-enumeration task at a time. |
| 361 | if (pending_enumerations_.empty()) { |
| 362 | RefreshDevices(""); |
[email protected] | d165a644 | 2013-08-08 19:38:11 | [diff] [blame] | 363 | } |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 364 | pending_enumerations_.push_back(callback); |
[email protected] | 22ac24e | 2012-10-03 17:56:05 | [diff] [blame] | 365 | } |
[email protected] | 22ac24e | 2012-10-03 17:56:05 | [diff] [blame] | 366 | } |
[email protected] | b550868 | 2014-04-22 17:10:01 | [diff] [blame] | 367 | |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 368 | #if defined(OS_WIN) |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 369 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 370 | void UsbServiceImpl::OnDeviceAdded(const GUID& class_guid, |
| 371 | const std::string& device_path) { |
| 372 | // Only the root node of a composite USB device has the class GUID |
| 373 | // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded. |
| 374 | // This first pass filter will catch anything that's sitting on the USB bus |
| 375 | // (including devices on 3rd party USB controllers) to avoid the more |
| 376 | // expensive driver check that needs to be done on the FILE thread. |
| 377 | if (device_path.find("usb") != std::string::npos) { |
| 378 | RefreshDevices(device_path); |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 379 | } |
| 380 | } |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 381 | |
| 382 | void UsbServiceImpl::OnDeviceRemoved(const GUID& class_guid, |
| 383 | const std::string& device_path) { |
| 384 | // The root USB device node is removed last |
| 385 | if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) { |
| 386 | RefreshDevices(""); |
| 387 | } |
| 388 | } |
| 389 | |
reillyg | 8cbc3ce | 2015-04-03 07:39:43 | [diff] [blame] | 390 | #endif // OS_WIN |
| 391 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 392 | void UsbServiceImpl::WillDestroyCurrentMessageLoop() { |
| 393 | DCHECK(CalledOnValidThread()); |
| 394 | delete this; |
| 395 | } |
| 396 | |
| 397 | void UsbServiceImpl::RefreshDevices(const std::string& new_device_path) { |
| 398 | DCHECK(CalledOnValidThread()); |
| 399 | |
| 400 | std::set<PlatformUsbDevice> current_devices; |
| 401 | for (const auto& map_entry : platform_devices_) { |
| 402 | current_devices.insert(map_entry.first); |
| 403 | } |
| 404 | blocking_task_runner_->PostTask( |
| 405 | FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesOnBlockingThread, |
| 406 | weak_factory_.GetWeakPtr(), new_device_path, |
| 407 | task_runner_, context_, current_devices)); |
| 408 | } |
| 409 | |
| 410 | // static |
| 411 | void UsbServiceImpl::RefreshDevicesOnBlockingThread( |
| 412 | base::WeakPtr<UsbServiceImpl> usb_service, |
| 413 | const std::string& new_device_path, |
| 414 | scoped_refptr<base::SequencedTaskRunner> task_runner, |
| 415 | scoped_refptr<UsbContext> usb_context, |
| 416 | const std::set<PlatformUsbDevice>& previous_devices) { |
| 417 | if (!new_device_path.empty()) { |
| 418 | #if defined(OS_WIN) |
| 419 | if (!IsWinUsbInterface(new_device_path)) { |
| 420 | // Wait to call libusb_get_device_list until libusb will be able to find |
| 421 | // a WinUSB interface for the device. |
| 422 | return; |
| 423 | } |
| 424 | #endif // defined(OS_WIN) |
| 425 | } |
| 426 | |
| 427 | libusb_device** platform_devices = NULL; |
| 428 | const ssize_t device_count = |
| 429 | libusb_get_device_list(usb_context->context(), &platform_devices); |
| 430 | if (device_count < 0) { |
| 431 | USB_LOG(ERROR) << "Failed to get device list: " |
| 432 | << ConvertPlatformUsbErrorToString(device_count); |
| 433 | task_runner->PostTask(FROM_HERE, |
| 434 | base::Bind(&UsbServiceImpl::RefreshDevicesComplete, |
| 435 | usb_service, nullptr, 0)); |
| 436 | return; |
| 437 | } |
| 438 | |
| 439 | // Find new devices. |
| 440 | for (ssize_t i = 0; i < device_count; ++i) { |
| 441 | PlatformUsbDevice platform_device = platform_devices[i]; |
| 442 | if (previous_devices.find(platform_device) == previous_devices.end()) { |
| 443 | libusb_ref_device(platform_device); |
| 444 | AddDeviceOnBlockingThread(usb_service, task_runner, platform_device); |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | // |platform_devices| will be freed in this callback. |
| 449 | task_runner->PostTask( |
| 450 | FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesComplete, |
| 451 | usb_service, platform_devices, device_count)); |
| 452 | } |
| 453 | |
| 454 | // static |
| 455 | void UsbServiceImpl::AddDeviceOnBlockingThread( |
| 456 | base::WeakPtr<UsbServiceImpl> usb_service, |
| 457 | scoped_refptr<base::SequencedTaskRunner> task_runner, |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 458 | PlatformUsbDevice platform_device) { |
| 459 | libusb_device_descriptor descriptor; |
| 460 | int rv = libusb_get_device_descriptor(platform_device, &descriptor); |
| 461 | if (rv == LIBUSB_SUCCESS) { |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 462 | base::string16 manufacturer_string; |
| 463 | base::string16 product_string; |
| 464 | base::string16 serial_number; |
| 465 | std::string device_node; |
| 466 | ReadDeviceStrings(platform_device, &descriptor, &manufacturer_string, |
| 467 | &product_string, &serial_number, &device_node); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 468 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 469 | task_runner->PostTask( |
| 470 | FROM_HERE, base::Bind(&UsbServiceImpl::AddDevice, usb_service, |
| 471 | platform_device, descriptor.idVendor, |
| 472 | descriptor.idProduct, manufacturer_string, |
| 473 | product_string, serial_number, device_node)); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 474 | } else { |
reillyg | 0f2dc63 | 2015-02-23 20:02:08 | [diff] [blame] | 475 | USB_LOG(EVENT) << "Failed to get device descriptor: " |
| 476 | << ConvertPlatformUsbErrorToString(rv); |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 477 | libusb_unref_device(platform_device); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 478 | } |
| 479 | } |
| 480 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 481 | void UsbServiceImpl::RefreshDevicesComplete(libusb_device** platform_devices, |
| 482 | ssize_t device_count) { |
| 483 | if (platform_devices) { |
| 484 | // Mark devices seen in this enumeration. |
| 485 | for (ssize_t i = 0; i < device_count; ++i) { |
| 486 | const PlatformDeviceMap::iterator it = |
| 487 | platform_devices_.find(platform_devices[i]); |
| 488 | if (it != platform_devices_.end()) { |
| 489 | it->second->set_visited(true); |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | // Remove devices not seen in this enumeration. |
| 494 | for (PlatformDeviceMap::iterator it = platform_devices_.begin(); |
| 495 | it != platform_devices_.end(); |
| 496 | /* incremented internally */) { |
| 497 | PlatformDeviceMap::iterator current = it++; |
| 498 | const scoped_refptr<UsbDeviceImpl>& device = current->second; |
| 499 | if (device->was_visited()) { |
| 500 | device->set_visited(false); |
| 501 | } else { |
| 502 | RemoveDevice(device); |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | libusb_free_device_list(platform_devices, true); |
| 507 | } |
| 508 | |
| 509 | enumeration_ready_ = true; |
| 510 | |
| 511 | if (!pending_enumerations_.empty()) { |
| 512 | std::vector<scoped_refptr<UsbDevice>> devices; |
| 513 | for (const auto& map_entry : devices_) { |
| 514 | devices.push_back(map_entry.second); |
| 515 | } |
| 516 | |
| 517 | std::vector<GetDevicesCallback> pending_enumerations; |
| 518 | pending_enumerations.swap(pending_enumerations_); |
| 519 | for (const GetDevicesCallback& callback : pending_enumerations) { |
| 520 | callback.Run(devices); |
| 521 | } |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | void UsbServiceImpl::AddDevice(PlatformUsbDevice platform_device, |
| 526 | uint16 vendor_id, |
| 527 | uint16 product_id, |
| 528 | base::string16 manufacturer_string, |
| 529 | base::string16 product_string, |
| 530 | base::string16 serial_number, |
| 531 | std::string device_node) { |
| 532 | uint32 unique_id; |
| 533 | do { |
| 534 | unique_id = ++next_unique_id_; |
| 535 | } while (devices_.find(unique_id) != devices_.end()); |
| 536 | |
| 537 | scoped_refptr<UsbDeviceImpl> device( |
| 538 | new UsbDeviceImpl(context_, platform_device, vendor_id, product_id, |
| 539 | unique_id, manufacturer_string, product_string, |
| 540 | serial_number, blocking_task_runner_)); |
| 541 | |
| 542 | platform_devices_[platform_device] = device; |
| 543 | devices_[unique_id] = device; |
| 544 | |
| 545 | USB_LOG(USER) << "USB device added: vendor=" << device->vendor_id() << " \"" |
| 546 | << device->manufacturer_string() |
| 547 | << "\", product=" << device->product_id() << " \"" |
| 548 | << device->product_string() << "\", serial=\"" |
| 549 | << device->serial_number() |
| 550 | << "\", uniqueId=" << device->unique_id(); |
| 551 | |
| 552 | if (enumeration_ready_) { |
| 553 | NotifyDeviceAdded(device); |
| 554 | } |
| 555 | |
| 556 | libusb_unref_device(platform_device); |
| 557 | } |
| 558 | |
| 559 | void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) { |
| 560 | platform_devices_.erase(device->platform_device()); |
| 561 | devices_.erase(device->unique_id()); |
| 562 | |
| 563 | USB_LOG(USER) << "USB device removed: uniqueId=" << device->unique_id(); |
| 564 | |
| 565 | NotifyDeviceRemoved(device); |
| 566 | device->OnDisconnect(); |
| 567 | } |
| 568 | |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 569 | // static |
| 570 | int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, |
| 571 | PlatformUsbDevice device, |
| 572 | libusb_hotplug_event event, |
| 573 | void* user_data) { |
| 574 | // It is safe to access the UsbServiceImpl* here because libusb takes a lock |
| 575 | // around registering, deregistering and calling hotplug callback functions |
| 576 | // and so guarantees that this function will not be called by the event |
| 577 | // processing thread after it has been deregistered. |
| 578 | UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); |
| 579 | switch (event) { |
| 580 | case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 581 | libusb_ref_device(device); // Released in OnPlatformDeviceAdded. |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 582 | if (self->task_runner_->BelongsToCurrentThread()) { |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 583 | self->OnPlatformDeviceAdded(device); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 584 | } else { |
| 585 | self->task_runner_->PostTask( |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 586 | FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceAdded, |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 587 | base::Unretained(self), device)); |
| 588 | } |
| 589 | break; |
| 590 | case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 591 | libusb_ref_device(device); // Released in OnPlatformDeviceRemoved. |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 592 | if (self->task_runner_->BelongsToCurrentThread()) { |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 593 | self->OnPlatformDeviceRemoved(device); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 594 | } else { |
| 595 | self->task_runner_->PostTask( |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 596 | FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceRemoved, |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 597 | base::Unretained(self), device)); |
| 598 | } |
| 599 | break; |
| 600 | default: |
| 601 | NOTREACHED(); |
| 602 | } |
| 603 | |
| 604 | return 0; |
| 605 | } |
| 606 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 607 | void UsbServiceImpl::OnPlatformDeviceAdded(PlatformUsbDevice platform_device) { |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 608 | DCHECK(CalledOnValidThread()); |
| 609 | DCHECK(!ContainsKey(platform_devices_, platform_device)); |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 610 | blocking_task_runner_->PostTask( |
| 611 | FROM_HERE, |
| 612 | base::Bind(&UsbServiceImpl::AddDeviceOnBlockingThread, |
| 613 | weak_factory_.GetWeakPtr(), task_runner_, platform_device)); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 614 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 615 | // libusb_unref_device(platform_device) is called by the task above. |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 616 | } |
| 617 | |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 618 | void UsbServiceImpl::OnPlatformDeviceRemoved( |
| 619 | PlatformUsbDevice platform_device) { |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 620 | DCHECK(CalledOnValidThread()); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 621 | PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); |
| 622 | if (it != platform_devices_.end()) { |
| 623 | scoped_refptr<UsbDeviceImpl> device = it->second; |
reillyg | b87cb27 | 2015-04-16 19:11:04 | [diff] [blame^] | 624 | // Serialize with calls to AddDeviceOnBlockingThread. |
| 625 | blocking_task_runner_->PostTaskAndReply( |
| 626 | FROM_HERE, base::Bind(&base::DoNothing), |
| 627 | base::Bind(&UsbServiceImpl::RemoveDevice, weak_factory_.GetWeakPtr(), |
| 628 | device)); |
reillyg | 0ae3a999 | 2015-01-05 20:12:42 | [diff] [blame] | 629 | } else { |
| 630 | NOTREACHED(); |
| 631 | } |
| 632 | |
| 633 | libusb_unref_device(platform_device); |
| 634 | } |
| 635 | |
reillyg | d77718d | 2014-09-04 00:57:56 | [diff] [blame] | 636 | } // namespace device |