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