Chromium Code Reviews| Index: device/usb/usb_service_impl.cc |
| diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc |
| index 215aba1817af302542804f3c510ecc01aa08706e..8542d05f413aea010c7d1d383410db9b162dcd66 100644 |
| --- a/device/usb/usb_service_impl.cc |
| +++ b/device/usb/usb_service_impl.cc |
| @@ -7,10 +7,12 @@ |
| #include <map> |
| #include <set> |
| +#include "base/bind.h" |
| #include "base/lazy_instance.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| +#include "base/thread_task_runner_handle.h" |
| #include "device/usb/usb_context.h" |
| #include "device/usb/usb_device_impl.h" |
| #include "device/usb/usb_error.h" |
| @@ -47,13 +49,26 @@ class UsbServiceImpl : public UsbService, |
| // Enumerate USB devices from OS and update devices_ map. |
| void RefreshDevices(); |
| + static int LIBUSB_CALL HotplugCallback(libusb_context* context, |
| + libusb_device* device, |
| + libusb_hotplug_event event, |
| + void* user_data); |
| + void OnDeviceAdded(PlatformUsbDevice device); |
| + void OnDeviceRemoved(PlatformUsbDevice device); |
| + |
| scoped_refptr<UsbContext> context_; |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; |
| - // TODO(reillyg): Figure out a better solution. |
| + // TODO(reillyg): Figure out a better solution for device IDs. |
| uint32 next_unique_id_; |
| + // When available the device list will be updated when new devices are |
| + // connected instead of only when a full enumeration is requested. |
| + // TODO(reillyg): Support this on all platforms. crbug.com/411715 |
| + bool hotplug_enabled_; |
| + libusb_hotplug_callback_handle hotplug_handle_; |
| + |
| // The map from unique IDs to UsbDevices. |
| typedef std::map<uint32, scoped_refptr<UsbDeviceImpl> > DeviceMap; |
| DeviceMap devices_; |
| @@ -80,10 +95,13 @@ void UsbServiceImpl::GetDevices( |
| std::vector<scoped_refptr<UsbDevice> >* devices) { |
| DCHECK(CalledOnValidThread()); |
| STLClearObject(devices); |
| - RefreshDevices(); |
| - for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { |
| - devices->push_back(it->second); |
| + if (!hotplug_enabled_) { |
| + RefreshDevices(); |
| + } |
| + |
| + for (const auto& map_entry : devices_) { |
| + devices->push_back(map_entry.second); |
| } |
| } |
| @@ -97,14 +115,29 @@ UsbServiceImpl::UsbServiceImpl( |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) |
| : context_(new UsbContext(context)), |
| ui_task_runner_(ui_task_runner), |
| - next_unique_id_(0) { |
| + next_unique_id_(0), |
| + hotplug_enabled_(false) { |
| base::MessageLoop::current()->AddDestructionObserver(this); |
| + task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| + int rv = libusb_hotplug_register_callback( |
| + context_->context(), |
| + static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | |
| + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), |
| + LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, |
| + LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, |
| + &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); |
| + if (rv == LIBUSB_SUCCESS) { |
| + hotplug_enabled_ = true; |
| + } |
| } |
| UsbServiceImpl::~UsbServiceImpl() { |
| base::MessageLoop::current()->RemoveDestructionObserver(this); |
| - for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { |
| - it->second->OnDisconnect(); |
| + if (hotplug_enabled_) { |
| + libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); |
| + } |
| + for (const auto& map_entry : devices_) { |
| + map_entry.second->OnDisconnect(); |
| } |
| } |
| @@ -177,6 +210,82 @@ void UsbServiceImpl::RefreshDevices() { |
| } |
| // static |
| +int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, |
| + libusb_device* device, |
| + libusb_hotplug_event event, |
| + void* user_data) { |
| + UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); |
| + switch (event) { |
| + case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: |
| + if (self->task_runner_->BelongsToCurrentThread()) { |
| + self->OnDeviceAdded(device); |
| + } else { |
| + self->task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded, |
| + 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.
|
| + } |
| + break; |
| + case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: |
| + if (self->task_runner_->BelongsToCurrentThread()) { |
| + self->OnDeviceRemoved(device); |
| + } else { |
| + self->task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved, |
| + base::Unretained(self), device)); |
|
rpaquay
2014/12/18 21:08:54
nit: Comment why using Unretained is ok?
Also, how
|
| + } |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(!ContainsKey(platform_devices_, platform_device)); |
| + |
| + libusb_device_descriptor descriptor; |
| + const int rv = libusb_get_device_descriptor(platform_device, &descriptor); |
| + // This test is needed. A valid vendor/produce pair is required. |
| + if (rv != LIBUSB_SUCCESS) { |
| + VLOG(1) << "Failed to get device descriptor: " |
| + << ConvertPlatformUsbErrorToString(rv); |
| + return; |
| + } |
| + |
| + uint32 unique_id; |
| + do { |
| + unique_id = ++next_unique_id_; |
| + } while (devices_.find(unique_id) != devices_.end()); |
| + |
| + scoped_refptr<UsbDeviceImpl> new_device( |
| + new UsbDeviceImpl(context_, ui_task_runner_, platform_device, |
| + descriptor.idVendor, descriptor.idProduct, unique_id)); |
| + platform_devices_[platform_device] = new_device; |
| + devices_[unique_id] = new_device; |
| +} |
| + |
| +void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); |
| + if (it != platform_devices_.end()) { |
| + scoped_refptr<UsbDeviceImpl> device = it->second; |
| + DeviceMap::iterator dev_it = devices_.find(device->unique_id()); |
| + if (dev_it != devices_.end()) { |
| + devices_.erase(dev_it); |
| + } else { |
| + NOTREACHED(); |
| + } |
| + device->OnDisconnect(); |
| + platform_devices_.erase(it); |
| + } else { |
| + NOTREACHED(); |
| + } |
| +} |
| + |
| +// static |
| UsbService* UsbService::GetInstance( |
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { |
| UsbService* instance = g_usb_service_instance.Get().get(); |