Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2692)

Unified Diff: device/usb/usb_device_handle_usbfs.cc

Issue 1877503003: Replace libusb in the Linux/Chrome OS USB I/O path. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased and addressed feedback. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « device/usb/usb_device_handle_usbfs.h ('k') | device/usb/usb_device_impl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: device/usb/usb_device_handle_usbfs.cc
diff --git a/device/usb/usb_device_handle_usbfs.cc b/device/usb/usb_device_handle_usbfs.cc
new file mode 100644
index 0000000000000000000000000000000000000000..693b21e042d82de24c3115baddf642734f184a2d
--- /dev/null
+++ b/device/usb/usb_device_handle_usbfs.cc
@@ -0,0 +1,800 @@
+// Copyright 2016 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_usbfs.h"
+
+#if defined(OS_ANDROID) && __ANDROID_API__ < 21
+#include <linux/usb_ch9.h>
+#else
+#include <linux/usb/ch9.h>
+#endif
+
+#include <linux/usbdevice_fs.h>
+#include <sys/ioctl.h>
+
+#include <numeric>
+
+#include "base/bind.h"
+#include "base/cancelable_callback.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump_libevent.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/device_event_log/device_event_log.h"
+#include "device/usb/usb_device_impl.h"
+#include "net/base/io_buffer.h"
+
+namespace device {
+
+namespace {
+
+uint8_t ConvertEndpointDirection(UsbEndpointDirection direction) {
+ switch (direction) {
+ case USB_DIRECTION_INBOUND:
+ return USB_DIR_IN;
+ case USB_DIRECTION_OUTBOUND:
+ return USB_DIR_OUT;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+uint8_t ConvertRequestType(UsbDeviceHandle::TransferRequestType request_type) {
+ switch (request_type) {
+ case UsbDeviceHandle::STANDARD:
+ return USB_TYPE_STANDARD;
+ case UsbDeviceHandle::CLASS:
+ return USB_TYPE_CLASS;
+ case UsbDeviceHandle::VENDOR:
+ return USB_TYPE_VENDOR;
+ case UsbDeviceHandle::RESERVED:
+ return USB_TYPE_RESERVED;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+uint8_t ConvertRecipient(UsbDeviceHandle::TransferRecipient recipient) {
+ switch (recipient) {
+ case UsbDeviceHandle::DEVICE:
+ return USB_RECIP_DEVICE;
+ case UsbDeviceHandle::INTERFACE:
+ return USB_RECIP_INTERFACE;
+ case UsbDeviceHandle::ENDPOINT:
+ return USB_RECIP_ENDPOINT;
+ case UsbDeviceHandle::OTHER:
+ return USB_RECIP_OTHER;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+scoped_refptr<net::IOBuffer> BuildControlTransferBuffer(
+ UsbEndpointDirection direction,
+ UsbDeviceHandle::TransferRequestType request_type,
+ UsbDeviceHandle::TransferRecipient recipient,
+ uint8_t request,
+ uint16_t value,
+ uint16_t index,
+ scoped_refptr<net::IOBuffer> original_buffer,
+ size_t length) {
+ scoped_refptr<net::IOBuffer> new_buffer(
+ new net::IOBuffer(length + sizeof(usb_ctrlrequest)));
+ usb_ctrlrequest* setup =
+ reinterpret_cast<usb_ctrlrequest*>(new_buffer->data());
+ setup->bRequestType = ConvertEndpointDirection(direction) |
+ ConvertRequestType(request_type) |
+ ConvertRecipient(recipient);
+ setup->bRequest = request;
+ setup->wValue = value;
+ setup->wIndex = index;
+ setup->wLength = length;
+ memcpy(new_buffer->data() + sizeof(usb_ctrlrequest), original_buffer->data(),
+ length);
+ return new_buffer;
+}
+
+uint8_t ConvertTransferType(UsbTransferType type) {
+ switch (type) {
+ case USB_TRANSFER_CONTROL:
+ return USBDEVFS_URB_TYPE_CONTROL;
+ case USB_TRANSFER_ISOCHRONOUS:
+ return USBDEVFS_URB_TYPE_ISO;
+ case USB_TRANSFER_BULK:
+ return USBDEVFS_URB_TYPE_BULK;
+ case USB_TRANSFER_INTERRUPT:
+ return USBDEVFS_URB_TYPE_INTERRUPT;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+UsbTransferStatus ConvertTransferResult(int rc) {
+ switch (rc) {
+ case 0:
+ return USB_TRANSFER_COMPLETED;
+ case EPIPE:
+ return USB_TRANSFER_STALLED;
+ case ENODEV:
+ case ESHUTDOWN:
+ return USB_TRANSFER_DISCONNECT;
+ default:
+ // TODO(reillyg): Add a specific error message whenever one of the cases
+ // above fails to match.
+ USB_LOG(ERROR) << "Unknown system error: "
+ << logging::SystemErrorCodeToString(rc);
+ return USB_TRANSFER_ERROR;
+ }
+}
+
+} // namespace
+
+class UsbDeviceHandleUsbfs::FileThreadHelper
+ : public base::MessagePumpLibevent::Watcher,
+ public base::MessageLoop::DestructionObserver {
+ public:
+ FileThreadHelper(int fd,
+ scoped_refptr<UsbDeviceHandleUsbfs> device_handle,
+ scoped_refptr<base::SequencedTaskRunner> task_runner);
+ ~FileThreadHelper() override;
+
+ static void Start(scoped_ptr<FileThreadHelper> self);
+
+ // base::MessagePumpLibevent::Watcher overrides.
+ void OnFileCanReadWithoutBlocking(int fd) override;
+ void OnFileCanWriteWithoutBlocking(int fd) override;
+
+ // base::MessageLoop::DestructionObserver overrides.
+ void WillDestroyCurrentMessageLoop() override;
+
+ private:
+ int fd_;
+ scoped_refptr<UsbDeviceHandleUsbfs> device_handle_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ base::MessagePumpLibevent::FileDescriptorWatcher file_watcher_;
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileThreadHelper);
+};
+
+UsbDeviceHandleUsbfs::FileThreadHelper::FileThreadHelper(
+ int fd,
+ scoped_refptr<UsbDeviceHandleUsbfs> device_handle,
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : fd_(fd), device_handle_(device_handle), task_runner_(task_runner) {}
+
+UsbDeviceHandleUsbfs::FileThreadHelper::~FileThreadHelper() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+// static
+void UsbDeviceHandleUsbfs::FileThreadHelper::Start(
+ scoped_ptr<FileThreadHelper> self) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ self->thread_checker_.DetachFromThread();
+
+ // Linux indicates that URBs are available to reap by marking the file
+ // descriptor writable.
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ self->fd_, true, base::MessageLoopForIO::WATCH_WRITE,
+ &self->file_watcher_, self.get())) {
+ USB_LOG(ERROR) << "Failed to start watching device file descriptor.";
+ }
+
+ // |self| is now owned by the current message loop.
+ base::MessageLoop::current()->AddDestructionObserver(self.release());
+}
+
+void UsbDeviceHandleUsbfs::FileThreadHelper::OnFileCanReadWithoutBlocking(
+ int fd) {
+ NOTREACHED(); // Only listening for writability.
+}
+
+void UsbDeviceHandleUsbfs::FileThreadHelper::OnFileCanWriteWithoutBlocking(
+ int fd) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(fd_, fd);
+
+ const size_t MAX_URBS_PER_EVENT = 10;
+ std::vector<usbdevfs_urb*> urbs;
+ urbs.reserve(MAX_URBS_PER_EVENT);
+ for (size_t i = 0; i < MAX_URBS_PER_EVENT; ++i) {
+ usbdevfs_urb* urb;
+ int rc = HANDLE_EINTR(ioctl(fd_, USBDEVFS_REAPURBNDELAY, &urb));
+ if (rc) {
+ if (errno == EAGAIN)
+ break;
+ USB_PLOG(DEBUG) << "Failed to reap urbs";
+ } else {
+ urbs.push_back(urb);
+ }
+ }
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleUsbfs::ReapedUrbs, device_handle_, urbs));
+}
+
+void UsbDeviceHandleUsbfs::FileThreadHelper::WillDestroyCurrentMessageLoop() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ delete this;
+}
+
+struct UsbDeviceHandleUsbfs::Transfer {
+ Transfer() = delete;
+ Transfer(scoped_refptr<net::IOBuffer> buffer,
+ const TransferCallback& callback);
+ Transfer(scoped_refptr<net::IOBuffer> buffer,
+ const IsochronousTransferCallback& callback);
+ ~Transfer();
+
+ void* operator new(std::size_t size, size_t number_of_iso_packets);
+
+ scoped_refptr<net::IOBuffer> control_transfer_buffer;
+ scoped_refptr<net::IOBuffer> buffer;
+ TransferCallback callback;
+ IsochronousTransferCallback isoc_callback;
+ base::CancelableClosure timeout_closure;
+ bool timed_out = false;
+
+ // The |urb| field must be the last in the struct so that the extra space
+ // allocated by the overridden new function above extends the length of its
+ // |iso_frame_desc| field.
+ usbdevfs_urb urb;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Transfer);
+};
+
+UsbDeviceHandleUsbfs::Transfer::Transfer(scoped_refptr<net::IOBuffer> buffer,
+ const TransferCallback& callback)
+ : buffer(buffer), callback(callback) {
+ memset(&urb, 0, sizeof(urb));
+ urb.usercontext = this;
+ urb.buffer = buffer->data();
+}
+
+UsbDeviceHandleUsbfs::Transfer::Transfer(
+ scoped_refptr<net::IOBuffer> buffer,
+ const IsochronousTransferCallback& callback)
+ : buffer(buffer), isoc_callback(callback) {
+ memset(&urb, 0, sizeof(urb) +
+ sizeof(usbdevfs_iso_packet_desc) * urb.number_of_packets);
+ urb.usercontext = this;
+ urb.buffer = buffer->data();
+}
+
+UsbDeviceHandleUsbfs::Transfer::~Transfer() = default;
+
+void* UsbDeviceHandleUsbfs::Transfer::operator new(
+ std::size_t size,
+ size_t number_of_iso_packets) {
+ void* p = ::operator new(
+ size + sizeof(usbdevfs_iso_packet_desc) * number_of_iso_packets);
+ Transfer* transfer = static_cast<Transfer*>(p);
+ transfer->urb.number_of_packets = number_of_iso_packets;
+ return p;
+}
+
+UsbDeviceHandleUsbfs::UsbDeviceHandleUsbfs(
+ scoped_refptr<UsbDevice> device,
+ base::ScopedFD fd,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+ : device_(device),
+ fd_(std::move(fd)),
+ blocking_task_runner_(blocking_task_runner) {
+ DCHECK(device_);
+ DCHECK(fd_.is_valid());
+ DCHECK(blocking_task_runner_);
+
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+ scoped_ptr<FileThreadHelper> helper(
+ new FileThreadHelper(fd_.get(), this, task_runner_));
+ helper_ = helper.get();
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&FileThreadHelper::Start, base::Passed(&helper)));
+}
+
+scoped_refptr<UsbDevice> UsbDeviceHandleUsbfs::GetDevice() const {
+ return device_;
+}
+
+void UsbDeviceHandleUsbfs::Close() {
+ // On the |task_runner_| thread check |device_| to see if the handle is
+ // closed. On the |blocking_task_runner_| thread check |fd_.is_valid()| to
+ // see if the handle is closed.
+ DCHECK(device_);
+#if !defined(OS_ANDROID)
+ static_cast<UsbDeviceImpl*>(device_.get())->HandleClosed(this);
+#endif
+ device_ = nullptr;
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::CloseBlocking, this));
+}
+
+void UsbDeviceHandleUsbfs::SetConfiguration(int configuration_value,
+ const ResultCallback& callback) {
+ // USBDEVFS_SETCONFIGURATION synchronously issues a SET_CONFIGURATION request
+ // to the device so it must be performed on a thread where it is okay to
+ // block.
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::SetConfigurationBlocking,
+ this, configuration_value, callback));
+}
+
+void UsbDeviceHandleUsbfs::ClaimInterface(int interface_number,
+ const ResultCallback& callback) {
+ if (!device_) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ if (ContainsKey(interfaces_, interface_number)) {
+ USB_LOG(DEBUG) << "Interface " << interface_number << " already claimed.";
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ // It appears safe to assume that this ioctl will not block.
+ int rc = HANDLE_EINTR(
+ ioctl(fd_.get(), USBDEVFS_CLAIMINTERFACE, &interface_number));
+ if (rc) {
+ USB_PLOG(DEBUG) << "Failed to claim interface " << interface_number;
+ } else {
+ interfaces_[interface_number].alternate_setting = 0;
+ RefreshEndpointInfo();
+ }
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
+}
+
+void UsbDeviceHandleUsbfs::ReleaseInterface(int interface_number,
+ const ResultCallback& callback) {
+ // USBDEVFS_RELEASEINTERFACE may issue a SET_INTERFACE request to the
+ // device to restore alternate setting 0 so it must be performed on a thread
+ // where it is okay to block.
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::ReleaseInterfaceBlocking,
+ this, interface_number, callback));
+}
+
+void UsbDeviceHandleUsbfs::SetInterfaceAlternateSetting(
+ int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) {
+ // USBDEVFS_SETINTERFACE is synchronous because it issues a SET_INTERFACE
+ // request to the device so it must be performed on a thread where it is okay
+ // to block.
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::SetInterfaceBlocking, this,
+ interface_number, alternate_setting, callback));
+}
+
+void UsbDeviceHandleUsbfs::ResetDevice(const ResultCallback& callback) {
+ // USBDEVFS_RESET is synchronous because it waits for the port to be reset
+ // and the device re-enumerated so it must be performed on a thread where it
+ // is okay to block.
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleUsbfs::ResetDeviceBlocking, this, callback));
+}
+
+void UsbDeviceHandleUsbfs::ClearHalt(uint8_t endpoint_address,
+ const ResultCallback& callback) {
+ // USBDEVFS_CLEAR_HALT is synchronous because it issues a CLEAR_FEATURE
+ // request to the device so it must be performed on a thread where it is okay
+ // to block.
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::ClearHaltBlocking, this,
+ endpoint_address, callback));
+}
+
+void UsbDeviceHandleUsbfs::ControlTransfer(UsbEndpointDirection direction,
+ TransferRequestType request_type,
+ TransferRecipient recipient,
+ uint8_t request,
+ uint16_t value,
+ uint16_t index,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) {
+ if (!device_) {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, USB_TRANSFER_DISCONNECT, nullptr, 0));
+ return;
+ }
+
+ scoped_ptr<Transfer> transfer(new (0) Transfer(buffer, callback));
+ transfer->control_transfer_buffer =
+ BuildControlTransferBuffer(direction, request_type, recipient, request,
+ value, index, buffer, length);
+ transfer->urb.type = USBDEVFS_URB_TYPE_CONTROL;
+ transfer->urb.endpoint = 0;
+ transfer->urb.buffer = transfer->control_transfer_buffer->data();
+ transfer->urb.buffer_length = 8 + length;
+
+ // USBDEVFS_SUBMITURB appears to be non-blocking as completion is reported
+ // by USBDEVFS_REAPURBNDELAY.
+ int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SUBMITURB, &transfer->urb));
+ if (rc) {
+ rc = logging::GetLastSystemErrorCode();
+ USB_PLOG(DEBUG) << "Failed to submit control transfer";
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, ConvertTransferResult(rc), nullptr, 0));
+ } else {
+ SetUpTimeoutCallback(transfer.get(), timeout);
+ transfers_.push_back(std::move(transfer));
+ }
+}
+
+void UsbDeviceHandleUsbfs::IsochronousTransferIn(
+ uint8_t endpoint_number,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) {
+ uint8_t endpoint_address = USB_DIR_IN | endpoint_number;
+ size_t total_length =
+ std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
+ IsochronousTransferInternal(endpoint_address, new net::IOBuffer(total_length),
+ total_length, packet_lengths, timeout, callback);
+}
+
+void UsbDeviceHandleUsbfs::IsochronousTransferOut(
+ uint8_t endpoint_number,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) {
+ uint8_t endpoint_address = USB_DIR_OUT | endpoint_number;
+ size_t total_length =
+ std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
+ IsochronousTransferInternal(endpoint_address, buffer, total_length,
+ packet_lengths, timeout, callback);
+}
+
+void UsbDeviceHandleUsbfs::GenericTransfer(UsbEndpointDirection direction,
+ uint8_t endpoint_number,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t length,
+ unsigned int timeout,
+ const TransferCallback& callback) {
+ if (!device_) {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, USB_TRANSFER_DISCONNECT, nullptr, 0));
+ return;
+ }
+
+ uint8_t endpoint_address =
+ ConvertEndpointDirection(direction) | endpoint_number;
+ auto it = endpoints_.find(endpoint_address);
+ if (it == endpoints_.end()) {
+ USB_LOG(USER) << "Endpoint address " << static_cast<int>(endpoint_address)
+ << " is not part of a claimed interface.";
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, USB_TRANSFER_ERROR, nullptr, 0));
+ return;
+ }
+
+ scoped_ptr<Transfer> transfer(new (0) Transfer(buffer, callback));
+ transfer->urb.endpoint = endpoint_address;
+ transfer->urb.buffer_length = length;
+ transfer->urb.type = ConvertTransferType(it->second.type);
+
+ // USBDEVFS_SUBMITURB appears to be non-blocking as completion is reported
+ // by USBDEVFS_REAPURBNDELAY. This code assumes a recent kernel that can
+ // accept arbitrarily large transfer requests, hopefully also using a scatter-
+ // gather list.
+ int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SUBMITURB, &transfer->urb));
+ if (rc) {
+ rc = logging::GetLastSystemErrorCode();
+ USB_PLOG(DEBUG) << "Failed to submit transfer";
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, ConvertTransferResult(rc), nullptr, 0));
+ } else {
+ SetUpTimeoutCallback(transfer.get(), timeout);
+ transfers_.push_back(std::move(transfer));
+ }
+}
+
+const UsbInterfaceDescriptor* UsbDeviceHandleUsbfs::FindInterfaceByEndpoint(
+ uint8_t endpoint_address) {
+ auto it = endpoints_.find(endpoint_address);
+ if (it != endpoints_.end())
+ return it->second.interface;
+ return nullptr;
+}
+
+UsbDeviceHandleUsbfs::~UsbDeviceHandleUsbfs() {
+ DCHECK(!device_) << "Handle must be closed before it is destroyed.";
+}
+
+void UsbDeviceHandleUsbfs::CloseBlocking() {
+ fd_.reset(-1);
+ delete helper_;
+}
+
+void UsbDeviceHandleUsbfs::SetConfigurationBlocking(
+ int configuration_value,
+ const ResultCallback& callback) {
+ if (!fd_.is_valid()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ int rc = HANDLE_EINTR(
+ ioctl(fd_.get(), USBDEVFS_SETCONFIGURATION, &configuration_value));
+ if (rc)
+ USB_PLOG(DEBUG) << "Failed to set configuration " << configuration_value;
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::SetConfigurationComplete,
+ this, configuration_value, rc == 0, callback));
+}
+
+void UsbDeviceHandleUsbfs::SetConfigurationComplete(
+ int configuration_value,
+ bool success,
+ const ResultCallback& callback) {
+ if (success && device_) {
+#if !defined(OS_ANDROID)
+ static_cast<UsbDeviceImpl*>(device_.get())
+ ->ActiveConfigurationChanged(configuration_value);
+#endif
+ // TODO(reillyg): If all interfaces are unclaimed before a new configuration
+ // is set then this will do nothing. Investigate.
+ RefreshEndpointInfo();
+ }
+ callback.Run(success);
+}
+
+void UsbDeviceHandleUsbfs::ReleaseInterfaceBlocking(
+ int interface_number,
+ const ResultCallback& callback) {
+ if (!fd_.is_valid()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ int rc = HANDLE_EINTR(
+ ioctl(fd_.get(), USBDEVFS_RELEASEINTERFACE, &interface_number));
+ if (rc) {
+ USB_PLOG(DEBUG) << "Failed to release interface " << interface_number;
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::ReleaseInterfaceComplete,
+ this, interface_number, callback));
+ }
+}
+
+void UsbDeviceHandleUsbfs::ReleaseInterfaceComplete(
+ int interface_number,
+ const ResultCallback& callback) {
+ auto it = interfaces_.find(interface_number);
+ DCHECK(it != interfaces_.end());
+ interfaces_.erase(it);
+ RefreshEndpointInfo();
+ callback.Run(true);
+}
+
+void UsbDeviceHandleUsbfs::SetInterfaceBlocking(
+ int interface_number,
+ int alternate_setting,
+ const ResultCallback& callback) {
+ if (!fd_.is_valid()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ usbdevfs_setinterface cmd = {0};
+ cmd.interface = interface_number;
+ cmd.altsetting = alternate_setting;
+ int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SETINTERFACE, &cmd));
+ if (rc) {
+ USB_PLOG(DEBUG) << "Failed to set interface " << interface_number
+ << " to alternate setting " << alternate_setting;
+ }
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
+}
+
+void UsbDeviceHandleUsbfs::ResetDeviceBlocking(const ResultCallback& callback) {
+ if (!fd_.is_valid()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ // TODO(reillyg): libusb releases interfaces before and then reclaims
+ // interfaces after a reset. We should probably do this too or document that
+ // callers have to call ClaimInterface as well.
+ int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_RESET, nullptr));
+ if (rc)
+ USB_PLOG(DEBUG) << "Failed to reset the device";
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
+}
+
+void UsbDeviceHandleUsbfs::ClearHaltBlocking(uint8_t endpoint_address,
+ const ResultCallback& callback) {
+ if (!fd_.is_valid()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
+ return;
+ }
+
+ int tmp_endpoint = endpoint_address;
+ int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_CLEAR_HALT, &tmp_endpoint));
+ if (rc) {
+ USB_PLOG(DEBUG) << "Failed to clear the stall condition on endpoint "
+ << static_cast<int>(endpoint_address);
+ }
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
+}
+
+void UsbDeviceHandleUsbfs::IsochronousTransferInternal(
+ uint8_t endpoint_address,
+ scoped_refptr<net::IOBuffer> buffer,
+ size_t total_length,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) {
+ if (!device_) {
+ ReportIsochronousError(packet_lengths, callback, USB_TRANSFER_DISCONNECT);
+ return;
+ }
+
+ auto it = endpoints_.find(endpoint_address);
+ if (it == endpoints_.end()) {
+ USB_LOG(USER) << "Endpoint address " << static_cast<int>(endpoint_address)
+ << " is not part of a claimed interface.";
+ ReportIsochronousError(packet_lengths, callback, USB_TRANSFER_ERROR);
+ return;
+ }
+
+ scoped_ptr<Transfer> transfer(new (packet_lengths.size())
+ Transfer(buffer, callback));
+ transfer->urb.type = USBDEVFS_URB_TYPE_ISO;
+ transfer->urb.endpoint = endpoint_address;
+ transfer->urb.buffer_length = total_length;
+
+ for (size_t i = 0; i < packet_lengths.size(); ++i)
+ transfer->urb.iso_frame_desc[i].length = packet_lengths[i];
+
+ // USBDEVFS_SUBMITURB appears to be non-blocking as completion is reported
+ // by USBDEVFS_REAPURBNDELAY. This code assumes a recent kernel that can
+ // accept arbitrarily large transfer requests, hopefully also using a scatter-
+ // gather list.
+ int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SUBMITURB, &transfer->urb));
+ if (rc) {
+ rc = logging::GetLastSystemErrorCode();
+ USB_PLOG(DEBUG) << "Failed to submit transfer";
+ ReportIsochronousError(packet_lengths, callback, ConvertTransferResult(rc));
+ } else {
+ SetUpTimeoutCallback(transfer.get(), timeout);
+ transfers_.push_back(std::move(transfer));
+ }
+}
+
+void UsbDeviceHandleUsbfs::ReapedUrbs(const std::vector<usbdevfs_urb*>& urbs) {
+ for (const auto& urb : urbs) {
+ Transfer* this_transfer = static_cast<Transfer*>(urb->usercontext);
+ DCHECK_EQ(urb, &this_transfer->urb);
+ auto it = std::find_if(
+ transfers_.begin(), transfers_.end(),
+ [this_transfer](const scoped_ptr<Transfer>& transfer) -> bool {
+ return transfer.get() == this_transfer;
+ });
+ DCHECK(it != transfers_.end());
+ scoped_ptr<Transfer> transfer = std::move(*it);
+ transfers_.erase(it);
+ TransferComplete(std::move(transfer));
+ }
+}
+
+void UsbDeviceHandleUsbfs::TransferComplete(scoped_ptr<Transfer> transfer) {
+ if (transfer->timed_out)
+ return;
+
+ // The transfer will soon be freed. Cancel the timeout callback so that the
+ // raw pointer it holds to |transfer| is not used.
+ transfer->timeout_closure.Cancel();
+
+ if (transfer->urb.type == USBDEVFS_URB_TYPE_ISO) {
+ std::vector<IsochronousPacket> packets(transfer->urb.number_of_packets);
+ for (size_t i = 0; i < packets.size(); ++i) {
+ packets[i].length = transfer->urb.iso_frame_desc[i].length;
+ packets[i].transferred_length =
+ transfer->urb.iso_frame_desc[i].actual_length;
+ packets[i].status = ConvertTransferResult(
+ transfer->urb.status == 0 ? transfer->urb.iso_frame_desc[i].status
+ : transfer->urb.status);
+ }
+
+ transfer->isoc_callback.Run(transfer->buffer, packets);
+ } else {
+ if (transfer->urb.status == 0 &&
+ transfer->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
+ // Copy the result of the control transfer back into the original buffer.
+ memcpy(transfer->buffer->data(),
+ transfer->control_transfer_buffer->data() + 8,
+ transfer->urb.actual_length);
+ }
+
+ transfer->callback.Run(ConvertTransferResult(-transfer->urb.status),
+ transfer->buffer, transfer->urb.actual_length);
+ }
+}
+
+void UsbDeviceHandleUsbfs::RefreshEndpointInfo() {
+ endpoints_.clear();
+
+ const UsbConfigDescriptor* config = device_->GetActiveConfiguration();
+ if (!config)
+ return;
+
+ for (const auto& entry : interfaces_) {
+ auto interface_it = std::find_if(
+ config->interfaces.begin(), config->interfaces.end(),
+ [entry](const UsbInterfaceDescriptor& interface) {
+ uint8_t interface_number = entry.first;
+ uint8_t alternate_setting = entry.second.alternate_setting;
+ return interface.interface_number == interface_number &&
+ interface.alternate_setting == alternate_setting;
+ });
+ DCHECK(interface_it != config->interfaces.end());
+
+ for (const auto& endpoint : interface_it->endpoints) {
+ EndpointInfo& info = endpoints_[endpoint.address];
+ info.type = endpoint.transfer_type;
+ info.interface = &*interface_it;
+ }
+ }
+}
+
+void UsbDeviceHandleUsbfs::ReportIsochronousError(
+ const std::vector<uint32_t>& packet_lengths,
+ const UsbDeviceHandle::IsochronousTransferCallback& callback,
+ UsbTransferStatus status) {
+ std::vector<UsbDeviceHandle::IsochronousPacket> 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;
+ }
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr, packets));
+}
+
+void UsbDeviceHandleUsbfs::SetUpTimeoutCallback(Transfer* transfer,
+ unsigned int timeout) {
+ if (timeout > 0) {
+ transfer->timeout_closure.Reset(
+ base::Bind(&UsbDeviceHandleUsbfs::TransferTimedOut, transfer));
+ task_runner_->PostDelayedTask(FROM_HERE,
+ transfer->timeout_closure.callback(),
+ base::TimeDelta::FromMilliseconds(timeout));
+ }
+}
+
+// static
+void UsbDeviceHandleUsbfs::TransferTimedOut(Transfer* transfer) {
+ // |transfer| must stay in |transfers_| as it is still being processed by the
+ // kernel and will be reaped later. Just tell the caller that the timeout
+ // elapsed.
+ transfer->timed_out = true;
+ if (transfer->urb.type == USBDEVFS_URB_TYPE_ISO) {
+ std::vector<IsochronousPacket> packets(transfer->urb.number_of_packets);
+ for (size_t i = 0; i < packets.size(); ++i) {
+ packets[i].length = transfer->urb.iso_frame_desc[i].length;
+ packets[i].transferred_length = 0;
+ packets[i].status = USB_TRANSFER_TIMEOUT;
+ }
+ transfer->isoc_callback.Run(transfer->buffer, packets);
+ } else {
+ transfer->callback.Run(USB_TRANSFER_TIMEOUT, transfer->buffer, 0);
+ }
+}
+
+} // namespace device
« no previous file with comments | « device/usb/usb_device_handle_usbfs.h ('k') | device/usb/usb_device_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698