diff options
Diffstat (limited to 'device/usb/mojo/device_impl.cc')
| -rw-r--r-- | device/usb/mojo/device_impl.cc | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/device/usb/mojo/device_impl.cc b/device/usb/mojo/device_impl.cc new file mode 100644 index 0000000..724e911 --- /dev/null +++ b/device/usb/mojo/device_impl.cc @@ -0,0 +1,464 @@ +// Copyright 2015 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/mojo/device_impl.h" + +#include <stddef.h> + +#include <algorithm> +#include <numeric> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/stl_util.h" +#include "device/usb/mojo/type_converters.h" +#include "device/usb/usb_descriptors.h" +#include "device/usb/usb_device.h" +#include "net/base/io_buffer.h" + +namespace device { +namespace usb { + +namespace { + +using MojoTransferInCallback = + mojo::Callback<void(TransferStatus, mojo::Array<uint8_t>)>; + +using MojoTransferOutCallback = mojo::Callback<void(TransferStatus)>; + +template <typename... Args> +void CallMojoCallback(scoped_ptr<mojo::Callback<void(Args...)>> callback, + Args... args) { + callback->Run(args...); +} + +// Generic wrapper to convert a Mojo callback to something we can rebind and +// pass around. This is only usable for callbacks with no move-only arguments. +template <typename... Args> +base::Callback<void(Args...)> WrapMojoCallback( + const mojo::Callback<void(Args...)>& callback) { + // mojo::Callback is not thread safe. By wrapping |callback| in a scoped_ptr + // we guarantee that it will be freed when CallMojoCallback is run and not + // retained until the base::Callback is destroyed, which could happen on any + // thread. This pattern is also used below in places where this generic + // wrapper is not used. + auto callback_ptr = + make_scoped_ptr(new mojo::Callback<void(Args...)>(callback)); + return base::Bind(&CallMojoCallback<Args...>, base::Passed(&callback_ptr)); +} + +void OnPermissionCheckComplete( + const base::Callback<void(bool)>& callback, + const base::Callback<void(const base::Callback<void(bool)>&)>& action, + bool allowed) { + if (allowed) + action.Run(callback); + else + callback.Run(false); +} + +scoped_refptr<net::IOBuffer> CreateTransferBuffer(size_t size) { + scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer( + std::max(static_cast<size_t>(1u), static_cast<size_t>(size))); + return buffer; +} + +void OnTransferIn(scoped_ptr<MojoTransferInCallback> callback, + UsbTransferStatus status, + scoped_refptr<net::IOBuffer> buffer, + size_t buffer_size) { + mojo::Array<uint8_t> data; + if (buffer) { + // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a + // std::vector<uint8_t> instead of net::IOBuffer. Then we could move + // instead of copy. + std::vector<uint8_t> bytes(buffer_size); + std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); + data.Swap(&bytes); + } + callback->Run(mojo::ConvertTo<TransferStatus>(status), std::move(data)); +} + +void OnControlTransferInPermissionCheckComplete( + scoped_refptr<UsbDeviceHandle> device_handle, + ControlTransferParamsPtr params, + int length, + int timeout, + scoped_ptr<Device::ControlTransferInCallback> callback, + bool allowed) { + if (allowed) { + scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(length); + device_handle->ControlTransfer( + USB_DIRECTION_INBOUND, + mojo::ConvertTo<UsbDeviceHandle::TransferRequestType>(params->type), + mojo::ConvertTo<UsbDeviceHandle::TransferRecipient>(params->recipient), + params->request, params->value, params->index, buffer, length, timeout, + base::Bind(&OnTransferIn, base::Passed(&callback))); + } else { + mojo::Array<uint8_t> data; + callback->Run(TransferStatus::PERMISSION_DENIED, std::move(data)); + } +} + +void OnTransferOut(scoped_ptr<MojoTransferOutCallback> callback, + UsbTransferStatus status, + scoped_refptr<net::IOBuffer> buffer, + size_t buffer_size) { + callback->Run(mojo::ConvertTo<TransferStatus>(status)); +} + +void OnControlTransferOutPermissionCheckComplete( + scoped_refptr<UsbDeviceHandle> device_handle, + ControlTransferParamsPtr params, + mojo::Array<uint8_t> data, + int timeout, + scoped_ptr<Device::ControlTransferOutCallback> callback, + bool allowed) { + if (allowed) { + scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); + const std::vector<uint8_t>& storage = data.storage(); + std::copy(storage.begin(), storage.end(), buffer->data()); + device_handle->ControlTransfer( + USB_DIRECTION_OUTBOUND, + mojo::ConvertTo<UsbDeviceHandle::TransferRequestType>(params->type), + mojo::ConvertTo<UsbDeviceHandle::TransferRecipient>(params->recipient), + params->request, params->value, params->index, buffer, data.size(), + timeout, base::Bind(&OnTransferOut, base::Passed(&callback))); + } else { + callback->Run(TransferStatus::PERMISSION_DENIED); + } +} + +mojo::Array<IsochronousPacketPtr> BuildIsochronousPacketArray( + mojo::Array<uint32_t> packet_lengths, + TransferStatus status) { + mojo::Array<IsochronousPacketPtr> packets(packet_lengths.size()); + for (size_t i = 0; i < packet_lengths.size(); ++i) { + packets[i] = IsochronousPacket::New(); + packets[i]->length = packet_lengths[i]; + packets[i]->status = status; + } + return packets; +} + +void OnIsochronousTransferIn( + scoped_ptr<Device::IsochronousTransferInCallback> callback, + scoped_refptr<net::IOBuffer> buffer, + const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) { + mojo::Array<uint8_t> data; + if (buffer) { + // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a + // std::vector<uint8_t> instead of net::IOBuffer. Then we could move + // instead of copy. + uint32_t buffer_size = + std::accumulate(packets.begin(), packets.end(), 0u, + [](const uint32_t& a, + const UsbDeviceHandle::IsochronousPacket& packet) { + return a + packet.length; + }); + std::vector<uint8_t> bytes(buffer_size); + std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); + data.Swap(&bytes); + } + callback->Run(std::move(data), + mojo::Array<IsochronousPacketPtr>::From(packets)); +} + +void OnIsochronousTransferOut( + scoped_ptr<Device::IsochronousTransferOutCallback> callback, + scoped_refptr<net::IOBuffer> buffer, + const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) { + callback->Run(mojo::Array<IsochronousPacketPtr>::From(packets)); +} + +} // namespace + +DeviceImpl::DeviceImpl(scoped_refptr<UsbDevice> device, + PermissionProviderPtr permission_provider, + mojo::InterfaceRequest<Device> request) + : binding_(this, std::move(request)), + device_(device), + permission_provider_(std::move(permission_provider)), + weak_factory_(this) { + // This object owns itself and will be destroyed if either the message pipe + // it is bound to is closed or the PermissionProvider it depends on is + // unavailable. + binding_.set_connection_error_handler([this]() { delete this; }); + permission_provider_.set_connection_error_handler([this]() { delete this; }); +} + +DeviceImpl::~DeviceImpl() { + CloseHandle(); +} + +void DeviceImpl::CloseHandle() { + if (device_handle_) + device_handle_->Close(); + device_handle_ = nullptr; +} + +void DeviceImpl::HasControlTransferPermission( + ControlTransferRecipient recipient, + uint16_t index, + const base::Callback<void(bool)>& callback) { + DCHECK(device_handle_); + const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); + + if (recipient == ControlTransferRecipient::INTERFACE || + recipient == ControlTransferRecipient::ENDPOINT) { + if (!config) { + callback.Run(false); + return; + } + + uint8_t interface_number = index & 0xff; + if (recipient == ControlTransferRecipient::ENDPOINT) { + if (!device_handle_->FindInterfaceByEndpoint(index & 0xff, + &interface_number)) { + callback.Run(false); + return; + } + } + + permission_provider_->HasInterfacePermission( + interface_number, config->configuration_value, + DeviceInfo::From(*device_), callback); + } else if (config) { + permission_provider_->HasConfigurationPermission( + config->configuration_value, DeviceInfo::From(*device_), callback); + } else { + // Client must already have device permission to have gotten this far. + callback.Run(true); + } +} + +void DeviceImpl::OnOpen(const OpenCallback& callback, + scoped_refptr<UsbDeviceHandle> handle) { + device_handle_ = handle; + callback.Run(handle ? OpenDeviceError::OK : OpenDeviceError::ACCESS_DENIED); +} + +void DeviceImpl::GetDeviceInfo(const GetDeviceInfoCallback& callback) { + callback.Run(DeviceInfo::From(*device_)); +} + +void DeviceImpl::GetConfiguration(const GetConfigurationCallback& callback) { + const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); + callback.Run(config ? config->configuration_value : 0); +} + +void DeviceImpl::Open(const OpenCallback& callback) { + device_->Open( + base::Bind(&DeviceImpl::OnOpen, weak_factory_.GetWeakPtr(), callback)); +} + +void DeviceImpl::Close(const CloseCallback& callback) { + CloseHandle(); + callback.Run(); +} + +void DeviceImpl::SetConfiguration(uint8_t value, + const SetConfigurationCallback& callback) { + if (!device_handle_) { + callback.Run(false); + return; + } + + auto set_configuration = + base::Bind(&UsbDeviceHandle::SetConfiguration, device_handle_, value); + permission_provider_->HasConfigurationPermission( + value, DeviceInfo::From(*device_), + base::Bind(&OnPermissionCheckComplete, WrapMojoCallback(callback), + set_configuration)); +} + +void DeviceImpl::ClaimInterface(uint8_t interface_number, + const ClaimInterfaceCallback& callback) { + if (!device_handle_) { + callback.Run(false); + return; + } + + const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); + if (!config) { + callback.Run(false); + return; + } + + auto claim_interface = base::Bind(&UsbDeviceHandle::ClaimInterface, + device_handle_, interface_number); + permission_provider_->HasInterfacePermission( + interface_number, config->configuration_value, DeviceInfo::From(*device_), + base::Bind(&OnPermissionCheckComplete, WrapMojoCallback(callback), + claim_interface)); +} + +void DeviceImpl::ReleaseInterface(uint8_t interface_number, + const ReleaseInterfaceCallback& callback) { + if (!device_handle_) { + callback.Run(false); + return; + } + + device_handle_->ReleaseInterface(interface_number, + WrapMojoCallback(callback)); +} + +void DeviceImpl::SetInterfaceAlternateSetting( + uint8_t interface_number, + uint8_t alternate_setting, + const SetInterfaceAlternateSettingCallback& callback) { + if (!device_handle_) { + callback.Run(false); + return; + } + + device_handle_->SetInterfaceAlternateSetting( + interface_number, alternate_setting, WrapMojoCallback(callback)); +} + +void DeviceImpl::Reset(const ResetCallback& callback) { + if (!device_handle_) { + callback.Run(false); + return; + } + + device_handle_->ResetDevice(WrapMojoCallback(callback)); +} + +void DeviceImpl::ClearHalt(uint8_t endpoint, + const ClearHaltCallback& callback) { + if (!device_handle_) { + callback.Run(false); + return; + } + + device_handle_->ClearHalt(endpoint, WrapMojoCallback(callback)); +} + +void DeviceImpl::ControlTransferIn(ControlTransferParamsPtr params, + uint32_t length, + uint32_t timeout, + const ControlTransferInCallback& callback) { + if (!device_handle_) { + callback.Run(TransferStatus::TRANSFER_ERROR, mojo::Array<uint8_t>()); + return; + } + + auto callback_ptr = make_scoped_ptr(new ControlTransferInCallback(callback)); + ControlTransferRecipient recipient = params->recipient; + uint16_t index = params->index; + HasControlTransferPermission( + recipient, index, + base::Bind(&OnControlTransferInPermissionCheckComplete, device_handle_, + base::Passed(¶ms), length, timeout, + base::Passed(&callback_ptr))); +} + +void DeviceImpl::ControlTransferOut( + ControlTransferParamsPtr params, + mojo::Array<uint8_t> data, + uint32_t timeout, + const ControlTransferOutCallback& callback) { + if (!device_handle_) { + callback.Run(TransferStatus::TRANSFER_ERROR); + return; + } + + auto callback_ptr = make_scoped_ptr(new ControlTransferOutCallback(callback)); + ControlTransferRecipient recipient = params->recipient; + uint16_t index = params->index; + HasControlTransferPermission( + recipient, index, + base::Bind(&OnControlTransferOutPermissionCheckComplete, device_handle_, + base::Passed(¶ms), base::Passed(&data), timeout, + base::Passed(&callback_ptr))); +} + +void DeviceImpl::GenericTransferIn(uint8_t endpoint_number, + uint32_t length, + uint32_t timeout, + const GenericTransferInCallback& callback) { + if (!device_handle_) { + callback.Run(TransferStatus::TRANSFER_ERROR, mojo::Array<uint8_t>()); + return; + } + + auto callback_ptr = make_scoped_ptr(new GenericTransferInCallback(callback)); + uint8_t endpoint_address = endpoint_number | 0x80; + scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(length); + device_handle_->GenericTransfer( + USB_DIRECTION_INBOUND, endpoint_address, buffer, length, timeout, + base::Bind(&OnTransferIn, base::Passed(&callback_ptr))); +} + +void DeviceImpl::GenericTransferOut( + uint8_t endpoint_number, + mojo::Array<uint8_t> data, + uint32_t timeout, + const GenericTransferOutCallback& callback) { + if (!device_handle_) { + callback.Run(TransferStatus::TRANSFER_ERROR); + return; + } + + auto callback_ptr = make_scoped_ptr(new GenericTransferOutCallback(callback)); + uint8_t endpoint_address = endpoint_number; + scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); + const std::vector<uint8_t>& storage = data.storage(); + std::copy(storage.begin(), storage.end(), buffer->data()); + device_handle_->GenericTransfer( + USB_DIRECTION_OUTBOUND, endpoint_address, buffer, data.size(), timeout, + base::Bind(&OnTransferOut, base::Passed(&callback_ptr))); +} + +void DeviceImpl::IsochronousTransferIn( + uint8_t endpoint_number, + mojo::Array<uint32_t> packet_lengths, + uint32_t timeout, + const IsochronousTransferInCallback& callback) { + if (!device_handle_) { + callback.Run(mojo::Array<uint8_t>(), + BuildIsochronousPacketArray(std::move(packet_lengths), + TransferStatus::TRANSFER_ERROR)); + return; + } + + auto callback_ptr = + make_scoped_ptr(new IsochronousTransferInCallback(callback)); + uint8_t endpoint_address = endpoint_number | 0x80; + device_handle_->IsochronousTransferIn( + endpoint_address, packet_lengths.storage(), timeout, + base::Bind(&OnIsochronousTransferIn, base::Passed(&callback_ptr))); +} + +void DeviceImpl::IsochronousTransferOut( + uint8_t endpoint_number, + mojo::Array<uint8_t> data, + mojo::Array<uint32_t> packet_lengths, + uint32_t timeout, + const IsochronousTransferOutCallback& callback) { + if (!device_handle_) { + callback.Run(BuildIsochronousPacketArray(std::move(packet_lengths), + TransferStatus::TRANSFER_ERROR)); + return; + } + + auto callback_ptr = + make_scoped_ptr(new IsochronousTransferOutCallback(callback)); + uint8_t endpoint_address = endpoint_number; + scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); + { + const std::vector<uint8_t>& storage = data.storage(); + std::copy(storage.begin(), storage.end(), buffer->data()); + } + device_handle_->IsochronousTransferOut( + endpoint_address, buffer, packet_lengths.storage(), timeout, + base::Bind(&OnIsochronousTransferOut, base::Passed(&callback_ptr))); +} + +} // namespace usb +} // namespace device |
