diff options
author | reillyg <reillyg@chromium.org> | 2014-09-03 17:57:56 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-04 01:03:27 +0000 |
commit | d77718de5c3963cf0fe31e59c27c3e2aebfb26cb (patch) | |
tree | 65a996cddf74653fa2b4d1c3d583110eaa8132cf /device | |
parent | 6b0fbe4cbbdd357a17c8a72f92535403fa50515a (diff) | |
download | chromium_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')
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 { |