// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "device/usb/usb_device_handle_impl.h" #include #include #include #include #include "base/bind.h" #include "base/location.h" #include "base/macros.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string16.h" #include "base/synchronization/lock.h" #include "base/thread_task_runner_handle.h" #include "components/device_event_log/device_event_log.h" #include "device/usb/usb_context.h" #include "device/usb/usb_descriptors.h" #include "device/usb/usb_device_impl.h" #include "device/usb/usb_error.h" #include "device/usb/usb_service.h" #include "net/base/io_buffer.h" #include "third_party/libusb/src/libusb/libusb.h" namespace device { typedef libusb_device* PlatformUsbDevice; void HandleTransferCompletion(PlatformUsbTransferHandle transfer); namespace { uint8_t ConvertTransferDirection(UsbEndpointDirection direction) { switch (direction) { case USB_DIRECTION_INBOUND: return LIBUSB_ENDPOINT_IN; case USB_DIRECTION_OUTBOUND: return LIBUSB_ENDPOINT_OUT; default: NOTREACHED(); return LIBUSB_ENDPOINT_IN; } } uint8_t CreateRequestType(UsbEndpointDirection direction, UsbDeviceHandle::TransferRequestType request_type, UsbDeviceHandle::TransferRecipient recipient) { uint8_t result = ConvertTransferDirection(direction); switch (request_type) { case UsbDeviceHandle::STANDARD: result |= LIBUSB_REQUEST_TYPE_STANDARD; break; case UsbDeviceHandle::CLASS: result |= LIBUSB_REQUEST_TYPE_CLASS; break; case UsbDeviceHandle::VENDOR: result |= LIBUSB_REQUEST_TYPE_VENDOR; break; case UsbDeviceHandle::RESERVED: result |= LIBUSB_REQUEST_TYPE_RESERVED; break; } switch (recipient) { case UsbDeviceHandle::DEVICE: result |= LIBUSB_RECIPIENT_DEVICE; break; case UsbDeviceHandle::INTERFACE: result |= LIBUSB_RECIPIENT_INTERFACE; break; case UsbDeviceHandle::ENDPOINT: result |= LIBUSB_RECIPIENT_ENDPOINT; break; case UsbDeviceHandle::OTHER: result |= LIBUSB_RECIPIENT_OTHER; break; } return result; } static UsbTransferStatus ConvertTransferStatus( const libusb_transfer_status status) { switch (status) { case LIBUSB_TRANSFER_COMPLETED: return USB_TRANSFER_COMPLETED; case LIBUSB_TRANSFER_ERROR: return USB_TRANSFER_ERROR; case LIBUSB_TRANSFER_TIMED_OUT: return USB_TRANSFER_TIMEOUT; case LIBUSB_TRANSFER_STALL: return USB_TRANSFER_STALLED; case LIBUSB_TRANSFER_NO_DEVICE: return USB_TRANSFER_DISCONNECT; case LIBUSB_TRANSFER_OVERFLOW: return USB_TRANSFER_OVERFLOW; case LIBUSB_TRANSFER_CANCELLED: return USB_TRANSFER_CANCELLED; default: NOTREACHED(); return USB_TRANSFER_ERROR; } } static void RunTransferCallback( scoped_refptr callback_task_runner, const UsbDeviceHandle::TransferCallback& callback, UsbTransferStatus status, scoped_refptr buffer, size_t result) { if (callback_task_runner->RunsTasksOnCurrentThread()) { callback.Run(status, buffer, result); } else { callback_task_runner->PostTask( FROM_HERE, base::Bind(callback, status, buffer, result)); } } void ReportIsochronousTransferError( scoped_refptr callback_task_runner, const UsbDeviceHandle::IsochronousTransferCallback& callback, const std::vector packet_lengths, UsbTransferStatus status) { std::vector packets( packet_lengths.size()); for (size_t i = 0; i < packet_lengths.size(); ++i) { packets[i].length = packet_lengths[i]; packets[i].transferred_length = 0; packets[i].status = status; } if (callback_task_runner->RunsTasksOnCurrentThread()) { callback.Run(nullptr, packets); } else { callback_task_runner->PostTask(FROM_HERE, base::Bind(callback, nullptr, packets)); } } } // namespace class UsbDeviceHandleImpl::InterfaceClaimer : public base::RefCountedThreadSafe { public: InterfaceClaimer(scoped_refptr handle, int interface_number, scoped_refptr task_runner); int interface_number() const { return interface_number_; } int alternate_setting() const { return alternate_setting_; } void set_alternate_setting(const int alternate_setting) { alternate_setting_ = alternate_setting; } void set_release_callback(const ResultCallback& callback) { release_callback_ = callback; } private: friend class base::RefCountedThreadSafe; ~InterfaceClaimer(); const scoped_refptr handle_; const int interface_number_; int alternate_setting_; const scoped_refptr task_runner_; ResultCallback release_callback_; base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(InterfaceClaimer); }; UsbDeviceHandleImpl::InterfaceClaimer::InterfaceClaimer( scoped_refptr handle, int interface_number, scoped_refptr task_runner) : handle_(handle), interface_number_(interface_number), alternate_setting_(0), task_runner_(task_runner) {} UsbDeviceHandleImpl::InterfaceClaimer::~InterfaceClaimer() { DCHECK(thread_checker_.CalledOnValidThread()); int rc = libusb_release_interface(handle_->handle(), interface_number_); if (rc != LIBUSB_SUCCESS) { USB_LOG(DEBUG) << "Failed to release interface: " << ConvertPlatformUsbErrorToString(rc); } if (!release_callback_.is_null()) { task_runner_->PostTask(FROM_HERE, base::Bind(release_callback_, rc == LIBUSB_SUCCESS)); } } // This inner class owns the underlying libusb_transfer and may outlast // the UsbDeviceHandle that created it. class UsbDeviceHandleImpl::Transfer { public: static scoped_ptr CreateControlTransfer( scoped_refptr device_handle, uint8_t type, uint8_t request, uint16_t value, uint16_t index, uint16_t length, scoped_refptr buffer, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback); static scoped_ptr CreateBulkTransfer( scoped_refptr device_handle, uint8_t endpoint, scoped_refptr buffer, int length, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback); static scoped_ptr CreateInterruptTransfer( scoped_refptr device_handle, uint8_t endpoint, scoped_refptr buffer, int length, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback); static scoped_ptr CreateIsochronousTransfer( scoped_refptr device_handle, uint8_t endpoint, scoped_refptr buffer, size_t length, const std::vector& packet_lengths, unsigned int timeout, scoped_refptr callback_task_runner, const IsochronousTransferCallback& callback); ~Transfer(); void Submit(); void Cancel(); void ProcessCompletion(); void TransferComplete(UsbTransferStatus status, size_t bytes_transferred); const UsbDeviceHandleImpl::InterfaceClaimer* claimed_interface() const { return claimed_interface_.get(); } scoped_refptr callback_task_runner() const { return callback_task_runner_; } private: Transfer(scoped_refptr device_handle, scoped_refptr claimed_interface, UsbTransferType transfer_type, scoped_refptr buffer, size_t length, scoped_refptr callback_task_runner, const TransferCallback& callback); Transfer(scoped_refptr device_handle, scoped_refptr claimed_interface, scoped_refptr buffer, scoped_refptr callback_task_runner, const IsochronousTransferCallback& callback); static void LIBUSB_CALL PlatformCallback(PlatformUsbTransferHandle handle); void IsochronousTransferComplete(); UsbTransferType transfer_type_; scoped_refptr device_handle_; PlatformUsbTransferHandle platform_transfer_ = nullptr; scoped_refptr buffer_; scoped_refptr claimed_interface_; size_t length_; bool cancelled_ = false; scoped_refptr task_runner_; scoped_refptr callback_task_runner_; TransferCallback callback_; IsochronousTransferCallback iso_callback_; }; // static scoped_ptr UsbDeviceHandleImpl::Transfer::CreateControlTransfer( scoped_refptr device_handle, uint8_t type, uint8_t request, uint16_t value, uint16_t index, uint16_t length, scoped_refptr buffer, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback) { scoped_ptr 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_) { USB_LOG(ERROR) << "Failed to allocate control transfer."; return nullptr; } libusb_fill_control_setup(reinterpret_cast(buffer->data()), type, request, value, index, length); libusb_fill_control_transfer(transfer->platform_transfer_, device_handle->handle_, reinterpret_cast(buffer->data()), &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(), timeout); return transfer; } // static scoped_ptr UsbDeviceHandleImpl::Transfer::CreateBulkTransfer( scoped_refptr device_handle, uint8_t endpoint, scoped_refptr buffer, int length, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback) { scoped_ptr 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_) { USB_LOG(ERROR) << "Failed to allocate bulk transfer."; return nullptr; } libusb_fill_bulk_transfer(transfer->platform_transfer_, device_handle->handle_, endpoint, reinterpret_cast(buffer->data()), length, &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(), timeout); return transfer; } // static scoped_ptr UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer( scoped_refptr device_handle, uint8_t endpoint, scoped_refptr buffer, int length, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback) { scoped_ptr 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_) { USB_LOG(ERROR) << "Failed to allocate interrupt transfer."; return nullptr; } libusb_fill_interrupt_transfer( transfer->platform_transfer_, device_handle->handle_, endpoint, reinterpret_cast(buffer->data()), length, &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(), timeout); return transfer; } // static scoped_ptr UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer( scoped_refptr device_handle, uint8_t endpoint, scoped_refptr buffer, size_t length, const std::vector& packet_lengths, unsigned int timeout, scoped_refptr callback_task_runner, const IsochronousTransferCallback& callback) { scoped_ptr transfer(new Transfer( device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint), buffer, callback_task_runner, callback)); int num_packets = static_cast(packet_lengths.size()); transfer->platform_transfer_ = libusb_alloc_transfer(num_packets); if (!transfer->platform_transfer_) { USB_LOG(ERROR) << "Failed to allocate isochronous transfer."; return nullptr; } libusb_fill_iso_transfer( transfer->platform_transfer_, device_handle->handle_, endpoint, reinterpret_cast(buffer->data()), static_cast(length), num_packets, &Transfer::PlatformCallback, transfer.get(), timeout); for (size_t i = 0; i < packet_lengths.size(); ++i) transfer->platform_transfer_->iso_packet_desc[i].length = packet_lengths[i]; return transfer; } UsbDeviceHandleImpl::Transfer::Transfer( scoped_refptr device_handle, scoped_refptr claimed_interface, UsbTransferType transfer_type, scoped_refptr buffer, size_t length, scoped_refptr callback_task_runner, const TransferCallback& callback) : transfer_type_(transfer_type), device_handle_(device_handle), buffer_(buffer), claimed_interface_(claimed_interface), length_(length), callback_task_runner_(callback_task_runner), callback_(callback) { task_runner_ = base::ThreadTaskRunnerHandle::Get(); } UsbDeviceHandleImpl::Transfer::Transfer( scoped_refptr device_handle, scoped_refptr claimed_interface, scoped_refptr buffer, scoped_refptr callback_task_runner, const IsochronousTransferCallback& callback) : transfer_type_(USB_TRANSFER_ISOCHRONOUS), device_handle_(device_handle), buffer_(buffer), claimed_interface_(claimed_interface), callback_task_runner_(callback_task_runner), iso_callback_(callback) { task_runner_ = base::ThreadTaskRunnerHandle::Get(); } UsbDeviceHandleImpl::Transfer::~Transfer() { if (platform_transfer_) { libusb_free_transfer(platform_transfer_); } } void UsbDeviceHandleImpl::Transfer::Submit() { const int rv = libusb_submit_transfer(platform_transfer_); if (rv != LIBUSB_SUCCESS) { USB_LOG(EVENT) << "Failed to submit transfer: " << ConvertPlatformUsbErrorToString(rv); TransferComplete(USB_TRANSFER_ERROR, 0); } } void UsbDeviceHandleImpl::Transfer::Cancel() { if (!cancelled_) { libusb_cancel_transfer(platform_transfer_); claimed_interface_ = nullptr; } cancelled_ = true; } void UsbDeviceHandleImpl::Transfer::ProcessCompletion() { DCHECK_GE(platform_transfer_->actual_length, 0) << "Negative actual length received"; size_t actual_length = static_cast(std::max(platform_transfer_->actual_length, 0)); DCHECK(length_ >= actual_length) << "data too big for our buffer (libusb failure?)"; switch (transfer_type_) { case USB_TRANSFER_CONTROL: // If the transfer is a control transfer we do not expose the control // setup header to the caller. This logic strips off the header if // present before invoking the callback provided with the transfer. if (actual_length > 0) { CHECK(length_ >= LIBUSB_CONTROL_SETUP_SIZE) << "buffer was not correctly set: too small for the control header"; if (length_ >= (LIBUSB_CONTROL_SETUP_SIZE + actual_length)) { // If the payload is zero bytes long, pad out the allocated buffer // size to one byte so that an IOBuffer of that size can be allocated. scoped_refptr resized_buffer = new net::IOBuffer( std::max(actual_length, static_cast(1))); memcpy(resized_buffer->data(), buffer_->data() + LIBUSB_CONTROL_SETUP_SIZE, actual_length); buffer_ = resized_buffer; } } // Fall through! case USB_TRANSFER_BULK: case USB_TRANSFER_INTERRUPT: TransferComplete(ConvertTransferStatus(platform_transfer_->status), actual_length); break; case USB_TRANSFER_ISOCHRONOUS: IsochronousTransferComplete(); break; default: NOTREACHED() << "Invalid usb transfer type"; break; } } /* static */ void LIBUSB_CALL UsbDeviceHandleImpl::Transfer::PlatformCallback( PlatformUsbTransferHandle platform_transfer) { Transfer* transfer = reinterpret_cast(platform_transfer->user_data); DCHECK(transfer->platform_transfer_ == platform_transfer); transfer->ProcessCompletion(); } void UsbDeviceHandleImpl::Transfer::TransferComplete(UsbTransferStatus status, size_t bytes_transferred) { base::Closure closure; if (transfer_type_ == USB_TRANSFER_ISOCHRONOUS) { DCHECK_NE(LIBUSB_TRANSFER_COMPLETED, platform_transfer_->status); std::vector packets(platform_transfer_->num_iso_packets); for (size_t i = 0; i < packets.size(); ++i) { packets[i].length = platform_transfer_->iso_packet_desc[i].length; packets[i].transferred_length = 0; packets[i].status = status; } closure = base::Bind(iso_callback_, buffer_, packets); } else { closure = base::Bind(callback_, status, buffer_, bytes_transferred); } task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::TransferComplete, device_handle_, base::Unretained(this), closure)); } void UsbDeviceHandleImpl::Transfer::IsochronousTransferComplete() { std::vector packets(platform_transfer_->num_iso_packets); for (size_t i = 0; i < packets.size(); ++i) { packets[i].length = platform_transfer_->iso_packet_desc[i].length; packets[i].transferred_length = platform_transfer_->iso_packet_desc[i].actual_length; packets[i].status = ConvertTransferStatus(platform_transfer_->iso_packet_desc[i].status); } task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::TransferComplete, device_handle_, base::Unretained(this), base::Bind(iso_callback_, buffer_, packets))); } scoped_refptr UsbDeviceHandleImpl::GetDevice() const { return device_; } void UsbDeviceHandleImpl::Close() { DCHECK(thread_checker_.CalledOnValidThread()); if (device_) device_->Close(this); } void UsbDeviceHandleImpl::SetConfiguration(int configuration_value, const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { callback.Run(false); return; } for (Transfer* transfer : transfers_) { transfer->Cancel(); } claimed_interfaces_.clear(); blocking_task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::SetConfigurationOnBlockingThread, this, configuration_value, callback)); } void UsbDeviceHandleImpl::ClaimInterface(int interface_number, const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { callback.Run(false); return; } if (ContainsKey(claimed_interfaces_, interface_number)) { callback.Run(true); return; } blocking_task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread, this, interface_number, callback)); } void UsbDeviceHandleImpl::ReleaseInterface(int interface_number, const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_ || !ContainsKey(claimed_interfaces_, interface_number)) { task_runner_->PostTask(FROM_HERE, base::Bind(callback, false)); return; } // Cancel all the transfers on that interface. InterfaceClaimer* interface_claimer = claimed_interfaces_[interface_number].get(); for (Transfer* transfer : transfers_) { if (transfer->claimed_interface() == interface_claimer) { transfer->Cancel(); } } interface_claimer->AddRef(); interface_claimer->set_release_callback(callback); blocking_task_runner_->ReleaseSoon(FROM_HERE, interface_claimer); claimed_interfaces_.erase(interface_number); RefreshEndpointMap(); } void UsbDeviceHandleImpl::SetInterfaceAlternateSetting( int interface_number, int alternate_setting, const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_ || !ContainsKey(claimed_interfaces_, interface_number)) { callback.Run(false); return; } blocking_task_runner_->PostTask( FROM_HERE, base::Bind( &UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread, this, 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, callback)); } void UsbDeviceHandleImpl::ClearHalt(uint8_t endpoint, const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { callback.Run(false); return; } InterfaceClaimer* interface_claimer = GetClaimedInterfaceForEndpoint(endpoint).get(); for (Transfer* transfer : transfers_) { if (transfer->claimed_interface() == interface_claimer) { transfer->Cancel(); } } blocking_task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ClearHaltOnBlockingThread, this, endpoint, callback)); } void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction, TransferRequestType request_type, TransferRecipient recipient, uint8_t request, uint16_t value, uint16_t index, scoped_refptr 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::IsochronousTransferIn( uint8_t endpoint_number, const std::vector& packet_lengths, unsigned int timeout, const IsochronousTransferCallback& callback) { uint8_t endpoint_address = ConvertTransferDirection(USB_DIRECTION_INBOUND) | endpoint_number; if (task_runner_->BelongsToCurrentThread()) { IsochronousTransferInInternal(endpoint_address, packet_lengths, timeout, task_runner_, callback); } else { task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::IsochronousTransferInInternal, this, endpoint_address, packet_lengths, timeout, base::ThreadTaskRunnerHandle::Get(), callback)); } } void UsbDeviceHandleImpl::IsochronousTransferOut( uint8_t endpoint_number, scoped_refptr buffer, const std::vector& packet_lengths, unsigned int timeout, const IsochronousTransferCallback& callback) { uint8_t endpoint_address = ConvertTransferDirection(USB_DIRECTION_OUTBOUND) | endpoint_number; if (task_runner_->BelongsToCurrentThread()) { IsochronousTransferOutInternal(endpoint_address, buffer, packet_lengths, timeout, task_runner_, callback); } else { task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::IsochronousTransferOutInternal, this, endpoint_address, buffer, packet_lengths, timeout, base::ThreadTaskRunnerHandle::Get(), callback)); } } void UsbDeviceHandleImpl::GenericTransfer(UsbEndpointDirection direction, uint8_t endpoint_number, scoped_refptr buffer, size_t length, unsigned int timeout, const TransferCallback& callback) { uint8_t endpoint_address = ConvertTransferDirection(direction) | endpoint_number; if (task_runner_->BelongsToCurrentThread()) { GenericTransferInternal(endpoint_address, buffer, length, timeout, task_runner_, callback); } else { task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::GenericTransferInternal, this, endpoint_address, buffer, length, timeout, base::ThreadTaskRunnerHandle::Get(), callback)); } } bool UsbDeviceHandleImpl::FindInterfaceByEndpoint(uint8_t endpoint_address, uint8_t* interface_number) { DCHECK(thread_checker_.CalledOnValidThread()); const auto endpoint_it = endpoint_map_.find(endpoint_address); if (endpoint_it != endpoint_map_.end()) { *interface_number = endpoint_it->second.interface_number; return true; } return false; } UsbDeviceHandleImpl::UsbDeviceHandleImpl( scoped_refptr context, scoped_refptr device, PlatformUsbDeviceHandle handle, scoped_refptr 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() { // This class is RefCountedThreadSafe and so the destructor may be called on // any thread. libusb is not safe to reentrancy so be sure not to try to close // the device from inside a transfer completion callback. if (blocking_task_runner_->RunsTasksOnCurrentThread()) { libusb_close(handle_); } else { blocking_task_runner_->PostTask(FROM_HERE, base::Bind(&libusb_close, handle_)); } } void UsbDeviceHandleImpl::SetConfigurationOnBlockingThread( 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_->RefreshActiveConfiguration(); RefreshEndpointMap(); } callback.Run(success); } void UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread( int interface_number, const ResultCallback& callback) { int rv = libusb_claim_interface(handle_, interface_number); scoped_refptr interface_claimer; if (rv == LIBUSB_SUCCESS) { interface_claimer = new InterfaceClaimer(this, interface_number, task_runner_); } else { USB_LOG(EVENT) << "Failed to claim interface: " << ConvertPlatformUsbErrorToString(rv); } task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ClaimInterfaceComplete, this, interface_claimer, callback)); } void UsbDeviceHandleImpl::ClaimInterfaceComplete( scoped_refptr interface_claimer, const ResultCallback& callback) { if (interface_claimer) { claimed_interfaces_[interface_claimer->interface_number()] = interface_claimer; RefreshEndpointMap(); } callback.Run(interface_claimer); } void UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread( 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); } task_runner_->PostTask( FROM_HERE, base::Bind(&UsbDeviceHandleImpl::SetInterfaceAlternateSettingComplete, this, interface_number, alternate_setting, rv == LIBUSB_SUCCESS, callback)); } 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); } void UsbDeviceHandleImpl::ResetDeviceOnBlockingThread( const ResultCallback& callback) { int rv = libusb_reset_device(handle_); if (rv != LIBUSB_SUCCESS) { USB_LOG(EVENT) << "Failed to reset device: " << ConvertPlatformUsbErrorToString(rv); } task_runner_->PostTask(FROM_HERE, base::Bind(callback, rv == LIBUSB_SUCCESS)); } void UsbDeviceHandleImpl::ClearHaltOnBlockingThread( uint8_t endpoint, const ResultCallback& callback) { int rv = libusb_clear_halt(handle_, endpoint); if (rv != LIBUSB_SUCCESS) { USB_LOG(EVENT) << "Failed to clear halt: " << ConvertPlatformUsbErrorToString(rv); } task_runner_->PostTask(FROM_HERE, base::Bind(callback, rv == LIBUSB_SUCCESS)); } void UsbDeviceHandleImpl::RefreshEndpointMap() { DCHECK(thread_checker_.CalledOnValidThread()); endpoint_map_.clear(); const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); if (config) { for (const auto& map_entry : claimed_interfaces_) { int interface_number = map_entry.first; const scoped_refptr& 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, endpoint.transfer_type}; } break; } } } } } scoped_refptr UsbDeviceHandleImpl::GetClaimedInterfaceForEndpoint(uint8_t endpoint) { const auto endpoint_it = endpoint_map_.find(endpoint); if (endpoint_it != endpoint_map_.end()) return claimed_interfaces_[endpoint_it->second.interface_number]; return nullptr; } void UsbDeviceHandleImpl::ControlTransferInternal( UsbEndpointDirection direction, TransferRequestType request_type, TransferRecipient recipient, uint8_t request, uint16_t value, uint16_t index, scoped_refptr buffer, size_t length, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_DISCONNECT, buffer, 0); return; } if (!base::IsValueInRangeForNumericType(length)) { USB_LOG(USER) << "Transfer too long."; RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR, buffer, 0); return; } const size_t resized_length = LIBUSB_CONTROL_SETUP_SIZE + length; scoped_refptr resized_buffer = new net::IOBufferWithSize(resized_length); if (!resized_buffer.get()) { RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR, buffer, 0); return; } memcpy(resized_buffer->data() + LIBUSB_CONTROL_SETUP_SIZE, buffer->data(), length); scoped_ptr transfer = Transfer::CreateControlTransfer( this, CreateRequestType(direction, request_type, recipient), request, value, index, static_cast(length), resized_buffer, timeout, callback_task_runner, callback); if (!transfer) { RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR, buffer, 0); return; } SubmitTransfer(std::move(transfer)); } void UsbDeviceHandleImpl::IsochronousTransferInInternal( uint8_t endpoint_address, const std::vector& packet_lengths, unsigned int timeout, scoped_refptr callback_task_runner, const IsochronousTransferCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { ReportIsochronousTransferError(callback_task_runner, callback, packet_lengths, USB_TRANSFER_DISCONNECT); return; } size_t length = std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u); scoped_refptr buffer(new net::IOBuffer(length)); scoped_ptr transfer = Transfer::CreateIsochronousTransfer( this, endpoint_address, buffer, length, packet_lengths, timeout, callback_task_runner, callback); SubmitTransfer(std::move(transfer)); } void UsbDeviceHandleImpl::IsochronousTransferOutInternal( uint8_t endpoint_address, scoped_refptr buffer, const std::vector& packet_lengths, unsigned int timeout, scoped_refptr callback_task_runner, const IsochronousTransferCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { ReportIsochronousTransferError(callback_task_runner, callback, packet_lengths, USB_TRANSFER_DISCONNECT); return; } size_t length = std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u); scoped_ptr transfer = Transfer::CreateIsochronousTransfer( this, endpoint_address, buffer, length, packet_lengths, timeout, callback_task_runner, callback); SubmitTransfer(std::move(transfer)); } void UsbDeviceHandleImpl::GenericTransferInternal( uint8_t endpoint_address, scoped_refptr buffer, size_t length, unsigned int timeout, scoped_refptr callback_task_runner, const TransferCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_DISCONNECT, buffer, 0); return; } const auto endpoint_it = endpoint_map_.find(endpoint_address); if (endpoint_it == endpoint_map_.end()) { USB_LOG(DEBUG) << "Failed to submit transfer because endpoint " << static_cast(endpoint_address) << " not part of a claimed interface."; RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR, buffer, 0); return; } if (!base::IsValueInRangeForNumericType(length)) { USB_LOG(DEBUG) << "Transfer too long."; RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR, buffer, 0); return; } scoped_ptr transfer; UsbTransferType transfer_type = endpoint_it->second.transfer_type; if (transfer_type == USB_TRANSFER_BULK) { transfer = Transfer::CreateBulkTransfer(this, endpoint_address, buffer, static_cast(length), timeout, callback_task_runner, callback); } else if (transfer_type == USB_TRANSFER_INTERRUPT) { transfer = Transfer::CreateInterruptTransfer( this, endpoint_address, buffer, static_cast(length), timeout, callback_task_runner, callback); } else { USB_LOG(DEBUG) << "Endpoint " << static_cast(endpoint_address) << " is not a bulk or interrupt endpoint."; RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR, buffer, 0); return; } SubmitTransfer(std::move(transfer)); } void UsbDeviceHandleImpl::SubmitTransfer(scoped_ptr transfer) { DCHECK(thread_checker_.CalledOnValidThread()); // 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::TransferComplete(Transfer* transfer, const base::Closure& callback) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(ContainsKey(transfers_, transfer)) << "Missing transfer completed"; transfers_.erase(transfer); if (transfer->callback_task_runner()->RunsTasksOnCurrentThread()) { callback.Run(); } else { transfer->callback_task_runner()->PostTask(FROM_HERE, callback); } // libusb_free_transfer races with libusb_submit_transfer and only work- // around is to make sure to call them on the same thread. blocking_task_runner_->DeleteSoon(FROM_HERE, transfer); } void UsbDeviceHandleImpl::InternalClose() { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) return; // Cancel all the transfers. for (Transfer* transfer : transfers_) { // The callback will be called some time later. transfer->Cancel(); } // Release all remaining interfaces once their transfers have completed. // This loop must ensure that what may be the final reference is released on // the right thread. for (auto& map_entry : claimed_interfaces_) { InterfaceClaimer* interface_claimer = map_entry.second.get(); interface_claimer->AddRef(); map_entry.second = nullptr; blocking_task_runner_->ReleaseSoon(FROM_HERE, interface_claimer); } // Cannot close device handle here. Need to wait for libusb_cancel_transfer to // finish. device_ = nullptr; } } // namespace device