 Chromium Code Reviews
 Chromium Code Reviews Issue 803653003:
  Use USB hotplug events from libusb when possible.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 803653003:
  Use USB hotplug events from libusb when possible.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| OLD | NEW | 
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "device/usb/usb_service.h" | 5 #include "device/usb/usb_service.h" | 
| 6 | 6 | 
| 7 #include <map> | 7 #include <map> | 
| 8 #include <set> | 8 #include <set> | 
| 9 | 9 | 
| 10 #include "base/bind.h" | |
| 10 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" | 
| 11 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" | 
| 12 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" | 
| 13 #include "base/stl_util.h" | 14 #include "base/stl_util.h" | 
| 15 #include "base/thread_task_runner_handle.h" | |
| 14 #include "device/usb/usb_context.h" | 16 #include "device/usb/usb_context.h" | 
| 15 #include "device/usb/usb_device_impl.h" | 17 #include "device/usb/usb_device_impl.h" | 
| 16 #include "device/usb/usb_error.h" | 18 #include "device/usb/usb_error.h" | 
| 17 #include "third_party/libusb/src/libusb/libusb.h" | 19 #include "third_party/libusb/src/libusb/libusb.h" | 
| 18 | 20 | 
| 19 namespace device { | 21 namespace device { | 
| 20 | 22 | 
| 21 namespace { | 23 namespace { | 
| 22 | 24 | 
| 23 base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance = | 25 base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance = | 
| (...skipping 16 matching lines...) Expand all Loading... | |
| 40 // device::UsbService implementation | 42 // device::UsbService implementation | 
| 41 scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; | 43 scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; | 
| 42 void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; | 44 void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; | 
| 43 | 45 | 
| 44 // base::MessageLoop::DestructionObserver implementation. | 46 // base::MessageLoop::DestructionObserver implementation. | 
| 45 void WillDestroyCurrentMessageLoop() override; | 47 void WillDestroyCurrentMessageLoop() override; | 
| 46 | 48 | 
| 47 // Enumerate USB devices from OS and update devices_ map. | 49 // Enumerate USB devices from OS and update devices_ map. | 
| 48 void RefreshDevices(); | 50 void RefreshDevices(); | 
| 49 | 51 | 
| 52 static int LIBUSB_CALL HotplugCallback(libusb_context* context, | |
| 53 libusb_device* device, | |
| 54 libusb_hotplug_event event, | |
| 55 void* user_data); | |
| 56 void OnDeviceAdded(PlatformUsbDevice device); | |
| 57 void OnDeviceRemoved(PlatformUsbDevice device); | |
| 58 | |
| 50 scoped_refptr<UsbContext> context_; | 59 scoped_refptr<UsbContext> context_; | 
| 51 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 60 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | 
| 52 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | 61 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; | 
| 53 | 62 | 
| 54 // TODO(reillyg): Figure out a better solution. | 63 // TODO(reillyg): Figure out a better solution for device IDs. | 
| 55 uint32 next_unique_id_; | 64 uint32 next_unique_id_; | 
| 56 | 65 | 
| 66 // When available the device list will be updated when new devices are | |
| 67 // connected instead of only when a full enumeration is requested. | |
| 68 // TODO(reillyg): Support this on all platforms. crbug.com/411715 | |
| 69 bool hotplug_enabled_; | |
| 70 libusb_hotplug_callback_handle hotplug_handle_; | |
| 71 | |
| 57 // The map from unique IDs to UsbDevices. | 72 // The map from unique IDs to UsbDevices. | 
| 58 typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; | 73 typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; | 
| 59 DeviceMap devices_; | 74 DeviceMap devices_; | 
| 60 | 75 | 
| 61 // The map from PlatformUsbDevices to UsbDevices. | 76 // The map from PlatformUsbDevices to UsbDevices. | 
| 62 typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > | 77 typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > | 
| 63 PlatformDeviceMap; | 78 PlatformDeviceMap; | 
| 64 PlatformDeviceMap platform_devices_; | 79 PlatformDeviceMap platform_devices_; | 
| 65 | 80 | 
| 66 DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); | 81 DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); | 
| 67 }; | 82 }; | 
| 68 | 83 | 
| 69 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { | 84 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { | 
| 70 DCHECK(CalledOnValidThread()); | 85 DCHECK(CalledOnValidThread()); | 
| 71 RefreshDevices(); | 86 RefreshDevices(); | 
| 72 DeviceMap::iterator it = devices_.find(unique_id); | 87 DeviceMap::iterator it = devices_.find(unique_id); | 
| 73 if (it != devices_.end()) { | 88 if (it != devices_.end()) { | 
| 74 return it->second; | 89 return it->second; | 
| 75 } | 90 } | 
| 76 return NULL; | 91 return NULL; | 
| 77 } | 92 } | 
| 78 | 93 | 
| 79 void UsbServiceImpl::GetDevices( | 94 void UsbServiceImpl::GetDevices( | 
| 80 std::vector<scoped_refptr<UsbDevice> >* devices) { | 95 std::vector<scoped_refptr<UsbDevice> >* devices) { | 
| 81 DCHECK(CalledOnValidThread()); | 96 DCHECK(CalledOnValidThread()); | 
| 82 STLClearObject(devices); | 97 STLClearObject(devices); | 
| 83 RefreshDevices(); | |
| 84 | 98 | 
| 85 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 99 if (!hotplug_enabled_) { | 
| 86 devices->push_back(it->second); | 100 RefreshDevices(); | 
| 101 } | |
| 102 | |
| 103 for (const auto& map_entry : devices_) { | |
| 104 devices->push_back(map_entry.second); | |
| 87 } | 105 } | 
| 88 } | 106 } | 
| 89 | 107 | 
| 90 void UsbServiceImpl::WillDestroyCurrentMessageLoop() { | 108 void UsbServiceImpl::WillDestroyCurrentMessageLoop() { | 
| 91 DCHECK(CalledOnValidThread()); | 109 DCHECK(CalledOnValidThread()); | 
| 92 g_usb_service_instance.Get().reset(NULL); | 110 g_usb_service_instance.Get().reset(NULL); | 
| 93 } | 111 } | 
| 94 | 112 | 
| 95 UsbServiceImpl::UsbServiceImpl( | 113 UsbServiceImpl::UsbServiceImpl( | 
| 96 PlatformUsbContext context, | 114 PlatformUsbContext context, | 
| 97 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) | 115 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) | 
| 98 : context_(new UsbContext(context)), | 116 : context_(new UsbContext(context)), | 
| 99 ui_task_runner_(ui_task_runner), | 117 ui_task_runner_(ui_task_runner), | 
| 100 next_unique_id_(0) { | 118 next_unique_id_(0), | 
| 119 hotplug_enabled_(false) { | |
| 101 base::MessageLoop::current()->AddDestructionObserver(this); | 120 base::MessageLoop::current()->AddDestructionObserver(this); | 
| 121 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | |
| 122 int rv = libusb_hotplug_register_callback( | |
| 123 context_->context(), | |
| 124 static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | | |
| 125 LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), | |
| 126 LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, | |
| 127 LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, | |
| 128 &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); | |
| 129 if (rv == LIBUSB_SUCCESS) { | |
| 130 hotplug_enabled_ = true; | |
| 131 } | |
| 102 } | 132 } | 
| 103 | 133 | 
| 104 UsbServiceImpl::~UsbServiceImpl() { | 134 UsbServiceImpl::~UsbServiceImpl() { | 
| 105 base::MessageLoop::current()->RemoveDestructionObserver(this); | 135 base::MessageLoop::current()->RemoveDestructionObserver(this); | 
| 106 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 136 if (hotplug_enabled_) { | 
| 107 it->second->OnDisconnect(); | 137 libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); | 
| 138 } | |
| 139 for (const auto& map_entry : devices_) { | |
| 140 map_entry.second->OnDisconnect(); | |
| 108 } | 141 } | 
| 109 } | 142 } | 
| 110 | 143 | 
| 111 void UsbServiceImpl::RefreshDevices() { | 144 void UsbServiceImpl::RefreshDevices() { | 
| 112 DCHECK(CalledOnValidThread()); | 145 DCHECK(CalledOnValidThread()); | 
| 113 | 146 | 
| 114 libusb_device** platform_devices = NULL; | 147 libusb_device** platform_devices = NULL; | 
| 115 const ssize_t device_count = | 148 const ssize_t device_count = | 
| 116 libusb_get_device_list(context_->context(), &platform_devices); | 149 libusb_get_device_list(context_->context(), &platform_devices); | 
| 117 if (device_count < 0) { | 150 if (device_count < 0) { | 
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 for (size_t i = 0; i < disconnected_devices.size(); ++i) { | 203 for (size_t i = 0; i < disconnected_devices.size(); ++i) { | 
| 171 // UsbDevice will be destroyed after this. The corresponding | 204 // UsbDevice will be destroyed after this. The corresponding | 
| 172 // PlatformUsbDevice will be unref'ed during this process. | 205 // PlatformUsbDevice will be unref'ed during this process. | 
| 173 platform_devices_.erase(disconnected_devices[i]); | 206 platform_devices_.erase(disconnected_devices[i]); | 
| 174 } | 207 } | 
| 175 | 208 | 
| 176 libusb_free_device_list(platform_devices, true); | 209 libusb_free_device_list(platform_devices, true); | 
| 177 } | 210 } | 
| 178 | 211 | 
| 179 // static | 212 // static | 
| 213 int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, | |
| 214 libusb_device* device, | |
| 215 libusb_hotplug_event event, | |
| 216 void* user_data) { | |
| 217 UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); | |
| 218 switch (event) { | |
| 219 case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: | |
| 220 if (self->task_runner_->BelongsToCurrentThread()) { | |
| 221 self->OnDeviceAdded(device); | |
| 222 } else { | |
| 223 self->task_runner_->PostTask( | |
| 224 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded, | |
| 225 base::Unretained(self), device)); | |
| 
rpaquay
2014/12/18 21:08:54
nit: Comment why using Unretained is ok?
Also, how
 
Reilly Grant (use Gerrit)
2014/12/18 22:28:13
libusb is nice and guarantees this is safe for us.
 | |
| 226 } | |
| 227 break; | |
| 228 case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: | |
| 229 if (self->task_runner_->BelongsToCurrentThread()) { | |
| 230 self->OnDeviceRemoved(device); | |
| 231 } else { | |
| 232 self->task_runner_->PostTask( | |
| 233 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved, | |
| 234 base::Unretained(self), device)); | |
| 
rpaquay
2014/12/18 21:08:54
nit: Comment why using Unretained is ok?
Also, how
 | |
| 235 } | |
| 236 break; | |
| 237 default: | |
| 238 NOTREACHED(); | |
| 239 } | |
| 240 | |
| 241 return 0; | |
| 242 } | |
| 243 | |
| 244 void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) { | |
| 245 DCHECK(CalledOnValidThread()); | |
| 246 DCHECK(!ContainsKey(platform_devices_, platform_device)); | |
| 247 | |
| 248 libusb_device_descriptor descriptor; | |
| 249 const int rv = libusb_get_device_descriptor(platform_device, &descriptor); | |
| 250 // This test is needed. A valid vendor/produce pair is required. | |
| 251 if (rv != LIBUSB_SUCCESS) { | |
| 252 VLOG(1) << "Failed to get device descriptor: " | |
| 253 << ConvertPlatformUsbErrorToString(rv); | |
| 254 return; | |
| 255 } | |
| 256 | |
| 257 uint32 unique_id; | |
| 258 do { | |
| 259 unique_id = ++next_unique_id_; | |
| 260 } while (devices_.find(unique_id) != devices_.end()); | |
| 261 | |
| 262 scoped_refptr<UsbDeviceImpl> new_device( | |
| 263 new UsbDeviceImpl(context_, ui_task_runner_, platform_device, | |
| 264 descriptor.idVendor, descriptor.idProduct, unique_id)); | |
| 265 platform_devices_[platform_device] = new_device; | |
| 266 devices_[unique_id] = new_device; | |
| 267 } | |
| 268 | |
| 269 void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { | |
| 270 DCHECK(CalledOnValidThread()); | |
| 271 | |
| 272 PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); | |
| 273 if (it != platform_devices_.end()) { | |
| 274 scoped_refptr<UsbDeviceImpl> device = it->second; | |
| 275 DeviceMap::iterator dev_it = devices_.find(device->unique_id()); | |
| 276 if (dev_it != devices_.end()) { | |
| 277 devices_.erase(dev_it); | |
| 278 } else { | |
| 279 NOTREACHED(); | |
| 280 } | |
| 281 device->OnDisconnect(); | |
| 282 platform_devices_.erase(it); | |
| 283 } else { | |
| 284 NOTREACHED(); | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 // static | |
| 180 UsbService* UsbService::GetInstance( | 289 UsbService* UsbService::GetInstance( | 
| 181 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { | 290 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { | 
| 182 UsbService* instance = g_usb_service_instance.Get().get(); | 291 UsbService* instance = g_usb_service_instance.Get().get(); | 
| 183 if (!instance) { | 292 if (!instance) { | 
| 184 PlatformUsbContext context = NULL; | 293 PlatformUsbContext context = NULL; | 
| 185 | 294 | 
| 186 const int rv = libusb_init(&context); | 295 const int rv = libusb_init(&context); | 
| 187 if (rv != LIBUSB_SUCCESS) { | 296 if (rv != LIBUSB_SUCCESS) { | 
| 188 VLOG(1) << "Failed to initialize libusb: " | 297 VLOG(1) << "Failed to initialize libusb: " | 
| 189 << ConvertPlatformUsbErrorToString(rv); | 298 << ConvertPlatformUsbErrorToString(rv); | 
| 190 return NULL; | 299 return NULL; | 
| 191 } | 300 } | 
| 192 if (!context) | 301 if (!context) | 
| 193 return NULL; | 302 return NULL; | 
| 194 | 303 | 
| 195 instance = new UsbServiceImpl(context, ui_task_runner); | 304 instance = new UsbServiceImpl(context, ui_task_runner); | 
| 196 g_usb_service_instance.Get().reset(instance); | 305 g_usb_service_instance.Get().reset(instance); | 
| 197 } | 306 } | 
| 198 return instance; | 307 return instance; | 
| 199 } | 308 } | 
| 200 | 309 | 
| 201 // static | 310 // static | 
| 202 void UsbService::SetInstanceForTest(UsbService* instance) { | 311 void UsbService::SetInstanceForTest(UsbService* instance) { | 
| 203 g_usb_service_instance.Get().reset(instance); | 312 g_usb_service_instance.Get().reset(instance); | 
| 204 } | 313 } | 
| 205 | 314 | 
| 206 } // namespace device | 315 } // namespace device | 
| OLD | NEW |