summaryrefslogtreecommitdiffstats
path: root/device/usb
diff options
context:
space:
mode:
Diffstat (limited to 'device/usb')
-rw-r--r--device/usb/usb_context.cc28
-rw-r--r--device/usb/usb_device.cc14
-rw-r--r--device/usb/usb_device.h45
-rw-r--r--device/usb/usb_device_filter_unittest.cc28
-rw-r--r--device/usb/usb_device_handle.h47
-rw-r--r--device/usb/usb_device_handle_impl.cc758
-rw-r--r--device/usb/usb_device_handle_impl.h137
-rw-r--r--device/usb/usb_device_handle_unittest.cc157
-rw-r--r--device/usb/usb_device_impl.cc205
-rw-r--r--device/usb/usb_device_impl.h45
-rw-r--r--device/usb/usb_service.cc46
-rw-r--r--device/usb/usb_service.h25
-rw-r--r--device/usb/usb_service_impl.cc614
-rw-r--r--device/usb/usb_service_impl.h93
-rw-r--r--device/usb/usb_service_unittest.cc27
15 files changed, 1319 insertions, 950 deletions
diff --git a/device/usb/usb_context.cc b/device/usb/usb_context.cc
index cb8214f..7661469 100644
--- a/device/usb/usb_context.cc
+++ b/device/usb/usb_context.cc
@@ -6,7 +6,6 @@
#include "base/atomicops.h"
#include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "device/usb/usb_error.h"
#include "third_party/libusb/src/libusb/interrupt.h"
@@ -25,35 +24,30 @@ class UsbContext::UsbEventHandler : public base::PlatformThread::Delegate {
// base::PlatformThread::Delegate
void ThreadMain() override;
+ void Stop();
+
private:
base::subtle::Atomic32 running_;
libusb_context* context_;
base::PlatformThreadHandle thread_handle_;
- base::WaitableEvent start_polling_;
DISALLOW_COPY_AND_ASSIGN(UsbEventHandler);
};
UsbContext::UsbEventHandler::UsbEventHandler(libusb_context* context)
- : context_(context), thread_handle_(0), start_polling_(false, false) {
+ : context_(context), thread_handle_(0) {
base::subtle::Release_Store(&running_, 1);
bool success = base::PlatformThread::Create(0, this, &thread_handle_);
DCHECK(success) << "Failed to create USB IO handling thread.";
- start_polling_.Wait();
}
UsbContext::UsbEventHandler::~UsbEventHandler() {
- base::subtle::Release_Store(&running_, 0);
- libusb_interrupt_handle_event(context_);
- base::PlatformThread::Join(thread_handle_);
+ libusb_exit(context_);
}
void UsbContext::UsbEventHandler::ThreadMain() {
base::PlatformThread::SetName("UsbEventHandler");
VLOG(1) << "UsbEventHandler started.";
- if (base::subtle::Acquire_Load(&running_)) {
- start_polling_.Signal();
- }
while (base::subtle::Acquire_Load(&running_)) {
const int rv = libusb_handle_events(context_);
if (rv != LIBUSB_SUCCESS) {
@@ -61,20 +55,24 @@ void UsbContext::UsbEventHandler::ThreadMain() {
<< ConvertPlatformUsbErrorToString(rv);
}
}
+
VLOG(1) << "UsbEventHandler shutting down.";
+ delete this;
+}
+
+void UsbContext::UsbEventHandler::Stop() {
+ base::subtle::Release_Store(&running_, 0);
+ libusb_interrupt_handle_event(context_);
}
UsbContext::UsbContext(PlatformUsbContext context) : context_(context) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ // Ownership of the PlatformUsbContext is passed to the event handler thread.
event_handler_ = new UsbEventHandler(context_);
}
UsbContext::~UsbContext() {
- // destruction of UsbEventHandler is a blocking operation.
DCHECK(thread_checker_.CalledOnValidThread());
- delete event_handler_;
- event_handler_ = NULL;
- libusb_exit(context_);
+ event_handler_->Stop();
}
} // namespace device
diff --git a/device/usb/usb_device.cc b/device/usb/usb_device.cc
index 4b9ed1e..da926bd 100644
--- a/device/usb/usb_device.cc
+++ b/device/usb/usb_device.cc
@@ -6,8 +6,18 @@
namespace device {
-UsbDevice::UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id)
- : vendor_id_(vendor_id), product_id_(product_id), unique_id_(unique_id) {
+UsbDevice::UsbDevice(uint16 vendor_id,
+ uint16 product_id,
+ uint32 unique_id,
+ const base::string16& manufacturer_string,
+ const base::string16& product_string,
+ const base::string16& serial_number)
+ : vendor_id_(vendor_id),
+ product_id_(product_id),
+ unique_id_(unique_id),
+ manufacturer_string_(manufacturer_string),
+ product_string_(product_string),
+ serial_number_(serial_number) {
}
UsbDevice::~UsbDevice() {
diff --git a/device/usb/usb_device.h b/device/usb/usb_device.h
index e824078..2286467 100644
--- a/device/usb/usb_device.h
+++ b/device/usb/usb_device.h
@@ -16,16 +16,24 @@ class UsbDeviceHandle;
struct UsbConfigDescriptor;
// A UsbDevice object represents a detected USB device, providing basic
-// information about it. For further manipulation of the device, a
-// UsbDeviceHandle must be created from Open() method.
+// information about it. Methods other than simple property accessors must be
+// called from the thread on which this object was created. For further
+// manipulation of the device, a UsbDeviceHandle must be created from Open()
+// method.
class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
public:
- typedef base::Callback<void(bool success)> ResultCallback;
+ using OpenCallback = base::Callback<void(scoped_refptr<UsbDeviceHandle>)>;
+ using ResultCallback = base::Callback<void(bool success)>;
// Accessors to basic information.
uint16 vendor_id() const { return vendor_id_; }
uint16 product_id() const { return product_id_; }
uint32 unique_id() const { return unique_id_; }
+ const base::string16& manufacturer_string() const {
+ return manufacturer_string_;
+ }
+ const base::string16& product_string() const { return product_string_; }
+ const base::string16& serial_number() const { return serial_number_; }
// On ChromeOS the permission_broker service is used to change the ownership
// of USB device nodes so that Chrome can open them. On other platforms these
@@ -37,37 +45,23 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
const ResultCallback& callback);
// Creates a UsbDeviceHandle for further manipulation.
- // Blocking method. Must be called on FILE thread.
- virtual scoped_refptr<UsbDeviceHandle> Open() = 0;
+ virtual void Open(const OpenCallback& callback) = 0;
// Explicitly closes a device handle. This method will be automatically called
// by the destructor of a UsbDeviceHandle as well.
- // Closing a closed handle is a safe
- // Blocking method. Must be called on FILE thread.
virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) = 0;
// Gets the UsbConfigDescriptor for the active device configuration or nullptr
// if the device is unconfigured.
- // Blocking method. Must be called on FILE thread.
virtual const UsbConfigDescriptor* GetConfiguration() = 0;
- // Gets the manufacturer string of the device, or false and an empty
- // string. This is a blocking method and must be called on FILE thread.
- // TODO(reillyg): Make this available from the UI thread. crbug.com/427985
- virtual bool GetManufacturer(base::string16* manufacturer) = 0;
-
- // Gets the product string of the device, or returns false and an empty
- // string. This is a blocking method and must be called on FILE thread.
- // TODO(reillyg): Make this available from the UI thread. crbug.com/427985
- virtual bool GetProduct(base::string16* product) = 0;
-
- // Gets the serial number string of the device, or returns false and an empty
- // string. This is a blocking method and must be called on FILE thread.
- // TODO(reillyg): Make this available from the UI thread. crbug.com/427985
- virtual bool GetSerialNumber(base::string16* serial) = 0;
-
protected:
- UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id);
+ UsbDevice(uint16 vendor_id,
+ uint16 product_id,
+ uint32 unique_id,
+ const base::string16& manufacturer_string,
+ const base::string16& product_string,
+ const base::string16& serial_number);
virtual ~UsbDevice();
private:
@@ -76,6 +70,9 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
const uint16 vendor_id_;
const uint16 product_id_;
const uint32 unique_id_;
+ const base::string16 manufacturer_string_;
+ const base::string16 product_string_;
+ const base::string16 serial_number_;
DISALLOW_COPY_AND_ASSIGN(UsbDevice);
};
diff --git a/device/usb/usb_device_filter_unittest.cc b/device/usb/usb_device_filter_unittest.cc
index d66a462..beb12df 100644
--- a/device/usb/usb_device_filter_unittest.cc
+++ b/device/usb/usb_device_filter_unittest.cc
@@ -21,14 +21,16 @@ using testing::Return;
class MockUsbDevice : public UsbDevice {
public:
MockUsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id)
- : UsbDevice(vendor_id, product_id, unique_id) {}
-
- MOCK_METHOD0(Open, scoped_refptr<UsbDeviceHandle>());
+ : UsbDevice(vendor_id,
+ product_id,
+ unique_id,
+ base::string16(),
+ base::string16(),
+ base::string16()) {}
+
+ MOCK_METHOD1(Open, void(const OpenCallback&));
MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>));
MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor*());
- MOCK_METHOD1(GetManufacturer, bool(base::string16*));
- MOCK_METHOD1(GetProduct, bool(base::string16*));
- MOCK_METHOD1(GetSerialNumber, bool(base::string16*));
private:
virtual ~MockUsbDevice() {}
@@ -46,8 +48,6 @@ class UsbFilterTest : public testing::Test {
config_.interfaces.push_back(interface);
android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, 0);
- ON_CALL(*android_phone_.get(), GetConfiguration())
- .WillByDefault(Return(&config_));
}
protected:
@@ -89,12 +89,16 @@ TEST_F(UsbFilterTest, MatchProductIdNegative) {
TEST_F(UsbFilterTest, MatchInterfaceClass) {
UsbDeviceFilter filter;
filter.SetInterfaceClass(0xff);
+ EXPECT_CALL(*android_phone_.get(), GetConfiguration())
+ .WillOnce(Return(&config_));
ASSERT_TRUE(filter.Matches(android_phone_));
}
TEST_F(UsbFilterTest, MatchInterfaceClassNegative) {
UsbDeviceFilter filter;
filter.SetInterfaceClass(0xe0);
+ EXPECT_CALL(*android_phone_.get(), GetConfiguration())
+ .WillOnce(Return(&config_));
ASSERT_FALSE(filter.Matches(android_phone_));
}
@@ -102,6 +106,8 @@ TEST_F(UsbFilterTest, MatchInterfaceSubclass) {
UsbDeviceFilter filter;
filter.SetInterfaceClass(0xff);
filter.SetInterfaceSubclass(0x42);
+ EXPECT_CALL(*android_phone_.get(), GetConfiguration())
+ .WillOnce(Return(&config_));
ASSERT_TRUE(filter.Matches(android_phone_));
}
@@ -109,6 +115,8 @@ TEST_F(UsbFilterTest, MatchInterfaceSubclassNegative) {
UsbDeviceFilter filter;
filter.SetInterfaceClass(0xff);
filter.SetInterfaceSubclass(0x01);
+ EXPECT_CALL(*android_phone_.get(), GetConfiguration())
+ .WillOnce(Return(&config_));
ASSERT_FALSE(filter.Matches(android_phone_));
}
@@ -117,6 +125,8 @@ TEST_F(UsbFilterTest, MatchInterfaceProtocol) {
filter.SetInterfaceClass(0xff);
filter.SetInterfaceSubclass(0x42);
filter.SetInterfaceProtocol(0x01);
+ EXPECT_CALL(*android_phone_.get(), GetConfiguration())
+ .WillOnce(Return(&config_));
ASSERT_TRUE(filter.Matches(android_phone_));
}
@@ -125,6 +135,8 @@ TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) {
filter.SetInterfaceClass(0xff);
filter.SetInterfaceSubclass(0x42);
filter.SetInterfaceProtocol(0x02);
+ EXPECT_CALL(*android_phone_.get(), GetConfiguration())
+ .WillOnce(Return(&config_));
ASSERT_FALSE(filter.Matches(android_phone_));
}
diff --git a/device/usb/usb_device_handle.h b/device/usb/usb_device_handle.h
index 7098d68..2c59f25 100644
--- a/device/usb/usb_device_handle.h
+++ b/device/usb/usb_device_handle.h
@@ -30,13 +30,13 @@ enum UsbTransferStatus {
USB_TRANSFER_LENGTH_SHORT,
};
-typedef base::Callback<
- void(UsbTransferStatus, scoped_refptr<net::IOBuffer>, size_t)>
- UsbTransferCallback;
-
// UsbDeviceHandle class provides basic I/O related functionalities.
class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
public:
+ using ResultCallback = base::Callback<void(bool)>;
+ using TransferCallback = base::Callback<
+ void(UsbTransferStatus, scoped_refptr<net::IOBuffer>, size_t)>;
+
enum TransferRequestType { STANDARD, CLASS, VENDOR, RESERVED };
enum TransferRecipient { DEVICE, INTERFACE, ENDPOINT, OTHER };
@@ -49,53 +49,52 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
// The platform device handle will be closed when UsbDeviceHandle destructs.
virtual void Close() = 0;
- // Device manipulation operations. These methods are blocking and must be
- // called on FILE thread.
- virtual bool SetConfiguration(int configuration_value) = 0;
- virtual bool ClaimInterface(int interface_number) = 0;
+ // Device manipulation operations.
+ virtual void SetConfiguration(int configuration_value,
+ const ResultCallback& callback) = 0;
+ virtual void ClaimInterface(int interface_number,
+ const ResultCallback& callback) = 0;
virtual bool ReleaseInterface(int interface_number) = 0;
- virtual bool SetInterfaceAlternateSetting(int interface_number,
- int alternate_setting) = 0;
- virtual bool ResetDevice() = 0;
-
- // Gets the string descriptor with the given index from the device, or returns
- // false. This method is blocking and must be called on the FILE thread.
- virtual bool GetStringDescriptor(uint8 string_id, base::string16* string) = 0;
+ virtual void SetInterfaceAlternateSetting(int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) = 0;
+ virtual void ResetDevice(const ResultCallback& callback) = 0;
- // Async IO. Can be called on any thread.
+ // The transfer functions may be called from any thread. The provided callback
+ // will be run on the caller's thread.
virtual void ControlTransfer(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
uint8 request,
uint16 value,
uint16 index,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) = 0;
+ const TransferCallback& callback) = 0;
virtual void BulkTransfer(UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) = 0;
+ const TransferCallback& callback) = 0;
virtual void InterruptTransfer(UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) = 0;
+ const TransferCallback& callback) = 0;
virtual void IsochronousTransfer(UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
- const UsbTransferCallback& callback) = 0;
+ const TransferCallback& callback) = 0;
protected:
friend class base::RefCountedThreadSafe<UsbDeviceHandle>;
diff --git a/device/usb/usb_device_handle_impl.cc b/device/usb/usb_device_handle_impl.cc
index fc14e82c..921af4f 100644
--- a/device/usb/usb_device_handle_impl.cc
+++ b/device/usb/usb_device_handle_impl.cc
@@ -157,6 +157,7 @@ bool UsbDeviceHandleImpl::InterfaceClaimer::Claim() const {
class UsbDeviceHandleImpl::Transfer {
public:
static scoped_ptr<Transfer> CreateControlTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 type,
uint8 request,
uint16 value,
@@ -164,62 +165,77 @@ class UsbDeviceHandleImpl::Transfer {
uint16 length,
scoped_refptr<net::IOBuffer> buffer,
unsigned int timeout,
- const UsbTransferCallback& callback);
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
static scoped_ptr<Transfer> CreateBulkTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
int length,
unsigned int timeout,
- const UsbTransferCallback& callback);
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
static scoped_ptr<Transfer> CreateInterruptTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
int length,
unsigned int timeout,
- const UsbTransferCallback& callback);
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
static scoped_ptr<Transfer> CreateIsochronousTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
- const UsbTransferCallback& callback);
+ scoped_refptr<base::TaskRunner> task_runner,
+ const TransferCallback& callback);
~Transfer();
- bool Submit(base::WeakPtr<UsbDeviceHandleImpl> device_handle);
+ void Submit();
void Cancel();
void ProcessCompletion();
- void Complete(UsbTransferStatus status, size_t bytes_transferred);
+ void TransferComplete(UsbTransferStatus status, size_t bytes_transferred);
const UsbDeviceHandleImpl::InterfaceClaimer* claimed_interface() const {
return claimed_interface_.get();
}
+ scoped_refptr<base::TaskRunner> callback_task_runner() const {
+ return callback_task_runner_;
+ }
+
private:
- Transfer(UsbTransferType transfer_type,
+ Transfer(scoped_refptr<UsbDeviceHandleImpl> device_handle,
+ scoped_refptr<InterfaceClaimer> claimed_interface,
+ UsbTransferType transfer_type,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
- const UsbTransferCallback& callback);
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
static void LIBUSB_CALL PlatformCallback(PlatformUsbTransferHandle handle);
UsbTransferType transfer_type_;
- base::WeakPtr<UsbDeviceHandleImpl> device_handle_;
- PlatformUsbTransferHandle platform_transfer_;
+ scoped_refptr<UsbDeviceHandleImpl> device_handle_;
+ PlatformUsbTransferHandle platform_transfer_ = nullptr;
scoped_refptr<net::IOBuffer> buffer_;
scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer> claimed_interface_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
size_t length_;
- bool cancelled_;
- UsbTransferCallback callback_;
- scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_;
+ bool cancelled_ = false;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ scoped_refptr<base::TaskRunner> callback_task_runner_;
+ TransferCallback callback_;
};
// static
scoped_ptr<UsbDeviceHandleImpl::Transfer>
UsbDeviceHandleImpl::Transfer::CreateControlTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 type,
uint8 request,
uint16 value,
@@ -227,10 +243,11 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer(
uint16 length,
scoped_refptr<net::IOBuffer> buffer,
unsigned int timeout,
- const UsbTransferCallback& callback) {
- scoped_ptr<Transfer> transfer(new Transfer(USB_TRANSFER_CONTROL, buffer,
- length + LIBUSB_CONTROL_SETUP_SIZE,
- callback));
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ scoped_ptr<Transfer> transfer(new Transfer(
+ device_handle, nullptr, USB_TRANSFER_CONTROL, buffer,
+ length + LIBUSB_CONTROL_SETUP_SIZE, callback_task_runner, callback));
transfer->platform_transfer_ = libusb_alloc_transfer(0);
if (!transfer->platform_transfer_) {
@@ -241,7 +258,7 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer(
libusb_fill_control_setup(reinterpret_cast<uint8*>(buffer->data()), type,
request, value, index, length);
libusb_fill_control_transfer(transfer->platform_transfer_,
- nullptr, /* filled in by Submit() */
+ device_handle->handle_,
reinterpret_cast<uint8*>(buffer->data()),
&UsbDeviceHandleImpl::Transfer::PlatformCallback,
transfer.get(), timeout);
@@ -252,13 +269,16 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer(
// static
scoped_ptr<UsbDeviceHandleImpl::Transfer>
UsbDeviceHandleImpl::Transfer::CreateBulkTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
int length,
unsigned int timeout,
- const UsbTransferCallback& callback) {
- scoped_ptr<Transfer> transfer(
- new Transfer(USB_TRANSFER_BULK, buffer, length, callback));
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ scoped_ptr<Transfer> transfer(new Transfer(
+ device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint),
+ USB_TRANSFER_BULK, buffer, length, callback_task_runner, callback));
transfer->platform_transfer_ = libusb_alloc_transfer(0);
if (!transfer->platform_transfer_) {
@@ -266,12 +286,11 @@ UsbDeviceHandleImpl::Transfer::CreateBulkTransfer(
return nullptr;
}
- libusb_fill_bulk_transfer(transfer->platform_transfer_,
- nullptr, /* filled in by Submit() */
- endpoint, reinterpret_cast<uint8*>(buffer->data()),
- static_cast<int>(length),
- &UsbDeviceHandleImpl::Transfer::PlatformCallback,
- transfer.get(), timeout);
+ libusb_fill_bulk_transfer(
+ transfer->platform_transfer_, device_handle->handle_, endpoint,
+ reinterpret_cast<uint8*>(buffer->data()), static_cast<int>(length),
+ &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(),
+ timeout);
return transfer.Pass();
}
@@ -279,13 +298,16 @@ UsbDeviceHandleImpl::Transfer::CreateBulkTransfer(
// static
scoped_ptr<UsbDeviceHandleImpl::Transfer>
UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
int length,
unsigned int timeout,
- const UsbTransferCallback& callback) {
- scoped_ptr<Transfer> transfer(
- new Transfer(USB_TRANSFER_INTERRUPT, buffer, length, callback));
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ scoped_ptr<Transfer> transfer(new Transfer(
+ device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint),
+ USB_TRANSFER_INTERRUPT, buffer, length, callback_task_runner, callback));
transfer->platform_transfer_ = libusb_alloc_transfer(0);
if (!transfer->platform_transfer_) {
@@ -294,9 +316,8 @@ UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer(
}
libusb_fill_interrupt_transfer(
- transfer->platform_transfer_, nullptr, /* filled in by Submit() */
- endpoint, reinterpret_cast<uint8*>(buffer->data()),
- static_cast<int>(length),
+ transfer->platform_transfer_, device_handle->handle_, endpoint,
+ reinterpret_cast<uint8*>(buffer->data()), static_cast<int>(length),
&UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(),
timeout);
@@ -306,18 +327,22 @@ UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer(
// static
scoped_ptr<UsbDeviceHandleImpl::Transfer>
UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
- const UsbTransferCallback& callback) {
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
DCHECK(packets <= length && (packets * packet_length) <= length)
<< "transfer length is too small";
- scoped_ptr<Transfer> transfer(
- new Transfer(USB_TRANSFER_ISOCHRONOUS, buffer, length, callback));
+ scoped_ptr<Transfer> transfer(new Transfer(
+ device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint),
+ USB_TRANSFER_ISOCHRONOUS, buffer, length, callback_task_runner,
+ callback));
transfer->platform_transfer_ = libusb_alloc_transfer(packets);
if (!transfer->platform_transfer_) {
@@ -326,27 +351,30 @@ UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer(
}
libusb_fill_iso_transfer(
- transfer->platform_transfer_, nullptr, /* filled in by Submit() */
- endpoint, reinterpret_cast<uint8*>(buffer->data()),
- static_cast<int>(length), packets, &Transfer::PlatformCallback,
- transfer.get(), timeout);
+ transfer->platform_transfer_, device_handle->handle_, endpoint,
+ reinterpret_cast<uint8*>(buffer->data()), static_cast<int>(length),
+ packets, &Transfer::PlatformCallback, transfer.get(), timeout);
libusb_set_iso_packet_lengths(transfer->platform_transfer_, packet_length);
return transfer.Pass();
}
-UsbDeviceHandleImpl::Transfer::Transfer(UsbTransferType transfer_type,
- scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- const UsbTransferCallback& callback)
+UsbDeviceHandleImpl::Transfer::Transfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
+ scoped_refptr<InterfaceClaimer> claimed_interface,
+ UsbTransferType transfer_type,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback)
: transfer_type_(transfer_type),
+ device_handle_(device_handle),
buffer_(buffer),
+ claimed_interface_(claimed_interface),
length_(length),
- cancelled_(false),
+ callback_task_runner_(callback_task_runner),
callback_(callback) {
- // Remember the thread from which this transfer was created so that |callback|
- // can be dispatched there.
- callback_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
UsbDeviceHandleImpl::Transfer::~Transfer() {
@@ -355,26 +383,12 @@ UsbDeviceHandleImpl::Transfer::~Transfer() {
}
}
-bool UsbDeviceHandleImpl::Transfer::Submit(
- base::WeakPtr<UsbDeviceHandleImpl> device_handle) {
- device_handle_ = device_handle;
- // Remember the thread from which this transfer was submitted so that it can
- // be marked complete there.
- task_runner_ = base::ThreadTaskRunnerHandle::Get();
- // GetClaimedInterfaceForEndpoint may return nullptr. libusb_submit_transfer
- // will fail if it requires an interface we didn't claim.
- claimed_interface_ = device_handle->GetClaimedInterfaceForEndpoint(
- platform_transfer_->endpoint);
- platform_transfer_->dev_handle = device_handle_->handle_;
-
+void UsbDeviceHandleImpl::Transfer::Submit() {
const int rv = libusb_submit_transfer(platform_transfer_);
- if (rv == LIBUSB_SUCCESS) {
- return true;
- } else {
+ if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to submit transfer: "
<< ConvertPlatformUsbErrorToString(rv);
- Complete(USB_TRANSFER_ERROR, 0);
- return false;
+ TransferComplete(USB_TRANSFER_ERROR, 0);
}
}
@@ -453,51 +467,26 @@ void UsbDeviceHandleImpl::Transfer::ProcessCompletion() {
break;
}
- Complete(ConvertTransferStatus(platform_transfer_->status), actual_length);
-}
-
-void UsbDeviceHandleImpl::Transfer::Complete(UsbTransferStatus status,
- size_t bytes_transferred) {
- if (callback_task_runner_->RunsTasksOnCurrentThread()) {
- callback_.Run(status, buffer_, bytes_transferred);
- } else {
- callback_task_runner_->PostTask(
- FROM_HERE, base::Bind(callback_, status, buffer_, bytes_transferred));
- }
+ TransferComplete(ConvertTransferStatus(platform_transfer_->status),
+ actual_length);
}
/* static */
void LIBUSB_CALL UsbDeviceHandleImpl::Transfer::PlatformCallback(
PlatformUsbTransferHandle platform_transfer) {
- scoped_ptr<Transfer> transfer(
- reinterpret_cast<Transfer*>(platform_transfer->user_data));
+ Transfer* transfer =
+ reinterpret_cast<Transfer*>(platform_transfer->user_data);
DCHECK(transfer->platform_transfer_ == platform_transfer);
-
- // Because device_handle_ is a weak pointer it is guaranteed that the callback
- // will be discarded if the handle has been freed.
- Transfer* tmp_transfer = transfer.get(); // base::Passed invalidates transfer
- tmp_transfer->task_runner_->PostTask(
- FROM_HERE, base::Bind(&UsbDeviceHandleImpl::CompleteTransfer,
- tmp_transfer->device_handle_,
- base::Passed(&transfer)));
-}
-
-UsbDeviceHandleImpl::UsbDeviceHandleImpl(scoped_refptr<UsbContext> context,
- scoped_refptr<UsbDeviceImpl> device,
- PlatformUsbDeviceHandle handle)
- : device_(device),
- handle_(handle),
- context_(context),
- task_runner_(base::ThreadTaskRunnerHandle::Get()),
- weak_factory_(this) {
- DCHECK(handle) << "Cannot create device with NULL handle.";
+ transfer->ProcessCompletion();
}
-UsbDeviceHandleImpl::~UsbDeviceHandleImpl() {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- libusb_close(handle_);
- handle_ = NULL;
+void UsbDeviceHandleImpl::Transfer::TransferComplete(UsbTransferStatus status,
+ size_t bytes_transferred) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::TransferComplete, device_handle_,
+ base::Owned(this),
+ base::Bind(callback_, status, buffer_, bytes_transferred)));
}
scoped_refptr<UsbDevice> UsbDeviceHandleImpl::GetDevice() const {
@@ -510,10 +499,12 @@ void UsbDeviceHandleImpl::Close() {
device_->Close(this);
}
-bool UsbDeviceHandleImpl::SetConfiguration(int configuration_value) {
+void UsbDeviceHandleImpl::SetConfiguration(int configuration_value,
+ const ResultCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!device_) {
- return false;
+ callback.Run(false);
+ return;
}
for (Transfer* transfer : transfers_) {
@@ -521,36 +512,31 @@ bool UsbDeviceHandleImpl::SetConfiguration(int configuration_value) {
}
claimed_interfaces_.clear();
- int rv = libusb_set_configuration(handle_, configuration_value);
- if (rv == LIBUSB_SUCCESS) {
- device_->RefreshConfiguration();
- RefreshEndpointMap();
- } else {
- USB_LOG(EVENT) << "Failed to set configuration " << configuration_value
- << ": " << ConvertPlatformUsbErrorToString(rv);
- }
- return rv == LIBUSB_SUCCESS;
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::SetConfigurationOnBlockingThread, this,
+ handle_, configuration_value, callback));
}
-bool UsbDeviceHandleImpl::ClaimInterface(const int interface_number) {
+void UsbDeviceHandleImpl::ClaimInterface(int interface_number,
+ const ResultCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!device_)
- return false;
- if (ContainsKey(claimed_interfaces_, interface_number))
- return true;
-
- scoped_refptr<InterfaceClaimer> claimer =
- new InterfaceClaimer(this, interface_number);
-
- if (claimer->Claim()) {
- claimed_interfaces_[interface_number] = claimer;
- RefreshEndpointMap();
- return true;
+ if (!device_) {
+ callback.Run(false);
+ return;
+ }
+ if (ContainsKey(claimed_interfaces_, interface_number)) {
+ callback.Run(true);
+ return;
}
- return false;
+
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread, this,
+ handle_, interface_number, callback));
}
-bool UsbDeviceHandleImpl::ReleaseInterface(const int interface_number) {
+bool UsbDeviceHandleImpl::ReleaseInterface(int interface_number) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!device_)
return false;
@@ -571,103 +557,282 @@ bool UsbDeviceHandleImpl::ReleaseInterface(const int interface_number) {
return true;
}
-bool UsbDeviceHandleImpl::SetInterfaceAlternateSetting(
- const int interface_number,
- const int alternate_setting) {
+void UsbDeviceHandleImpl::SetInterfaceAlternateSetting(
+ int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!device_)
- return false;
- if (!ContainsKey(claimed_interfaces_, interface_number))
- return false;
- const int rv = libusb_set_interface_alt_setting(
- handle_, interface_number, alternate_setting);
- if (rv == LIBUSB_SUCCESS) {
- claimed_interfaces_[interface_number]->set_alternate_setting(
- alternate_setting);
- RefreshEndpointMap();
+ if (!device_ || !ContainsKey(claimed_interfaces_, interface_number)) {
+ callback.Run(false);
+ return;
+ }
+
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread,
+ this, handle_, interface_number, alternate_setting, callback));
+}
+
+void UsbDeviceHandleImpl::ResetDevice(const ResultCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!device_) {
+ callback.Run(false);
+ return;
+ }
+
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ResetDeviceOnBlockingThread,
+ this, handle_, callback));
+}
+
+void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction,
+ TransferRequestType request_type,
+ TransferRecipient recipient,
+ uint8 request,
+ uint16 value,
+ uint16 index,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) {
+ if (task_runner_->BelongsToCurrentThread()) {
+ ControlTransferInternal(direction, request_type, recipient, request, value,
+ index, buffer, length, timeout, task_runner_,
+ callback);
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ControlTransferInternal,
+ this, direction, request_type, recipient, request,
+ value, index, buffer, length, timeout,
+ base::ThreadTaskRunnerHandle::Get(), callback));
+ }
+}
+
+void UsbDeviceHandleImpl::BulkTransfer(UsbEndpointDirection direction,
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) {
+ if (task_runner_->BelongsToCurrentThread()) {
+ BulkTransferInternal(direction, endpoint, buffer, length, timeout,
+ task_runner_, callback);
} else {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::BulkTransferInternal, this,
+ direction, endpoint, buffer, length, timeout,
+ base::ThreadTaskRunnerHandle::Get(), callback));
+ }
+}
+
+void UsbDeviceHandleImpl::InterruptTransfer(UsbEndpointDirection direction,
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) {
+ if (task_runner_->BelongsToCurrentThread()) {
+ InterruptTransferInternal(direction, endpoint, buffer, length, timeout,
+ task_runner_, callback);
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::InterruptTransferInternal, this,
+ direction, endpoint, buffer, length, timeout,
+ base::ThreadTaskRunnerHandle::Get(), callback));
+ }
+}
+
+void UsbDeviceHandleImpl::IsochronousTransfer(
+ UsbEndpointDirection direction,
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int packets,
+ unsigned int packet_length,
+ unsigned int timeout,
+ const TransferCallback& callback) {
+ if (task_runner_->BelongsToCurrentThread()) {
+ IsochronousTransferInternal(direction, endpoint, buffer, length, packets,
+ packet_length, timeout, task_runner_, callback);
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::IsochronousTransferInternal, this,
+ direction, endpoint, buffer, length, packets, packet_length,
+ timeout, base::ThreadTaskRunnerHandle::Get(), callback));
+ }
+}
+
+UsbDeviceHandleImpl::UsbDeviceHandleImpl(
+ scoped_refptr<UsbContext> context,
+ scoped_refptr<UsbDeviceImpl> device,
+ PlatformUsbDeviceHandle handle,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+ : device_(device),
+ handle_(handle),
+ context_(context),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ blocking_task_runner_(blocking_task_runner) {
+ DCHECK(handle) << "Cannot create device with NULL handle.";
+}
+
+UsbDeviceHandleImpl::~UsbDeviceHandleImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ libusb_close(handle_);
+ handle_ = NULL;
+}
+
+void UsbDeviceHandleImpl::SetConfigurationOnBlockingThread(
+ PlatformUsbDeviceHandle handle,
+ int configuration_value,
+ const ResultCallback& callback) {
+ int rv = libusb_set_configuration(handle_, configuration_value);
+ if (rv != LIBUSB_SUCCESS) {
+ USB_LOG(EVENT) << "Failed to set configuration " << configuration_value
+ << ": " << ConvertPlatformUsbErrorToString(rv);
+ }
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::SetConfigurationComplete,
+ this, rv == LIBUSB_SUCCESS, callback));
+}
+
+void UsbDeviceHandleImpl::SetConfigurationComplete(
+ bool success,
+ const ResultCallback& callback) {
+ if (success) {
+ device_->RefreshConfiguration();
+ RefreshEndpointMap();
+ }
+ callback.Run(success);
+}
+
+void UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread(
+ PlatformUsbDeviceHandle handle,
+ int interface_number,
+ const ResultCallback& callback) {
+ int rv = libusb_claim_interface(handle, interface_number);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to claim interface: "
+ << ConvertPlatformUsbErrorToString(rv);
+ }
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ClaimInterfaceComplete, this,
+ interface_number, rv == LIBUSB_SUCCESS, callback));
+}
+
+void UsbDeviceHandleImpl::ClaimInterfaceComplete(
+ int interface_number,
+ bool success,
+ const ResultCallback& callback) {
+ if (success) {
+ claimed_interfaces_[interface_number] =
+ new InterfaceClaimer(this, interface_number);
+ RefreshEndpointMap();
+ }
+ callback.Run(success);
+}
+
+void UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread(
+ PlatformUsbDeviceHandle handle,
+ int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) {
+ int rv = libusb_set_interface_alt_setting(handle, interface_number,
+ alternate_setting);
+ if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to set interface " << interface_number
<< " to alternate setting " << alternate_setting << ": "
<< ConvertPlatformUsbErrorToString(rv);
}
- return rv == LIBUSB_SUCCESS;
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::SetInterfaceAlternateSettingComplete,
+ this, interface_number, alternate_setting,
+ rv == LIBUSB_SUCCESS, callback));
}
-bool UsbDeviceHandleImpl::ResetDevice() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!device_)
- return false;
+void UsbDeviceHandleImpl::SetInterfaceAlternateSettingComplete(
+ int interface_number,
+ int alternate_setting,
+ bool success,
+ const ResultCallback& callback) {
+ if (success) {
+ claimed_interfaces_[interface_number]->set_alternate_setting(
+ alternate_setting);
+ RefreshEndpointMap();
+ }
+ callback.Run(success);
+}
- const int rv = libusb_reset_device(handle_);
+void UsbDeviceHandleImpl::ResetDeviceOnBlockingThread(
+ PlatformUsbDeviceHandle handle,
+ const ResultCallback& callback) {
+ int rv = libusb_reset_device(handle);
if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to reset device: "
<< ConvertPlatformUsbErrorToString(rv);
}
- return rv == LIBUSB_SUCCESS;
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ResetDeviceComplete, this,
+ rv == LIBUSB_SUCCESS, callback));
}
-bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id,
- base::string16* string) {
- if (!GetSupportedLanguages()) {
- return false;
- }
+void UsbDeviceHandleImpl::ResetDeviceComplete(bool success,
+ const ResultCallback& callback) {
+ callback.Run(success);
+}
- std::map<uint8, base::string16>::const_iterator it = strings_.find(string_id);
- if (it != strings_.end()) {
- *string = it->second;
- return true;
- }
-
- for (size_t i = 0; i < languages_.size(); ++i) {
- // Get the string using language ID.
- uint16 language_id = languages_[i];
- // The 1-byte length field limits the descriptor to 256-bytes (128 char16s).
- base::char16 text[128];
- int size =
- libusb_get_string_descriptor(handle_,
- string_id,
- language_id,
- reinterpret_cast<unsigned char*>(&text[0]),
- sizeof(text));
- if (size < 0) {
- USB_LOG(EVENT) << "Failed to get string descriptor " << string_id
- << " (langid " << language_id
- << "): " << ConvertPlatformUsbErrorToString(size);
- continue;
- } else if (size < 2) {
- USB_LOG(EVENT) << "String descriptor " << string_id << " (langid "
- << language_id << ") has no header.";
- continue;
- // The first 2 bytes of the descriptor are the total length and type tag.
- } else if ((text[0] & 0xff) != size) {
- USB_LOG(EVENT) << "String descriptor " << string_id << " (langid "
- << language_id << ") size mismatch: " << (text[0] & 0xff)
- << " != " << size;
- continue;
- } else if ((text[0] >> 8) != LIBUSB_DT_STRING) {
- USB_LOG(EVENT) << "String descriptor " << string_id << " (langid "
- << language_id << ") is not a string descriptor.";
- continue;
- }
+void UsbDeviceHandleImpl::RefreshEndpointMap() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ endpoint_map_.clear();
+ const UsbConfigDescriptor* config = device_->GetConfiguration();
+ if (config) {
+ for (const auto& map_entry : claimed_interfaces_) {
+ int interface_number = map_entry.first;
+ const scoped_refptr<InterfaceClaimer>& claimed_iface = map_entry.second;
- *string = base::string16(text + 1, (size - 2) / 2);
- strings_[string_id] = *string;
- return true;
+ for (const UsbInterfaceDescriptor& iface : config->interfaces) {
+ if (iface.interface_number == interface_number &&
+ iface.alternate_setting == claimed_iface->alternate_setting()) {
+ for (const UsbEndpointDescriptor& endpoint : iface.endpoints) {
+ endpoint_map_[endpoint.address] = interface_number;
+ }
+ break;
+ }
+ }
+ }
}
+}
- return false;
+scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer>
+UsbDeviceHandleImpl::GetClaimedInterfaceForEndpoint(unsigned char endpoint) {
+ if (ContainsKey(endpoint_map_, endpoint))
+ return claimed_interfaces_[endpoint_map_[endpoint]];
+ return NULL;
}
-void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction,
- TransferRequestType request_type,
- TransferRecipient recipient,
- uint8 request,
- uint16 value,
- uint16 index,
- net::IOBuffer* buffer,
- size_t length,
- unsigned int timeout,
- const UsbTransferCallback& callback) {
+void UsbDeviceHandleImpl::ControlTransferInternal(
+ UsbEndpointDirection direction,
+ TransferRequestType request_type,
+ TransferRecipient recipient,
+ uint8 request,
+ uint16 value,
+ uint16 index,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
if (length > UINT16_MAX) {
USB_LOG(USER) << "Transfer too long.";
callback.Run(USB_TRANSFER_ERROR, buffer, 0);
@@ -685,22 +850,32 @@ void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction,
length);
scoped_ptr<Transfer> transfer = Transfer::CreateControlTransfer(
- CreateRequestType(direction, request_type, recipient), request, value,
- index, static_cast<uint16>(length), resized_buffer, timeout, callback);
+ this, CreateRequestType(direction, request_type, recipient), request,
+ value, index, static_cast<uint16>(length), resized_buffer, timeout,
+ callback_task_runner, callback);
if (!transfer) {
callback.Run(USB_TRANSFER_ERROR, buffer, 0);
return;
}
- PostOrSubmitTransfer(transfer.Pass());
+ SubmitTransfer(transfer.Pass());
}
-void UsbDeviceHandleImpl::BulkTransfer(const UsbEndpointDirection direction,
- const uint8 endpoint,
- net::IOBuffer* buffer,
- const size_t length,
- const unsigned int timeout,
- const UsbTransferCallback& callback) {
+void UsbDeviceHandleImpl::BulkTransferInternal(
+ const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ const size_t length,
+ const unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
if (length > INT_MAX) {
USB_LOG(USER) << "Transfer too long.";
callback.Run(USB_TRANSFER_ERROR, buffer, 0);
@@ -708,19 +883,27 @@ void UsbDeviceHandleImpl::BulkTransfer(const UsbEndpointDirection direction,
}
scoped_ptr<Transfer> transfer = Transfer::CreateBulkTransfer(
- ConvertTransferDirection(direction) | endpoint, buffer,
- static_cast<int>(length), timeout, callback);
+ this, ConvertTransferDirection(direction) | endpoint, buffer,
+ static_cast<int>(length), timeout, callback_task_runner, callback);
- PostOrSubmitTransfer(transfer.Pass());
+ SubmitTransfer(transfer.Pass());
}
-void UsbDeviceHandleImpl::InterruptTransfer(
+void UsbDeviceHandleImpl::InterruptTransferInternal(
UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) {
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
if (length > INT_MAX) {
USB_LOG(USER) << "Transfer too long.";
callback.Run(USB_TRANSFER_ERROR, buffer, 0);
@@ -728,21 +911,29 @@ void UsbDeviceHandleImpl::InterruptTransfer(
}
scoped_ptr<Transfer> transfer = Transfer::CreateInterruptTransfer(
- ConvertTransferDirection(direction) | endpoint, buffer,
- static_cast<int>(length), timeout, callback);
+ this, ConvertTransferDirection(direction) | endpoint, buffer,
+ static_cast<int>(length), timeout, callback_task_runner, callback);
- PostOrSubmitTransfer(transfer.Pass());
+ SubmitTransfer(transfer.Pass());
}
-void UsbDeviceHandleImpl::IsochronousTransfer(
+void UsbDeviceHandleImpl::IsochronousTransferInternal(
const UsbEndpointDirection direction,
- const uint8 endpoint,
- net::IOBuffer* buffer,
- const size_t length,
- const unsigned int packets,
- const unsigned int packet_length,
- const unsigned int timeout,
- const UsbTransferCallback& callback) {
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int packets,
+ unsigned int packet_length,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
if (length > INT_MAX) {
USB_LOG(USER) << "Transfer too long.";
callback.Run(USB_TRANSFER_ERROR, buffer, 0);
@@ -750,104 +941,35 @@ void UsbDeviceHandleImpl::IsochronousTransfer(
}
scoped_ptr<Transfer> transfer = Transfer::CreateIsochronousTransfer(
- ConvertTransferDirection(direction) | endpoint, buffer,
- static_cast<int>(length), packets, packet_length, timeout, callback);
+ this, ConvertTransferDirection(direction) | endpoint, buffer,
+ static_cast<int>(length), packets, packet_length, timeout,
+ callback_task_runner, callback);
- PostOrSubmitTransfer(transfer.Pass());
+ SubmitTransfer(transfer.Pass());
}
-void UsbDeviceHandleImpl::RefreshEndpointMap() {
+void UsbDeviceHandleImpl::SubmitTransfer(scoped_ptr<Transfer> transfer) {
DCHECK(thread_checker_.CalledOnValidThread());
- endpoint_map_.clear();
- const UsbConfigDescriptor* config = device_->GetConfiguration();
- if (config) {
- for (const auto& map_entry : claimed_interfaces_) {
- int interface_number = map_entry.first;
- const scoped_refptr<InterfaceClaimer>& claimed_iface = map_entry.second;
- for (const UsbInterfaceDescriptor& iface : config->interfaces) {
- if (iface.interface_number == interface_number &&
- iface.alternate_setting == claimed_iface->alternate_setting()) {
- for (const UsbEndpointDescriptor& endpoint : iface.endpoints) {
- endpoint_map_[endpoint.address] = interface_number;
- }
- break;
- }
- }
- }
- }
-}
-
-scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer>
-UsbDeviceHandleImpl::GetClaimedInterfaceForEndpoint(unsigned char endpoint) {
- if (ContainsKey(endpoint_map_, endpoint))
- return claimed_interfaces_[endpoint_map_[endpoint]];
- return NULL;
-}
-
-void UsbDeviceHandleImpl::PostOrSubmitTransfer(scoped_ptr<Transfer> transfer) {
- if (task_runner_->RunsTasksOnCurrentThread()) {
- SubmitTransfer(transfer.Pass());
- } else {
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&UsbDeviceHandleImpl::SubmitTransfer, this,
- base::Passed(&transfer)));
- }
+ // Transfer is owned by libusb until its completion callback is run. This
+ // object holds a weak reference.
+ transfers_.insert(transfer.get());
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Transfer::Submit, base::Unretained(transfer.release())));
}
-void UsbDeviceHandleImpl::SubmitTransfer(scoped_ptr<Transfer> transfer) {
+void UsbDeviceHandleImpl::TransferComplete(Transfer* transfer,
+ const base::Closure& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ContainsKey(transfers_, transfer)) << "Missing transfer completed";
+ transfers_.erase(transfer);
- if (device_) {
- if (transfer->Submit(weak_factory_.GetWeakPtr())) {
- // Transfer is now owned by libusb until its completion callback is run.
- // This object holds a weak reference.
- transfers_.insert(transfer.release());
- }
+ if (transfer->callback_task_runner()->RunsTasksOnCurrentThread()) {
+ callback.Run();
} else {
- transfer->Complete(USB_TRANSFER_DISCONNECT, 0);
- }
-}
-
-void UsbDeviceHandleImpl::CompleteTransfer(scoped_ptr<Transfer> transfer) {
- DCHECK(ContainsKey(transfers_, transfer.get()))
- << "Missing transfer completed";
- transfers_.erase(transfer.get());
- transfer->ProcessCompletion();
-}
-
-bool UsbDeviceHandleImpl::GetSupportedLanguages() {
- if (!languages_.empty()) {
- return true;
+ transfer->callback_task_runner()->PostTask(FROM_HERE, callback);
}
-
- // The 1-byte length field limits the descriptor to 256-bytes (128 uint16s).
- uint16 languages[128];
- int size = libusb_get_string_descriptor(
- handle_,
- 0,
- 0,
- reinterpret_cast<unsigned char*>(&languages[0]),
- sizeof(languages));
- if (size < 0) {
- USB_LOG(EVENT) << "Failed to get list of supported languages: "
- << ConvertPlatformUsbErrorToString(size);
- return false;
- } else if (size < 2) {
- USB_LOG(EVENT) << "String descriptor zero has no header.";
- return false;
- // The first 2 bytes of the descriptor are the total length and type tag.
- } else if ((languages[0] & 0xff) != size) {
- USB_LOG(EVENT) << "String descriptor zero size mismatch: "
- << (languages[0] & 0xff) << " != " << size;
- return false;
- } else if ((languages[0] >> 8) != LIBUSB_DT_STRING) {
- USB_LOG(EVENT) << "String descriptor zero is not a string descriptor.";
- return false;
- }
-
- languages_.assign(languages[1], languages[(size - 2) / 2]);
- return true;
}
void UsbDeviceHandleImpl::InternalClose() {
diff --git a/device/usb/usb_device_handle_impl.h b/device/usb/usb_device_handle_impl.h
index 688f8b9..a60f502 100644
--- a/device/usb/usb_device_handle_impl.h
+++ b/device/usb/usb_device_handle_impl.h
@@ -11,15 +11,18 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string16.h"
#include "base/threading/thread_checker.h"
#include "device/usb/usb_device_handle.h"
-#include "net/base/io_buffer.h"
#include "third_party/libusb/src/libusb/libusb.h"
namespace base {
+class SequencedTaskRunner;
class SingleThreadTaskRunner;
+class TaskRunner;
+}
+
+namespace net {
+class IOBuffer;
}
namespace device {
@@ -37,13 +40,15 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
public:
scoped_refptr<UsbDevice> GetDevice() const override;
void Close() override;
- bool SetConfiguration(int configuration_value) override;
- bool ClaimInterface(int interface_number) override;
+ void SetConfiguration(int configuration_value,
+ const ResultCallback& callback) override;
+ void ClaimInterface(int interface_number,
+ const ResultCallback& callback) override;
bool ReleaseInterface(int interface_number) override;
- bool SetInterfaceAlternateSetting(int interface_number,
- int alternate_setting) override;
- bool ResetDevice() override;
- bool GetStringDescriptor(uint8 string_id, base::string16* string) override;
+ void SetInterfaceAlternateSetting(int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) override;
+ void ResetDevice(const ResultCallback& callback) override;
void ControlTransfer(UsbEndpointDirection direction,
TransferRequestType request_type,
@@ -51,52 +56,75 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
uint8 request,
uint16 value,
uint16 index,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) override;
+ const TransferCallback& callback) override;
void BulkTransfer(UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) override;
+ const TransferCallback& callback) override;
void InterruptTransfer(UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
- const UsbTransferCallback& callback) override;
+ const TransferCallback& callback) override;
void IsochronousTransfer(UsbEndpointDirection direction,
uint8 endpoint,
- net::IOBuffer* buffer,
+ scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
- const UsbTransferCallback& callback) override;
-
- PlatformUsbDeviceHandle handle() const { return handle_; }
+ const TransferCallback& callback) override;
protected:
friend class UsbDeviceImpl;
- // This constructor is called by UsbDevice.
- UsbDeviceHandleImpl(scoped_refptr<UsbContext> context,
- scoped_refptr<UsbDeviceImpl> device,
- PlatformUsbDeviceHandle handle);
+ // This constructor is called by UsbDeviceImpl.
+ UsbDeviceHandleImpl(
+ scoped_refptr<UsbContext> context,
+ scoped_refptr<UsbDeviceImpl> device,
+ PlatformUsbDeviceHandle handle,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
~UsbDeviceHandleImpl() override;
- private:
- friend class Transfer;
+ PlatformUsbDeviceHandle handle() const { return handle_; }
+ private:
class InterfaceClaimer;
class Transfer;
+ void SetConfigurationOnBlockingThread(PlatformUsbDeviceHandle handle,
+ int configuration_value,
+ const ResultCallback& callback);
+ void SetConfigurationComplete(bool success, const ResultCallback& callback);
+ void ClaimInterfaceOnBlockingThread(PlatformUsbDeviceHandle handle,
+ int interface_number,
+ const ResultCallback& callback);
+ void ClaimInterfaceComplete(int interface_number,
+ bool success,
+ const ResultCallback& callback);
+ void SetInterfaceAlternateSettingOnBlockingThread(
+ PlatformUsbDeviceHandle handle,
+ int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback);
+ void SetInterfaceAlternateSettingComplete(int interface_number,
+ int alternate_setting,
+ bool success,
+ const ResultCallback& callback);
+ void ResetDeviceOnBlockingThread(PlatformUsbDeviceHandle handle,
+ const ResultCallback& callback);
+ void ResetDeviceComplete(bool success, const ResultCallback& callback);
+
// Refresh endpoint_map_ after ClaimInterface, ReleaseInterface and
// SetInterfaceAlternateSetting.
void RefreshEndpointMap();
@@ -106,22 +134,56 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
scoped_refptr<InterfaceClaimer> GetClaimedInterfaceForEndpoint(
unsigned char endpoint);
- // If the device's task runner is on the current thread then the transfer will
- // be submitted directly, otherwise a task to do so it posted. The callback
- // will be called on the current message loop of the thread where this
- // function was called.
- void PostOrSubmitTransfer(scoped_ptr<Transfer> transfer);
+ void ControlTransferInternal(
+ UsbEndpointDirection direction,
+ TransferRequestType request_type,
+ TransferRecipient recipient,
+ uint8 request,
+ uint16 value,
+ uint16 index,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
+
+ void BulkTransferInternal(
+ UsbEndpointDirection direction,
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
+
+ void InterruptTransferInternal(
+ UsbEndpointDirection direction,
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
+
+ void IsochronousTransferInternal(
+ UsbEndpointDirection direction,
+ uint8 endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int packets,
+ unsigned int packet_length,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const TransferCallback& callback);
// Submits a transfer and starts tracking it. Retains the buffer and copies
// the completion callback until the transfer finishes, whereupon it invokes
// the callback then releases the buffer.
void SubmitTransfer(scoped_ptr<Transfer> transfer);
- // Invokes the callbacks associated with a given transfer, and removes it from
- // the in-flight transfer set.
- void CompleteTransfer(scoped_ptr<Transfer> transfer);
-
- bool GetSupportedLanguages();
+ // Removes the transfer from the in-flight transfer set and invokes the
+ // completion callback.
+ void TransferComplete(Transfer* transfer, const base::Closure& callback);
// Informs the object to drop internal references.
void InternalClose();
@@ -130,9 +192,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
PlatformUsbDeviceHandle handle_;
- std::vector<uint16> languages_;
- std::map<uint8, base::string16> strings_;
-
typedef std::map<int, scoped_refptr<InterfaceClaimer>> ClaimedInterfaceMap;
ClaimedInterfaceMap claimed_interfaces_;
@@ -148,9 +207,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
scoped_refptr<UsbContext> context_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
base::ThreadChecker thread_checker_;
- base::WeakPtrFactory<UsbDeviceHandleImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(UsbDeviceHandleImpl);
};
diff --git a/device/usb/usb_device_handle_unittest.cc b/device/usb/usb_device_handle_unittest.cc
index 4cb4dff..4d5586e 100644
--- a/device/usb/usb_device_handle_unittest.cc
+++ b/device/usb/usb_device_handle_unittest.cc
@@ -6,6 +6,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_io_thread.h"
#include "device/test/usb_test_gadget.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h"
@@ -18,35 +19,63 @@ namespace {
class UsbDeviceHandleTest : public ::testing::Test {
public:
void SetUp() override {
- if (!UsbTestGadget::IsTestEnabled()) {
- return;
- }
+ message_loop_.reset(new base::MessageLoopForUI);
+ io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
+ }
- message_loop_.reset(new base::MessageLoopForIO);
+ protected:
+ scoped_ptr<base::TestIOThread> io_thread_;
- gadget_ = UsbTestGadget::Claim();
- ASSERT_TRUE(gadget_.get());
+ private:
+ scoped_ptr<base::MessageLoop> message_loop_;
+};
- ASSERT_TRUE(gadget_->SetType(UsbTestGadget::ECHO));
+class TestOpenCallback {
+ public:
+ TestOpenCallback()
+ : callback_(
+ base::Bind(&TestOpenCallback::SetResult, base::Unretained(this))) {}
- handle_ = gadget_->GetDevice()->Open();
- ASSERT_TRUE(handle_.get());
+ scoped_refptr<UsbDeviceHandle> WaitForResult() {
+ run_loop_.Run();
+ return device_handle_;
}
- void TearDown() override {
- if (handle_.get()) {
- handle_->Close();
- }
- gadget_.reset(NULL);
- message_loop_.reset(NULL);
+ const UsbDevice::OpenCallback& callback() const { return callback_; }
+
+ private:
+ void SetResult(scoped_refptr<UsbDeviceHandle> device_handle) {
+ device_handle_ = device_handle;
+ run_loop_.Quit();
}
- protected:
- scoped_refptr<UsbDeviceHandle> handle_;
+ const UsbDevice::OpenCallback callback_;
+ base::RunLoop run_loop_;
+ scoped_refptr<UsbDeviceHandle> device_handle_;
+};
+
+class TestResultCallback {
+ public:
+ TestResultCallback()
+ : callback_(base::Bind(&TestResultCallback::SetResult,
+ base::Unretained(this))) {}
+
+ bool WaitForResult() {
+ run_loop_.Run();
+ return success_;
+ }
+
+ const UsbDeviceHandle::ResultCallback& callback() const { return callback_; }
private:
- scoped_ptr<UsbTestGadget> gadget_;
- scoped_ptr<base::MessageLoop> message_loop_;
+ void SetResult(bool success) {
+ success_ = success;
+ run_loop_.Quit();
+ }
+
+ const UsbDeviceHandle::ResultCallback callback_;
+ base::RunLoop run_loop_;
+ bool success_;
};
class TestCompletionCallback {
@@ -55,6 +84,15 @@ class TestCompletionCallback {
: callback_(base::Bind(&TestCompletionCallback::SetResult,
base::Unretained(this))) {}
+ void WaitForResult() { run_loop_.Run(); }
+
+ const UsbDeviceHandle::TransferCallback& callback() const {
+ return callback_;
+ }
+ UsbTransferStatus status() const { return status_; }
+ size_t transferred() const { return transferred_; }
+
+ private:
void SetResult(UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
size_t transferred) {
@@ -63,34 +101,37 @@ class TestCompletionCallback {
run_loop_.Quit();
}
- void WaitForResult() { run_loop_.Run(); }
-
- const UsbTransferCallback& callback() const { return callback_; }
- UsbTransferStatus status() const { return status_; }
- size_t transferred() const { return transferred_; }
-
- private:
- const UsbTransferCallback callback_;
+ const UsbDeviceHandle::TransferCallback callback_;
base::RunLoop run_loop_;
UsbTransferStatus status_;
size_t transferred_;
};
TEST_F(UsbDeviceHandleTest, InterruptTransfer) {
- if (!handle_.get()) {
+ if (!UsbTestGadget::IsTestEnabled()) {
return;
}
- ASSERT_TRUE(handle_->ClaimInterface(0));
+ scoped_ptr<UsbTestGadget> gadget =
+ UsbTestGadget::Claim(io_thread_->task_runner());
+ ASSERT_TRUE(gadget.get());
+ ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
+
+ TestOpenCallback open_device;
+ gadget->GetDevice()->Open(open_device.callback());
+ scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
+ ASSERT_TRUE(handle.get());
+
+ TestResultCallback claim_interface;
+ handle->ClaimInterface(0, claim_interface.callback());
+ ASSERT_TRUE(claim_interface.WaitForResult());
scoped_refptr<net::IOBufferWithSize> in_buffer(new net::IOBufferWithSize(64));
TestCompletionCallback in_completion;
- handle_->InterruptTransfer(USB_DIRECTION_INBOUND,
- 0x81,
- in_buffer.get(),
- in_buffer->size(),
- 5000, // 5 second timeout
- in_completion.callback());
+ handle->InterruptTransfer(USB_DIRECTION_INBOUND, 0x81, in_buffer.get(),
+ in_buffer->size(),
+ 5000, // 5 second timeout
+ in_completion.callback());
scoped_refptr<net::IOBufferWithSize> out_buffer(
new net::IOBufferWithSize(in_buffer->size()));
@@ -99,12 +140,10 @@ TEST_F(UsbDeviceHandleTest, InterruptTransfer) {
out_buffer->data()[i] = i;
}
- handle_->InterruptTransfer(USB_DIRECTION_OUTBOUND,
- 0x01,
- out_buffer.get(),
- out_buffer->size(),
- 5000, // 5 second timeout
- out_completion.callback());
+ handle->InterruptTransfer(USB_DIRECTION_OUTBOUND, 0x01, out_buffer.get(),
+ out_buffer->size(),
+ 5000, // 5 second timeout
+ out_completion.callback());
out_completion.WaitForResult();
ASSERT_EQ(USB_TRANSFER_COMPLETED, out_completion.status());
EXPECT_EQ(static_cast<size_t>(out_buffer->size()),
@@ -117,22 +156,36 @@ TEST_F(UsbDeviceHandleTest, InterruptTransfer) {
for (size_t i = 0; i < in_completion.transferred(); ++i) {
EXPECT_EQ(out_buffer->data()[i], in_buffer->data()[i]);
}
+
+ handle->Close();
}
TEST_F(UsbDeviceHandleTest, BulkTransfer) {
- if (!handle_.get()) {
+ if (!UsbTestGadget::IsTestEnabled()) {
return;
}
- ASSERT_TRUE(handle_->ClaimInterface(1));
+ scoped_ptr<UsbTestGadget> gadget =
+ UsbTestGadget::Claim(io_thread_->task_runner());
+ ASSERT_TRUE(gadget.get());
+ ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
+
+ TestOpenCallback open_device;
+ gadget->GetDevice()->Open(open_device.callback());
+ scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
+ ASSERT_TRUE(handle.get());
+
+ TestResultCallback claim_interface;
+ handle->ClaimInterface(1, claim_interface.callback());
+ ASSERT_TRUE(claim_interface.WaitForResult());
scoped_refptr<net::IOBufferWithSize> in_buffer(
new net::IOBufferWithSize(512));
TestCompletionCallback in_completion;
- handle_->BulkTransfer(USB_DIRECTION_INBOUND, 0x82, in_buffer.get(),
- in_buffer->size(),
- 5000, // 5 second timeout
- in_completion.callback());
+ handle->BulkTransfer(USB_DIRECTION_INBOUND, 0x82, in_buffer.get(),
+ in_buffer->size(),
+ 5000, // 5 second timeout
+ in_completion.callback());
scoped_refptr<net::IOBufferWithSize> out_buffer(
new net::IOBufferWithSize(in_buffer->size()));
@@ -141,10 +194,10 @@ TEST_F(UsbDeviceHandleTest, BulkTransfer) {
out_buffer->data()[i] = i;
}
- handle_->BulkTransfer(USB_DIRECTION_OUTBOUND, 0x02, out_buffer.get(),
- out_buffer->size(),
- 5000, // 5 second timeout
- out_completion.callback());
+ handle->BulkTransfer(USB_DIRECTION_OUTBOUND, 0x02, out_buffer.get(),
+ out_buffer->size(),
+ 5000, // 5 second timeout
+ out_completion.callback());
out_completion.WaitForResult();
ASSERT_EQ(USB_TRANSFER_COMPLETED, out_completion.status());
EXPECT_EQ(static_cast<size_t>(out_buffer->size()),
@@ -157,6 +210,8 @@ TEST_F(UsbDeviceHandleTest, BulkTransfer) {
for (size_t i = 0; i < in_completion.transferred(); ++i) {
EXPECT_EQ(out_buffer->data()[i], in_buffer->data()[i]);
}
+
+ handle->Close();
}
} // namespace
diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc
index 02ceb6f..f04092f 100644
--- a/device/usb/usb_device_impl.cc
+++ b/device/usb/usb_device_impl.cc
@@ -8,10 +8,9 @@
#include "base/bind.h"
#include "base/location.h"
+#include "base/sequenced_task_runner.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_context.h"
@@ -25,25 +24,10 @@
#include "chromeos/dbus/permission_broker_client.h"
#endif // defined(OS_CHROMEOS)
-#if defined(USE_UDEV)
-#include "device/udev_linux/scoped_udev.h"
-#endif // defined(USE_UDEV)
-
namespace device {
namespace {
-#if defined(OS_CHROMEOS)
-
-void PostResultOnTaskRunner(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
- const base::Callback<void(bool success)>& callback,
- bool success) {
- task_runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
-
-#endif // defined(OS_CHROMEOS)
-
UsbEndpointDirection GetDirection(
const libusb_endpoint_descriptor* descriptor) {
switch (descriptor->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) {
@@ -108,69 +92,27 @@ UsbUsageType GetUsageType(const libusb_endpoint_descriptor* descriptor) {
UsbDeviceImpl::UsbDeviceImpl(
scoped_refptr<UsbContext> context,
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
PlatformUsbDevice platform_device,
uint16 vendor_id,
uint16 product_id,
- uint32 unique_id)
- : UsbDevice(vendor_id, product_id, unique_id),
+ uint32 unique_id,
+ const base::string16& manufacturer_string,
+ const base::string16& product_string,
+ const base::string16& serial_number,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+ : UsbDevice(vendor_id,
+ product_id,
+ unique_id,
+ manufacturer_string,
+ product_string,
+ serial_number),
platform_device_(platform_device),
context_(context),
- ui_task_runner_(ui_task_runner) {
+ task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ blocking_task_runner_(blocking_task_runner) {
CHECK(platform_device) << "platform_device cannot be NULL";
libusb_ref_device(platform_device);
RefreshConfiguration();
-#if defined(USE_UDEV)
- 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;
- }
-
-#if defined(OS_CHROMEOS)
- value = udev_device_get_devnode(device.get());
- if (value) {
- devnode_ = value;
- }
-#endif
- value = udev_device_get_sysattr_value(device.get(), "manufacturer");
- if (value) {
- manufacturer_ = base::UTF8ToUTF16(value);
- }
- value = udev_device_get_sysattr_value(device.get(), "product");
- if (value) {
- product_ = base::UTF8ToUTF16(value);
- }
- value = udev_device_get_sysattr_value(device.get(), "serial");
- if (value) {
- serial_number_ = base::UTF8ToUTF16(value);
- }
- break;
- }
- }
-#else
- strings_cached_ = false;
-#endif
}
UsbDeviceImpl::~UsbDeviceImpl() {
@@ -182,51 +124,28 @@ UsbDeviceImpl::~UsbDeviceImpl() {
void UsbDeviceImpl::CheckUsbAccess(const ResultCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
-
chromeos::PermissionBrokerClient* client =
chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
DCHECK(client) << "Could not get permission broker client.";
-
- ui_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&chromeos::PermissionBrokerClient::CheckPathAccess,
- base::Unretained(client), devnode_,
- base::Bind(&PostResultOnTaskRunner,
- base::ThreadTaskRunnerHandle::Get(), callback)));
+ client->CheckPathAccess(devnode_, callback);
}
void UsbDeviceImpl::RequestUsbAccess(int interface_id,
const ResultCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
-
chromeos::PermissionBrokerClient* client =
chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
DCHECK(client) << "Could not get permission broker client.";
-
- ui_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&chromeos::PermissionBrokerClient::RequestPathAccess,
- base::Unretained(client), devnode_, interface_id,
- base::Bind(&PostResultOnTaskRunner,
- base::ThreadTaskRunnerHandle::Get(), callback)));
+ client->RequestPathAccess(devnode_, interface_id, callback);
}
#endif
-scoped_refptr<UsbDeviceHandle> UsbDeviceImpl::Open() {
+void UsbDeviceImpl::Open(const OpenCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- PlatformUsbDeviceHandle handle;
- const int rv = libusb_open(platform_device_, &handle);
- if (LIBUSB_SUCCESS == rv) {
- scoped_refptr<UsbDeviceHandleImpl> device_handle =
- new UsbDeviceHandleImpl(context_, this, handle);
- handles_.push_back(device_handle);
- return device_handle;
- } else {
- USB_LOG(EVENT) << "Failed to open device: "
- << ConvertPlatformUsbErrorToString(rv);
- return NULL;
- }
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceImpl::OpenOnBlockingThread, this, callback));
}
bool UsbDeviceImpl::Close(scoped_refptr<UsbDeviceHandle> handle) {
@@ -248,45 +167,6 @@ const UsbConfigDescriptor* UsbDeviceImpl::GetConfiguration() {
return configuration_.get();
}
-bool UsbDeviceImpl::GetManufacturer(base::string16* manufacturer) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
-#if !defined(USE_UDEV)
- if (!strings_cached_) {
- CacheStrings();
- }
-#endif
-
- *manufacturer = manufacturer_;
- return !manufacturer_.empty();
-}
-
-bool UsbDeviceImpl::GetProduct(base::string16* product) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
-#if !defined(USE_UDEV)
- if (!strings_cached_) {
- CacheStrings();
- }
-#endif
-
- *product = product_;
- return !product_.empty();
-}
-
-bool UsbDeviceImpl::GetSerialNumber(base::string16* serial_number) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
-#if !defined(USE_UDEV)
- if (!strings_cached_) {
- CacheStrings();
- }
-#endif
-
- *serial_number = serial_number_;
- return !serial_number_.empty();
-}
-
void UsbDeviceImpl::OnDisconnect() {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -365,35 +245,26 @@ void UsbDeviceImpl::RefreshConfiguration() {
libusb_free_config_descriptor(platform_config);
}
-#if !defined(USE_UDEV)
-void UsbDeviceImpl::CacheStrings() {
- DCHECK(thread_checker_.CalledOnValidThread());
- // This is a non-blocking call as libusb has the descriptor in memory.
- libusb_device_descriptor desc;
- const int rv = libusb_get_device_descriptor(platform_device_, &desc);
- if (rv == LIBUSB_SUCCESS) {
- scoped_refptr<UsbDeviceHandle> device_handle = Open();
- if (device_handle.get()) {
- if (desc.iManufacturer != 0) {
- device_handle->GetStringDescriptor(desc.iManufacturer, &manufacturer_);
- }
- if (desc.iProduct != 0) {
- device_handle->GetStringDescriptor(desc.iProduct, &product_);
- }
- if (desc.iSerialNumber != 0) {
- device_handle->GetStringDescriptor(desc.iSerialNumber, &serial_number_);
- }
- device_handle->Close();
- } else {
- USB_LOG(EVENT) << "Failed to open device to cache string descriptors.";
- }
+void UsbDeviceImpl::OpenOnBlockingThread(const OpenCallback& callback) {
+ PlatformUsbDeviceHandle handle;
+ const int rv = libusb_open(platform_device_, &handle);
+ if (LIBUSB_SUCCESS == rv) {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceImpl::Opened, this, handle, callback));
} else {
- USB_LOG(EVENT)
- << "Failed to read device descriptor to cache string descriptors: "
- << ConvertPlatformUsbErrorToString(rv);
+ USB_LOG(EVENT) << "Failed to open device: "
+ << ConvertPlatformUsbErrorToString(rv);
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
}
- strings_cached_ = true;
}
-#endif // !defined(USE_UDEV)
+
+void UsbDeviceImpl::Opened(PlatformUsbDeviceHandle platform_handle,
+ const OpenCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ scoped_refptr<UsbDeviceHandleImpl> device_handle = new UsbDeviceHandleImpl(
+ context_, this, platform_handle, blocking_task_runner_);
+ handles_.push_back(device_handle);
+ callback.Run(device_handle);
+}
} // namespace device
diff --git a/device/usb/usb_device_impl.h b/device/usb/usb_device_impl.h
index 897b4910..a865ea2 100644
--- a/device/usb/usb_device_impl.h
+++ b/device/usb/usb_device_impl.h
@@ -15,9 +15,10 @@
struct libusb_device;
struct libusb_config_descriptor;
+struct libusb_device_handle;
namespace base {
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
}
namespace device {
@@ -25,8 +26,9 @@ namespace device {
class UsbDeviceHandleImpl;
class UsbContext;
-typedef libusb_device* PlatformUsbDevice;
-typedef libusb_config_descriptor* PlatformUsbConfigDescriptor;
+typedef struct libusb_device* PlatformUsbDevice;
+typedef struct libusb_config_descriptor* PlatformUsbConfigDescriptor;
+typedef struct libusb_device_handle* PlatformUsbDeviceHandle;
class UsbDeviceImpl : public UsbDevice {
public:
@@ -37,12 +39,9 @@ class UsbDeviceImpl : public UsbDevice {
void RequestUsbAccess(int interface_id,
const ResultCallback& callback) override;
#endif // OS_CHROMEOS
- scoped_refptr<UsbDeviceHandle> Open() override;
+ void Open(const OpenCallback& callback) override;
bool Close(scoped_refptr<UsbDeviceHandle> handle) override;
const UsbConfigDescriptor* GetConfiguration() override;
- bool GetManufacturer(base::string16* manufacturer) override;
- bool GetProduct(base::string16* product) override;
- bool GetSerialNumber(base::string16* serial_number) override;
protected:
friend class UsbServiceImpl;
@@ -50,37 +49,35 @@ class UsbDeviceImpl : public UsbDevice {
// Called by UsbServiceImpl only;
UsbDeviceImpl(scoped_refptr<UsbContext> context,
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
PlatformUsbDevice platform_device,
uint16 vendor_id,
uint16 product_id,
- uint32 unique_id);
+ uint32 unique_id,
+ const base::string16& manufacturer_string,
+ const base::string16& product_string,
+ const base::string16& serial_number,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
~UsbDeviceImpl() override;
// Called only by UsbServiceImpl.
+ PlatformUsbDevice platform_device() const { return platform_device_; }
+ void set_visited(bool visited) { visited_ = visited; }
+ bool was_visited() const { return visited_; }
void OnDisconnect();
// Called by UsbDeviceHandleImpl.
void RefreshConfiguration();
private:
+ void OpenOnBlockingThread(const OpenCallback& callback);
+ void Opened(PlatformUsbDeviceHandle platform_handle,
+ const OpenCallback& callback);
+
base::ThreadChecker thread_checker_;
PlatformUsbDevice platform_device_;
+ bool visited_ = false;
- // On Linux these properties are read from sysfs when the device is enumerated
- // to avoid hitting the permission broker on Chrome OS for a real string
- // descriptor request.
- base::string16 manufacturer_;
- base::string16 product_;
- base::string16 serial_number_;
-#if !defined(USE_UDEV)
- // On other platforms the device must be opened in order to cache them. This
- // should be delayed until the strings are needed to avoid poor interactions
- // with other applications.
- void CacheStrings();
- bool strings_cached_;
-#endif
#if defined(OS_CHROMEOS)
// On Chrome OS save the devnode string for requesting path access from
// permission broker.
@@ -98,8 +95,8 @@ class UsbDeviceImpl : public UsbDevice {
typedef std::vector<scoped_refptr<UsbDeviceHandleImpl> > HandlesVector;
HandlesVector handles_;
- // Reference to the UI thread for permission-broker calls.
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl);
};
diff --git a/device/usb/usb_service.cc b/device/usb/usb_service.cc
index 10338fc..18732b5 100644
--- a/device/usb/usb_service.cc
+++ b/device/usb/usb_service.cc
@@ -17,28 +17,6 @@ UsbService* g_service;
} // namespace
-// This class manages the lifetime of the global UsbService instance so that
-// it is destroyed when the current message loop is destroyed. A lazy instance
-// cannot be used because this object does not live on the main thread.
-class UsbService::Destroyer : private base::MessageLoop::DestructionObserver {
- public:
- explicit Destroyer(UsbService* usb_service) : usb_service_(usb_service) {
- base::MessageLoop::current()->AddDestructionObserver(this);
- }
- ~Destroyer() override {}
-
- private:
- // base::MessageLoop::DestructionObserver implementation.
- void WillDestroyCurrentMessageLoop() override {
- base::MessageLoop::current()->RemoveDestructionObserver(this);
- delete usb_service_;
- delete this;
- g_service = nullptr;
- }
-
- UsbService* usb_service_;
-};
-
void UsbService::Observer::OnDeviceAdded(scoped_refptr<UsbDevice> device) {
}
@@ -51,25 +29,23 @@ void UsbService::Observer::OnDeviceRemovedCleanup(
// static
UsbService* UsbService::GetInstance(
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
if (!g_service) {
- g_service = UsbServiceImpl::Create(ui_task_runner);
- // This object will clean itself up when the message loop is destroyed.
- new Destroyer(g_service);
+ // UsbService constructor saves the pointer this returns and UsbServiceImpl
+ // will destroy itself when the current message loop exits.
+ UsbServiceImpl::Create(blocking_task_runner);
}
return g_service;
}
-// static
-void UsbService::SetInstanceForTest(UsbService* instance) {
- g_service = instance;
- new Destroyer(instance);
-}
-
UsbService::UsbService() {
+ DCHECK(!g_service);
+ g_service = this;
}
UsbService::~UsbService() {
+ DCHECK(g_service);
+ g_service = nullptr;
}
void UsbService::AddObserver(Observer* observer) {
@@ -85,18 +61,12 @@ void UsbService::RemoveObserver(Observer* observer) {
void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) {
DCHECK(CalledOnValidThread());
- USB_LOG(USER) << "USB device added: vendorId = " << device->vendor_id()
- << ", productId = " << device->product_id()
- << ", uniqueId = " << device->unique_id();
-
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device));
}
void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) {
DCHECK(CalledOnValidThread());
- USB_LOG(USER) << "USB device removed: uniqueId = " << device->unique_id();
-
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device));
FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemovedCleanup(device));
}
diff --git a/device/usb/usb_service.h b/device/usb/usb_service.h
index a2dfa9e..5dfe942 100644
--- a/device/usb/usb_service.h
+++ b/device/usb/usb_service.h
@@ -7,13 +7,14 @@
#include <vector>
+#include "base/bind_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/threading/non_thread_safe.h"
namespace base {
-class SingleThreadTaskRunner;
+class SequencedTaskRunner;
}
namespace device {
@@ -24,11 +25,11 @@ class UsbDevice;
// used to manage and dispatch USB events. It is also responsible for device
// discovery on the system, which allows it to re-use device handles to prevent
// competition for the same USB device.
-//
-// All functions on this object must be called from a thread with a
-// MessageLoopForIO (for example, BrowserThread::FILE).
class UsbService : public base::NonThreadSafe {
public:
+ using GetDevicesCallback =
+ base::Callback<void(const std::vector<scoped_refptr<UsbDevice>>&)>;
+
class Observer {
public:
// These events are delivered from the thread on which the UsbService object
@@ -40,19 +41,15 @@ class UsbService : public base::NonThreadSafe {
virtual void OnDeviceRemovedCleanup(scoped_refptr<UsbDevice> device);
};
- // The UI task runner reference is used to talk to the PermissionBrokerClient
- // on ChromeOS (UI thread). Returns NULL when initialization fails.
+ // The file task runner reference is used for blocking I/O operations.
+ // Returns NULL when initialization fails.
static UsbService* GetInstance(
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
-
- static void SetInstanceForTest(UsbService* instance);
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) = 0;
- // Get all of the devices attached to the system, inserting them into
- // |devices|. Clears |devices| before use. The result will be sorted by id
- // in increasing order.
- virtual void GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) = 0;
+ // Enumerates available devices.
+ virtual void GetDevices(const GetDevicesCallback& callback) = 0;
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
@@ -67,7 +64,7 @@ class UsbService : public base::NonThreadSafe {
ObserverList<Observer, true> observer_list_;
private:
- class Destroyer;
+ friend void base::DeletePointer<UsbService>(UsbService* service);
DISALLOW_COPY_AND_ASSIGN(UsbService);
};
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();
}
diff --git a/device/usb/usb_service_impl.h b/device/usb/usb_service_impl.h
index 44fff41..e4c4443 100644
--- a/device/usb/usb_service_impl.h
+++ b/device/usb/usb_service_impl.h
@@ -5,42 +5,92 @@
#include "device/usb/usb_service.h"
#include <map>
+#include <set>
#include "base/memory/weak_ptr.h"
-#include "base/single_thread_task_runner.h"
+#include "base/message_loop/message_loop.h"
#include "device/usb/usb_context.h"
#include "device/usb/usb_device_impl.h"
#include "third_party/libusb/src/libusb/libusb.h"
+#if defined(OS_WIN)
+#include "base/scoped_observer.h"
+#include "device/core/device_monitor_win.h"
+#endif // OS_WIN
+
+struct libusb_device;
+struct libusb_context;
+
+namespace base {
+class SequencedTaskRunner;
+class SingleThreadTaskRunner;
+}
+
namespace device {
typedef struct libusb_device* PlatformUsbDevice;
typedef struct libusb_context* PlatformUsbContext;
-class UsbServiceImpl : public UsbService {
+class UsbServiceImpl : public UsbService,
+#if defined(OS_WIN)
+ public DeviceMonitorWin::Observer,
+#endif // OS_WIN
+ public base::MessageLoop::DestructionObserver {
public:
static UsbService* Create(
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
private:
explicit UsbServiceImpl(
PlatformUsbContext context,
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
~UsbServiceImpl() override;
// device::UsbService implementation
scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override;
- void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override;
-
- // Enumerate USB devices from OS and update devices_ map.
- void RefreshDevices();
+ void GetDevices(const GetDevicesCallback& callback) override;
#if defined(OS_WIN)
- void RefreshDevicesIfWinUsbDevice(const std::string& device_path);
+ // device::DeviceMonitorWin::Observer implementation
+ void OnDeviceAdded(const GUID& class_guid,
+ const std::string& device_path) override;
+ void OnDeviceRemoved(const GUID& class_guid,
+ const std::string& device_path) override;
#endif // OS_WIN
+ // base::MessageLoop::DestructionObserver implementation
+ void WillDestroyCurrentMessageLoop() override;
+
+ // Enumerate USB devices from OS and update devices_ map. |new_device_path| is
+ // an optional hint used on Windows to prevent enumerations before drivers for
+ // a new device have been completely loaded.
+ void RefreshDevices(const std::string& new_device_path);
+
+ static void 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);
+
+ static void AddDeviceOnBlockingThread(
+ base::WeakPtr<UsbServiceImpl> usb_service,
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ PlatformUsbDevice platform_device);
+
+ void RefreshDevicesComplete(libusb_device** platform_devices,
+ ssize_t device_count);
+
// Adds a new UsbDevice to the devices_ map based on the given libusb device.
- scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device);
+ void 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);
+
+ void RemoveDevice(scoped_refptr<UsbDeviceImpl> device);
// Handle hotplug events from libusb.
static int LIBUSB_CALL HotplugCallback(libusb_context* context,
@@ -48,27 +98,26 @@ class UsbServiceImpl : public UsbService {
libusb_hotplug_event event,
void* user_data);
// These functions release a reference to the provided platform device.
- void OnDeviceAdded(PlatformUsbDevice platform_device);
- void OnDeviceRemoved(PlatformUsbDevice platform_device);
+ void OnPlatformDeviceAdded(PlatformUsbDevice platform_device);
+ void OnPlatformDeviceRemoved(PlatformUsbDevice platform_device);
scoped_refptr<UsbContext> context_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
-
-#if defined(OS_WIN)
- class UIThreadHelper;
- UIThreadHelper* ui_thread_helper_;
-#endif // OS_WIN
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
// TODO(reillyg): Figure out a better solution for device IDs.
- uint32 next_unique_id_;
+ uint32 next_unique_id_ = 0;
// When available the device list will be updated when new devices are
// connected instead of only when a full enumeration is requested.
// TODO(reillyg): Support this on all platforms. crbug.com/411715
- bool hotplug_enabled_;
+ bool hotplug_enabled_ = false;
libusb_hotplug_callback_handle hotplug_handle_;
+ // Enumeration callbacks are queued until an enumeration completes.
+ bool enumeration_ready_ = false;
+ std::vector<GetDevicesCallback> pending_enumerations_;
+
// The map from unique IDs to UsbDevices.
typedef std::map<uint32, scoped_refptr<UsbDeviceImpl>> DeviceMap;
DeviceMap devices_;
@@ -78,6 +127,10 @@ class UsbServiceImpl : public UsbService {
PlatformDeviceMap;
PlatformDeviceMap platform_devices_;
+#if defined(OS_WIN)
+ ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
+#endif // OS_WIN
+
base::WeakPtrFactory<UsbServiceImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl);
diff --git a/device/usb/usb_service_unittest.cc b/device/usb/usb_service_unittest.cc
index edd0712..f0e5ae2 100644
--- a/device/usb/usb_service_unittest.cc
+++ b/device/usb/usb_service_unittest.cc
@@ -4,6 +4,7 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_io_thread.h"
#include "device/test/usb_test_gadget.h"
#include "device/usb/usb_device.h"
#include "device/usb/usb_device_handle.h"
@@ -15,34 +16,34 @@ namespace {
class UsbServiceTest : public ::testing::Test {
public:
- void SetUp() override { message_loop_.reset(new base::MessageLoopForIO); }
+ void SetUp() override {
+ message_loop_.reset(new base::MessageLoopForUI);
+ io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart));
+ }
- private:
+ protected:
scoped_ptr<base::MessageLoop> message_loop_;
+ scoped_ptr<base::TestIOThread> io_thread_;
};
TEST_F(UsbServiceTest, ClaimGadget) {
if (!UsbTestGadget::IsTestEnabled()) return;
- scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim();
+ scoped_ptr<UsbTestGadget> gadget =
+ UsbTestGadget::Claim(io_thread_->task_runner());
ASSERT_TRUE(gadget.get());
scoped_refptr<UsbDevice> device = gadget->GetDevice();
- base::string16 utf16;
- ASSERT_TRUE(device->GetManufacturer(&utf16));
- ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
-
- ASSERT_TRUE(device->GetProduct(&utf16));
- ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
-
- ASSERT_TRUE(device->GetSerialNumber(&utf16));
- ASSERT_EQ(gadget->GetSerialNumber(), base::UTF16ToUTF8(utf16));
+ ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(device->manufacturer_string()));
+ ASSERT_EQ("Test Gadget (default state)",
+ base::UTF16ToUTF8(device->product_string()));
}
TEST_F(UsbServiceTest, DisconnectAndReconnect) {
if (!UsbTestGadget::IsTestEnabled()) return;
- scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim();
+ scoped_ptr<UsbTestGadget> gadget =
+ UsbTestGadget::Claim(io_thread_->task_runner());
ASSERT_TRUE(gadget.get());
ASSERT_TRUE(gadget->Disconnect());
ASSERT_TRUE(gadget->Reconnect());