| Index: device/usb/usb_service_impl.cc
|
| diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc
|
| index 6bdac2bfdc9744212da07fd7aa5f58871223b60a..ce5b51a4f8be15011f4d937a71120646fa118b9e 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, ®_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, ®_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();
|
| }
|
|
|