// Copyright (c) 2012 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 "chrome/browser/extensions/api/usb/usb_device_resource.h" #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/synchronization/lock.h" #include "chrome/browser/extensions/api/api_resource_event_notifier.h" #include "chrome/browser/extensions/api/api_resource.h" #include "chrome/browser/usb/usb_device.h" #include "chrome/common/extensions/api/experimental_usb.h" using extensions::api::experimental_usb::ControlTransferInfo; using extensions::api::experimental_usb::GenericTransferInfo; using extensions::api::experimental_usb::IsochronousTransferInfo; using std::string; using std::vector; namespace { static const char* kDirectionIn = "in"; static const char* kDirectionOut = "out"; static const char* kRequestTypeStandard = "standard"; static const char* kRequestTypeClass = "class"; static const char* kRequestTypeVendor = "vendor"; static const char* kRequestTypeReserved = "reserved"; static const char* kRecipientDevice = "device"; static const char* kRecipientInterface = "interface"; static const char* kRecipientEndpoint = "endpoint"; static const char* kRecipientOther = "other"; static const char* kErrorGeneric = "Transfer failed"; static const char* kErrorTimeout = "Transfer timed out"; static const char* kErrorCancelled = "Transfer was cancelled"; static const char* kErrorStalled = "Transfer stalled"; static const char* kErrorDisconnect = "Device disconnected"; static const char* kErrorOverflow = "Inbound transfer overflow"; static const char* kErrorTransferLength = "Transfer length is insufficient"; static bool ConvertDirection(const string& input, UsbDevice::TransferDirection* output) { if (input == kDirectionIn) { *output = UsbDevice::INBOUND; return true; } else if (input == kDirectionOut) { *output = UsbDevice::OUTBOUND; return true; } return false; } static bool ConvertRequestType(const string& input, UsbDevice::TransferRequestType* output) { if (input == kRequestTypeStandard) { *output = UsbDevice::STANDARD; return true; } else if (input == kRequestTypeClass) { *output = UsbDevice::CLASS; return true; } else if (input == kRequestTypeVendor) { *output = UsbDevice::VENDOR; return true; } else if (input == kRequestTypeReserved) { *output = UsbDevice::RESERVED; return true; } return false; } static bool ConvertRecipient(const string& input, UsbDevice::TransferRecipient* output) { if (input == kRecipientDevice) { *output = UsbDevice::DEVICE; return true; } else if (input == kRecipientInterface) { *output = UsbDevice::INTERFACE; return true; } else if (input == kRecipientEndpoint) { *output = UsbDevice::ENDPOINT; return true; } else if (input == kRecipientOther) { *output = UsbDevice::OTHER; return true; } return false; } template static bool GetTransferSize(const T& input, size_t* output) { if (input.direction == kDirectionIn) { const int* length = input.length.get(); if (length) { *output = *length; return true; } } else if (input.direction == kDirectionOut) { if (input.data.get()) { *output = input.data->size(); return true; } } return false; } template static scoped_refptr CreateBufferForTransfer(const T& input) { size_t size = 0; if (!GetTransferSize(input, &size)) return NULL; // Allocate a |size|-bytes buffer, or a one-byte buffer if |size| is 0. This // is due to an impedance mismatch between IOBuffer and URBs. An IOBuffer // cannot represent a zero-length buffer, while an URB can. scoped_refptr buffer = new net::IOBuffer(std::max( static_cast(1), size)); if (!input.data.get()) return buffer; memcpy(buffer->data(), input.data->data(), size); return buffer; } static const char* ConvertTransferStatusToErrorString( const UsbTransferStatus status) { switch (status) { case USB_TRANSFER_COMPLETED: return ""; case USB_TRANSFER_ERROR: return kErrorGeneric; case USB_TRANSFER_TIMEOUT: return kErrorTimeout; case USB_TRANSFER_CANCELLED: return kErrorCancelled; case USB_TRANSFER_STALLED: return kErrorStalled; case USB_TRANSFER_DISCONNECT: return kErrorDisconnect; case USB_TRANSFER_OVERFLOW: return kErrorOverflow; case USB_TRANSFER_LENGTH_SHORT: return kErrorTransferLength; } NOTREACHED(); return ""; } } // namespace namespace extensions { UsbDeviceResource::UsbDeviceResource(const std::string& owner_extension_id, ApiResourceEventNotifier* notifier, UsbDevice* device) : ApiResource(owner_extension_id, notifier), device_(device) {} UsbDeviceResource::~UsbDeviceResource() { Close(); } void UsbDeviceResource::Close() { device_->Close(); } void UsbDeviceResource::ControlTransfer(const ControlTransferInfo& transfer) { UsbDevice::TransferDirection direction; UsbDevice::TransferRequestType request_type; UsbDevice::TransferRecipient recipient; size_t size; scoped_refptr buffer = CreateBufferForTransfer(transfer); if (!ConvertDirection(transfer.direction, &direction) || !ConvertRequestType(transfer.request_type, &request_type) || !ConvertRecipient(transfer.recipient, &recipient) || !GetTransferSize(transfer, &size) || !buffer) { LOG(INFO) << "Malformed transfer parameters."; return; } device_->ControlTransfer(direction, request_type, recipient, transfer.request, transfer.value, transfer.index, buffer, size, 0, base::Bind(&UsbDeviceResource::TransferComplete, base::Unretained(this))); } void UsbDeviceResource::InterruptTransfer(const GenericTransferInfo& transfer) { size_t size; UsbDevice::TransferDirection direction; scoped_refptr buffer = CreateBufferForTransfer(transfer); if (!ConvertDirection(transfer.direction, &direction) || !GetTransferSize(transfer, &size) || !buffer) { LOG(INFO) << "Malformed transfer parameters."; return; } device_->InterruptTransfer(direction, transfer.endpoint, buffer, size, 0, base::Bind(&UsbDeviceResource::TransferComplete, base::Unretained(this))); } void UsbDeviceResource::BulkTransfer(const GenericTransferInfo& transfer) { size_t size; UsbDevice::TransferDirection direction; scoped_refptr buffer = CreateBufferForTransfer(transfer); if (!ConvertDirection(transfer.direction, &direction) || !GetTransferSize(transfer, &size) || !buffer) { LOG(INFO) << "Malformed transfer parameters."; return; } device_->BulkTransfer(direction, transfer.endpoint, buffer, size, 0, base::Bind(&UsbDeviceResource::TransferComplete, base::Unretained(this))); } void UsbDeviceResource::IsochronousTransfer( const IsochronousTransferInfo& transfer) { const GenericTransferInfo& generic_transfer = transfer.transfer_info; size_t size; UsbDevice::TransferDirection direction; scoped_refptr buffer = CreateBufferForTransfer( generic_transfer); if (!ConvertDirection(generic_transfer.direction, &direction) || !GetTransferSize(generic_transfer, &size) || !buffer) { LOG(INFO) << "Malformed transfer parameters."; return; } device_->IsochronousTransfer(direction, generic_transfer.endpoint, buffer, size, transfer.packets, transfer.packet_length, 0, base::Bind( &UsbDeviceResource::TransferComplete, base::Unretained(this))); } void UsbDeviceResource::TransferComplete(UsbTransferStatus status, scoped_refptr buffer, size_t length) { base::BinaryValue* response_buffer = base::BinaryValue::CreateWithCopiedBuffer(buffer->data(), length); event_notifier()->OnTransferComplete(status, ConvertTransferStatusToErrorString(status), response_buffer); } } // namespace extensions