// 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 #include #include #include #include #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)>; using MojoTransferOutCallback = mojo::Callback; template void CallMojoCallback(scoped_ptr> 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 base::Callback WrapMojoCallback( const mojo::Callback& 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(callback)); return base::Bind(&CallMojoCallback, base::Passed(&callback_ptr)); } scoped_refptr CreateTransferBuffer(size_t size) { scoped_refptr buffer = new net::IOBuffer( std::max(static_cast(1u), static_cast(size))); return buffer; } void OnTransferIn(scoped_ptr callback, UsbTransferStatus status, scoped_refptr buffer, size_t buffer_size) { mojo::Array data; if (buffer) { // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a // std::vector instead of net::IOBuffer. Then we could move // instead of copy. std::vector bytes(buffer_size); std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); data.Swap(&bytes); } callback->Run(mojo::ConvertTo(status), std::move(data)); } void OnTransferOut(scoped_ptr callback, UsbTransferStatus status, scoped_refptr buffer, size_t buffer_size) { callback->Run(mojo::ConvertTo(status)); } mojo::Array BuildIsochronousPacketArray( mojo::Array packet_lengths, TransferStatus status) { mojo::Array 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 callback, scoped_refptr buffer, const std::vector& packets) { mojo::Array data; if (buffer) { // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a // std::vector 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 bytes(buffer_size); std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); data.Swap(&bytes); } callback->Run(std::move(data), mojo::Array::From(packets)); } void OnIsochronousTransferOut( scoped_ptr callback, scoped_refptr buffer, const std::vector& packets) { callback->Run(mojo::Array::From(packets)); } } // namespace DeviceImpl::DeviceImpl(scoped_refptr device, DeviceInfoPtr device_info, base::WeakPtr permission_provider, mojo::InterfaceRequest request) : device_(device), device_info_(std::move(device_info)), permission_provider_(permission_provider), observer_(this), binding_(this, std::move(request)), weak_factory_(this) { DCHECK(device_); // This object owns itself and will be destroyed if, // * the device is disconnected or // * the message pipe it is bound to is closed observer_.Add(device_.get()); binding_.set_connection_error_handler([this]() { delete this; }); } DeviceImpl::~DeviceImpl() { CloseHandle(); } void DeviceImpl::CloseHandle() { if (device_handle_) device_handle_->Close(); device_handle_ = nullptr; } bool DeviceImpl::HasControlTransferPermission( ControlTransferRecipient recipient, uint16_t index) { DCHECK(device_handle_); const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); if (!permission_provider_) return false; if (recipient == ControlTransferRecipient::INTERFACE || recipient == ControlTransferRecipient::ENDPOINT) { if (!config) return false; uint8_t interface_number = index & 0xff; if (recipient == ControlTransferRecipient::ENDPOINT && !device_handle_->FindInterfaceByEndpoint(index & 0xff, &interface_number)) { return false; } return permission_provider_->HasInterfacePermission( interface_number, config->configuration_value, *device_info_); } else if (config) { return permission_provider_->HasConfigurationPermission( config->configuration_value, *device_info_); } else { // Client must already have device permission to have gotten this far. return true; } } void DeviceImpl::OnOpen(const OpenCallback& callback, scoped_refptr handle) { device_handle_ = handle; callback.Run(handle ? OpenDeviceError::OK : OpenDeviceError::ACCESS_DENIED); } void DeviceImpl::GetDeviceInfo(const GetDeviceInfoCallback& callback) { callback.Run(device_info_->Clone()); } void DeviceImpl::GetConfiguration(const GetConfigurationCallback& callback) { const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); callback.Run(config ? config->configuration_value : 0); } void DeviceImpl::Open(const OpenCallback& callback) { if (device_handle_) callback.Run(OpenDeviceError::ALREADY_OPEN); else 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; } if (permission_provider_ && permission_provider_->HasConfigurationPermission(value, *device_info_)) { device_handle_->SetConfiguration(value, WrapMojoCallback(callback)); } else { callback.Run(false); } } 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; } if (permission_provider_ && permission_provider_->HasInterfacePermission( interface_number, config->configuration_value, *device_info_)) { device_handle_->ClaimInterface(interface_number, WrapMojoCallback(callback)); } else { callback.Run(false); } } 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()); return; } if (HasControlTransferPermission(params->recipient, params->index)) { scoped_refptr buffer = CreateTransferBuffer(length); auto callback_ptr = make_scoped_ptr(new ControlTransferInCallback(callback)); device_handle_->ControlTransfer( USB_DIRECTION_INBOUND, mojo::ConvertTo(params->type), mojo::ConvertTo(params->recipient), params->request, params->value, params->index, buffer, length, timeout, base::Bind(&OnTransferIn, base::Passed(&callback_ptr))); } else { mojo::Array data; callback.Run(TransferStatus::PERMISSION_DENIED, std::move(data)); } } void DeviceImpl::ControlTransferOut( ControlTransferParamsPtr params, mojo::Array data, uint32_t timeout, const ControlTransferOutCallback& callback) { if (!device_handle_) { callback.Run(TransferStatus::TRANSFER_ERROR); return; } if (HasControlTransferPermission(params->recipient, params->index)) { scoped_refptr buffer = CreateTransferBuffer(data.size()); const std::vector& storage = data.storage(); std::copy(storage.begin(), storage.end(), buffer->data()); auto callback_ptr = make_scoped_ptr(new ControlTransferOutCallback(callback)); device_handle_->ControlTransfer( USB_DIRECTION_OUTBOUND, mojo::ConvertTo(params->type), mojo::ConvertTo(params->recipient), params->request, params->value, params->index, buffer, data.size(), timeout, base::Bind(&OnTransferOut, base::Passed(&callback_ptr))); } else { callback.Run(TransferStatus::PERMISSION_DENIED); } } 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()); return; } auto callback_ptr = make_scoped_ptr(new GenericTransferInCallback(callback)); uint8_t endpoint_address = endpoint_number | 0x80; scoped_refptr 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 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 buffer = CreateTransferBuffer(data.size()); const std::vector& 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 packet_lengths, uint32_t timeout, const IsochronousTransferInCallback& callback) { if (!device_handle_) { callback.Run(mojo::Array(), 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 data, mojo::Array 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 buffer = CreateTransferBuffer(data.size()); { const std::vector& 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))); } void DeviceImpl::OnDeviceRemoved(scoped_refptr device) { DCHECK_EQ(device_, device); delete this; } } // namespace usb } // namespace device