| 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..a23a4a1548b913efe763a5b140951f056b6d424c 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,31 @@ class UsbServiceImpl : public UsbService,
|
| // Enumerate USB devices from OS and update devices_ map.
|
| void RefreshDevices();
|
|
|
| + // Adds a new UsbDevice to the devices_ map based on the given libusb device.
|
| + scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device);
|
| +
|
| + // Handle hotplug events from libusb.
|
| + static int LIBUSB_CALL HotplugCallback(libusb_context* context,
|
| + PlatformUsbDevice device,
|
| + libusb_hotplug_event event,
|
| + void* user_data);
|
| + // These functions release a reference to the provided platform device.
|
| + void OnDeviceAdded(PlatformUsbDevice platform_device);
|
| + void OnDeviceRemoved(PlatformUsbDevice platform_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 +100,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 +120,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();
|
| }
|
| }
|
|
|
| @@ -125,31 +163,10 @@ void UsbServiceImpl::RefreshDevices() {
|
| // Populates new devices.
|
| for (ssize_t i = 0; i < device_count; ++i) {
|
| if (!ContainsKey(platform_devices_, platform_devices[i])) {
|
| - libusb_device_descriptor descriptor;
|
| - const int rv =
|
| - libusb_get_device_descriptor(platform_devices[i], &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);
|
| - continue;
|
| + scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]);
|
| + if (new_device) {
|
| + connected_devices.insert(new_device.get());
|
| }
|
| -
|
| - 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_devices[i],
|
| - descriptor.idVendor,
|
| - descriptor.idProduct,
|
| - unique_id));
|
| - platform_devices_[platform_devices[i]] = new_device;
|
| - devices_[unique_id] = new_device;
|
| - connected_devices.insert(new_device.get());
|
| } else {
|
| connected_devices.insert(platform_devices_[platform_devices[i]].get());
|
| }
|
| @@ -176,6 +193,96 @@ void UsbServiceImpl::RefreshDevices() {
|
| libusb_free_device_list(platform_devices, true);
|
| }
|
|
|
| +scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice(
|
| + PlatformUsbDevice platform_device) {
|
| + libusb_device_descriptor descriptor;
|
| + int rv = libusb_get_device_descriptor(platform_device, &descriptor);
|
| + if (rv == LIBUSB_SUCCESS) {
|
| + 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;
|
| + return new_device;
|
| + } else {
|
| + VLOG(1) << "Failed to get device descriptor: "
|
| + << ConvertPlatformUsbErrorToString(rv);
|
| + return nullptr;
|
| + }
|
| +}
|
| +
|
| +// static
|
| +int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
|
| + PlatformUsbDevice device,
|
| + libusb_hotplug_event event,
|
| + void* user_data) {
|
| + // It is safe to access the UsbServiceImpl* here because libusb takes a lock
|
| + // around registering, deregistering and calling hotplug callback functions
|
| + // and so guarantees that this function will not be called by the event
|
| + // processing thread after it has been deregistered.
|
| + UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data);
|
| + switch (event) {
|
| + case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
|
| + libusb_ref_device(device); // Released in OnDeviceAdded.
|
| + if (self->task_runner_->BelongsToCurrentThread()) {
|
| + self->OnDeviceAdded(device);
|
| + } else {
|
| + self->task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded,
|
| + base::Unretained(self), device));
|
| + }
|
| + break;
|
| + case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
|
| + libusb_ref_device(device); // Released in OnDeviceRemoved.
|
| + if (self->task_runner_->BelongsToCurrentThread()) {
|
| + self->OnDeviceRemoved(device);
|
| + } else {
|
| + self->task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved,
|
| + base::Unretained(self), device));
|
| + }
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) {
|
| + DCHECK(CalledOnValidThread());
|
| + DCHECK(!ContainsKey(platform_devices_, platform_device));
|
| +
|
| + AddDevice(platform_device);
|
| + libusb_unref_device(platform_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();
|
| + }
|
| +
|
| + libusb_unref_device(platform_device);
|
| +}
|
| +
|
| // static
|
| UsbService* UsbService::GetInstance(
|
| scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
|
|
|