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(); |
} |