summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2014-09-03 17:57:56 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-04 01:03:27 +0000
commitd77718de5c3963cf0fe31e59c27c3e2aebfb26cb (patch)
tree65a996cddf74653fa2b4d1c3d583110eaa8132cf /device
parent6b0fbe4cbbdd357a17c8a72f92535403fa50515a (diff)
downloadchromium_src-d77718de5c3963cf0fe31e59c27c3e2aebfb26cb.zip
chromium_src-d77718de5c3963cf0fe31e59c27c3e2aebfb26cb.tar.gz
chromium_src-d77718de5c3963cf0fe31e59c27c3e2aebfb26cb.tar.bz2
Merge components/usb_service into device/usb.
Unify these two parts of out USB device support. The //device tree is the cannonical location for hardware device APIs. BUG= Review URL: https://codereview.chromium.org/497363004 Cr-Commit-Position: refs/heads/master@{#293246}
Diffstat (limited to 'device')
-rw-r--r--device/core/device_client.cc2
-rw-r--r--device/core/device_client.h8
-rw-r--r--device/device_tests.gyp3
-rw-r--r--device/test/DEPS1
-rw-r--r--device/test/usb_test_gadget.h8
-rw-r--r--device/test/usb_test_gadget_impl.cc9
-rw-r--r--device/usb/BUILD.gn25
-rw-r--r--device/usb/DEPS7
-rw-r--r--device/usb/OWNERS7
-rw-r--r--device/usb/usb.gyp32
-rw-r--r--device/usb/usb_context.cc80
-rw-r--r--device/usb/usb_context.h45
-rw-r--r--device/usb/usb_context_unittest.cc54
-rw-r--r--device/usb/usb_device.h70
-rw-r--r--device/usb/usb_device_filter.cc140
-rw-r--r--device/usb/usb_device_filter.h52
-rw-r--r--device/usb/usb_device_filter_unittest.cc252
-rw-r--r--device/usb/usb_device_handle.h110
-rw-r--r--device/usb/usb_device_handle_impl.cc765
-rw-r--r--device/usb/usb_device_handle_impl.h174
-rw-r--r--device/usb/usb_device_impl.cc154
-rw-r--r--device/usb/usb_device_impl.h77
-rw-r--r--device/usb/usb_error.cc15
-rw-r--r--device/usb/usb_error.h18
-rw-r--r--device/usb/usb_interface.h112
-rw-r--r--device/usb/usb_interface_impl.cc168
-rw-r--r--device/usb/usb_interface_impl.h117
-rw-r--r--device/usb/usb_service.h53
-rw-r--r--device/usb/usb_service_impl.cc190
-rw-r--r--device/usb/usb_service_unittest.cc6
30 files changed, 2727 insertions, 27 deletions
diff --git a/device/core/device_client.cc b/device/core/device_client.cc
index ad96b1e..f92ec17 100644
--- a/device/core/device_client.cc
+++ b/device/core/device_client.cc
@@ -29,7 +29,7 @@ DeviceClient* DeviceClient::Get() {
return g_instance;
}
-usb_service::UsbService* DeviceClient::GetUsbService() {
+UsbService* DeviceClient::GetUsbService() {
// This should never be called by clients which do not support the USB API.
NOTREACHED();
return NULL;
diff --git a/device/core/device_client.h b/device/core/device_client.h
index d8d938b..3ca3130 100644
--- a/device/core/device_client.h
+++ b/device/core/device_client.h
@@ -7,12 +7,10 @@
#include "base/macros.h"
-namespace usb_service {
-class UsbService;
-}
-
namespace device {
+class UsbService;
+
// Interface used by consumers of //device APIs to get pointers to the service
// singletons appropriate for a given embedding application. For an example see
// //chrome/browser/chrome_device_client.h.
@@ -28,7 +26,7 @@ class DeviceClient {
static DeviceClient* Get();
// Returns the UsbService instance for this embedder.
- virtual usb_service::UsbService* GetUsbService();
+ virtual UsbService* GetUsbService();
private:
DISALLOW_COPY_AND_ASSIGN(DeviceClient);
diff --git a/device/device_tests.gyp b/device/device_tests.gyp
index 6b5a59b..9faa1a3 100644
--- a/device/device_tests.gyp
+++ b/device/device_tests.gyp
@@ -12,7 +12,6 @@
'type': '<(gtest_target_type)',
'dependencies': [
'../base/base.gyp:test_support_base',
- '../components/components.gyp:usb_service',
'../mojo/mojo_base.gyp:mojo_environment_chromium',
'../mojo/mojo_base.gyp:mojo_system_impl',
'../testing/gmock.gyp:gmock',
@@ -41,6 +40,8 @@
'bluetooth/bluetooth_uuid_unittest.cc',
'nfc/nfc_chromeos_unittest.cc',
'nfc/nfc_ndef_record_unittest.cc',
+ 'usb/usb_context_unittest.cc',
+ 'usb/usb_device_filter_unittest.cc',
'usb/usb_ids_unittest.cc',
'usb/usb_service_unittest.cc',
'hid/hid_connection_unittest.cc',
diff --git a/device/test/DEPS b/device/test/DEPS
index 9b323d8..a29f20f 100644
--- a/device/test/DEPS
+++ b/device/test/DEPS
@@ -1,5 +1,4 @@
include_rules = [
- "+components/usb_service",
"+mojo/embedder",
"+net/proxy",
"+net/url_request",
diff --git a/device/test/usb_test_gadget.h b/device/test/usb_test_gadget.h
index 1c835e2..1433bf8 100644
--- a/device/test/usb_test_gadget.h
+++ b/device/test/usb_test_gadget.h
@@ -10,12 +10,10 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
-namespace usb_service {
-class UsbDevice;
-} // namespace usb_service
-
namespace device {
+class UsbDevice;
+
class UsbTestGadget {
public:
enum Type {
@@ -35,7 +33,7 @@ class UsbTestGadget {
virtual bool Reconnect() = 0;
virtual bool SetType(Type type) = 0;
- virtual usb_service::UsbDevice* GetDevice() const = 0;
+ virtual UsbDevice* GetDevice() const = 0;
virtual std::string GetSerial() const = 0;
protected:
diff --git a/device/test/usb_test_gadget_impl.cc b/device/test/usb_test_gadget_impl.cc
index e95188b..5450b9e 100644
--- a/device/test/usb_test_gadget_impl.cc
+++ b/device/test/usb_test_gadget_impl.cc
@@ -23,9 +23,9 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
-#include "components/usb_service/usb_device.h"
-#include "components/usb_service/usb_device_handle.h"
-#include "components/usb_service/usb_service.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_device_handle.h"
+#include "device/usb/usb_service.h"
#include "net/proxy/proxy_service.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
@@ -36,9 +36,6 @@
using ::base::PlatformThread;
using ::base::TimeDelta;
-using ::usb_service::UsbDevice;
-using ::usb_service::UsbDeviceHandle;
-using ::usb_service::UsbService;
namespace device {
diff --git a/device/usb/BUILD.gn b/device/usb/BUILD.gn
index d8a7d77..0e97bfe 100644
--- a/device/usb/BUILD.gn
+++ b/device/usb/BUILD.gn
@@ -7,14 +7,39 @@ generated_ids = "$target_gen_dir/usb_ids_gen.cc"
source_set("usb") {
sources = [
+ "usb_context.cc",
+ "usb_context.h",
+ "usb_device_impl.cc",
+ "usb_device_impl.h",
+ "usb_device.h",
+ "usb_device_filter.cc",
+ "usb_device_filter.h",
+ "usb_device_handle_impl.cc",
+ "usb_device_handle_impl.h",
+ "usb_device_handle.h",
+ "usb_error.cc",
+ "usb_error.h",
"usb_ids.cc",
"usb_ids.h",
+ "usb_interface.h",
+ "usb_interface_impl.cc",
+ "usb_interface_impl.h",
+ "usb_service.h",
+ "usb_service_impl.cc",
generated_ids,
]
+
deps = [
":usb_device_ids",
"//base",
+ "//base/third_party/dynamic_annotations",
+ "//net",
+ "//third_party/libusb",
]
+
+ if (is_linux) {
+ configs += [ "//build/config/linux:udev" ]
+ }
}
action("usb_device_ids") {
diff --git a/device/usb/DEPS b/device/usb/DEPS
index 70ac41d..c114bca 100644
--- a/device/usb/DEPS
+++ b/device/usb/DEPS
@@ -1,3 +1,8 @@
include_rules = [
- "+components/usb_service",
+ "+chromeos",
+
+ "-net",
+ "+net/base",
+
+ "+third_party/libusb",
]
diff --git a/device/usb/OWNERS b/device/usb/OWNERS
index 51dcade..ee9faf8 100644
--- a/device/usb/OWNERS
+++ b/device/usb/OWNERS
@@ -1,3 +1,4 @@
-ikarienator@chromium.org
-gdk@chromium.org
-bryeung@chromium.org
+pfeldman@chromium.org
+reillyg@chromium.org
+rockot@chromium.org
+rpaquay@chromium.org
diff --git a/device/usb/usb.gyp b/device/usb/usb.gyp
index 47cf824..81bdf40 100644
--- a/device/usb/usb.gyp
+++ b/device/usb/usb.gyp
@@ -10,12 +10,32 @@
{
'target_name': 'device_usb',
'type': 'static_library',
+ 'dependencies': [
+ '../../third_party/libusb/libusb.gyp:libusb',
+ ],
'include_dirs': [
'../..',
],
'sources': [
+ 'usb_context.cc',
+ 'usb_context.h',
+ 'usb_device_impl.cc',
+ 'usb_device_impl.h',
+ 'usb_device.h',
+ 'usb_device_filter.cc',
+ 'usb_device_filter.h',
+ 'usb_device_handle_impl.cc',
+ 'usb_device_handle_impl.h',
+ 'usb_device_handle.h',
+ 'usb_error.cc',
+ 'usb_error.h',
'usb_ids.cc',
'usb_ids.h',
+ 'usb_interface.h',
+ 'usb_interface_impl.cc',
+ 'usb_interface_impl.h',
+ 'usb_service.h',
+ 'usb_service_impl.cc',
],
'actions': [
{
@@ -40,6 +60,18 @@
'process_outputs_as_sources': 1,
},
],
+ 'conditions': [
+ ['use_udev == 1', {
+ 'dependencies': [
+ '../../build/linux/system.gyp:udev',
+ ],
+ }],
+ ['chromeos==1', {
+ 'dependencies': [
+ '../../chromeos/chromeos.gyp:chromeos',
+ ],
+ }],
+ ]
},
],
}
diff --git a/device/usb/usb_context.cc b/device/usb/usb_context.cc
new file mode 100644
index 0000000..72a3835
--- /dev/null
+++ b/device/usb/usb_context.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_context.h"
+
+#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"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+// The UsbEventHandler works around a design flaw in the libusb interface. There
+// is currently no way to signal to libusb that any caller into one of the event
+// handler calls should return without handling any events.
+class UsbContext::UsbEventHandler : public base::PlatformThread::Delegate {
+ public:
+ explicit UsbEventHandler(libusb_context* context);
+ virtual ~UsbEventHandler();
+
+ // base::PlatformThread::Delegate
+ virtual void ThreadMain() OVERRIDE;
+
+ 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) {
+ 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_);
+}
+
+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) {
+ VLOG(1) << "Failed to handle events: "
+ << ConvertPlatformUsbErrorToString(rv);
+ }
+ }
+ VLOG(1) << "UsbEventHandler shutting down.";
+}
+
+UsbContext::UsbContext(PlatformUsbContext context) : context_(context) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ 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_);
+}
+
+} // namespace device
diff --git a/device/usb/usb_context.h b/device/usb/usb_context.h
new file mode 100644
index 0000000..b628b4b
--- /dev/null
+++ b/device/usb/usb_context.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_CONTEXT_H_
+#define DEVICE_USB_USB_CONTEXT_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+
+struct libusb_context;
+
+namespace device {
+
+typedef libusb_context* PlatformUsbContext;
+
+// Ref-counted wrapper for libusb_context*.
+// It also manages the life-cycle of UsbEventHandler.
+// It is a blocking operation to delete UsbContext.
+// Destructor must be called on FILE thread.
+class UsbContext : public base::RefCountedThreadSafe<UsbContext> {
+ public:
+ PlatformUsbContext context() const { return context_; }
+
+ protected:
+ friend class UsbServiceImpl;
+ friend class base::RefCountedThreadSafe<UsbContext>;
+
+ explicit UsbContext(PlatformUsbContext context);
+ virtual ~UsbContext();
+
+ private:
+ class UsbEventHandler;
+ PlatformUsbContext context_;
+ UsbEventHandler* event_handler_;
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbContext);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_CONTEXT_H_
diff --git a/device/usb/usb_context_unittest.cc b/device/usb/usb_context_unittest.cc
new file mode 100644
index 0000000..e00062c
--- /dev/null
+++ b/device/usb/usb_context_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "device/usb/usb_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+namespace {
+
+class UsbContextTest : public testing::Test {
+ protected:
+ class UsbContextForTest : public UsbContext {
+ public:
+ explicit UsbContextForTest(PlatformUsbContext context)
+ : UsbContext(context) {}
+
+ private:
+ virtual ~UsbContextForTest() {}
+ DISALLOW_COPY_AND_ASSIGN(UsbContextForTest);
+ };
+};
+
+} // namespace
+
+#if defined(OS_LINUX)
+// Linux trybot does not support usb.
+#define MAYBE_GracefulShutdown DISABLED_GracefulShutdown
+#elif defined(OS_ANDROID)
+// Android build does not include usb support.
+#define MAYBE_GracefulShutdown DISABLED_GracefulShutdown
+#else
+#define MAYBE_GracefulShutdown GracefulShutdown
+#endif
+
+TEST_F(UsbContextTest, MAYBE_GracefulShutdown) {
+ base::TimeTicks start = base::TimeTicks::Now();
+ {
+ PlatformUsbContext platform_context;
+ ASSERT_EQ(LIBUSB_SUCCESS, libusb_init(&platform_context));
+ scoped_refptr<UsbContextForTest> context(
+ new UsbContextForTest(platform_context));
+ }
+ base::TimeDelta elapse = base::TimeTicks::Now() - start;
+ if (elapse > base::TimeDelta::FromSeconds(2)) {
+ FAIL();
+ }
+}
+
+} // namespace device
diff --git a/device/usb/usb_device.h b/device/usb/usb_device.h
new file mode 100644
index 0000000..586fd19
--- /dev/null
+++ b/device/usb/usb_device.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_DEVICE_H_
+#define DEVICE_USB_USB_DEVICE_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+
+namespace device {
+
+class UsbDeviceHandle;
+class 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.
+class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
+ public:
+ // 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_; }
+
+#if defined(OS_CHROMEOS)
+ // On ChromeOS, if an interface of a claimed device is not claimed, the
+ // permission broker can change the owner of the device so that the unclaimed
+ // interfaces can be used. If this argument is missing, permission broker will
+ // not be used and this method fails if the device is claimed.
+ virtual void RequestUsbAccess(
+ int interface_id,
+ const base::Callback<void(bool success)>& callback) = 0;
+#endif // OS_CHROMEOS
+
+ // Creates a UsbDeviceHandle for further manipulation.
+ // Blocking method. Must be called on FILE thread.
+ virtual scoped_refptr<UsbDeviceHandle> Open() = 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;
+
+ // Lists the interfaces provided by the device and fills the given
+ // UsbConfigDescriptor.
+ // Blocking method. Must be called on FILE thread.
+ virtual scoped_refptr<UsbConfigDescriptor> ListInterfaces() = 0;
+
+ protected:
+ UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id)
+ : vendor_id_(vendor_id), product_id_(product_id), unique_id_(unique_id) {}
+
+ virtual ~UsbDevice() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<UsbDevice>;
+
+ const uint16 vendor_id_;
+ const uint16 product_id_;
+ const uint32 unique_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbDevice);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_DEVICE_H_
diff --git a/device/usb/usb_device_filter.cc b/device/usb/usb_device_filter.cc
new file mode 100644
index 0000000..cc919e2
--- /dev/null
+++ b/device/usb/usb_device_filter.cc
@@ -0,0 +1,140 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_device_filter.h"
+
+#include "base/values.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_interface.h"
+
+namespace device {
+
+namespace {
+
+const char kProductIdKey[] = "productId";
+const char kVendorIdKey[] = "vendorId";
+const char kInterfaceClassKey[] = "interfaceClass";
+const char kInterfaceSubclassKey[] = "interfaceSubclass";
+const char kInterfaceProtocolKey[] = "interfaceProtocol";
+
+} // namespace
+
+UsbDeviceFilter::UsbDeviceFilter()
+ : vendor_id_set_(false),
+ product_id_set_(false),
+ interface_class_set_(false),
+ interface_subclass_set_(false),
+ interface_protocol_set_(false) {
+}
+
+UsbDeviceFilter::~UsbDeviceFilter() {
+}
+
+void UsbDeviceFilter::SetVendorId(uint16 vendor_id) {
+ vendor_id_set_ = true;
+ vendor_id_ = vendor_id;
+}
+
+void UsbDeviceFilter::SetProductId(uint16 product_id) {
+ product_id_set_ = true;
+ product_id_ = product_id;
+}
+
+void UsbDeviceFilter::SetInterfaceClass(uint8 interface_class) {
+ interface_class_set_ = true;
+ interface_class_ = interface_class;
+}
+
+void UsbDeviceFilter::SetInterfaceSubclass(uint8 interface_subclass) {
+ interface_subclass_set_ = true;
+ interface_subclass_ = interface_subclass;
+}
+
+void UsbDeviceFilter::SetInterfaceProtocol(uint8 interface_protocol) {
+ interface_protocol_set_ = true;
+ interface_protocol_ = interface_protocol;
+}
+
+bool UsbDeviceFilter::Matches(scoped_refptr<UsbDevice> device) const {
+ if (vendor_id_set_) {
+ if (device->vendor_id() != vendor_id_) {
+ return false;
+ }
+
+ if (product_id_set_ && device->product_id() != product_id_) {
+ return false;
+ }
+ }
+
+ if (interface_class_set_) {
+ bool foundMatch = false;
+ scoped_refptr<const UsbConfigDescriptor> config = device->ListInterfaces();
+
+ // TODO(reillyg): Check device configuration if the class is not defined at
+ // a per-interface level. This is not really important because most devices
+ // have per-interface classes. The only counter-examples I know of are hubs.
+
+ for (size_t i = 0; i < config->GetNumInterfaces() && !foundMatch; ++i) {
+ scoped_refptr<const UsbInterfaceDescriptor> iface =
+ config->GetInterface(i);
+
+ for (size_t j = 0; j < iface->GetNumAltSettings() && !foundMatch; ++j) {
+ scoped_refptr<const UsbInterfaceAltSettingDescriptor> altSetting =
+ iface->GetAltSetting(j);
+
+ if (altSetting->GetInterfaceClass() == interface_class_ &&
+ (!interface_subclass_set_ ||
+ (altSetting->GetInterfaceSubclass() == interface_subclass_ &&
+ (!interface_protocol_set_ ||
+ altSetting->GetInterfaceProtocol() == interface_protocol_)))) {
+ foundMatch = true;
+ }
+ }
+ }
+
+ if (!foundMatch) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+base::Value* UsbDeviceFilter::ToValue() const {
+ scoped_ptr<base::DictionaryValue> obj(new base::DictionaryValue());
+
+ if (vendor_id_set_) {
+ obj->SetInteger(kVendorIdKey, vendor_id_);
+ if (product_id_set_) {
+ obj->SetInteger(kProductIdKey, product_id_);
+ }
+ }
+
+ if (interface_class_set_) {
+ obj->SetInteger(kInterfaceClassKey, interface_class_);
+ if (interface_subclass_set_) {
+ obj->SetInteger(kInterfaceSubclassKey, interface_subclass_);
+ if (interface_protocol_set_) {
+ obj->SetInteger(kInterfaceProtocolKey, interface_protocol_);
+ }
+ }
+ }
+
+ return obj.release();
+}
+
+// static
+bool UsbDeviceFilter::MatchesAny(scoped_refptr<UsbDevice> device,
+ const std::vector<UsbDeviceFilter>& filters) {
+ for (std::vector<UsbDeviceFilter>::const_iterator i = filters.begin();
+ i != filters.end();
+ ++i) {
+ if (i->Matches(device)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace device
diff --git a/device/usb/usb_device_filter.h b/device/usb/usb_device_filter.h
new file mode 100644
index 0000000..6525f8f
--- /dev/null
+++ b/device/usb/usb_device_filter.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_DEVICE_FILTER_H_
+#define DEVICE_USB_USB_DEVICE_FILTER_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+
+namespace base {
+class Value;
+}
+
+namespace device {
+
+class UsbDevice;
+
+class UsbDeviceFilter {
+ public:
+ UsbDeviceFilter();
+ ~UsbDeviceFilter();
+
+ void SetVendorId(uint16 vendor_id);
+ void SetProductId(uint16 product_id);
+ void SetInterfaceClass(uint8 interface_class);
+ void SetInterfaceSubclass(uint8 interface_subclass);
+ void SetInterfaceProtocol(uint8 interface_protocol);
+
+ bool Matches(scoped_refptr<UsbDevice> device) const;
+ base::Value* ToValue() const;
+
+ static bool MatchesAny(scoped_refptr<UsbDevice> device,
+ const std::vector<UsbDeviceFilter>& filters);
+
+ private:
+ uint16 vendor_id_;
+ uint16 product_id_;
+ uint8 interface_class_;
+ uint8 interface_subclass_;
+ uint8 interface_protocol_;
+ bool vendor_id_set_ : 1;
+ bool product_id_set_ : 1;
+ bool interface_class_set_ : 1;
+ bool interface_subclass_set_ : 1;
+ bool interface_protocol_set_ : 1;
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_DEVICE_FILTER_H_
diff --git a/device/usb/usb_device_filter_unittest.cc b/device/usb/usb_device_filter_unittest.cc
new file mode 100644
index 0000000..1fdccd4
--- /dev/null
+++ b/device/usb/usb_device_filter_unittest.cc
@@ -0,0 +1,252 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_device_filter.h"
+#include "device/usb/usb_device_handle.h"
+#include "device/usb/usb_interface.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+namespace {
+
+class MockUsbInterfaceAltSettingDescriptor
+ : public UsbInterfaceAltSettingDescriptor {
+ public:
+ MockUsbInterfaceAltSettingDescriptor(int interface_number,
+ int alternate_setting,
+ int interface_class,
+ int interface_subclass,
+ int interface_protocol)
+ : interface_number_(interface_number),
+ alternate_setting_(alternate_setting),
+ interface_class_(interface_class),
+ interface_subclass_(interface_subclass),
+ interface_protocol_(interface_protocol) {}
+
+ virtual size_t GetNumEndpoints() const OVERRIDE { return 0; }
+ virtual scoped_refptr<const UsbEndpointDescriptor> GetEndpoint(
+ size_t index) const OVERRIDE {
+ return NULL;
+ }
+ virtual int GetInterfaceNumber() const OVERRIDE { return interface_number_; }
+ virtual int GetAlternateSetting() const OVERRIDE {
+ return alternate_setting_;
+ }
+ virtual int GetInterfaceClass() const OVERRIDE { return interface_class_; }
+ virtual int GetInterfaceSubclass() const OVERRIDE {
+ return interface_subclass_;
+ }
+ virtual int GetInterfaceProtocol() const OVERRIDE {
+ return interface_protocol_;
+ }
+
+ protected:
+ virtual ~MockUsbInterfaceAltSettingDescriptor() {}
+
+ private:
+ int interface_number_;
+ int alternate_setting_;
+ int interface_class_;
+ int interface_subclass_;
+ int interface_protocol_;
+};
+
+typedef std::vector<scoped_refptr<UsbInterfaceAltSettingDescriptor> >
+ UsbInterfaceAltSettingDescriptorList;
+
+class MockUsbInterfaceDescriptor : public UsbInterfaceDescriptor {
+ public:
+ MockUsbInterfaceDescriptor(
+ const UsbInterfaceAltSettingDescriptorList& alt_settings)
+ : alt_settings_(alt_settings) {}
+
+ virtual size_t GetNumAltSettings() const OVERRIDE {
+ return alt_settings_.size();
+ }
+ virtual scoped_refptr<const UsbInterfaceAltSettingDescriptor> GetAltSetting(
+ size_t index) const OVERRIDE {
+ return alt_settings_[index];
+ }
+
+ protected:
+ virtual ~MockUsbInterfaceDescriptor() {}
+
+ private:
+ UsbInterfaceAltSettingDescriptorList alt_settings_;
+};
+
+typedef std::vector<scoped_refptr<UsbInterfaceDescriptor> >
+ UsbInterfaceDescriptorList;
+
+class MockUsbConfigDescriptor : public UsbConfigDescriptor {
+ public:
+ MockUsbConfigDescriptor(const UsbInterfaceDescriptorList& interfaces)
+ : interfaces_(interfaces) {}
+
+ virtual size_t GetNumInterfaces() const OVERRIDE {
+ return interfaces_.size();
+ }
+ virtual scoped_refptr<const UsbInterfaceDescriptor> GetInterface(
+ size_t index) const OVERRIDE {
+ return interfaces_[index];
+ }
+
+ protected:
+ virtual ~MockUsbConfigDescriptor() {}
+
+ private:
+ UsbInterfaceDescriptorList interfaces_;
+};
+
+class MockUsbDevice : public UsbDevice {
+ public:
+ MockUsbDevice(uint16 vendor_id,
+ uint16 product_id,
+ uint32 unique_id,
+ scoped_refptr<UsbConfigDescriptor> config_desc)
+ : UsbDevice(vendor_id, product_id, unique_id),
+ config_desc_(config_desc) {}
+
+ virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE { return NULL; }
+ virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE {
+ NOTREACHED();
+ return true;
+ }
+#if defined(OS_CHROMEOS)
+ virtual void RequestUsbAccess(
+ int interface_id,
+ const base::Callback<void(bool success)>& callback) OVERRIDE {
+ NOTREACHED();
+ }
+#endif // OS_CHROMEOS
+ virtual scoped_refptr<UsbConfigDescriptor> ListInterfaces() OVERRIDE {
+ return config_desc_;
+ }
+
+ protected:
+ virtual ~MockUsbDevice() {}
+
+ private:
+ scoped_refptr<UsbConfigDescriptor> config_desc_;
+};
+
+class UsbFilterTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ UsbInterfaceAltSettingDescriptorList alt_settings;
+ alt_settings.push_back(make_scoped_refptr(
+ new MockUsbInterfaceAltSettingDescriptor(1, 0, 0xFF, 0x42, 1)));
+
+ UsbInterfaceDescriptorList interfaces;
+ interfaces.push_back(
+ make_scoped_refptr(new MockUsbInterfaceDescriptor(alt_settings)));
+
+ scoped_refptr<UsbConfigDescriptor> config_desc(
+ new MockUsbConfigDescriptor(interfaces));
+
+ android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, 0, config_desc);
+ }
+
+ protected:
+ scoped_refptr<UsbDevice> android_phone_;
+};
+
+TEST_F(UsbFilterTest, MatchAny) {
+ UsbDeviceFilter filter;
+ ASSERT_TRUE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchVendorId) {
+ UsbDeviceFilter filter;
+ filter.SetVendorId(0x18d1);
+ ASSERT_TRUE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchVendorIdNegative) {
+ UsbDeviceFilter filter;
+ filter.SetVendorId(0x1d6b);
+ ASSERT_FALSE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchProductId) {
+ UsbDeviceFilter filter;
+ filter.SetVendorId(0x18d1);
+ filter.SetProductId(0x4ee2);
+ ASSERT_TRUE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchProductIdNegative) {
+ UsbDeviceFilter filter;
+ filter.SetVendorId(0x18d1);
+ filter.SetProductId(0x4ee1);
+ ASSERT_FALSE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceClass) {
+ UsbDeviceFilter filter;
+ filter.SetInterfaceClass(0xff);
+ ASSERT_TRUE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceClassNegative) {
+ UsbDeviceFilter filter;
+ filter.SetInterfaceClass(0xe0);
+ ASSERT_FALSE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceSubclass) {
+ UsbDeviceFilter filter;
+ filter.SetInterfaceClass(0xff);
+ filter.SetInterfaceSubclass(0x42);
+ ASSERT_TRUE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceSubclassNegative) {
+ UsbDeviceFilter filter;
+ filter.SetInterfaceClass(0xff);
+ filter.SetInterfaceSubclass(0x01);
+ ASSERT_FALSE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceProtocol) {
+ UsbDeviceFilter filter;
+ filter.SetInterfaceClass(0xff);
+ filter.SetInterfaceSubclass(0x42);
+ filter.SetInterfaceProtocol(0x01);
+ ASSERT_TRUE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) {
+ UsbDeviceFilter filter;
+ filter.SetInterfaceClass(0xff);
+ filter.SetInterfaceSubclass(0x42);
+ filter.SetInterfaceProtocol(0x02);
+ ASSERT_FALSE(filter.Matches(android_phone_));
+}
+
+TEST_F(UsbFilterTest, MatchAnyEmptyListNegative) {
+ std::vector<UsbDeviceFilter> filters;
+ ASSERT_FALSE(UsbDeviceFilter::MatchesAny(android_phone_, filters));
+}
+
+TEST_F(UsbFilterTest, MatchesAnyVendorId) {
+ std::vector<UsbDeviceFilter> filters(1);
+ filters.back().SetVendorId(0x18d1);
+ ASSERT_TRUE(UsbDeviceFilter::MatchesAny(android_phone_, filters));
+}
+
+TEST_F(UsbFilterTest, MatchesAnyVendorIdNegative) {
+ std::vector<UsbDeviceFilter> filters(1);
+ filters.back().SetVendorId(0x1d6b);
+ ASSERT_FALSE(UsbDeviceFilter::MatchesAny(android_phone_, filters));
+}
+
+} // namespace
+
+} // namespace device
diff --git a/device/usb/usb_device_handle.h b/device/usb/usb_device_handle.h
new file mode 100644
index 0000000..be7de09
--- /dev/null
+++ b/device/usb/usb_device_handle.h
@@ -0,0 +1,110 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_DEVICE_HANDLE_H_
+#define DEVICE_USB_USB_DEVICE_HANDLE_H_
+
+#include <map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
+#include "device/usb/usb_interface.h"
+#include "net/base/io_buffer.h"
+
+namespace device {
+
+class UsbDevice;
+
+enum UsbTransferStatus {
+ USB_TRANSFER_COMPLETED = 0,
+ USB_TRANSFER_ERROR,
+ USB_TRANSFER_TIMEOUT,
+ USB_TRANSFER_CANCELLED,
+ USB_TRANSFER_STALLED,
+ USB_TRANSFER_DISCONNECT,
+ USB_TRANSFER_OVERFLOW,
+ 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:
+ enum TransferRequestType { STANDARD, CLASS, VENDOR, RESERVED };
+ enum TransferRecipient { DEVICE, INTERFACE, ENDPOINT, OTHER };
+
+ virtual scoped_refptr<UsbDevice> GetDevice() const = 0;
+
+ // Notifies UsbDevice to drop the reference of this object; cancels all the
+ // flying transfers.
+ // It is possible that the object has no other reference after this call. So
+ // if it is called using a raw pointer, it could be invalidated.
+ // 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 ClaimInterface(const int interface_number) = 0;
+ virtual bool ReleaseInterface(const int interface_number) = 0;
+ virtual bool SetInterfaceAlternateSetting(const int interface_number,
+ const int alternate_setting) = 0;
+ virtual bool ResetDevice() = 0;
+ virtual bool GetManufacturer(base::string16* manufacturer) = 0;
+ virtual bool GetProduct(base::string16* product) = 0;
+ virtual bool GetSerial(base::string16* serial) = 0;
+
+ // Async IO. Can be called on any thread.
+ virtual void ControlTransfer(const UsbEndpointDirection direction,
+ const TransferRequestType request_type,
+ const TransferRecipient recipient,
+ const uint8 request,
+ const uint16 value,
+ const uint16 index,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) = 0;
+
+ virtual void BulkTransfer(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) = 0;
+
+ virtual void InterruptTransfer(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) = 0;
+
+ virtual void IsochronousTransfer(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) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<UsbDeviceHandle>;
+
+ UsbDeviceHandle() {};
+
+ virtual ~UsbDeviceHandle() {};
+
+ DISALLOW_COPY_AND_ASSIGN(UsbDeviceHandle);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_DEVICE_HANDLE_H_
diff --git a/device/usb/usb_device_handle_impl.cc b/device/usb/usb_device_handle_impl.cc
new file mode 100644
index 0000000..c9d69a4
--- /dev/null
+++ b/device/usb/usb_device_handle_impl.cc
@@ -0,0 +1,765 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_device_handle_impl.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_task_runner_handle.h"
+#include "device/usb/usb_context.h"
+#include "device/usb/usb_device_impl.h"
+#include "device/usb/usb_error.h"
+#include "device/usb/usb_interface.h"
+#include "device/usb/usb_service.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+typedef libusb_device* PlatformUsbDevice;
+
+void HandleTransferCompletion(PlatformUsbTransferHandle transfer);
+
+namespace {
+
+static uint8 ConvertTransferDirection(const UsbEndpointDirection direction) {
+ switch (direction) {
+ case USB_DIRECTION_INBOUND:
+ return LIBUSB_ENDPOINT_IN;
+ case USB_DIRECTION_OUTBOUND:
+ return LIBUSB_ENDPOINT_OUT;
+ default:
+ NOTREACHED();
+ return LIBUSB_ENDPOINT_IN;
+ }
+}
+
+static uint8 CreateRequestType(
+ const UsbEndpointDirection direction,
+ const UsbDeviceHandle::TransferRequestType request_type,
+ const UsbDeviceHandle::TransferRecipient recipient) {
+ uint8 result = ConvertTransferDirection(direction);
+
+ switch (request_type) {
+ case UsbDeviceHandle::STANDARD:
+ result |= LIBUSB_REQUEST_TYPE_STANDARD;
+ break;
+ case UsbDeviceHandle::CLASS:
+ result |= LIBUSB_REQUEST_TYPE_CLASS;
+ break;
+ case UsbDeviceHandle::VENDOR:
+ result |= LIBUSB_REQUEST_TYPE_VENDOR;
+ break;
+ case UsbDeviceHandle::RESERVED:
+ result |= LIBUSB_REQUEST_TYPE_RESERVED;
+ break;
+ }
+
+ switch (recipient) {
+ case UsbDeviceHandle::DEVICE:
+ result |= LIBUSB_RECIPIENT_DEVICE;
+ break;
+ case UsbDeviceHandle::INTERFACE:
+ result |= LIBUSB_RECIPIENT_INTERFACE;
+ break;
+ case UsbDeviceHandle::ENDPOINT:
+ result |= LIBUSB_RECIPIENT_ENDPOINT;
+ break;
+ case UsbDeviceHandle::OTHER:
+ result |= LIBUSB_RECIPIENT_OTHER;
+ break;
+ }
+
+ return result;
+}
+
+static UsbTransferStatus ConvertTransferStatus(
+ const libusb_transfer_status status) {
+ switch (status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ return USB_TRANSFER_COMPLETED;
+ case LIBUSB_TRANSFER_ERROR:
+ return USB_TRANSFER_ERROR;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ return USB_TRANSFER_TIMEOUT;
+ case LIBUSB_TRANSFER_STALL:
+ return USB_TRANSFER_STALLED;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ return USB_TRANSFER_DISCONNECT;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ return USB_TRANSFER_OVERFLOW;
+ case LIBUSB_TRANSFER_CANCELLED:
+ return USB_TRANSFER_CANCELLED;
+ default:
+ NOTREACHED();
+ return USB_TRANSFER_ERROR;
+ }
+}
+
+} // namespace
+
+class UsbDeviceHandleImpl::InterfaceClaimer
+ : public base::RefCountedThreadSafe<UsbDeviceHandleImpl::InterfaceClaimer> {
+ public:
+ InterfaceClaimer(const scoped_refptr<UsbDeviceHandleImpl> handle,
+ const int interface_number);
+
+ bool Claim() const;
+
+ int alternate_setting() const { return alternate_setting_; }
+ void set_alternate_setting(const int alternate_setting) {
+ alternate_setting_ = alternate_setting;
+ }
+
+ private:
+ friend class UsbDevice;
+ friend class base::RefCountedThreadSafe<InterfaceClaimer>;
+ ~InterfaceClaimer();
+
+ const scoped_refptr<UsbDeviceHandleImpl> handle_;
+ const int interface_number_;
+ int alternate_setting_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterfaceClaimer);
+};
+
+UsbDeviceHandleImpl::InterfaceClaimer::InterfaceClaimer(
+ const scoped_refptr<UsbDeviceHandleImpl> handle,
+ const int interface_number)
+ : handle_(handle),
+ interface_number_(interface_number),
+ alternate_setting_(0) {
+}
+
+UsbDeviceHandleImpl::InterfaceClaimer::~InterfaceClaimer() {
+ libusb_release_interface(handle_->handle(), interface_number_);
+}
+
+bool UsbDeviceHandleImpl::InterfaceClaimer::Claim() const {
+ const int rv = libusb_claim_interface(handle_->handle(), interface_number_);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to claim interface: "
+ << ConvertPlatformUsbErrorToString(rv);
+ }
+ return rv == LIBUSB_SUCCESS;
+}
+
+struct UsbDeviceHandleImpl::Transfer {
+ Transfer();
+ ~Transfer();
+
+ void Complete(UsbTransferStatus status, size_t bytes_transferred);
+
+ UsbTransferType transfer_type;
+ scoped_refptr<net::IOBuffer> buffer;
+ scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer> claimed_interface;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner;
+ size_t length;
+ UsbTransferCallback callback;
+};
+
+UsbDeviceHandleImpl::Transfer::Transfer()
+ : transfer_type(USB_TRANSFER_CONTROL), length(0) {
+}
+
+UsbDeviceHandleImpl::Transfer::~Transfer() {
+}
+
+void UsbDeviceHandleImpl::Transfer::Complete(UsbTransferStatus status,
+ size_t bytes_transferred) {
+ if (task_runner->RunsTasksOnCurrentThread()) {
+ callback.Run(status, buffer, bytes_transferred);
+ } else {
+ task_runner->PostTask(
+ FROM_HERE, base::Bind(callback, status, buffer, bytes_transferred));
+ }
+}
+
+UsbDeviceHandleImpl::UsbDeviceHandleImpl(
+ scoped_refptr<UsbContext> context,
+ UsbDeviceImpl* device,
+ PlatformUsbDeviceHandle handle,
+ scoped_refptr<UsbConfigDescriptor> interfaces)
+ : device_(device),
+ handle_(handle),
+ interfaces_(interfaces),
+ context_(context),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+ DCHECK(handle) << "Cannot create device with NULL handle.";
+ DCHECK(interfaces_.get()) << "Unable to list interfaces";
+}
+
+UsbDeviceHandleImpl::~UsbDeviceHandleImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ libusb_close(handle_);
+ handle_ = NULL;
+}
+
+scoped_refptr<UsbDevice> UsbDeviceHandleImpl::GetDevice() const {
+ return static_cast<UsbDevice*>(device_);
+}
+
+void UsbDeviceHandleImpl::Close() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (device_)
+ device_->Close(this);
+}
+
+/* static */
+void LIBUSB_CALL UsbDeviceHandleImpl::PlatformTransferCallback(
+ PlatformUsbTransferHandle transfer) {
+ UsbDeviceHandleImpl* device_handle =
+ reinterpret_cast<UsbDeviceHandleImpl*>(transfer->user_data);
+ device_handle->task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &UsbDeviceHandleImpl::CompleteTransfer, device_handle, transfer));
+}
+
+void UsbDeviceHandleImpl::CompleteTransfer(PlatformUsbTransferHandle handle) {
+ DCHECK(ContainsKey(transfers_, handle)) << "Missing transfer completed";
+
+ Transfer transfer = transfers_[handle];
+ transfers_.erase(handle);
+
+ DCHECK_GE(handle->actual_length, 0) << "Negative actual length received";
+ size_t actual_length =
+ static_cast<size_t>(std::max(handle->actual_length, 0));
+
+ DCHECK(transfer.length >= actual_length)
+ << "data too big for our buffer (libusb failure?)";
+
+ switch (transfer.transfer_type) {
+ case USB_TRANSFER_CONTROL:
+ // If the transfer is a control transfer we do not expose the control
+ // setup header to the caller. This logic strips off the header if
+ // present before invoking the callback provided with the transfer.
+ if (actual_length > 0) {
+ CHECK(transfer.length >= LIBUSB_CONTROL_SETUP_SIZE)
+ << "buffer was not correctly set: too small for the control header";
+
+ if (transfer.length >= (LIBUSB_CONTROL_SETUP_SIZE + actual_length)) {
+ // If the payload is zero bytes long, pad out the allocated buffer
+ // size to one byte so that an IOBuffer of that size can be allocated.
+ scoped_refptr<net::IOBuffer> resized_buffer =
+ new net::IOBuffer(static_cast<int>(
+ std::max(actual_length, static_cast<size_t>(1))));
+ memcpy(resized_buffer->data(),
+ transfer.buffer->data() + LIBUSB_CONTROL_SETUP_SIZE,
+ actual_length);
+ transfer.buffer = resized_buffer;
+ }
+ }
+ break;
+
+ case USB_TRANSFER_ISOCHRONOUS:
+ // Isochronous replies might carry data in the different isoc packets even
+ // if the transfer actual_data value is zero. Furthermore, not all of the
+ // received packets might contain data, so we need to calculate how many
+ // data bytes we are effectively providing and pack the results.
+ if (actual_length == 0) {
+ size_t packet_buffer_start = 0;
+ for (int i = 0; i < handle->num_iso_packets; ++i) {
+ PlatformUsbIsoPacketDescriptor packet = &handle->iso_packet_desc[i];
+ if (packet->actual_length > 0) {
+ // We don't need to copy as long as all packets until now provide
+ // all the data the packet can hold.
+ if (actual_length < packet_buffer_start) {
+ CHECK(packet_buffer_start + packet->actual_length <=
+ transfer.length);
+ memmove(transfer.buffer->data() + actual_length,
+ transfer.buffer->data() + packet_buffer_start,
+ packet->actual_length);
+ }
+ actual_length += packet->actual_length;
+ }
+
+ packet_buffer_start += packet->length;
+ }
+ }
+ break;
+
+ case USB_TRANSFER_BULK:
+ case USB_TRANSFER_INTERRUPT:
+ break;
+
+ default:
+ NOTREACHED() << "Invalid usb transfer type";
+ break;
+ }
+
+ transfer.Complete(ConvertTransferStatus(handle->status), actual_length);
+ libusb_free_transfer(handle);
+
+ // Must release interface first before actually delete this.
+ transfer.claimed_interface = NULL;
+}
+
+bool UsbDeviceHandleImpl::ClaimInterface(const int interface_number) {
+ 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;
+ }
+ return false;
+}
+
+bool UsbDeviceHandleImpl::ReleaseInterface(const int interface_number) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!device_)
+ return false;
+ if (!ContainsKey(claimed_interfaces_, interface_number))
+ return false;
+
+ // Cancel all the transfers on that interface.
+ InterfaceClaimer* interface_claimer =
+ claimed_interfaces_[interface_number].get();
+ for (TransferMap::iterator it = transfers_.begin(); it != transfers_.end();
+ ++it) {
+ if (it->second.claimed_interface.get() == interface_claimer)
+ libusb_cancel_transfer(it->first);
+ }
+ claimed_interfaces_.erase(interface_number);
+
+ RefreshEndpointMap();
+ return true;
+}
+
+bool UsbDeviceHandleImpl::SetInterfaceAlternateSetting(
+ const int interface_number,
+ const int alternate_setting) {
+ 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();
+ } else {
+ VLOG(1) << "Failed to set interface (" << interface_number << ", "
+ << alternate_setting
+ << "): " << ConvertPlatformUsbErrorToString(rv);
+ }
+ return rv == LIBUSB_SUCCESS;
+}
+
+bool UsbDeviceHandleImpl::ResetDevice() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!device_)
+ return false;
+
+ const int rv = libusb_reset_device(handle_);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to reset device: "
+ << ConvertPlatformUsbErrorToString(rv);
+ }
+ return rv == LIBUSB_SUCCESS;
+}
+
+bool UsbDeviceHandleImpl::GetSupportedLanguages() {
+ if (!languages_.empty()) {
+ return true;
+ }
+
+ // 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) {
+ VLOG(1) << "Failed to get list of supported languages: "
+ << ConvertPlatformUsbErrorToString(size);
+ return false;
+ } else if (size < 2) {
+ VLOG(1) << "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) {
+ VLOG(1) << "String descriptor zero size mismatch: " << (languages[0] & 0xff)
+ << " != " << size;
+ return false;
+ } else if ((languages[0] >> 8) != LIBUSB_DT_STRING) {
+ VLOG(1) << "String descriptor zero is not a string descriptor.";
+ return false;
+ }
+
+ languages_.assign(languages[1], languages[(size - 2) / 2]);
+ return true;
+}
+
+bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id,
+ base::string16* string) {
+ if (!GetSupportedLanguages()) {
+ return false;
+ }
+
+ 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) {
+ VLOG(1) << "Failed to get string descriptor " << string_id << " (langid "
+ << language_id << "): " << ConvertPlatformUsbErrorToString(size);
+ continue;
+ } else if (size < 2) {
+ VLOG(1) << "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) {
+ VLOG(1) << "String descriptor " << string_id << " (langid " << language_id
+ << ") size mismatch: " << (text[0] & 0xff) << " != " << size;
+ continue;
+ } else if ((text[0] >> 8) != LIBUSB_DT_STRING) {
+ VLOG(1) << "String descriptor " << string_id << " (langid " << language_id
+ << ") is not a string descriptor.";
+ continue;
+ }
+
+ *string = base::string16(text + 1, (size - 2) / 2);
+ strings_[string_id] = *string;
+ return true;
+ }
+
+ return false;
+}
+
+bool UsbDeviceHandleImpl::GetManufacturer(base::string16* manufacturer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ PlatformUsbDevice device = libusb_get_device(handle_);
+ libusb_device_descriptor desc;
+
+ // This is a non-blocking call as libusb has the descriptor in memory.
+ const int rv = libusb_get_device_descriptor(device, &desc);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to read device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return false;
+ }
+
+ if (desc.iManufacturer == 0) {
+ return false;
+ }
+
+ return GetStringDescriptor(desc.iManufacturer, manufacturer);
+}
+
+bool UsbDeviceHandleImpl::GetProduct(base::string16* product) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ PlatformUsbDevice device = libusb_get_device(handle_);
+ libusb_device_descriptor desc;
+
+ // This is a non-blocking call as libusb has the descriptor in memory.
+ const int rv = libusb_get_device_descriptor(device, &desc);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to read device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return false;
+ }
+
+ if (desc.iProduct == 0) {
+ return false;
+ }
+
+ return GetStringDescriptor(desc.iProduct, product);
+}
+
+bool UsbDeviceHandleImpl::GetSerial(base::string16* serial) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ PlatformUsbDevice device = libusb_get_device(handle_);
+ libusb_device_descriptor desc;
+
+ // This is a non-blocking call as libusb has the descriptor in memory.
+ const int rv = libusb_get_device_descriptor(device, &desc);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to read device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return false;
+ }
+
+ if (desc.iSerialNumber == 0) {
+ return false;
+ }
+
+ return GetStringDescriptor(desc.iSerialNumber, serial);
+}
+
+void UsbDeviceHandleImpl::ControlTransfer(
+ const UsbEndpointDirection direction,
+ const TransferRequestType request_type,
+ const TransferRecipient recipient,
+ const uint8 request,
+ const uint16 value,
+ const uint16 index,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) {
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
+ const size_t resized_length = LIBUSB_CONTROL_SETUP_SIZE + length;
+ scoped_refptr<net::IOBuffer> resized_buffer(
+ new net::IOBufferWithSize(static_cast<int>(resized_length)));
+ if (!resized_buffer.get()) {
+ callback.Run(USB_TRANSFER_ERROR, buffer, 0);
+ return;
+ }
+ memcpy(resized_buffer->data() + LIBUSB_CONTROL_SETUP_SIZE,
+ buffer->data(),
+ static_cast<int>(length));
+
+ PlatformUsbTransferHandle const transfer = libusb_alloc_transfer(0);
+ const uint8 converted_type =
+ CreateRequestType(direction, request_type, recipient);
+ libusb_fill_control_setup(reinterpret_cast<uint8*>(resized_buffer->data()),
+ converted_type,
+ request,
+ value,
+ index,
+ static_cast<int16>(length));
+ libusb_fill_control_transfer(transfer,
+ handle_,
+ reinterpret_cast<uint8*>(resized_buffer->data()),
+ &UsbDeviceHandleImpl::PlatformTransferCallback,
+ this,
+ timeout);
+
+ PostOrSubmitTransfer(
+ transfer, USB_TRANSFER_CONTROL, resized_buffer, resized_length, callback);
+}
+
+void UsbDeviceHandleImpl::BulkTransfer(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) {
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
+ PlatformUsbTransferHandle const transfer = libusb_alloc_transfer(0);
+ const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint;
+ libusb_fill_bulk_transfer(transfer,
+ handle_,
+ new_endpoint,
+ reinterpret_cast<uint8*>(buffer->data()),
+ static_cast<int>(length),
+ &UsbDeviceHandleImpl::PlatformTransferCallback,
+ this,
+ timeout);
+
+ PostOrSubmitTransfer(transfer, USB_TRANSFER_BULK, buffer, length, callback);
+}
+
+void UsbDeviceHandleImpl::InterruptTransfer(
+ const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) {
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
+ PlatformUsbTransferHandle const transfer = libusb_alloc_transfer(0);
+ const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint;
+ libusb_fill_interrupt_transfer(transfer,
+ handle_,
+ new_endpoint,
+ reinterpret_cast<uint8*>(buffer->data()),
+ static_cast<int>(length),
+ &UsbDeviceHandleImpl::PlatformTransferCallback,
+ this,
+ timeout);
+
+ PostOrSubmitTransfer(
+ transfer, USB_TRANSFER_INTERRUPT, buffer, length, callback);
+}
+
+void UsbDeviceHandleImpl::IsochronousTransfer(
+ 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) {
+ if (!device_) {
+ callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0);
+ return;
+ }
+
+ const uint64 total_length = packets * packet_length;
+ CHECK(packets <= length && total_length <= length)
+ << "transfer length is too small";
+
+ PlatformUsbTransferHandle const transfer = libusb_alloc_transfer(packets);
+ const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint;
+ libusb_fill_iso_transfer(transfer,
+ handle_,
+ new_endpoint,
+ reinterpret_cast<uint8*>(buffer->data()),
+ static_cast<int>(length),
+ packets,
+ &UsbDeviceHandleImpl::PlatformTransferCallback,
+ this,
+ timeout);
+ libusb_set_iso_packet_lengths(transfer, packet_length);
+
+ PostOrSubmitTransfer(
+ transfer, USB_TRANSFER_ISOCHRONOUS, buffer, length, callback);
+}
+
+void UsbDeviceHandleImpl::RefreshEndpointMap() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ endpoint_map_.clear();
+ for (ClaimedInterfaceMap::iterator it = claimed_interfaces_.begin();
+ it != claimed_interfaces_.end();
+ ++it) {
+ scoped_refptr<const UsbInterfaceAltSettingDescriptor> interface_desc =
+ interfaces_->GetInterface(it->first)
+ ->GetAltSetting(it->second->alternate_setting());
+ for (size_t i = 0; i < interface_desc->GetNumEndpoints(); i++) {
+ scoped_refptr<const UsbEndpointDescriptor> endpoint =
+ interface_desc->GetEndpoint(i);
+ endpoint_map_[endpoint->GetAddress()] = it->first;
+ }
+ }
+}
+
+scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer>
+UsbDeviceHandleImpl::GetClaimedInterfaceForEndpoint(unsigned char endpoint) {
+ unsigned char address = endpoint & LIBUSB_ENDPOINT_ADDRESS_MASK;
+ if (ContainsKey(endpoint_map_, address))
+ return claimed_interfaces_[endpoint_map_[address]];
+ return NULL;
+}
+
+void UsbDeviceHandleImpl::PostOrSubmitTransfer(
+ PlatformUsbTransferHandle transfer,
+ UsbTransferType transfer_type,
+ net::IOBuffer* buffer,
+ size_t length,
+ const UsbTransferCallback& callback) {
+ if (task_runner_->RunsTasksOnCurrentThread()) {
+ SubmitTransfer(transfer,
+ transfer_type,
+ buffer,
+ length,
+ base::ThreadTaskRunnerHandle::Get(),
+ callback);
+ } else {
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::SubmitTransfer,
+ this,
+ transfer,
+ transfer_type,
+ make_scoped_refptr(buffer),
+ length,
+ base::ThreadTaskRunnerHandle::Get(),
+ callback));
+ }
+}
+
+void UsbDeviceHandleImpl::SubmitTransfer(
+ PlatformUsbTransferHandle handle,
+ UsbTransferType transfer_type,
+ net::IOBuffer* buffer,
+ const size_t length,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const UsbTransferCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ Transfer transfer;
+ transfer.transfer_type = transfer_type;
+ transfer.buffer = buffer;
+ transfer.length = length;
+ transfer.callback = callback;
+ transfer.task_runner = task_runner;
+
+ if (!device_) {
+ transfer.Complete(USB_TRANSFER_DISCONNECT, 0);
+ return;
+ }
+
+ // It's OK for this method to return NULL. libusb_submit_transfer will fail if
+ // it requires an interface we didn't claim.
+ transfer.claimed_interface = GetClaimedInterfaceForEndpoint(handle->endpoint);
+
+ const int rv = libusb_submit_transfer(handle);
+ if (rv == LIBUSB_SUCCESS) {
+ transfers_[handle] = transfer;
+ } else {
+ VLOG(1) << "Failed to submit transfer: "
+ << ConvertPlatformUsbErrorToString(rv);
+ transfer.Complete(USB_TRANSFER_ERROR, 0);
+ }
+}
+
+void UsbDeviceHandleImpl::InternalClose() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!device_)
+ return;
+
+ // Cancel all the transfers.
+ for (TransferMap::iterator it = transfers_.begin(); it != transfers_.end();
+ ++it) {
+ // The callback will be called some time later.
+ libusb_cancel_transfer(it->first);
+ }
+
+ // Attempt-release all the interfaces.
+ // It will be retained until the transfer cancellation is finished.
+ claimed_interfaces_.clear();
+
+ // Cannot close device handle here. Need to wait for libusb_cancel_transfer to
+ // finish.
+ device_ = NULL;
+}
+
+} // namespace device
diff --git a/device/usb/usb_device_handle_impl.h b/device/usb/usb_device_handle_impl.h
new file mode 100644
index 0000000..e4e5163
--- /dev/null
+++ b/device/usb/usb_device_handle_impl.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_DEVICE_HANDLE_IMPL_H_
+#define DEVICE_USB_USB_DEVICE_HANDLE_IMPL_H_
+
+#include <map>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
+#include "device/usb/usb_device_handle.h"
+#include "device/usb/usb_interface.h"
+#include "net/base/io_buffer.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace device {
+
+class UsbContext;
+class UsbConfigDescriptor;
+class UsbDeviceImpl;
+
+typedef libusb_device_handle* PlatformUsbDeviceHandle;
+typedef libusb_iso_packet_descriptor* PlatformUsbIsoPacketDescriptor;
+typedef libusb_transfer* PlatformUsbTransferHandle;
+
+// UsbDeviceHandle class provides basic I/O related functionalities.
+class UsbDeviceHandleImpl : public UsbDeviceHandle {
+ public:
+ virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual bool ClaimInterface(const int interface_number) OVERRIDE;
+ virtual bool ReleaseInterface(const int interface_number) OVERRIDE;
+ virtual bool SetInterfaceAlternateSetting(
+ const int interface_number,
+ const int alternate_setting) OVERRIDE;
+ virtual bool ResetDevice() OVERRIDE;
+ virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE;
+ virtual bool GetProduct(base::string16* product) OVERRIDE;
+ virtual bool GetSerial(base::string16* serial) OVERRIDE;
+ virtual void ControlTransfer(const UsbEndpointDirection direction,
+ const TransferRequestType request_type,
+ const TransferRecipient recipient,
+ const uint8 request,
+ const uint16 value,
+ const uint16 index,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) OVERRIDE;
+
+ virtual void BulkTransfer(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) OVERRIDE;
+
+ virtual void InterruptTransfer(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback) OVERRIDE;
+
+ virtual void IsochronousTransfer(
+ 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) OVERRIDE;
+
+ PlatformUsbDeviceHandle handle() const { return handle_; }
+
+ protected:
+ friend class UsbDeviceImpl;
+
+ // This constructor is called by UsbDevice.
+ UsbDeviceHandleImpl(scoped_refptr<UsbContext> context,
+ UsbDeviceImpl* device,
+ PlatformUsbDeviceHandle handle,
+ scoped_refptr<UsbConfigDescriptor> interfaces);
+
+ virtual ~UsbDeviceHandleImpl();
+
+ private:
+ class InterfaceClaimer;
+ struct Transfer;
+
+ // Refresh endpoint_map_ after ClaimInterface, ReleaseInterface and
+ // SetInterfaceAlternateSetting.
+ void RefreshEndpointMap();
+
+ // Look up the claimed interface by endpoint. Return NULL if the interface
+ // of the endpoint is not found.
+ 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(PlatformUsbTransferHandle handle,
+ UsbTransferType transfer_type,
+ net::IOBuffer* buffer,
+ size_t length,
+ const UsbTransferCallback& 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(PlatformUsbTransferHandle handle,
+ UsbTransferType transfer_type,
+ net::IOBuffer* buffer,
+ const size_t length,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const UsbTransferCallback& callback);
+
+ static void LIBUSB_CALL
+ PlatformTransferCallback(PlatformUsbTransferHandle handle);
+
+ // Invokes the callbacks associated with a given transfer, and removes it from
+ // the in-flight transfer set.
+ void CompleteTransfer(PlatformUsbTransferHandle transfer);
+
+ bool GetSupportedLanguages();
+ bool GetStringDescriptor(uint8 string_id, base::string16* string);
+
+ // Informs the object to drop internal references.
+ void InternalClose();
+
+ UsbDeviceImpl* device_;
+
+ PlatformUsbDeviceHandle handle_;
+
+ scoped_refptr<UsbConfigDescriptor> interfaces_;
+
+ std::vector<uint16> languages_;
+ std::map<uint8, base::string16> strings_;
+
+ typedef std::map<int, scoped_refptr<InterfaceClaimer> > ClaimedInterfaceMap;
+ ClaimedInterfaceMap claimed_interfaces_;
+
+ typedef std::map<PlatformUsbTransferHandle, Transfer> TransferMap;
+ TransferMap transfers_;
+
+ // A map from endpoints to interfaces
+ typedef std::map<int, int> EndpointMap;
+ EndpointMap endpoint_map_;
+
+ // Retain the UsbContext so that the platform context will not be destroyed
+ // before this handle.
+ scoped_refptr<UsbContext> context_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbDeviceHandleImpl);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_DEVICE_HANDLE_IMPL_H_
diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc
new file mode 100644
index 0000000..03c8ee0
--- /dev/null
+++ b/device/usb/usb_device_impl.cc
@@ -0,0 +1,154 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_device_impl.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "device/usb/usb_context.h"
+#include "device/usb/usb_device_handle_impl.h"
+#include "device/usb/usb_error.h"
+#include "device/usb/usb_interface_impl.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+#if defined(OS_CHROMEOS)
+#include "base/sys_info.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/permission_broker_client.h"
+#endif // defined(OS_CHROMEOS)
+
+namespace {
+
+#if defined(OS_CHROMEOS)
+void OnRequestUsbAccessReplied(
+ 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)
+
+} // namespace
+
+namespace device {
+
+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),
+ platform_device_(platform_device),
+ context_(context),
+ ui_task_runner_(ui_task_runner) {
+ CHECK(platform_device) << "platform_device cannot be NULL";
+ libusb_ref_device(platform_device);
+}
+
+UsbDeviceImpl::~UsbDeviceImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (HandlesVector::iterator it = handles_.begin(); it != handles_.end();
+ ++it) {
+ (*it)->InternalClose();
+ }
+ STLClearObject(&handles_);
+ libusb_unref_device(platform_device_);
+}
+
+#if defined(OS_CHROMEOS)
+
+void UsbDeviceImpl::RequestUsbAccess(
+ int interface_id,
+ const base::Callback<void(bool success)>& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
+ // use permission broker.
+ if (base::SysInfo::IsRunningOnChromeOS()) {
+ chromeos::PermissionBrokerClient* client =
+ chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
+ DCHECK(client) << "Could not get permission broker client.";
+ if (!client) {
+ callback.Run(false);
+ return;
+ }
+
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&chromeos::PermissionBrokerClient::RequestUsbAccess,
+ base::Unretained(client),
+ vendor_id(),
+ product_id(),
+ interface_id,
+ base::Bind(&OnRequestUsbAccessReplied,
+ base::ThreadTaskRunnerHandle::Get(),
+ callback)));
+ }
+}
+
+#endif
+
+scoped_refptr<UsbDeviceHandle> UsbDeviceImpl::Open() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ PlatformUsbDeviceHandle handle;
+ const int rv = libusb_open(platform_device_, &handle);
+ if (LIBUSB_SUCCESS == rv) {
+ scoped_refptr<UsbConfigDescriptor> interfaces = ListInterfaces();
+ if (!interfaces.get())
+ return NULL;
+ scoped_refptr<UsbDeviceHandleImpl> device_handle =
+ new UsbDeviceHandleImpl(context_, this, handle, interfaces);
+ handles_.push_back(device_handle);
+ return device_handle;
+ } else {
+ VLOG(1) << "Failed to open device: " << ConvertPlatformUsbErrorToString(rv);
+ return NULL;
+ }
+}
+
+bool UsbDeviceImpl::Close(scoped_refptr<UsbDeviceHandle> handle) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ for (HandlesVector::iterator it = handles_.begin(); it != handles_.end();
+ ++it) {
+ if (it->get() == handle.get()) {
+ (*it)->InternalClose();
+ handles_.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+scoped_refptr<UsbConfigDescriptor> UsbDeviceImpl::ListInterfaces() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ PlatformUsbConfigDescriptor platform_config;
+ const int rv =
+ libusb_get_active_config_descriptor(platform_device_, &platform_config);
+ if (rv == LIBUSB_SUCCESS) {
+ return new UsbConfigDescriptorImpl(platform_config);
+ } else {
+ VLOG(1) << "Failed to get config descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return NULL;
+ }
+}
+
+void UsbDeviceImpl::OnDisconnect() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ HandlesVector handles;
+ swap(handles, handles_);
+ for (HandlesVector::iterator it = handles.begin(); it != handles.end(); ++it)
+ (*it)->InternalClose();
+}
+
+} // namespace device
diff --git a/device/usb/usb_device_impl.h b/device/usb/usb_device_impl.h
new file mode 100644
index 0000000..1ece2e0
--- /dev/null
+++ b/device/usb/usb_device_impl.h
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_DEVICE_IMPL_H_
+#define DEVICE_USB_USB_DEVICE_IMPL_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/threading/thread_checker.h"
+#include "device/usb/usb_device.h"
+
+struct libusb_device;
+struct libusb_config_descriptor;
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace device {
+
+class UsbDeviceHandleImpl;
+class UsbContext;
+
+typedef libusb_device* PlatformUsbDevice;
+typedef libusb_config_descriptor* PlatformUsbConfigDescriptor;
+
+class UsbDeviceImpl : public UsbDevice {
+ public:
+// UsbDevice implementation:
+#if defined(OS_CHROMEOS)
+ virtual void RequestUsbAccess(
+ int interface_id,
+ const base::Callback<void(bool success)>& callback) OVERRIDE;
+#endif // OS_CHROMEOS
+ virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE;
+ virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE;
+ virtual scoped_refptr<UsbConfigDescriptor> ListInterfaces() OVERRIDE;
+
+ protected:
+ friend class UsbServiceImpl;
+
+ // 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);
+
+ virtual ~UsbDeviceImpl();
+
+ // Called only be UsbService.
+ void OnDisconnect();
+
+ private:
+ base::ThreadChecker thread_checker_;
+ PlatformUsbDevice platform_device_;
+
+ // Retain the context so that it will not be released before UsbDevice.
+ scoped_refptr<UsbContext> context_;
+
+ // Opened handles.
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_DEVICE_IMPL_H_
diff --git a/device/usb/usb_error.cc b/device/usb/usb_error.cc
new file mode 100644
index 0000000..9fadb16
--- /dev/null
+++ b/device/usb/usb_error.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_error.h"
+
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+std::string ConvertPlatformUsbErrorToString(int errcode) {
+ return libusb_strerror(static_cast<libusb_error>(errcode));
+}
+
+} // namespace device
diff --git a/device/usb/usb_error.h b/device/usb/usb_error.h
new file mode 100644
index 0000000..590c450
--- /dev/null
+++ b/device/usb/usb_error.h
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_ERROR_H_
+#define DEVICE_USB_USB_ERROR_H_
+
+#include <string>
+
+namespace device {
+
+// A number of libusb functions which return a member of enum libusb_error
+// return an int instead so for convenience this function takes an int.
+std::string ConvertPlatformUsbErrorToString(int errcode);
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_ERROR_H_
diff --git a/device/usb/usb_interface.h b/device/usb/usb_interface.h
new file mode 100644
index 0000000..9285718
--- /dev/null
+++ b/device/usb/usb_interface.h
@@ -0,0 +1,112 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_INTERFACE_H_
+#define DEVICE_USB_USB_INTERFACE_H_
+
+#include "base/memory/ref_counted.h"
+
+namespace device {
+
+enum UsbTransferType {
+ USB_TRANSFER_CONTROL = 0,
+ USB_TRANSFER_ISOCHRONOUS,
+ USB_TRANSFER_BULK,
+ USB_TRANSFER_INTERRUPT,
+};
+
+enum UsbEndpointDirection {
+ USB_DIRECTION_INBOUND = 0,
+ USB_DIRECTION_OUTBOUND,
+};
+
+enum UsbSynchronizationType {
+ USB_SYNCHRONIZATION_NONE = 0,
+ USB_SYNCHRONIZATION_ASYNCHRONOUS,
+ USB_SYNCHRONIZATION_ADAPTIVE,
+ USB_SYNCHRONIZATION_SYNCHRONOUS,
+};
+
+enum UsbUsageType {
+ USB_USAGE_DATA = 0,
+ USB_USAGE_FEEDBACK,
+ USB_USAGE_EXPLICIT_FEEDBACK
+};
+
+class UsbEndpointDescriptor
+ : public base::RefCounted<const UsbEndpointDescriptor> {
+ public:
+ virtual int GetAddress() const = 0;
+ virtual UsbEndpointDirection GetDirection() const = 0;
+ virtual int GetMaximumPacketSize() const = 0;
+ virtual UsbSynchronizationType GetSynchronizationType() const = 0;
+ virtual UsbTransferType GetTransferType() const = 0;
+ virtual UsbUsageType GetUsageType() const = 0;
+ virtual int GetPollingInterval() const = 0;
+
+ protected:
+ friend class base::RefCounted<const UsbEndpointDescriptor>;
+
+ UsbEndpointDescriptor() {};
+ virtual ~UsbEndpointDescriptor() {};
+
+ DISALLOW_COPY_AND_ASSIGN(UsbEndpointDescriptor);
+};
+
+class UsbInterfaceAltSettingDescriptor
+ : public base::RefCounted<const UsbInterfaceAltSettingDescriptor> {
+ public:
+ virtual size_t GetNumEndpoints() const = 0;
+ virtual scoped_refptr<const UsbEndpointDescriptor> GetEndpoint(
+ size_t index) const = 0;
+
+ virtual int GetInterfaceNumber() const = 0;
+ virtual int GetAlternateSetting() const = 0;
+ virtual int GetInterfaceClass() const = 0;
+ virtual int GetInterfaceSubclass() const = 0;
+ virtual int GetInterfaceProtocol() const = 0;
+
+ protected:
+ friend class base::RefCounted<const UsbInterfaceAltSettingDescriptor>;
+
+ UsbInterfaceAltSettingDescriptor() {};
+ virtual ~UsbInterfaceAltSettingDescriptor() {};
+
+ DISALLOW_COPY_AND_ASSIGN(UsbInterfaceAltSettingDescriptor);
+};
+
+class UsbInterfaceDescriptor
+ : public base::RefCounted<const UsbInterfaceDescriptor> {
+ public:
+ virtual size_t GetNumAltSettings() const = 0;
+ virtual scoped_refptr<const UsbInterfaceAltSettingDescriptor> GetAltSetting(
+ size_t index) const = 0;
+
+ protected:
+ friend class base::RefCounted<const UsbInterfaceDescriptor>;
+
+ UsbInterfaceDescriptor() {};
+ virtual ~UsbInterfaceDescriptor() {};
+
+ DISALLOW_COPY_AND_ASSIGN(UsbInterfaceDescriptor);
+};
+
+class UsbConfigDescriptor : public base::RefCounted<UsbConfigDescriptor> {
+ public:
+ virtual size_t GetNumInterfaces() const = 0;
+ virtual scoped_refptr<const UsbInterfaceDescriptor> GetInterface(
+ size_t index) const = 0;
+
+ protected:
+ friend class base::RefCounted<UsbConfigDescriptor>;
+
+ UsbConfigDescriptor() {};
+ virtual ~UsbConfigDescriptor() {};
+
+ DISALLOW_COPY_AND_ASSIGN(UsbConfigDescriptor);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_INTERFACE_H_
diff --git a/device/usb/usb_interface_impl.cc b/device/usb/usb_interface_impl.cc
new file mode 100644
index 0000000..af3d0e3
--- /dev/null
+++ b/device/usb/usb_interface_impl.cc
@@ -0,0 +1,168 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_interface_impl.h"
+
+#include "base/logging.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+UsbEndpointDescriptorImpl::UsbEndpointDescriptorImpl(
+ scoped_refptr<const UsbConfigDescriptor> config,
+ PlatformUsbEndpointDescriptor descriptor)
+ : config_(config), descriptor_(descriptor) {
+}
+
+UsbEndpointDescriptorImpl::~UsbEndpointDescriptorImpl() {
+}
+
+int UsbEndpointDescriptorImpl::GetAddress() const {
+ return descriptor_->bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK;
+}
+
+UsbEndpointDirection UsbEndpointDescriptorImpl::GetDirection() const {
+ switch (descriptor_->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) {
+ case LIBUSB_ENDPOINT_IN:
+ return USB_DIRECTION_INBOUND;
+ case LIBUSB_ENDPOINT_OUT:
+ return USB_DIRECTION_OUTBOUND;
+ default:
+ NOTREACHED();
+ return USB_DIRECTION_INBOUND;
+ }
+}
+
+int UsbEndpointDescriptorImpl::GetMaximumPacketSize() const {
+ return descriptor_->wMaxPacketSize;
+}
+
+UsbSynchronizationType UsbEndpointDescriptorImpl::GetSynchronizationType()
+ const {
+ switch (descriptor_->bmAttributes & LIBUSB_ISO_SYNC_TYPE_MASK) {
+ case LIBUSB_ISO_SYNC_TYPE_NONE:
+ return USB_SYNCHRONIZATION_NONE;
+ case LIBUSB_ISO_SYNC_TYPE_ASYNC:
+ return USB_SYNCHRONIZATION_ASYNCHRONOUS;
+ case LIBUSB_ISO_SYNC_TYPE_ADAPTIVE:
+ return USB_SYNCHRONIZATION_ADAPTIVE;
+ case LIBUSB_ISO_SYNC_TYPE_SYNC:
+ return USB_SYNCHRONIZATION_SYNCHRONOUS;
+ default:
+ NOTREACHED();
+ return USB_SYNCHRONIZATION_NONE;
+ }
+}
+
+UsbTransferType UsbEndpointDescriptorImpl::GetTransferType() const {
+ switch (descriptor_->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ return USB_TRANSFER_CONTROL;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ return USB_TRANSFER_ISOCHRONOUS;
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ return USB_TRANSFER_BULK;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ return USB_TRANSFER_INTERRUPT;
+ default:
+ NOTREACHED();
+ return USB_TRANSFER_CONTROL;
+ }
+}
+
+UsbUsageType UsbEndpointDescriptorImpl::GetUsageType() const {
+ switch (descriptor_->bmAttributes & LIBUSB_ISO_USAGE_TYPE_MASK) {
+ case LIBUSB_ISO_USAGE_TYPE_DATA:
+ return USB_USAGE_DATA;
+ case LIBUSB_ISO_USAGE_TYPE_FEEDBACK:
+ return USB_USAGE_FEEDBACK;
+ case LIBUSB_ISO_USAGE_TYPE_IMPLICIT:
+ return USB_USAGE_EXPLICIT_FEEDBACK;
+ default:
+ NOTREACHED();
+ return USB_USAGE_DATA;
+ }
+}
+
+int UsbEndpointDescriptorImpl::GetPollingInterval() const {
+ return descriptor_->bInterval;
+}
+
+UsbInterfaceAltSettingDescriptorImpl::UsbInterfaceAltSettingDescriptorImpl(
+ scoped_refptr<const UsbConfigDescriptor> config,
+ PlatformUsbInterfaceDescriptor descriptor)
+ : config_(config), descriptor_(descriptor) {
+}
+
+UsbInterfaceAltSettingDescriptorImpl::~UsbInterfaceAltSettingDescriptorImpl() {
+}
+
+size_t UsbInterfaceAltSettingDescriptorImpl::GetNumEndpoints() const {
+ return descriptor_->bNumEndpoints;
+}
+
+scoped_refptr<const UsbEndpointDescriptor>
+UsbInterfaceAltSettingDescriptorImpl::GetEndpoint(size_t index) const {
+ return new UsbEndpointDescriptorImpl(config_, &descriptor_->endpoint[index]);
+}
+
+int UsbInterfaceAltSettingDescriptorImpl::GetInterfaceNumber() const {
+ return descriptor_->bInterfaceNumber;
+}
+
+int UsbInterfaceAltSettingDescriptorImpl::GetAlternateSetting() const {
+ return descriptor_->bAlternateSetting;
+}
+
+int UsbInterfaceAltSettingDescriptorImpl::GetInterfaceClass() const {
+ return descriptor_->bInterfaceClass;
+}
+
+int UsbInterfaceAltSettingDescriptorImpl::GetInterfaceSubclass() const {
+ return descriptor_->bInterfaceSubClass;
+}
+
+int UsbInterfaceAltSettingDescriptorImpl::GetInterfaceProtocol() const {
+ return descriptor_->bInterfaceProtocol;
+}
+
+UsbInterfaceDescriptorImpl::UsbInterfaceDescriptorImpl(
+ scoped_refptr<const UsbConfigDescriptor> config,
+ PlatformUsbInterface usbInterface)
+ : config_(config), interface_(usbInterface) {
+}
+
+UsbInterfaceDescriptorImpl::~UsbInterfaceDescriptorImpl() {
+}
+
+size_t UsbInterfaceDescriptorImpl::GetNumAltSettings() const {
+ return interface_->num_altsetting;
+}
+
+scoped_refptr<const UsbInterfaceAltSettingDescriptor>
+UsbInterfaceDescriptorImpl::GetAltSetting(size_t index) const {
+ return new UsbInterfaceAltSettingDescriptorImpl(
+ config_, &interface_->altsetting[index]);
+}
+
+UsbConfigDescriptorImpl::UsbConfigDescriptorImpl(
+ PlatformUsbConfigDescriptor config)
+ : config_(config) {
+ DCHECK(config);
+}
+
+UsbConfigDescriptorImpl::~UsbConfigDescriptorImpl() {
+ libusb_free_config_descriptor(config_);
+}
+
+size_t UsbConfigDescriptorImpl::GetNumInterfaces() const {
+ return config_->bNumInterfaces;
+}
+
+scoped_refptr<const UsbInterfaceDescriptor>
+UsbConfigDescriptorImpl::GetInterface(size_t index) const {
+ return new UsbInterfaceDescriptorImpl(this, &config_->interface[index]);
+}
+
+} // namespace device
diff --git a/device/usb/usb_interface_impl.h b/device/usb/usb_interface_impl.h
new file mode 100644
index 0000000..0f6b913
--- /dev/null
+++ b/device/usb/usb_interface_impl.h
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_INTERFACE_IMPL_H_
+#define DEVICE_USB_USB_INTERFACE_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "device/usb/usb_interface.h"
+
+struct libusb_config_descriptor;
+struct libusb_endpoint_descriptor;
+struct libusb_interface;
+struct libusb_interface_descriptor;
+
+namespace device {
+
+typedef libusb_config_descriptor* PlatformUsbConfigDescriptor;
+typedef const libusb_endpoint_descriptor* PlatformUsbEndpointDescriptor;
+typedef const libusb_interface* PlatformUsbInterface;
+typedef const libusb_interface_descriptor* PlatformUsbInterfaceDescriptor;
+
+class UsbConfigDescriptorImpl;
+class UsbInterfaceAltSettingDescriptor;
+
+class UsbEndpointDescriptorImpl : public UsbEndpointDescriptor {
+ public:
+ virtual int GetAddress() const OVERRIDE;
+ virtual UsbEndpointDirection GetDirection() const OVERRIDE;
+ virtual int GetMaximumPacketSize() const OVERRIDE;
+ virtual UsbSynchronizationType GetSynchronizationType() const OVERRIDE;
+ virtual UsbTransferType GetTransferType() const OVERRIDE;
+ virtual UsbUsageType GetUsageType() const OVERRIDE;
+ virtual int GetPollingInterval() const OVERRIDE;
+
+ private:
+ friend class base::RefCounted<const UsbEndpointDescriptorImpl>;
+ friend class UsbInterfaceAltSettingDescriptorImpl;
+
+ UsbEndpointDescriptorImpl(scoped_refptr<const UsbConfigDescriptor> config,
+ PlatformUsbEndpointDescriptor descriptor);
+ virtual ~UsbEndpointDescriptorImpl();
+
+ scoped_refptr<const UsbConfigDescriptor> config_;
+ PlatformUsbEndpointDescriptor descriptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbEndpointDescriptorImpl);
+};
+
+class UsbInterfaceAltSettingDescriptorImpl
+ : public UsbInterfaceAltSettingDescriptor {
+ public:
+ virtual size_t GetNumEndpoints() const OVERRIDE;
+ virtual scoped_refptr<const UsbEndpointDescriptor> GetEndpoint(
+ size_t index) const OVERRIDE;
+
+ virtual int GetInterfaceNumber() const OVERRIDE;
+ virtual int GetAlternateSetting() const OVERRIDE;
+ virtual int GetInterfaceClass() const OVERRIDE;
+ virtual int GetInterfaceSubclass() const OVERRIDE;
+ virtual int GetInterfaceProtocol() const OVERRIDE;
+
+ private:
+ friend class UsbInterfaceDescriptorImpl;
+
+ UsbInterfaceAltSettingDescriptorImpl(
+ scoped_refptr<const UsbConfigDescriptor> config,
+ PlatformUsbInterfaceDescriptor descriptor);
+ virtual ~UsbInterfaceAltSettingDescriptorImpl();
+
+ scoped_refptr<const UsbConfigDescriptor> config_;
+ PlatformUsbInterfaceDescriptor descriptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbInterfaceAltSettingDescriptorImpl);
+};
+
+class UsbInterfaceDescriptorImpl : public UsbInterfaceDescriptor {
+ public:
+ virtual size_t GetNumAltSettings() const OVERRIDE;
+ virtual scoped_refptr<const UsbInterfaceAltSettingDescriptor> GetAltSetting(
+ size_t index) const OVERRIDE;
+
+ private:
+ friend class base::RefCounted<const UsbInterfaceDescriptorImpl>;
+ friend class UsbConfigDescriptorImpl;
+
+ UsbInterfaceDescriptorImpl(scoped_refptr<const UsbConfigDescriptor> config,
+ PlatformUsbInterface usbInterface);
+ virtual ~UsbInterfaceDescriptorImpl();
+
+ scoped_refptr<const UsbConfigDescriptor> config_;
+ PlatformUsbInterface interface_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbInterfaceDescriptorImpl);
+};
+
+class UsbConfigDescriptorImpl : public UsbConfigDescriptor {
+ public:
+ virtual size_t GetNumInterfaces() const OVERRIDE;
+ virtual scoped_refptr<const UsbInterfaceDescriptor> GetInterface(
+ size_t index) const OVERRIDE;
+
+ private:
+ friend class base::RefCounted<UsbConfigDescriptor>;
+ friend class UsbDeviceImpl;
+
+ explicit UsbConfigDescriptorImpl(PlatformUsbConfigDescriptor config);
+ virtual ~UsbConfigDescriptorImpl();
+
+ PlatformUsbConfigDescriptor config_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbConfigDescriptorImpl);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_INTERFACE_IMPL_H_
diff --git a/device/usb/usb_service.h b/device/usb/usb_service.h
new file mode 100644
index 0000000..0d49c2f
--- /dev/null
+++ b/device/usb/usb_service.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_USB_USB_SERVICE_H_
+#define DEVICE_USB_USB_SERVICE_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/non_thread_safe.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace device {
+
+class UsbDevice;
+
+// The USB service handles creating and managing an event handler thread that is
+// 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.
+class UsbService : public base::NonThreadSafe {
+ public:
+ // Must be called on a thread with a MessageLoopForIO (for example
+ // BrowserThread::FILE). The UI task runner reference is used to talk to the
+ // PermissionBrokerClient on ChromeOS (UI thread). Returns NULL when
+ // initialization fails.
+ static UsbService* GetInstance(
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+
+ static void SetInstanceForTest(UsbService* instance);
+
+ 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. Must be called on FILE thread.
+ virtual void GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) = 0;
+
+ protected:
+ friend struct base::DefaultDeleter<UsbService>;
+ UsbService() {}
+ virtual ~UsbService() {}
+ DISALLOW_COPY_AND_ASSIGN(UsbService);
+};
+
+} // namespace device
+
+#endif // DEVICE_USB_USB_SERVICE_H_
diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc
new file mode 100644
index 0000000..78846d3
--- /dev/null
+++ b/device/usb/usb_service_impl.cc
@@ -0,0 +1,190 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/usb/usb_service.h"
+
+#include <map>
+#include <set>
+
+#include "base/lazy_instance.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "device/usb/usb_context.h"
+#include "device/usb/usb_device_impl.h"
+#include "device/usb/usb_error.h"
+#include "third_party/libusb/src/libusb/libusb.h"
+
+namespace device {
+
+namespace {
+
+base::LazyInstance<scoped_ptr<UsbService> >::Leaky g_usb_service_instance =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+typedef struct libusb_device* PlatformUsbDevice;
+typedef struct libusb_context* PlatformUsbContext;
+
+class UsbServiceImpl : public UsbService,
+ private base::MessageLoop::DestructionObserver {
+ public:
+ explicit UsbServiceImpl(
+ PlatformUsbContext context,
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+ virtual ~UsbServiceImpl();
+
+ private:
+ // device::UsbService implementation
+ virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) OVERRIDE;
+ virtual void GetDevices(
+ std::vector<scoped_refptr<UsbDevice> >* devices) OVERRIDE;
+
+ // base::MessageLoop::DestructionObserver implementation.
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
+
+ // Enumerate USB devices from OS and Update devices_ map.
+ void RefreshDevices();
+
+ scoped_refptr<UsbContext> context_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
+ // TODO(ikarienator): Figure out a better solution.
+ uint32 next_unique_id_;
+
+ // The map from PlatformUsbDevices to UsbDevices.
+ typedef std::map<PlatformUsbDevice, scoped_refptr<UsbDeviceImpl> > DeviceMap;
+ DeviceMap devices_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl);
+};
+
+scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
+ DCHECK(CalledOnValidThread());
+ RefreshDevices();
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ if (it->second->unique_id() == unique_id)
+ return it->second;
+ }
+ return NULL;
+}
+
+void UsbServiceImpl::GetDevices(
+ std::vector<scoped_refptr<UsbDevice> >* devices) {
+ DCHECK(CalledOnValidThread());
+ STLClearObject(devices);
+ RefreshDevices();
+
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ devices->push_back(it->second);
+ }
+}
+
+void UsbServiceImpl::WillDestroyCurrentMessageLoop() {
+ DCHECK(CalledOnValidThread());
+ g_usb_service_instance.Get().reset(NULL);
+}
+
+UsbServiceImpl::UsbServiceImpl(
+ PlatformUsbContext context,
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+ : context_(new UsbContext(context)),
+ ui_task_runner_(ui_task_runner),
+ next_unique_id_(0) {
+ base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+UsbServiceImpl::~UsbServiceImpl() {
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ it->second->OnDisconnect();
+ }
+}
+
+void UsbServiceImpl::RefreshDevices() {
+ DCHECK(CalledOnValidThread());
+
+ libusb_device** platform_devices = NULL;
+ const ssize_t device_count =
+ libusb_get_device_list(context_->context(), &platform_devices);
+ if (device_count < 0) {
+ VLOG(1) << "Failed to get device list: "
+ << ConvertPlatformUsbErrorToString(device_count);
+ }
+
+ std::set<UsbDevice*> connected_devices;
+ std::vector<PlatformUsbDevice> disconnected_devices;
+
+ // Populates new devices.
+ for (ssize_t i = 0; i < device_count; ++i) {
+ if (!ContainsKey(devices_, platform_devices[i])) {
+ libusb_device_descriptor descriptor;
+ const int rv =
+ libusb_get_device_descriptor(platform_devices[i], &descriptor);
+ // This test is needed. A valid vendor/produce pair is required.
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to get device descriptor: "
+ << ConvertPlatformUsbErrorToString(rv);
+ continue;
+ }
+ UsbDeviceImpl* new_device = new UsbDeviceImpl(context_,
+ ui_task_runner_,
+ platform_devices[i],
+ descriptor.idVendor,
+ descriptor.idProduct,
+ ++next_unique_id_);
+ devices_[platform_devices[i]] = new_device;
+ connected_devices.insert(new_device);
+ } else {
+ connected_devices.insert(devices_[platform_devices[i]].get());
+ }
+ }
+
+ // Find disconnected devices.
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ if (!ContainsKey(connected_devices, it->second)) {
+ disconnected_devices.push_back(it->first);
+ }
+ }
+
+ // Remove disconnected devices from devices_.
+ for (size_t i = 0; i < disconnected_devices.size(); ++i) {
+ // UsbDevice will be destroyed after this. The corresponding
+ // PlatformUsbDevice will be unref'ed during this process.
+ devices_.erase(disconnected_devices[i]);
+ }
+
+ libusb_free_device_list(platform_devices, true);
+}
+
+// static
+UsbService* UsbService::GetInstance(
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+ UsbService* instance = g_usb_service_instance.Get().get();
+ if (!instance) {
+ PlatformUsbContext context = NULL;
+
+ const int rv = libusb_init(&context);
+ if (rv != LIBUSB_SUCCESS) {
+ VLOG(1) << "Failed to initialize libusb: "
+ << ConvertPlatformUsbErrorToString(rv);
+ return NULL;
+ }
+ if (!context)
+ return NULL;
+
+ instance = new UsbServiceImpl(context, ui_task_runner);
+ g_usb_service_instance.Get().reset(instance);
+ }
+ return instance;
+}
+
+// static
+void UsbService::SetInstanceForTest(UsbService* instance) {
+ g_usb_service_instance.Get().reset(instance);
+}
+
+} // namespace device
diff --git a/device/usb/usb_service_unittest.cc b/device/usb/usb_service_unittest.cc
index 1f8b8e2..5436200 100644
--- a/device/usb/usb_service_unittest.cc
+++ b/device/usb/usb_service_unittest.cc
@@ -4,13 +4,11 @@
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
-#include "components/usb_service/usb_device.h"
-#include "components/usb_service/usb_device_handle.h"
#include "device/test/usb_test_gadget.h"
+#include "device/usb/usb_device.h"
+#include "device/usb/usb_device_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
-using ::usb_service::UsbDeviceHandle;
-
namespace device {
namespace {