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