Commit 5c6598f4 authored by Reilly Grant's avatar Reilly Grant Committed by Commit Bot

Create ScopedLibusbDevice to simplify reference counting

This change wraps strong references to libusb_device objects in a
ScopedLibusbDevice class (specializing ScopedGeneric) in order to better
document when these references are expected to be dropped.

Bug: 819356
Change-Id: I9671d340f0d5353637405ddd72846456e23a5f1f
Reviewed-on: https://chromium-review.googlesource.com/972623Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545620}
parent 18d470b2
...@@ -81,6 +81,7 @@ static_library("usb") { ...@@ -81,6 +81,7 @@ static_library("usb") {
if (is_win || is_mac) { if (is_win || is_mac) {
sources += [ sources += [
"scoped_libusb_device_ref.h",
"usb_context.cc", "usb_context.cc",
"usb_context.h", "usb_context.h",
"usb_device_handle_impl.cc", "usb_device_handle_impl.cc",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DEVICE_USB_SCOPED_LIBUSB_DEVICE_REF_H_
#define DEVICE_USB_SCOPED_LIBUSB_DEVICE_REF_H_
#include "base/scoped_generic.h"
#include "third_party/libusb/src/libusb/libusb.h"
namespace device {
struct LibusbDeviceRefTraits {
static libusb_device* InvalidValue() { return nullptr; }
static void Free(libusb_device* device) { libusb_unref_device(device); }
};
using ScopedLibusbDeviceRef =
base::ScopedGeneric<libusb_device*, LibusbDeviceRefTraits>;
} // namespace device
#endif // DEVICE_USB_SCOPED_LIBUSB_DEVICE_REF_H_
...@@ -29,8 +29,6 @@ ...@@ -29,8 +29,6 @@
namespace device { namespace device {
typedef libusb_device* PlatformUsbDevice;
void HandleTransferCompletion(PlatformUsbTransferHandle transfer); void HandleTransferCompletion(PlatformUsbTransferHandle transfer);
namespace { namespace {
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
namespace device { namespace device {
UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context, UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context,
PlatformUsbDevice platform_device, ScopedLibusbDeviceRef platform_device,
const libusb_device_descriptor& descriptor) const libusb_device_descriptor& descriptor)
: UsbDevice(descriptor.bcdUSB, : UsbDevice(descriptor.bcdUSB,
descriptor.bDeviceClass, descriptor.bDeviceClass,
...@@ -41,17 +41,15 @@ UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context, ...@@ -41,17 +41,15 @@ UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context,
base::string16(), base::string16(),
base::string16(), base::string16(),
base::string16()), base::string16()),
platform_device_(platform_device), platform_device_(std::move(platform_device)),
context_(context) { context_(std::move(context)) {
CHECK(platform_device) << "platform_device cannot be NULL"; CHECK(platform_device_.is_valid()) << "platform_device must be valid";
libusb_ref_device(platform_device);
ReadAllConfigurations(); ReadAllConfigurations();
RefreshActiveConfiguration(); RefreshActiveConfiguration();
} }
UsbDeviceImpl::~UsbDeviceImpl() { UsbDeviceImpl::~UsbDeviceImpl() {
// The destructor must be safe to call from any thread. // The destructor must be safe to call from any thread.
libusb_unref_device(platform_device_);
} }
void UsbDeviceImpl::Open(OpenCallback callback) { void UsbDeviceImpl::Open(OpenCallback callback) {
...@@ -68,11 +66,11 @@ void UsbDeviceImpl::Open(OpenCallback callback) { ...@@ -68,11 +66,11 @@ void UsbDeviceImpl::Open(OpenCallback callback) {
void UsbDeviceImpl::ReadAllConfigurations() { void UsbDeviceImpl::ReadAllConfigurations() {
libusb_device_descriptor device_descriptor; libusb_device_descriptor device_descriptor;
int rv = libusb_get_device_descriptor(platform_device_, &device_descriptor); int rv = libusb_get_device_descriptor(platform_device(), &device_descriptor);
if (rv == LIBUSB_SUCCESS) { if (rv == LIBUSB_SUCCESS) {
for (uint8_t i = 0; i < device_descriptor.bNumConfigurations; ++i) { for (uint8_t i = 0; i < device_descriptor.bNumConfigurations; ++i) {
unsigned char* buffer; unsigned char* buffer;
rv = libusb_get_raw_config_descriptor(platform_device_, i, &buffer); rv = libusb_get_raw_config_descriptor(platform_device(), i, &buffer);
if (rv < 0) { if (rv < 0) {
USB_LOG(EVENT) << "Failed to get config descriptor: " USB_LOG(EVENT) << "Failed to get config descriptor: "
<< ConvertPlatformUsbErrorToString(rv); << ConvertPlatformUsbErrorToString(rv);
...@@ -91,7 +89,7 @@ void UsbDeviceImpl::ReadAllConfigurations() { ...@@ -91,7 +89,7 @@ void UsbDeviceImpl::ReadAllConfigurations() {
void UsbDeviceImpl::RefreshActiveConfiguration() { void UsbDeviceImpl::RefreshActiveConfiguration() {
uint8_t config_value; uint8_t config_value;
int rv = libusb_get_active_config_value(platform_device_, &config_value); int rv = libusb_get_active_config_value(platform_device(), &config_value);
if (rv != LIBUSB_SUCCESS) { if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to get active configuration: " USB_LOG(EVENT) << "Failed to get active configuration: "
<< ConvertPlatformUsbErrorToString(rv); << ConvertPlatformUsbErrorToString(rv);
...@@ -107,7 +105,7 @@ void UsbDeviceImpl::OpenOnBlockingThread( ...@@ -107,7 +105,7 @@ void UsbDeviceImpl::OpenOnBlockingThread(
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
base::AssertBlockingAllowed(); base::AssertBlockingAllowed();
PlatformUsbDeviceHandle handle; PlatformUsbDeviceHandle handle;
const int rv = libusb_open(platform_device_, &handle); const int rv = libusb_open(platform_device(), &handle);
if (LIBUSB_SUCCESS == rv) { if (LIBUSB_SUCCESS == rv) {
task_runner->PostTask( task_runner->PostTask(
FROM_HERE, base::BindOnce(&UsbDeviceImpl::Opened, this, handle, FROM_HERE, base::BindOnce(&UsbDeviceImpl::Opened, this, handle,
......
...@@ -17,13 +17,13 @@ ...@@ -17,13 +17,13 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "device/usb/scoped_libusb_device_ref.h"
#include "device/usb/usb_descriptors.h" #include "device/usb/usb_descriptors.h"
#include "device/usb/usb_device.h" #include "device/usb/usb_device.h"
struct libusb_device; struct libusb_device;
struct libusb_device_descriptor; struct libusb_device_descriptor;
struct libusb_device_handle; struct libusb_device_handle;
struct libusb_config_descriptor;
namespace base { namespace base {
class SequencedTaskRunner; class SequencedTaskRunner;
...@@ -34,12 +34,14 @@ namespace device { ...@@ -34,12 +34,14 @@ namespace device {
class UsbDeviceHandleImpl; class UsbDeviceHandleImpl;
class UsbContext; class UsbContext;
typedef struct libusb_device* PlatformUsbDevice;
typedef struct libusb_config_descriptor* PlatformUsbConfigDescriptor;
typedef struct libusb_device_handle* PlatformUsbDeviceHandle; typedef struct libusb_device_handle* PlatformUsbDeviceHandle;
class UsbDeviceImpl : public UsbDevice { class UsbDeviceImpl : public UsbDevice {
public: public:
UsbDeviceImpl(scoped_refptr<UsbContext> context,
ScopedLibusbDeviceRef platform_device,
const libusb_device_descriptor& descriptor);
// UsbDevice implementation: // UsbDevice implementation:
void Open(OpenCallback callback) override; void Open(OpenCallback callback) override;
...@@ -56,17 +58,12 @@ class UsbDeviceImpl : public UsbDevice { ...@@ -56,17 +58,12 @@ class UsbDeviceImpl : public UsbDevice {
} }
void set_webusb_landing_page(const GURL& url) { webusb_landing_page_ = url; } void set_webusb_landing_page(const GURL& url) { webusb_landing_page_ = url; }
PlatformUsbDevice platform_device() const { return platform_device_; } libusb_device* platform_device() const { return platform_device_.get(); }
protected: protected:
friend class UsbServiceImpl; friend class UsbServiceImpl;
friend class UsbDeviceHandleImpl; friend class UsbDeviceHandleImpl;
// Called by UsbServiceImpl only;
UsbDeviceImpl(scoped_refptr<UsbContext> context,
PlatformUsbDevice platform_device,
const libusb_device_descriptor& descriptor);
~UsbDeviceImpl() override; ~UsbDeviceImpl() override;
void ReadAllConfigurations(); void ReadAllConfigurations();
...@@ -87,11 +84,11 @@ class UsbDeviceImpl : public UsbDevice { ...@@ -87,11 +84,11 @@ class UsbDeviceImpl : public UsbDevice {
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
base::ThreadChecker thread_checker_; base::ThreadChecker thread_checker_;
PlatformUsbDevice platform_device_; const ScopedLibusbDeviceRef platform_device_;
bool visited_ = false; bool visited_ = false;
// Retain the context so that it will not be released before UsbDevice. // Retain the context so that it will not be released before UsbDevice.
scoped_refptr<UsbContext> context_; const scoped_refptr<UsbContext> context_;
DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl); DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl);
}; };
......
...@@ -103,13 +103,15 @@ void GetDeviceListOnBlockingThread( ...@@ -103,13 +103,15 @@ void GetDeviceListOnBlockingThread(
const std::string& new_device_path, const std::string& new_device_path,
scoped_refptr<UsbContext> usb_context, scoped_refptr<UsbContext> usb_context,
scoped_refptr<base::SequencedTaskRunner> task_runner, scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::Callback<void(libusb_device**, size_t)>& callback) { base::OnceCallback<void(base::Optional<std::vector<ScopedLibusbDeviceRef>>)>
callback) {
#if defined(OS_WIN) #if defined(OS_WIN)
if (!new_device_path.empty()) { if (!new_device_path.empty()) {
if (!IsWinUsbInterface(new_device_path)) { if (!IsWinUsbInterface(new_device_path)) {
// Wait to call libusb_get_device_list until libusb will be able to find // Wait to call libusb_get_device_list until libusb will be able to find
// a WinUSB interface for the device. // a WinUSB interface for the device.
task_runner->PostTask(FROM_HERE, base::BindOnce(callback, nullptr, 0)); task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), base::nullopt));
return; return;
} }
} }
...@@ -121,18 +123,28 @@ void GetDeviceListOnBlockingThread( ...@@ -121,18 +123,28 @@ void GetDeviceListOnBlockingThread(
if (device_count < 0) { if (device_count < 0) {
USB_LOG(ERROR) << "Failed to get device list: " USB_LOG(ERROR) << "Failed to get device list: "
<< ConvertPlatformUsbErrorToString(device_count); << ConvertPlatformUsbErrorToString(device_count);
task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr, 0)); task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), base::nullopt));
return; return;
} }
task_runner->PostTask(FROM_HERE, std::vector<ScopedLibusbDeviceRef> scoped_devices;
base::Bind(callback, platform_devices, device_count)); scoped_devices.reserve(device_count);
for (ssize_t i = 0; i < device_count; ++i)
scoped_devices.emplace_back(platform_devices[i]);
// Free the list but don't unref the devices because ownership has been
// been transfered to the elements of |scoped_devices|.
libusb_free_device_list(platform_devices, false);
task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback),
std::move(scoped_devices)));
} }
void CloseHandleAndRunContinuation(scoped_refptr<UsbDeviceHandle> device_handle, void CloseHandleAndRunContinuation(scoped_refptr<UsbDeviceHandle> device_handle,
const base::Closure& continuation) { base::OnceClosure continuation) {
device_handle->Close(); device_handle->Close();
continuation.Run(); std::move(continuation).Run();
} }
void SaveStringsAndRunContinuation( void SaveStringsAndRunContinuation(
...@@ -168,8 +180,8 @@ void OnDeviceOpenedReadDescriptors( ...@@ -168,8 +180,8 @@ void OnDeviceOpenedReadDescriptors(
uint8_t product, uint8_t product,
uint8_t serial_number, uint8_t serial_number,
bool read_bos_descriptors, bool read_bos_descriptors,
const base::Closure& success_closure, base::OnceClosure success_closure,
const base::Closure& failure_closure, base::OnceClosure failure_closure,
scoped_refptr<UsbDeviceHandle> device_handle) { scoped_refptr<UsbDeviceHandle> device_handle) {
if (device_handle) { if (device_handle) {
std::unique_ptr<std::map<uint8_t, base::string16>> string_map( std::unique_ptr<std::map<uint8_t, base::string16>> string_map(
...@@ -188,9 +200,9 @@ void OnDeviceOpenedReadDescriptors( ...@@ -188,9 +200,9 @@ void OnDeviceOpenedReadDescriptors(
count++; count++;
DCHECK_GT(count, 0); DCHECK_GT(count, 0);
base::Closure barrier = base::RepeatingClosure barrier = base::BarrierClosure(
base::BarrierClosure(count, base::Bind(&CloseHandleAndRunContinuation, count, base::BindOnce(&CloseHandleAndRunContinuation, device_handle,
device_handle, success_closure)); std::move(success_closure)));
if (!string_map->empty()) { if (!string_map->empty()) {
scoped_refptr<UsbDeviceImpl> device = scoped_refptr<UsbDeviceImpl> device =
...@@ -207,7 +219,7 @@ void OnDeviceOpenedReadDescriptors( ...@@ -207,7 +219,7 @@ void OnDeviceOpenedReadDescriptors(
device_handle, barrier)); device_handle, barrier));
} }
} else { } else {
failure_closure.Run(); std::move(failure_closure).Run();
} }
} }
...@@ -229,8 +241,6 @@ UsbServiceImpl::UsbServiceImpl() ...@@ -229,8 +241,6 @@ UsbServiceImpl::UsbServiceImpl()
UsbServiceImpl::~UsbServiceImpl() { UsbServiceImpl::~UsbServiceImpl() {
if (hotplug_enabled_) if (hotplug_enabled_)
libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
for (auto* platform_device : ignored_devices_)
libusb_unref_device(platform_device);
} }
void UsbServiceImpl::GetDevices(const GetDevicesCallback& callback) { void UsbServiceImpl::GetDevices(const GetDevicesCallback& callback) {
...@@ -320,45 +330,38 @@ void UsbServiceImpl::RefreshDevices() { ...@@ -320,45 +330,38 @@ void UsbServiceImpl::RefreshDevices() {
pending_path_enumerations_.pop(); pending_path_enumerations_.pop();
} }
base::PostTaskWithTraits(FROM_HERE, kBlockingTaskTraits, base::PostTaskWithTraits(
base::Bind(&GetDeviceListOnBlockingThread, FROM_HERE, kBlockingTaskTraits,
device_path, context_, task_runner(), base::BindOnce(&GetDeviceListOnBlockingThread, device_path, context_,
base::Bind(&UsbServiceImpl::OnDeviceList, task_runner(),
weak_factory_.GetWeakPtr()))); base::BindOnce(&UsbServiceImpl::OnDeviceList,
weak_factory_.GetWeakPtr())));
} }
void UsbServiceImpl::OnDeviceList(libusb_device** platform_devices, void UsbServiceImpl::OnDeviceList(
size_t device_count) { base::Optional<std::vector<ScopedLibusbDeviceRef>> devices) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!platform_devices) { if (!devices) {
RefreshDevicesComplete(); RefreshDevicesComplete();
return; return;
} }
base::Closure refresh_complete = std::vector<ScopedLibusbDeviceRef> new_devices;
base::BarrierClosure(static_cast<int>(device_count),
base::Bind(&UsbServiceImpl::RefreshDevicesComplete,
weak_factory_.GetWeakPtr()));
std::list<PlatformUsbDevice> new_devices;
std::set<PlatformUsbDevice> existing_ignored_devices;
// Look for new and existing devices. // Look for new and existing devices.
for (size_t i = 0; i < device_count; ++i) { for (auto& device : *devices) {
PlatformUsbDevice platform_device = platform_devices[i]; // Ignore devices that have failed enumeration previously.
// Ignore some devices. if (base::ContainsValue(ignored_devices_, device.get()))
if (base::ContainsKey(ignored_devices_, platform_device)) {
existing_ignored_devices.insert(platform_device);
refresh_complete.Run();
continue; continue;
}
auto it = platform_devices_.find(platform_device);
auto it = platform_devices_.find(device.get());
if (it == platform_devices_.end()) { if (it == platform_devices_.end()) {
new_devices.push_back(platform_device); new_devices.push_back(std::move(device));
} else { } else {
// Mark the existing device object visited and remove it from the list so
// it will not be ignored.
it->second->set_visited(true); it->second->set_visited(true);
refresh_complete.Run(); device.reset();
} }
} }
...@@ -375,21 +378,21 @@ void UsbServiceImpl::OnDeviceList(libusb_device** platform_devices, ...@@ -375,21 +378,21 @@ void UsbServiceImpl::OnDeviceList(libusb_device** platform_devices,
} }
} }
// Remove devices not seen in this enumeration from |ignored_devices_|. // Remaining devices are being ignored. Clear the old list so that devices
for (auto it = ignored_devices_.begin(); it != ignored_devices_.end(); // that have been removed don't remain in |ignored_devices_| indefinitely.
/* incremented internally */) { ignored_devices_.clear();
auto current = it++; for (auto& device : *devices) {
if (!base::ContainsKey(existing_ignored_devices, *current)) { if (device.is_valid())
libusb_unref_device(*current); ignored_devices_.push_back(std::move(device));
ignored_devices_.erase(current);
}
}
for (PlatformUsbDevice platform_device : new_devices) {
EnumerateDevice(platform_device, refresh_complete);
} }
libusb_free_device_list(platform_devices, true); // Enumerate new devices.
base::RepeatingClosure refresh_complete = base::BarrierClosure(
new_devices.size(),
base::BindOnce(&UsbServiceImpl::RefreshDevicesComplete,
weak_factory_.GetWeakPtr()));
for (auto& device : new_devices)
EnumerateDevice(std::move(device), refresh_complete);
} }
void UsbServiceImpl::RefreshDevicesComplete() { void UsbServiceImpl::RefreshDevicesComplete() {
...@@ -417,58 +420,63 @@ void UsbServiceImpl::RefreshDevicesComplete() { ...@@ -417,58 +420,63 @@ void UsbServiceImpl::RefreshDevicesComplete() {
} }
} }
void UsbServiceImpl::EnumerateDevice(PlatformUsbDevice platform_device, void UsbServiceImpl::EnumerateDevice(ScopedLibusbDeviceRef platform_device,
const base::Closure& refresh_complete) { const base::Closure& refresh_complete) {
DCHECK(context_); DCHECK(context_);
devices_being_enumerated_.insert(platform_device);
libusb_device_descriptor descriptor; libusb_device_descriptor descriptor;
int rv = libusb_get_device_descriptor(platform_device, &descriptor); int rv = libusb_get_device_descriptor(platform_device.get(), &descriptor);
if (rv == LIBUSB_SUCCESS) { if (rv != LIBUSB_SUCCESS) {
if (descriptor.bDeviceClass == LIBUSB_CLASS_HUB) { USB_LOG(EVENT) << "Failed to get device descriptor: "
// Don't try to enumerate hubs. We never want to connect to a hub. << ConvertPlatformUsbErrorToString(rv);
libusb_ref_device(platform_device); EnumerationFailed(std::move(platform_device), refresh_complete);
ignored_devices_.insert(platform_device); return;
refresh_complete.Run(); }
return;
}
scoped_refptr<UsbDeviceImpl> device( if (descriptor.bDeviceClass == LIBUSB_CLASS_HUB) {
new UsbDeviceImpl(context_, platform_device, descriptor)); // Don't try to enumerate hubs. We never want to connect to a hub.
base::Closure add_device = EnumerationFailed(std::move(platform_device), refresh_complete);
base::Bind(&UsbServiceImpl::AddDevice, weak_factory_.GetWeakPtr(), return;
refresh_complete, device); }
base::Closure enumeration_failed = base::Bind(
&UsbServiceImpl::EnumerationFailed, weak_factory_.GetWeakPtr(),
platform_device, refresh_complete);
bool read_bos_descriptors = descriptor.bcdUSB >= kUsbVersion2_1;
if (descriptor.iManufacturer == 0 && descriptor.iProduct == 0 && devices_being_enumerated_.insert(platform_device.get());
descriptor.iSerialNumber == 0 && !read_bos_descriptors) {
// Don't bother disturbing the device if it has no descriptors to offer. auto device = base::MakeRefCounted<UsbDeviceImpl>(
add_device.Run(); context_, std::move(platform_device), descriptor);
} else { base::OnceClosure add_device =
device->Open(base::Bind(&OnDeviceOpenedReadDescriptors, base::BindOnce(&UsbServiceImpl::AddDevice, weak_factory_.GetWeakPtr(),
descriptor.iManufacturer, descriptor.iProduct, refresh_complete, device);
descriptor.iSerialNumber, read_bos_descriptors,
add_device, enumeration_failed)); bool read_bos_descriptors = descriptor.bcdUSB >= kUsbVersion2_1;
} if (descriptor.iManufacturer == 0 && descriptor.iProduct == 0 &&
descriptor.iSerialNumber == 0 && !read_bos_descriptors) {
// Don't bother disturbing the device if it has no descriptors to offer.
std::move(add_device).Run();
} else { } else {
USB_LOG(EVENT) << "Failed to get device descriptor: " // Take an additional reference to the libusb_device object that will be
<< ConvertPlatformUsbErrorToString(rv); // owned by this callback.
refresh_complete.Run(); libusb_ref_device(device->platform_device());
base::OnceClosure enumeration_failed = base::BindOnce(
&UsbServiceImpl::EnumerationFailed, weak_factory_.GetWeakPtr(),
ScopedLibusbDeviceRef(device->platform_device()), refresh_complete);
device->Open(base::BindOnce(
&OnDeviceOpenedReadDescriptors, descriptor.iManufacturer,
descriptor.iProduct, descriptor.iSerialNumber, read_bos_descriptors,
std::move(add_device), std::move(enumeration_failed)));
} }
} }
void UsbServiceImpl::AddDevice(const base::Closure& refresh_complete, void UsbServiceImpl::AddDevice(const base::Closure& refresh_complete,
scoped_refptr<UsbDeviceImpl> device) { scoped_refptr<UsbDeviceImpl> device) {
auto it = devices_being_enumerated_.find(device->platform_device()); if (!base::ContainsKey(devices_being_enumerated_,
if (it == devices_being_enumerated_.end()) { device->platform_device())) {
// Device was removed while being enumerated. // Device was removed while being enumerated.
refresh_complete.Run(); refresh_complete.Run();
return; return;
} }
DCHECK(!base::ContainsKey(platform_devices_, device->platform_device()));
platform_devices_[device->platform_device()] = device; platform_devices_[device->platform_device()] = device;
DCHECK(!base::ContainsKey(devices(), device->guid())); DCHECK(!base::ContainsKey(devices(), device->guid()));
devices()[device->guid()] = device; devices()[device->guid()] = device;
...@@ -479,9 +487,8 @@ void UsbServiceImpl::AddDevice(const base::Closure& refresh_complete, ...@@ -479,9 +487,8 @@ void UsbServiceImpl::AddDevice(const base::Closure& refresh_complete,
<< device->product_string() << "\", serial=\"" << device->product_string() << "\", serial=\""
<< device->serial_number() << "\", guid=" << device->guid(); << device->serial_number() << "\", guid=" << device->guid();
if (enumeration_ready_) { if (enumeration_ready_)
NotifyDeviceAdded(device); NotifyDeviceAdded(device);
}
refresh_complete.Run(); refresh_complete.Run();
} }
...@@ -498,7 +505,7 @@ void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) { ...@@ -498,7 +505,7 @@ void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) {
// static // static
int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
PlatformUsbDevice device, libusb_device* device_raw,
libusb_hotplug_event event, libusb_hotplug_event event,
void* user_data) { void* user_data) {
// It is safe to access the UsbServiceImpl* here because libusb takes a lock // It is safe to access the UsbServiceImpl* here because libusb takes a lock
...@@ -507,18 +514,22 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, ...@@ -507,18 +514,22 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
// processing thread after it has been deregistered. // processing thread after it has been deregistered.
UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data);
DCHECK(!self->task_runner()->BelongsToCurrentThread()); DCHECK(!self->task_runner()->BelongsToCurrentThread());
// libusb does not transfer ownership of |device_raw| to this function so a
// reference must be taken here.
libusb_ref_device(device_raw);
ScopedLibusbDeviceRef device(device_raw);
switch (event) { switch (event) {
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
libusb_ref_device(device); // Released in OnPlatformDeviceAdded.
self->task_runner()->PostTask( self->task_runner()->PostTask(
FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceAdded, FROM_HERE, base::BindOnce(&UsbServiceImpl::OnPlatformDeviceAdded,
base::Unretained(self), device)); base::Unretained(self), std::move(device)));
break; break;
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
libusb_ref_device(device); // Released in OnPlatformDeviceRemoved.
self->task_runner()->PostTask( self->task_runner()->PostTask(
FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceRemoved, FROM_HERE, base::BindOnce(&UsbServiceImpl::OnPlatformDeviceRemoved,
base::Unretained(self), device)); base::Unretained(self), std::move(device)));
break; break;
default: default:
NOTREACHED(); NOTREACHED();
...@@ -527,29 +538,26 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, ...@@ -527,29 +538,26 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
return 0; return 0;
} }
void UsbServiceImpl::OnPlatformDeviceAdded(PlatformUsbDevice platform_device) { void UsbServiceImpl::OnPlatformDeviceAdded(
ScopedLibusbDeviceRef platform_device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!base::ContainsKey(platform_devices_, platform_device)); DCHECK(!base::ContainsKey(platform_devices_, platform_device.get()));
EnumerateDevice(platform_device, base::DoNothing()); EnumerateDevice(std::move(platform_device), base::DoNothing());
libusb_unref_device(platform_device);
} }
void UsbServiceImpl::OnPlatformDeviceRemoved( void UsbServiceImpl::OnPlatformDeviceRemoved(
PlatformUsbDevice platform_device) { ScopedLibusbDeviceRef platform_device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); auto it = platform_devices_.find(platform_device.get());
if (it != platform_devices_.end()) { if (it == platform_devices_.end())
devices_being_enumerated_.erase(platform_device.get());
else
RemoveDevice(it->second); RemoveDevice(it->second);
} else {
devices_being_enumerated_.erase(platform_device);
}
libusb_unref_device(platform_device);
} }
void UsbServiceImpl::EnumerationFailed(PlatformUsbDevice platform_device, void UsbServiceImpl::EnumerationFailed(ScopedLibusbDeviceRef platform_device,
const base::Closure& refresh_complete) { const base::Closure& refresh_complete) {
libusb_ref_device(platform_device); ignored_devices_.push_back(std::move(platform_device));
ignored_devices_.insert(platform_device);
refresh_complete.Run(); refresh_complete.Run();
} }
......
...@@ -11,11 +11,14 @@ ...@@ -11,11 +11,14 @@
#include <map> #include <map>
#include <set> #include <set>
#include <vector>
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "device/usb/scoped_libusb_device_ref.h"
#include "device/usb/usb_context.h" #include "device/usb/usb_context.h"
#include "device/usb/usb_device_impl.h" #include "device/usb/usb_device_impl.h"
#include "third_party/libusb/src/libusb/libusb.h" #include "third_party/libusb/src/libusb/libusb.h"
...@@ -25,12 +28,10 @@ ...@@ -25,12 +28,10 @@
#include "device/base/device_monitor_win.h" #include "device/base/device_monitor_win.h"
#endif // OS_WIN #endif // OS_WIN
struct libusb_device;
struct libusb_context; struct libusb_context;
namespace device { namespace device {
typedef struct libusb_device* PlatformUsbDevice;
typedef struct libusb_context* PlatformUsbContext; typedef struct libusb_context* PlatformUsbContext;
class UsbDeviceImpl; class UsbDeviceImpl;
...@@ -60,11 +61,12 @@ class UsbServiceImpl : ...@@ -60,11 +61,12 @@ class UsbServiceImpl :
// Enumerate USB devices from OS and update devices_ map. // Enumerate USB devices from OS and update devices_ map.
void RefreshDevices(); void RefreshDevices();
void OnDeviceList(libusb_device** platform_devices, size_t device_count); void OnDeviceList(
base::Optional<std::vector<ScopedLibusbDeviceRef>> platform_devices);
void RefreshDevicesComplete(); void RefreshDevicesComplete();
// Creates a new UsbDevice based on the given libusb device. // Creates a new UsbDevice based on the given libusb device.
void EnumerateDevice(PlatformUsbDevice platform_device, void EnumerateDevice(ScopedLibusbDeviceRef platform_device,
const base::Closure& refresh_complete); const base::Closure& refresh_complete);
void AddDevice(const base::Closure& refresh_complete, void AddDevice(const base::Closure& refresh_complete,
...@@ -73,16 +75,16 @@ class UsbServiceImpl : ...@@ -73,16 +75,16 @@ class UsbServiceImpl :
// Handle hotplug events from libusb. // Handle hotplug events from libusb.
static int LIBUSB_CALL HotplugCallback(libusb_context* context, static int LIBUSB_CALL HotplugCallback(libusb_context* context,
PlatformUsbDevice device, libusb_device* device,
libusb_hotplug_event event, libusb_hotplug_event event,
void* user_data); void* user_data);
// These functions release a reference to the provided platform device. // These functions release a reference to the provided platform device.
void OnPlatformDeviceAdded(PlatformUsbDevice platform_device); void OnPlatformDeviceAdded(ScopedLibusbDeviceRef platform_device);
void OnPlatformDeviceRemoved(PlatformUsbDevice platform_device); void OnPlatformDeviceRemoved(ScopedLibusbDeviceRef platform_device);
// Add |platform_device| to the |ignored_devices_| and // Add |platform_device| to the |ignored_devices_| and
// run |refresh_complete|. // run |refresh_complete|.
void EnumerationFailed(PlatformUsbDevice platform_device, void EnumerationFailed(ScopedLibusbDeviceRef platform_device,
const base::Closure& refresh_complete); const base::Closure& refresh_complete);
scoped_refptr<UsbContext> context_; scoped_refptr<UsbContext> context_;
...@@ -100,18 +102,20 @@ class UsbServiceImpl : ...@@ -100,18 +102,20 @@ class UsbServiceImpl :
base::queue<std::string> pending_path_enumerations_; base::queue<std::string> pending_path_enumerations_;
std::vector<GetDevicesCallback> pending_enumeration_callbacks_; std::vector<GetDevicesCallback> pending_enumeration_callbacks_;
// The map from PlatformUsbDevices to UsbDevices. // The map from libusb_device to UsbDeviceImpl. The key is a weak pointer to
typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl>> // the libusb_device object owned by the UsbDeviceImpl.
typedef std::map<libusb_device*, scoped_refptr<UsbDeviceImpl>>
PlatformDeviceMap; PlatformDeviceMap;
PlatformDeviceMap platform_devices_; PlatformDeviceMap platform_devices_;
// The set of devices that only need to be enumerated once and then can be // The set of devices that only need to be enumerated once and then can be
// ignored (for example, hub devices, devices that failed enumeration, etc.). // ignored (for example, hub devices, devices that failed enumeration, etc.).
std::set<PlatformUsbDevice> ignored_devices_; std::vector<ScopedLibusbDeviceRef> ignored_devices_;
// Tracks PlatformUsbDevices that might be removed while they are being // Tracks libusb_devices that might be removed while they are being
// enumerated. // enumerated. This is a weak pointer to a libusb_device object owned by a
std::set<PlatformUsbDevice> devices_being_enumerated_; // UsbDeviceImpl.
std::set<libusb_device*> devices_being_enumerated_;
#if defined(OS_WIN) #if defined(OS_WIN)
ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment