summaryrefslogtreecommitdiffstats
path: root/device/usb/usb_service_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'device/usb/usb_service_impl.cc')
-rw-r--r--device/usb/usb_service_impl.cc614
1 files changed, 421 insertions, 193 deletions
diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc
index 6bdac2b..ce5b51a 100644
--- a/device/usb/usb_service_impl.cc
+++ b/device/usb/usb_service_impl.cc
@@ -4,6 +4,7 @@
#include "device/usb/usb_service_impl.h"
+#include <algorithm>
#include <set>
#include "base/bind.h"
@@ -11,25 +12,160 @@
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "components/device_event_log/device_event_log.h"
#include "device/usb/usb_error.h"
+#include "third_party/libusb/src/libusb/libusb.h"
#if defined(OS_WIN)
#include <setupapi.h>
#include <usbiodef.h>
-#include "base/scoped_observer.h"
#include "base/strings/string_util.h"
-#include "device/core/device_monitor_win.h"
#endif // OS_WIN
-namespace device {
+#if defined(USE_UDEV)
+#include "device/udev_linux/scoped_udev.h"
+#endif // USE_UDEV
-#if defined(OS_WIN)
+namespace device {
namespace {
+#if defined(USE_UDEV)
+
+void ReadDeviceStrings(PlatformUsbDevice platform_device,
+ libusb_device_descriptor* descriptor,
+ base::string16* manufacturer_string,
+ base::string16* product_string,
+ base::string16* serial_number,
+ std::string* device_node) {
+ ScopedUdevPtr udev(udev_new());
+ ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev.get()));
+
+ udev_enumerate_add_match_subsystem(enumerate.get(), "usb");
+ if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
+ return;
+ }
+ std::string bus_number =
+ base::IntToString(libusb_get_bus_number(platform_device));
+ std::string device_address =
+ base::IntToString(libusb_get_device_address(platform_device));
+ udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
+ for (udev_list_entry* i = devices; i != NULL;
+ i = udev_list_entry_get_next(i)) {
+ ScopedUdevDevicePtr device(
+ udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i)));
+ if (device) {
+ const char* value = udev_device_get_sysattr_value(device.get(), "busnum");
+ if (!value || bus_number != value) {
+ continue;
+ }
+ value = udev_device_get_sysattr_value(device.get(), "devnum");
+ if (!value || device_address != value) {
+ continue;
+ }
+
+ value = udev_device_get_devnode(device.get());
+ if (value) {
+ *device_node = value;
+ }
+ value = udev_device_get_sysattr_value(device.get(), "manufacturer");
+ if (value) {
+ *manufacturer_string = base::UTF8ToUTF16(value);
+ }
+ value = udev_device_get_sysattr_value(device.get(), "product");
+ if (value) {
+ *product_string = base::UTF8ToUTF16(value);
+ }
+ value = udev_device_get_sysattr_value(device.get(), "serial");
+ if (value) {
+ *serial_number = base::UTF8ToUTF16(value);
+ }
+ break;
+ }
+ }
+}
+
+#else
+
+uint16 ReadDeviceLanguage(PlatformUsbDeviceHandle handle) {
+ uint16 language_id = 0x0409;
+ uint8 buffer[256];
+ int size =
+ libusb_get_string_descriptor(handle, 0, 0, &buffer[0], sizeof(buffer));
+ if (size < 0) {
+ USB_LOG(EVENT) << "Failed to get supported string languages: "
+ << ConvertPlatformUsbErrorToString(size);
+ } else if (size >= 4) {
+ // Just pick the first supported language.
+ language_id = buffer[2] | (buffer[3] << 8);
+ } else {
+ USB_LOG(EVENT) << "List of available string languages invalid.";
+ }
+
+ return language_id;
+}
+
+void ReadDeviceString(PlatformUsbDeviceHandle handle,
+ uint8 string_id,
+ uint16 language_id,
+ base::string16* string) {
+ if (string_id == 0) {
+ return;
+ }
+
+ uint8 buffer[256];
+ int size = libusb_get_string_descriptor(handle, string_id, language_id,
+ &buffer[0], sizeof(buffer));
+ if (size < 0) {
+ USB_LOG(EVENT) << "Failed to read string " << (int)string_id
+ << " from the device: "
+ << ConvertPlatformUsbErrorToString(size);
+ } else if (size > 2) {
+ *string = base::string16(reinterpret_cast<base::char16*>(&buffer[2]),
+ size / 2 - 1);
+ } else {
+ USB_LOG(EVENT) << "String descriptor " << string_id << " is invalid.";
+ }
+}
+
+void ReadDeviceStrings(PlatformUsbDevice platform_device,
+ libusb_device_descriptor* descriptor,
+ base::string16* manufacturer_string,
+ base::string16* product_string,
+ base::string16* serial_number,
+ std::string* device_node) {
+ if (descriptor->iManufacturer == 0 && descriptor->iProduct == 0 &&
+ descriptor->iSerialNumber == 0) {
+ // Don't bother distrubing the device if it doesn't have any string
+ // descriptors we care about.
+ return;
+ }
+
+ PlatformUsbDeviceHandle handle;
+ int rv = libusb_open(platform_device, &handle);
+ if (rv != LIBUSB_SUCCESS) {
+ USB_LOG(EVENT) << "Failed to open device to read string descriptors: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return;
+ }
+
+ uint16 language_id = ReadDeviceLanguage(handle);
+ ReadDeviceString(handle, descriptor->iManufacturer, language_id,
+ manufacturer_string);
+ ReadDeviceString(handle, descriptor->iProduct, language_id, product_string);
+ ReadDeviceString(handle, descriptor->iSerialNumber, language_id,
+ serial_number);
+ libusb_close(handle);
+}
+
+#endif // USE_UDEV
+
+#if defined(OS_WIN)
+
// Wrapper around a HDEVINFO that automatically destroys it.
class ScopedDeviceInfoList {
public:
@@ -81,62 +217,57 @@ class ScopedDeviceInfo {
SP_DEVINFO_DATA dev_info_data_;
};
-} // namespace
-
-// This class lives on the application main thread so that it can listen for
-// device change notification window messages. It registers for notifications
-// that may indicate new devices that the UsbService will enumerate.
-class UsbServiceImpl::UIThreadHelper final
- : private DeviceMonitorWin::Observer {
- public:
- UIThreadHelper(base::WeakPtr<UsbServiceImpl> usb_service)
- : task_runner_(base::ThreadTaskRunnerHandle::Get()),
- usb_service_(usb_service),
- device_observer_(this) {}
-
- ~UIThreadHelper() {}
+bool IsWinUsbInterface(const std::string& device_path) {
+ ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL));
+ if (!dev_info_list.valid()) {
+ USB_PLOG(ERROR) << "Failed to create a device information set";
+ return false;
+ }
- void Start() {
- DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
- if (device_monitor) {
- device_observer_.Add(device_monitor);
- }
+ // This will add the device to |dev_info_list| so we can query driver info.
+ if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0,
+ NULL)) {
+ USB_PLOG(ERROR) << "Failed to get device interface data for "
+ << device_path;
+ return false;
}
- private:
- void OnDeviceAdded(const GUID& class_guid,
- const std::string& device_path) override {
- // Only the root node of a composite USB device has the class GUID
- // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded.
- // This first pass filter will catch anything that's sitting on the USB bus
- // (including devices on 3rd party USB controllers) to avoid the more
- // expensive driver check that needs to be done on the FILE thread.
- if (device_path.find("usb") != std::string::npos) {
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesIfWinUsbDevice,
- usb_service_, device_path));
- }
+ ScopedDeviceInfo dev_info;
+ if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) {
+ USB_PLOG(ERROR) << "Failed to get device info for " << device_path;
+ return false;
}
+ dev_info.set_valid(dev_info_list.get());
- void OnDeviceRemoved(const GUID& class_guid,
- const std::string& device_path) override {
- // The root USB device node is removed last
- if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) {
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevices, usb_service_));
- }
+ DWORD reg_data_type;
+ BYTE buffer[256];
+ if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(),
+ SPDRP_SERVICE, &reg_data_type,
+ &buffer[0], sizeof buffer, NULL)) {
+ USB_PLOG(ERROR) << "Failed to get device service property";
+ return false;
+ }
+ if (reg_data_type != REG_SZ) {
+ USB_LOG(ERROR) << "Unexpected data type for driver service: "
+ << reg_data_type;
+ return false;
}
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- base::WeakPtr<UsbServiceImpl> usb_service_;
- ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
-};
+ USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << ".";
+ if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") ==
+ 0) {
+ return true;
+ }
+ return false;
+}
#endif // OS_WIN
+} // namespace
+
// static
UsbService* UsbServiceImpl::Create(
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
PlatformUsbContext context = NULL;
const int rv = libusb_init(&context);
if (rv != LIBUSB_SUCCESS) {
@@ -148,42 +279,21 @@ UsbService* UsbServiceImpl::Create(
return nullptr;
}
- return new UsbServiceImpl(context, ui_task_runner);
-}
-
-scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
- DCHECK(CalledOnValidThread());
- RefreshDevices();
- DeviceMap::iterator it = devices_.find(unique_id);
- if (it != devices_.end()) {
- return it->second;
- }
- return NULL;
-}
-
-void UsbServiceImpl::GetDevices(
- std::vector<scoped_refptr<UsbDevice> >* devices) {
- DCHECK(CalledOnValidThread());
- STLClearObject(devices);
-
- if (!hotplug_enabled_) {
- RefreshDevices();
- }
-
- for (const auto& map_entry : devices_) {
- devices->push_back(map_entry.second);
- }
+ return new UsbServiceImpl(context, blocking_task_runner);
}
UsbServiceImpl::UsbServiceImpl(
PlatformUsbContext context,
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
: context_(new UsbContext(context)),
- ui_task_runner_(ui_task_runner),
- next_unique_id_(0),
- hotplug_enabled_(false),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ blocking_task_runner_(blocking_task_runner),
+#if defined(OS_WIN)
+ device_observer_(this),
+#endif
weak_factory_(this) {
- task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ base::MessageLoop::current()->AddDestructionObserver(this);
+
int rv = libusb_hotplug_register_callback(
context_->context(),
static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
@@ -193,149 +303,269 @@ UsbServiceImpl::UsbServiceImpl(
&UsbServiceImpl::HotplugCallback, this, &hotplug_handle_);
if (rv == LIBUSB_SUCCESS) {
hotplug_enabled_ = true;
+
+ // libusb will call the hotplug callback for each device currently
+ // enumerated. Once this is complete enumeration_ready_ can be set to true
+ // but we must first wait for any tasks posted to blocking_task_runner_ to
+ // complete.
+ blocking_task_runner_->PostTaskAndReply(
+ FROM_HERE, base::Bind(&base::DoNothing),
+ base::Bind(&UsbServiceImpl::RefreshDevicesComplete,
+ weak_factory_.GetWeakPtr(), nullptr, 0));
} else {
+ RefreshDevices("");
#if defined(OS_WIN)
- ui_thread_helper_ = new UIThreadHelper(weak_factory_.GetWeakPtr());
- ui_task_runner_->PostTask(FROM_HERE,
- base::Bind(&UIThreadHelper::Start,
- base::Unretained(ui_thread_helper_)));
+ DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
+ if (device_monitor) {
+ device_observer_.Add(device_monitor);
+ }
#endif // OS_WIN
}
}
UsbServiceImpl::~UsbServiceImpl() {
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+
if (hotplug_enabled_) {
libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
}
-#if defined(OS_WIN)
- if (ui_thread_helper_) {
- ui_task_runner_->DeleteSoon(FROM_HERE, ui_thread_helper_);
- }
-#endif // OS_WIN
for (const auto& map_entry : devices_) {
map_entry.second->OnDisconnect();
}
}
-void UsbServiceImpl::RefreshDevices() {
+scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
DCHECK(CalledOnValidThread());
-
- libusb_device** platform_devices = NULL;
- const ssize_t device_count =
- libusb_get_device_list(context_->context(), &platform_devices);
- if (device_count < 0) {
- USB_LOG(ERROR) << "Failed to get device list: "
- << ConvertPlatformUsbErrorToString(device_count);
+ DeviceMap::iterator it = devices_.find(unique_id);
+ if (it != devices_.end()) {
+ return it->second;
}
+ return NULL;
+}
- std::set<UsbDevice*> connected_devices;
- std::vector<PlatformUsbDevice> disconnected_devices;
+void UsbServiceImpl::GetDevices(const GetDevicesCallback& callback) {
+ DCHECK(CalledOnValidThread());
- // Populates new devices.
- for (ssize_t i = 0; i < device_count; ++i) {
- if (!ContainsKey(platform_devices_, platform_devices[i])) {
- scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]);
- if (new_device) {
- connected_devices.insert(new_device.get());
- }
- } else {
- connected_devices.insert(platform_devices_[platform_devices[i]].get());
+ if (!enumeration_ready_) {
+ // On startup wait for the first enumeration,
+ pending_enumerations_.push_back(callback);
+ } else if (hotplug_enabled_) {
+ // The device list is updated live when hotplug events are supported.
+ std::vector<scoped_refptr<UsbDevice>> devices;
+ for (const auto& map_entry : devices_) {
+ devices.push_back(map_entry.second);
+ }
+ callback.Run(devices);
+ } else {
+ // Only post one re-enumeration task at a time.
+ if (pending_enumerations_.empty()) {
+ RefreshDevices("");
}
+ pending_enumerations_.push_back(callback);
}
+}
- // Find disconnected devices.
- for (const auto& map_entry : platform_devices_) {
- PlatformUsbDevice platform_device = map_entry.first;
- scoped_refptr<UsbDeviceImpl> device = map_entry.second;
- if (!ContainsKey(connected_devices, device.get())) {
- disconnected_devices.push_back(platform_device);
- devices_.erase(device->unique_id());
-
- NotifyDeviceRemoved(device);
- device->OnDisconnect();
- }
+#if defined(OS_WIN)
+
+void UsbServiceImpl::OnDeviceAdded(const GUID& class_guid,
+ const std::string& device_path) {
+ // Only the root node of a composite USB device has the class GUID
+ // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded.
+ // This first pass filter will catch anything that's sitting on the USB bus
+ // (including devices on 3rd party USB controllers) to avoid the more
+ // expensive driver check that needs to be done on the FILE thread.
+ if (device_path.find("usb") != std::string::npos) {
+ RefreshDevices(device_path);
}
+}
- // Remove disconnected devices from platform_devices_.
- for (const PlatformUsbDevice& platform_device : disconnected_devices) {
- // UsbDevice will be destroyed after this. The corresponding
- // PlatformUsbDevice will be unref'ed during this process.
- platform_devices_.erase(platform_device);
+void UsbServiceImpl::OnDeviceRemoved(const GUID& class_guid,
+ const std::string& device_path) {
+ // The root USB device node is removed last
+ if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) {
+ RefreshDevices("");
}
+}
- libusb_free_device_list(platform_devices, true);
+#endif // OS_WIN
+
+void UsbServiceImpl::WillDestroyCurrentMessageLoop() {
+ DCHECK(CalledOnValidThread());
+ delete this;
}
-#if defined(OS_WIN)
-void UsbServiceImpl::RefreshDevicesIfWinUsbDevice(
- const std::string& device_path) {
- ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL));
- if (!dev_info_list.valid()) {
- USB_PLOG(ERROR) << "Failed to create a device information set";
- return;
- }
+void UsbServiceImpl::RefreshDevices(const std::string& new_device_path) {
+ DCHECK(CalledOnValidThread());
- // This will add the device to |dev_info_list| so we can query driver info.
- if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0,
- NULL)) {
- USB_PLOG(ERROR) << "Failed to get device interface data for "
- << device_path;
- return;
+ std::set<PlatformUsbDevice> current_devices;
+ for (const auto& map_entry : platform_devices_) {
+ current_devices.insert(map_entry.first);
}
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesOnBlockingThread,
+ weak_factory_.GetWeakPtr(), new_device_path,
+ task_runner_, context_, current_devices));
+}
- ScopedDeviceInfo dev_info;
- if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) {
- USB_PLOG(ERROR) << "Failed to get device info for " << device_path;
- return;
+// static
+void UsbServiceImpl::RefreshDevicesOnBlockingThread(
+ base::WeakPtr<UsbServiceImpl> usb_service,
+ const std::string& new_device_path,
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ scoped_refptr<UsbContext> usb_context,
+ const std::set<PlatformUsbDevice>& previous_devices) {
+ if (!new_device_path.empty()) {
+#if defined(OS_WIN)
+ if (!IsWinUsbInterface(new_device_path)) {
+ // Wait to call libusb_get_device_list until libusb will be able to find
+ // a WinUSB interface for the device.
+ return;
+ }
+#endif // defined(OS_WIN)
}
- dev_info.set_valid(dev_info_list.get());
- DWORD reg_data_type;
- BYTE buffer[256];
- if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(),
- SPDRP_SERVICE, &reg_data_type,
- &buffer[0], sizeof buffer, NULL)) {
- USB_PLOG(ERROR) << "Failed to get device service property";
- return;
- }
- if (reg_data_type != REG_SZ) {
- USB_LOG(ERROR) << "Unexpected data type for driver service: "
- << reg_data_type;
+ libusb_device** platform_devices = NULL;
+ const ssize_t device_count =
+ libusb_get_device_list(usb_context->context(), &platform_devices);
+ if (device_count < 0) {
+ USB_LOG(ERROR) << "Failed to get device list: "
+ << ConvertPlatformUsbErrorToString(device_count);
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&UsbServiceImpl::RefreshDevicesComplete,
+ usb_service, nullptr, 0));
return;
}
- USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << ".";
- if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") ==
- 0) {
- RefreshDevices();
+ // Find new devices.
+ for (ssize_t i = 0; i < device_count; ++i) {
+ PlatformUsbDevice platform_device = platform_devices[i];
+ if (previous_devices.find(platform_device) == previous_devices.end()) {
+ libusb_ref_device(platform_device);
+ AddDeviceOnBlockingThread(usb_service, task_runner, platform_device);
+ }
}
+
+ // |platform_devices| will be freed in this callback.
+ task_runner->PostTask(
+ FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesComplete,
+ usb_service, platform_devices, device_count));
}
-#endif // OS_WIN
-scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice(
+// static
+void UsbServiceImpl::AddDeviceOnBlockingThread(
+ base::WeakPtr<UsbServiceImpl> usb_service,
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
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;
- NotifyDeviceAdded(new_device);
- return new_device;
+ base::string16 manufacturer_string;
+ base::string16 product_string;
+ base::string16 serial_number;
+ std::string device_node;
+ ReadDeviceStrings(platform_device, &descriptor, &manufacturer_string,
+ &product_string, &serial_number, &device_node);
+
+ task_runner->PostTask(
+ FROM_HERE, base::Bind(&UsbServiceImpl::AddDevice, usb_service,
+ platform_device, descriptor.idVendor,
+ descriptor.idProduct, manufacturer_string,
+ product_string, serial_number, device_node));
} else {
USB_LOG(EVENT) << "Failed to get device descriptor: "
<< ConvertPlatformUsbErrorToString(rv);
- return nullptr;
+ libusb_unref_device(platform_device);
}
}
+void UsbServiceImpl::RefreshDevicesComplete(libusb_device** platform_devices,
+ ssize_t device_count) {
+ if (platform_devices) {
+ // Mark devices seen in this enumeration.
+ for (ssize_t i = 0; i < device_count; ++i) {
+ const PlatformDeviceMap::iterator it =
+ platform_devices_.find(platform_devices[i]);
+ if (it != platform_devices_.end()) {
+ it->second->set_visited(true);
+ }
+ }
+
+ // Remove devices not seen in this enumeration.
+ for (PlatformDeviceMap::iterator it = platform_devices_.begin();
+ it != platform_devices_.end();
+ /* incremented internally */) {
+ PlatformDeviceMap::iterator current = it++;
+ const scoped_refptr<UsbDeviceImpl>& device = current->second;
+ if (device->was_visited()) {
+ device->set_visited(false);
+ } else {
+ RemoveDevice(device);
+ }
+ }
+
+ libusb_free_device_list(platform_devices, true);
+ }
+
+ enumeration_ready_ = true;
+
+ if (!pending_enumerations_.empty()) {
+ std::vector<scoped_refptr<UsbDevice>> devices;
+ for (const auto& map_entry : devices_) {
+ devices.push_back(map_entry.second);
+ }
+
+ std::vector<GetDevicesCallback> pending_enumerations;
+ pending_enumerations.swap(pending_enumerations_);
+ for (const GetDevicesCallback& callback : pending_enumerations) {
+ callback.Run(devices);
+ }
+ }
+}
+
+void UsbServiceImpl::AddDevice(PlatformUsbDevice platform_device,
+ uint16 vendor_id,
+ uint16 product_id,
+ base::string16 manufacturer_string,
+ base::string16 product_string,
+ base::string16 serial_number,
+ std::string device_node) {
+ uint32 unique_id;
+ do {
+ unique_id = ++next_unique_id_;
+ } while (devices_.find(unique_id) != devices_.end());
+
+ scoped_refptr<UsbDeviceImpl> device(
+ new UsbDeviceImpl(context_, platform_device, vendor_id, product_id,
+ unique_id, manufacturer_string, product_string,
+ serial_number, blocking_task_runner_));
+
+ platform_devices_[platform_device] = device;
+ devices_[unique_id] = device;
+
+ USB_LOG(USER) << "USB device added: vendor=" << device->vendor_id() << " \""
+ << device->manufacturer_string()
+ << "\", product=" << device->product_id() << " \""
+ << device->product_string() << "\", serial=\""
+ << device->serial_number()
+ << "\", uniqueId=" << device->unique_id();
+
+ if (enumeration_ready_) {
+ NotifyDeviceAdded(device);
+ }
+
+ libusb_unref_device(platform_device);
+}
+
+void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) {
+ platform_devices_.erase(device->platform_device());
+ devices_.erase(device->unique_id());
+
+ USB_LOG(USER) << "USB device removed: uniqueId=" << device->unique_id();
+
+ NotifyDeviceRemoved(device);
+ device->OnDisconnect();
+}
+
// static
int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
PlatformUsbDevice device,
@@ -348,22 +578,22 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data);
switch (event) {
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
- libusb_ref_device(device); // Released in OnDeviceAdded.
+ libusb_ref_device(device); // Released in OnPlatformDeviceAdded.
if (self->task_runner_->BelongsToCurrentThread()) {
- self->OnDeviceAdded(device);
+ self->OnPlatformDeviceAdded(device);
} else {
self->task_runner_->PostTask(
- FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded,
+ FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceAdded,
base::Unretained(self), device));
}
break;
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
- libusb_ref_device(device); // Released in OnDeviceRemoved.
+ libusb_ref_device(device); // Released in OnPlatformDeviceRemoved.
if (self->task_runner_->BelongsToCurrentThread()) {
- self->OnDeviceRemoved(device);
+ self->OnPlatformDeviceRemoved(device);
} else {
self->task_runner_->PostTask(
- FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved,
+ FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceRemoved,
base::Unretained(self), device));
}
break;
@@ -374,30 +604,28 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
return 0;
}
-void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) {
+void UsbServiceImpl::OnPlatformDeviceAdded(PlatformUsbDevice platform_device) {
DCHECK(CalledOnValidThread());
DCHECK(!ContainsKey(platform_devices_, platform_device));
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbServiceImpl::AddDeviceOnBlockingThread,
+ weak_factory_.GetWeakPtr(), task_runner_, platform_device));
- AddDevice(platform_device);
- libusb_unref_device(platform_device);
+ // libusb_unref_device(platform_device) is called by the task above.
}
-void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) {
+void UsbServiceImpl::OnPlatformDeviceRemoved(
+ 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();
- }
- platform_devices_.erase(it);
-
- NotifyDeviceRemoved(device);
- device->OnDisconnect();
+ // Serialize with calls to AddDeviceOnBlockingThread.
+ blocking_task_runner_->PostTaskAndReply(
+ FROM_HERE, base::Bind(&base::DoNothing),
+ base::Bind(&UsbServiceImpl::RemoveDevice, weak_factory_.GetWeakPtr(),
+ device));
} else {
NOTREACHED();
}