| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "device/devices_app/usb/device_impl.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <numeric> | |
| 11 #include <utility> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "base/bind.h" | |
| 15 #include "base/callback.h" | |
| 16 #include "base/stl_util.h" | |
| 17 #include "device/devices_app/usb/type_converters.h" | |
| 18 #include "device/usb/usb_descriptors.h" | |
| 19 #include "device/usb/usb_device.h" | |
| 20 #include "net/base/io_buffer.h" | |
| 21 | |
| 22 namespace device { | |
| 23 namespace usb { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 using MojoTransferInCallback = | |
| 28 mojo::Callback<void(TransferStatus, mojo::Array<uint8_t>)>; | |
| 29 | |
| 30 using MojoTransferOutCallback = mojo::Callback<void(TransferStatus)>; | |
| 31 | |
| 32 template <typename... Args> | |
| 33 void CallMojoCallback(scoped_ptr<mojo::Callback<void(Args...)>> callback, | |
| 34 Args... args) { | |
| 35 callback->Run(args...); | |
| 36 } | |
| 37 | |
| 38 // Generic wrapper to convert a Mojo callback to something we can rebind and | |
| 39 // pass around. This is only usable for callbacks with no move-only arguments. | |
| 40 template <typename... Args> | |
| 41 base::Callback<void(Args...)> WrapMojoCallback( | |
| 42 const mojo::Callback<void(Args...)>& callback) { | |
| 43 // mojo::Callback is not thread safe. By wrapping |callback| in a scoped_ptr | |
| 44 // we guarantee that it will be freed when CallMojoCallback is run and not | |
| 45 // retained until the base::Callback is destroyed, which could happen on any | |
| 46 // thread. This pattern is also used below in places where this generic | |
| 47 // wrapper is not used. | |
| 48 auto callback_ptr = | |
| 49 make_scoped_ptr(new mojo::Callback<void(Args...)>(callback)); | |
| 50 return base::Bind(&CallMojoCallback<Args...>, base::Passed(&callback_ptr)); | |
| 51 } | |
| 52 | |
| 53 void OnPermissionCheckComplete( | |
| 54 const base::Callback<void(bool)>& callback, | |
| 55 const base::Callback<void(const base::Callback<void(bool)>&)>& action, | |
| 56 bool allowed) { | |
| 57 if (allowed) | |
| 58 action.Run(callback); | |
| 59 else | |
| 60 callback.Run(false); | |
| 61 } | |
| 62 | |
| 63 scoped_refptr<net::IOBuffer> CreateTransferBuffer(size_t size) { | |
| 64 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer( | |
| 65 std::max(static_cast<size_t>(1u), static_cast<size_t>(size))); | |
| 66 return buffer; | |
| 67 } | |
| 68 | |
| 69 void OnTransferIn(scoped_ptr<MojoTransferInCallback> callback, | |
| 70 UsbTransferStatus status, | |
| 71 scoped_refptr<net::IOBuffer> buffer, | |
| 72 size_t buffer_size) { | |
| 73 mojo::Array<uint8_t> data; | |
| 74 if (buffer) { | |
| 75 // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a | |
| 76 // std::vector<uint8_t> instead of net::IOBuffer. Then we could move | |
| 77 // instead of copy. | |
| 78 std::vector<uint8_t> bytes(buffer_size); | |
| 79 std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); | |
| 80 data.Swap(&bytes); | |
| 81 } | |
| 82 callback->Run(mojo::ConvertTo<TransferStatus>(status), std::move(data)); | |
| 83 } | |
| 84 | |
| 85 void OnControlTransferInPermissionCheckComplete( | |
| 86 scoped_refptr<UsbDeviceHandle> device_handle, | |
| 87 ControlTransferParamsPtr params, | |
| 88 int length, | |
| 89 int timeout, | |
| 90 scoped_ptr<Device::ControlTransferInCallback> callback, | |
| 91 bool allowed) { | |
| 92 if (allowed) { | |
| 93 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(length); | |
| 94 device_handle->ControlTransfer( | |
| 95 USB_DIRECTION_INBOUND, | |
| 96 mojo::ConvertTo<UsbDeviceHandle::TransferRequestType>(params->type), | |
| 97 mojo::ConvertTo<UsbDeviceHandle::TransferRecipient>(params->recipient), | |
| 98 params->request, params->value, params->index, buffer, length, timeout, | |
| 99 base::Bind(&OnTransferIn, base::Passed(&callback))); | |
| 100 } else { | |
| 101 mojo::Array<uint8_t> data; | |
| 102 callback->Run(TransferStatus::PERMISSION_DENIED, std::move(data)); | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 void OnTransferOut(scoped_ptr<MojoTransferOutCallback> callback, | |
| 107 UsbTransferStatus status, | |
| 108 scoped_refptr<net::IOBuffer> buffer, | |
| 109 size_t buffer_size) { | |
| 110 callback->Run(mojo::ConvertTo<TransferStatus>(status)); | |
| 111 } | |
| 112 | |
| 113 void OnControlTransferOutPermissionCheckComplete( | |
| 114 scoped_refptr<UsbDeviceHandle> device_handle, | |
| 115 ControlTransferParamsPtr params, | |
| 116 mojo::Array<uint8_t> data, | |
| 117 int timeout, | |
| 118 scoped_ptr<Device::ControlTransferOutCallback> callback, | |
| 119 bool allowed) { | |
| 120 if (allowed) { | |
| 121 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); | |
| 122 const std::vector<uint8_t>& storage = data.storage(); | |
| 123 std::copy(storage.begin(), storage.end(), buffer->data()); | |
| 124 device_handle->ControlTransfer( | |
| 125 USB_DIRECTION_OUTBOUND, | |
| 126 mojo::ConvertTo<UsbDeviceHandle::TransferRequestType>(params->type), | |
| 127 mojo::ConvertTo<UsbDeviceHandle::TransferRecipient>(params->recipient), | |
| 128 params->request, params->value, params->index, buffer, data.size(), | |
| 129 timeout, base::Bind(&OnTransferOut, base::Passed(&callback))); | |
| 130 } else { | |
| 131 callback->Run(TransferStatus::PERMISSION_DENIED); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 mojo::Array<IsochronousPacketPtr> BuildIsochronousPacketArray( | |
| 136 mojo::Array<uint32_t> packet_lengths, | |
| 137 TransferStatus status) { | |
| 138 mojo::Array<IsochronousPacketPtr> packets(packet_lengths.size()); | |
| 139 for (size_t i = 0; i < packet_lengths.size(); ++i) { | |
| 140 packets[i] = IsochronousPacket::New(); | |
| 141 packets[i]->length = packet_lengths[i]; | |
| 142 packets[i]->status = status; | |
| 143 } | |
| 144 return packets; | |
| 145 } | |
| 146 | |
| 147 void OnIsochronousTransferIn( | |
| 148 scoped_ptr<Device::IsochronousTransferInCallback> callback, | |
| 149 scoped_refptr<net::IOBuffer> buffer, | |
| 150 const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) { | |
| 151 mojo::Array<uint8_t> data; | |
| 152 if (buffer) { | |
| 153 // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a | |
| 154 // std::vector<uint8_t> instead of net::IOBuffer. Then we could move | |
| 155 // instead of copy. | |
| 156 uint32_t buffer_size = | |
| 157 std::accumulate(packets.begin(), packets.end(), 0u, | |
| 158 [](const uint32_t& a, | |
| 159 const UsbDeviceHandle::IsochronousPacket& packet) { | |
| 160 return a + packet.length; | |
| 161 }); | |
| 162 std::vector<uint8_t> bytes(buffer_size); | |
| 163 std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); | |
| 164 data.Swap(&bytes); | |
| 165 } | |
| 166 callback->Run(std::move(data), | |
| 167 mojo::Array<IsochronousPacketPtr>::From(packets)); | |
| 168 } | |
| 169 | |
| 170 void OnIsochronousTransferOut( | |
| 171 scoped_ptr<Device::IsochronousTransferOutCallback> callback, | |
| 172 scoped_refptr<net::IOBuffer> buffer, | |
| 173 const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) { | |
| 174 callback->Run(mojo::Array<IsochronousPacketPtr>::From(packets)); | |
| 175 } | |
| 176 | |
| 177 } // namespace | |
| 178 | |
| 179 DeviceImpl::DeviceImpl(scoped_refptr<UsbDevice> device, | |
| 180 PermissionProviderPtr permission_provider, | |
| 181 mojo::InterfaceRequest<Device> request) | |
| 182 : binding_(this, std::move(request)), | |
| 183 device_(device), | |
| 184 permission_provider_(std::move(permission_provider)), | |
| 185 weak_factory_(this) { | |
| 186 // This object owns itself and will be destroyed if either the message pipe | |
| 187 // it is bound to is closed or the PermissionProvider it depends on is | |
| 188 // unavailable. | |
| 189 binding_.set_connection_error_handler([this]() { delete this; }); | |
| 190 permission_provider_.set_connection_error_handler([this]() { delete this; }); | |
| 191 } | |
| 192 | |
| 193 DeviceImpl::~DeviceImpl() { | |
| 194 CloseHandle(); | |
| 195 } | |
| 196 | |
| 197 void DeviceImpl::CloseHandle() { | |
| 198 if (device_handle_) | |
| 199 device_handle_->Close(); | |
| 200 device_handle_ = nullptr; | |
| 201 } | |
| 202 | |
| 203 void DeviceImpl::HasControlTransferPermission( | |
| 204 ControlTransferRecipient recipient, | |
| 205 uint16_t index, | |
| 206 const base::Callback<void(bool)>& callback) { | |
| 207 DCHECK(device_handle_); | |
| 208 const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); | |
| 209 | |
| 210 if (recipient == ControlTransferRecipient::INTERFACE || | |
| 211 recipient == ControlTransferRecipient::ENDPOINT) { | |
| 212 if (!config) { | |
| 213 callback.Run(false); | |
| 214 return; | |
| 215 } | |
| 216 | |
| 217 uint8_t interface_number = index & 0xff; | |
| 218 if (recipient == ControlTransferRecipient::ENDPOINT) { | |
| 219 if (!device_handle_->FindInterfaceByEndpoint(index & 0xff, | |
| 220 &interface_number)) { | |
| 221 callback.Run(false); | |
| 222 return; | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 permission_provider_->HasInterfacePermission( | |
| 227 interface_number, config->configuration_value, | |
| 228 DeviceInfo::From(*device_), callback); | |
| 229 } else if (config) { | |
| 230 permission_provider_->HasConfigurationPermission( | |
| 231 config->configuration_value, DeviceInfo::From(*device_), callback); | |
| 232 } else { | |
| 233 // Client must already have device permission to have gotten this far. | |
| 234 callback.Run(true); | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 void DeviceImpl::OnOpen(const OpenCallback& callback, | |
| 239 scoped_refptr<UsbDeviceHandle> handle) { | |
| 240 device_handle_ = handle; | |
| 241 callback.Run(handle ? OpenDeviceError::OK : OpenDeviceError::ACCESS_DENIED); | |
| 242 } | |
| 243 | |
| 244 void DeviceImpl::GetDeviceInfo(const GetDeviceInfoCallback& callback) { | |
| 245 callback.Run(DeviceInfo::From(*device_)); | |
| 246 } | |
| 247 | |
| 248 void DeviceImpl::GetConfiguration(const GetConfigurationCallback& callback) { | |
| 249 const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); | |
| 250 callback.Run(config ? config->configuration_value : 0); | |
| 251 } | |
| 252 | |
| 253 void DeviceImpl::Open(const OpenCallback& callback) { | |
| 254 device_->Open( | |
| 255 base::Bind(&DeviceImpl::OnOpen, weak_factory_.GetWeakPtr(), callback)); | |
| 256 } | |
| 257 | |
| 258 void DeviceImpl::Close(const CloseCallback& callback) { | |
| 259 CloseHandle(); | |
| 260 callback.Run(); | |
| 261 } | |
| 262 | |
| 263 void DeviceImpl::SetConfiguration(uint8_t value, | |
| 264 const SetConfigurationCallback& callback) { | |
| 265 if (!device_handle_) { | |
| 266 callback.Run(false); | |
| 267 return; | |
| 268 } | |
| 269 | |
| 270 auto set_configuration = | |
| 271 base::Bind(&UsbDeviceHandle::SetConfiguration, device_handle_, value); | |
| 272 permission_provider_->HasConfigurationPermission( | |
| 273 value, DeviceInfo::From(*device_), | |
| 274 base::Bind(&OnPermissionCheckComplete, WrapMojoCallback(callback), | |
| 275 set_configuration)); | |
| 276 } | |
| 277 | |
| 278 void DeviceImpl::ClaimInterface(uint8_t interface_number, | |
| 279 const ClaimInterfaceCallback& callback) { | |
| 280 if (!device_handle_) { | |
| 281 callback.Run(false); | |
| 282 return; | |
| 283 } | |
| 284 | |
| 285 const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); | |
| 286 if (!config) { | |
| 287 callback.Run(false); | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 auto claim_interface = base::Bind(&UsbDeviceHandle::ClaimInterface, | |
| 292 device_handle_, interface_number); | |
| 293 permission_provider_->HasInterfacePermission( | |
| 294 interface_number, config->configuration_value, DeviceInfo::From(*device_), | |
| 295 base::Bind(&OnPermissionCheckComplete, WrapMojoCallback(callback), | |
| 296 claim_interface)); | |
| 297 } | |
| 298 | |
| 299 void DeviceImpl::ReleaseInterface(uint8_t interface_number, | |
| 300 const ReleaseInterfaceCallback& callback) { | |
| 301 if (!device_handle_) { | |
| 302 callback.Run(false); | |
| 303 return; | |
| 304 } | |
| 305 | |
| 306 device_handle_->ReleaseInterface(interface_number, | |
| 307 WrapMojoCallback(callback)); | |
| 308 } | |
| 309 | |
| 310 void DeviceImpl::SetInterfaceAlternateSetting( | |
| 311 uint8_t interface_number, | |
| 312 uint8_t alternate_setting, | |
| 313 const SetInterfaceAlternateSettingCallback& callback) { | |
| 314 if (!device_handle_) { | |
| 315 callback.Run(false); | |
| 316 return; | |
| 317 } | |
| 318 | |
| 319 device_handle_->SetInterfaceAlternateSetting( | |
| 320 interface_number, alternate_setting, WrapMojoCallback(callback)); | |
| 321 } | |
| 322 | |
| 323 void DeviceImpl::Reset(const ResetCallback& callback) { | |
| 324 if (!device_handle_) { | |
| 325 callback.Run(false); | |
| 326 return; | |
| 327 } | |
| 328 | |
| 329 device_handle_->ResetDevice(WrapMojoCallback(callback)); | |
| 330 } | |
| 331 | |
| 332 void DeviceImpl::ClearHalt(uint8_t endpoint, | |
| 333 const ClearHaltCallback& callback) { | |
| 334 if (!device_handle_) { | |
| 335 callback.Run(false); | |
| 336 return; | |
| 337 } | |
| 338 | |
| 339 device_handle_->ClearHalt(endpoint, WrapMojoCallback(callback)); | |
| 340 } | |
| 341 | |
| 342 void DeviceImpl::ControlTransferIn(ControlTransferParamsPtr params, | |
| 343 uint32_t length, | |
| 344 uint32_t timeout, | |
| 345 const ControlTransferInCallback& callback) { | |
| 346 if (!device_handle_) { | |
| 347 callback.Run(TransferStatus::TRANSFER_ERROR, mojo::Array<uint8_t>()); | |
| 348 return; | |
| 349 } | |
| 350 | |
| 351 auto callback_ptr = make_scoped_ptr(new ControlTransferInCallback(callback)); | |
| 352 ControlTransferRecipient recipient = params->recipient; | |
| 353 uint16_t index = params->index; | |
| 354 HasControlTransferPermission( | |
| 355 recipient, index, | |
| 356 base::Bind(&OnControlTransferInPermissionCheckComplete, device_handle_, | |
| 357 base::Passed(¶ms), length, timeout, | |
| 358 base::Passed(&callback_ptr))); | |
| 359 } | |
| 360 | |
| 361 void DeviceImpl::ControlTransferOut( | |
| 362 ControlTransferParamsPtr params, | |
| 363 mojo::Array<uint8_t> data, | |
| 364 uint32_t timeout, | |
| 365 const ControlTransferOutCallback& callback) { | |
| 366 if (!device_handle_) { | |
| 367 callback.Run(TransferStatus::TRANSFER_ERROR); | |
| 368 return; | |
| 369 } | |
| 370 | |
| 371 auto callback_ptr = make_scoped_ptr(new ControlTransferOutCallback(callback)); | |
| 372 ControlTransferRecipient recipient = params->recipient; | |
| 373 uint16_t index = params->index; | |
| 374 HasControlTransferPermission( | |
| 375 recipient, index, | |
| 376 base::Bind(&OnControlTransferOutPermissionCheckComplete, device_handle_, | |
| 377 base::Passed(¶ms), base::Passed(&data), timeout, | |
| 378 base::Passed(&callback_ptr))); | |
| 379 } | |
| 380 | |
| 381 void DeviceImpl::GenericTransferIn(uint8_t endpoint_number, | |
| 382 uint32_t length, | |
| 383 uint32_t timeout, | |
| 384 const GenericTransferInCallback& callback) { | |
| 385 if (!device_handle_) { | |
| 386 callback.Run(TransferStatus::TRANSFER_ERROR, mojo::Array<uint8_t>()); | |
| 387 return; | |
| 388 } | |
| 389 | |
| 390 auto callback_ptr = make_scoped_ptr(new GenericTransferInCallback(callback)); | |
| 391 uint8_t endpoint_address = endpoint_number | 0x80; | |
| 392 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(length); | |
| 393 device_handle_->GenericTransfer( | |
| 394 USB_DIRECTION_INBOUND, endpoint_address, buffer, length, timeout, | |
| 395 base::Bind(&OnTransferIn, base::Passed(&callback_ptr))); | |
| 396 } | |
| 397 | |
| 398 void DeviceImpl::GenericTransferOut( | |
| 399 uint8_t endpoint_number, | |
| 400 mojo::Array<uint8_t> data, | |
| 401 uint32_t timeout, | |
| 402 const GenericTransferOutCallback& callback) { | |
| 403 if (!device_handle_) { | |
| 404 callback.Run(TransferStatus::TRANSFER_ERROR); | |
| 405 return; | |
| 406 } | |
| 407 | |
| 408 auto callback_ptr = make_scoped_ptr(new GenericTransferOutCallback(callback)); | |
| 409 uint8_t endpoint_address = endpoint_number; | |
| 410 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); | |
| 411 const std::vector<uint8_t>& storage = data.storage(); | |
| 412 std::copy(storage.begin(), storage.end(), buffer->data()); | |
| 413 device_handle_->GenericTransfer( | |
| 414 USB_DIRECTION_OUTBOUND, endpoint_address, buffer, data.size(), timeout, | |
| 415 base::Bind(&OnTransferOut, base::Passed(&callback_ptr))); | |
| 416 } | |
| 417 | |
| 418 void DeviceImpl::IsochronousTransferIn( | |
| 419 uint8_t endpoint_number, | |
| 420 mojo::Array<uint32_t> packet_lengths, | |
| 421 uint32_t timeout, | |
| 422 const IsochronousTransferInCallback& callback) { | |
| 423 if (!device_handle_) { | |
| 424 callback.Run(mojo::Array<uint8_t>(), | |
| 425 BuildIsochronousPacketArray(std::move(packet_lengths), | |
| 426 TransferStatus::TRANSFER_ERROR)); | |
| 427 return; | |
| 428 } | |
| 429 | |
| 430 auto callback_ptr = | |
| 431 make_scoped_ptr(new IsochronousTransferInCallback(callback)); | |
| 432 uint8_t endpoint_address = endpoint_number | 0x80; | |
| 433 device_handle_->IsochronousTransferIn( | |
| 434 endpoint_address, packet_lengths.storage(), timeout, | |
| 435 base::Bind(&OnIsochronousTransferIn, base::Passed(&callback_ptr))); | |
| 436 } | |
| 437 | |
| 438 void DeviceImpl::IsochronousTransferOut( | |
| 439 uint8_t endpoint_number, | |
| 440 mojo::Array<uint8_t> data, | |
| 441 mojo::Array<uint32_t> packet_lengths, | |
| 442 uint32_t timeout, | |
| 443 const IsochronousTransferOutCallback& callback) { | |
| 444 if (!device_handle_) { | |
| 445 callback.Run(BuildIsochronousPacketArray(std::move(packet_lengths), | |
| 446 TransferStatus::TRANSFER_ERROR)); | |
| 447 return; | |
| 448 } | |
| 449 | |
| 450 auto callback_ptr = | |
| 451 make_scoped_ptr(new IsochronousTransferOutCallback(callback)); | |
| 452 uint8_t endpoint_address = endpoint_number; | |
| 453 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); | |
| 454 { | |
| 455 const std::vector<uint8_t>& storage = data.storage(); | |
| 456 std::copy(storage.begin(), storage.end(), buffer->data()); | |
| 457 } | |
| 458 device_handle_->IsochronousTransferOut( | |
| 459 endpoint_address, buffer, packet_lengths.storage(), timeout, | |
| 460 base::Bind(&OnIsochronousTransferOut, base::Passed(&callback_ptr))); | |
| 461 } | |
| 462 | |
| 463 } // namespace usb | |
| 464 } // namespace device | |
| OLD | NEW |