root/chrome/browser/extensions/api/usb/usb_api.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ConvertDirectionToApi
  2. ConvertSynchronizationTypeToApi
  3. ConvertTransferTypeToApi
  4. ConvertUsageTypeToApi
  5. ConvertDirection
  6. ConvertRequestType
  7. ConvertRecipient
  8. GetTransferSize
  9. CreateBufferForTransfer
  10. ConvertTransferStatusToErrorString
  11. RequestUsbDevicesAccessHelper
  12. RequestUsbDevicesAccess
  13. CreateTransferInfo
  14. PopulateConnectionHandle
  15. PopulateDevice
  16. PopulateInterfaceDescriptor
  17. PrePrepare
  18. Respond
  19. GetDeviceOrOrCompleteWithError
  20. GetDeviceHandleOrCompleteWithError
  21. RemoveUsbDeviceResource
  22. CompleteWithError
  23. OnCompleted
  24. ConvertDirectionSafely
  25. ConvertRequestTypeSafely
  26. ConvertRecipientSafely
  27. Prepare
  28. AsyncWorkStart
  29. OpenDevices
  30. SetDeviceForTest
  31. Prepare
  32. AsyncWorkStart
  33. Prepare
  34. AsyncWorkStart
  35. OnCompleted
  36. Prepare
  37. AsyncWorkStart
  38. Prepare
  39. AsyncWorkStart
  40. ConvertDirectionSafely
  41. ConvertSynchronizationTypeSafely
  42. ConvertTransferTypeSafely
  43. ConvertUsageTypeSafely
  44. Prepare
  45. AsyncWorkStart
  46. Prepare
  47. AsyncWorkStart
  48. Prepare
  49. AsyncWorkStart
  50. UsbSetInterfaceAlternateSettingFunction
  51. UsbSetInterfaceAlternateSettingFunction
  52. Prepare
  53. AsyncWorkStart
  54. Prepare
  55. AsyncWorkStart
  56. Prepare
  57. AsyncWorkStart
  58. Prepare
  59. AsyncWorkStart
  60. Prepare
  61. AsyncWorkStart
  62. Prepare
  63. AsyncWorkStart

// 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_api.h"

#include <string>
#include <vector>

#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "chrome/browser/extensions/api/usb/usb_device_resource.h"
#include "chrome/browser/usb/usb_device_handle.h"
#include "chrome/browser/usb/usb_service.h"
#include "chrome/common/extensions/api/usb.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/usb_device_permission.h"

namespace usb = extensions::api::usb;
namespace BulkTransfer = usb::BulkTransfer;
namespace ClaimInterface = usb::ClaimInterface;
namespace CloseDevice = usb::CloseDevice;
namespace ControlTransfer = usb::ControlTransfer;
namespace FindDevices = usb::FindDevices;
namespace GetDevices = usb::GetDevices;
namespace InterruptTransfer = usb::InterruptTransfer;
namespace IsochronousTransfer = usb::IsochronousTransfer;
namespace ListInterfaces = usb::ListInterfaces;
namespace OpenDevice = usb::OpenDevice;
namespace ReleaseInterface = usb::ReleaseInterface;
namespace RequestAccess = usb::RequestAccess;
namespace ResetDevice = usb::ResetDevice;
namespace SetInterfaceAlternateSetting = usb::SetInterfaceAlternateSetting;

using content::BrowserThread;
using std::string;
using std::vector;
using usb::ControlTransferInfo;
using usb::ConnectionHandle;
using usb::Device;
using usb::Direction;
using usb::EndpointDescriptor;
using usb::GenericTransferInfo;
using usb::InterfaceDescriptor;
using usb::IsochronousTransferInfo;
using usb::Recipient;
using usb::RequestType;
using usb::SynchronizationType;
using usb::TransferType;
using usb::UsageType;

typedef std::vector<scoped_refptr<UsbDevice> > DeviceVector;
typedef scoped_ptr<DeviceVector> ScopedDeviceVector;

namespace {

const char kDataKey[] = "data";
const char kResultCodeKey[] = "resultCode";

const char kErrorInitService[] = "Failed to initialize USB service.";

const char kErrorOpen[] = "Failed to open device.";
const char kErrorCancelled[] = "Transfer was cancelled.";
const char kErrorDisconnect[] = "Device disconnected.";
const char kErrorGeneric[] = "Transfer failed.";
#if !defined(OS_CHROMEOS)
const char kErrorNotSupported[] = "Not supported on this platform.";
#endif
const char kErrorOverflow[] = "Inbound transfer overflow.";
const char kErrorStalled[] = "Transfer stalled.";
const char kErrorTimeout[] = "Transfer timed out.";
const char kErrorTransferLength[] = "Transfer length is insufficient.";

const char kErrorCannotListInterfaces[] = "Error listing interfaces.";
const char kErrorCannotClaimInterface[] = "Error claiming interface.";
const char kErrorCannotReleaseInterface[] = "Error releasing interface.";
const char kErrorCannotSetInterfaceAlternateSetting[] =
    "Error setting alternate interface setting.";
const char kErrorConvertDirection[] = "Invalid transfer direction.";
const char kErrorConvertRecipient[] = "Invalid transfer recipient.";
const char kErrorConvertRequestType[] = "Invalid request type.";
const char kErrorConvertSynchronizationType[] = "Invalid synchronization type";
const char kErrorConvertTransferType[] = "Invalid endpoint type.";
const char kErrorConvertUsageType[] = "Invalid usage type.";
const char kErrorMalformedParameters[] = "Error parsing parameters.";
const char kErrorNoDevice[] = "No such device.";
const char kErrorPermissionDenied[] =
    "Permission to access device was denied";
const char kErrorInvalidTransferLength[] =
    "Transfer length must be a positive number less than 104,857,600.";
const char kErrorInvalidNumberOfPackets[] =
    "Number of packets must be a positive number less than 4,194,304.";
const char kErrorInvalidPacketLength[] = "Packet length must be a "
    "positive number less than 65,536.";
const char kErrorResetDevice[] =
    "Error resetting the device. The device has been closed.";

const size_t kMaxTransferLength = 100 * 1024 * 1024;
const int kMaxPackets = 4 * 1024 * 1024;
const int kMaxPacketLength = 64 * 1024;

UsbDevice* g_device_for_test = NULL;

bool ConvertDirectionToApi(const UsbEndpointDirection& input,
                           Direction* output) {
  switch (input) {
    case USB_DIRECTION_INBOUND:
      *output = usb::DIRECTION_IN;
      return true;
    case USB_DIRECTION_OUTBOUND:
      *output = usb::DIRECTION_OUT;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

bool ConvertSynchronizationTypeToApi(const UsbSynchronizationType& input,
                                     usb::SynchronizationType* output) {
  switch (input) {
    case USB_SYNCHRONIZATION_NONE:
      *output = usb::SYNCHRONIZATION_TYPE_NONE;
      return true;
    case USB_SYNCHRONIZATION_ASYNCHRONOUS:
      *output = usb::SYNCHRONIZATION_TYPE_ASYNCHRONOUS;
      return true;
    case USB_SYNCHRONIZATION_ADAPTIVE:
      *output = usb::SYNCHRONIZATION_TYPE_ADAPTIVE;
      return true;
    case USB_SYNCHRONIZATION_SYNCHRONOUS:
      *output = usb::SYNCHRONIZATION_TYPE_SYNCHRONOUS;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

bool ConvertTransferTypeToApi(
    const UsbTransferType& input,
    usb::TransferType* output) {
  switch (input) {
    case USB_TRANSFER_CONTROL:
      *output = usb::TRANSFER_TYPE_CONTROL;
      return true;
    case USB_TRANSFER_INTERRUPT:
      *output = usb::TRANSFER_TYPE_INTERRUPT;
      return true;
    case USB_TRANSFER_ISOCHRONOUS:
      *output = usb::TRANSFER_TYPE_ISOCHRONOUS;
      return true;
    case USB_TRANSFER_BULK:
      *output = usb::TRANSFER_TYPE_BULK;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

bool ConvertUsageTypeToApi(const UsbUsageType& input, usb::UsageType* output) {
  switch (input) {
    case USB_USAGE_DATA:
      *output = usb::USAGE_TYPE_DATA;
      return true;
    case USB_USAGE_FEEDBACK:
      *output = usb::USAGE_TYPE_FEEDBACK;
      return true;
    case USB_USAGE_EXPLICIT_FEEDBACK:
      *output = usb::USAGE_TYPE_EXPLICITFEEDBACK;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

bool ConvertDirection(const Direction& input,
                      UsbEndpointDirection* output) {
  switch (input) {
    case usb::DIRECTION_IN:
      *output = USB_DIRECTION_INBOUND;
      return true;
    case usb::DIRECTION_OUT:
      *output = USB_DIRECTION_OUTBOUND;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

bool ConvertRequestType(const RequestType& input,
                        UsbDeviceHandle::TransferRequestType* output) {
  switch (input) {
    case usb::REQUEST_TYPE_STANDARD:
      *output = UsbDeviceHandle::STANDARD;
      return true;
    case usb::REQUEST_TYPE_CLASS:
      *output = UsbDeviceHandle::CLASS;
      return true;
    case usb::REQUEST_TYPE_VENDOR:
      *output = UsbDeviceHandle::VENDOR;
      return true;
    case usb::REQUEST_TYPE_RESERVED:
      *output = UsbDeviceHandle::RESERVED;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

bool ConvertRecipient(const Recipient& input,
                      UsbDeviceHandle::TransferRecipient* output) {
  switch (input) {
    case usb::RECIPIENT_DEVICE:
      *output = UsbDeviceHandle::DEVICE;
      return true;
    case usb::RECIPIENT_INTERFACE:
      *output = UsbDeviceHandle::INTERFACE;
      return true;
    case usb::RECIPIENT_ENDPOINT:
      *output = UsbDeviceHandle::ENDPOINT;
      return true;
    case usb::RECIPIENT_OTHER:
      *output = UsbDeviceHandle::OTHER;
      return true;
    default:
      NOTREACHED();
      return false;
  }
}

template<class T>
bool GetTransferSize(const T& input, size_t* output) {
  if (input.direction == usb::DIRECTION_IN) {
    const int* length = input.length.get();
    if (length && *length >= 0 &&
        static_cast<size_t>(*length) < kMaxTransferLength) {
      *output = *length;
      return true;
    }
  } else if (input.direction == usb::DIRECTION_OUT) {
    if (input.data.get()) {
      *output = input.data->size();
      return true;
    }
  }
  return false;
}

template<class T>
scoped_refptr<net::IOBuffer> CreateBufferForTransfer(
    const T& input, UsbEndpointDirection direction, size_t size) {

  if (size >= kMaxTransferLength)
    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<net::IOBuffer> buffer = new net::IOBuffer(std::max(
      static_cast<size_t>(1), size));

  if (direction == USB_DIRECTION_INBOUND) {
    return buffer;
  } else if (direction == USB_DIRECTION_OUTBOUND) {
    if (input.data.get() && size <= input.data->size()) {
      memcpy(buffer->data(), input.data->data(), size);
      return buffer;
    }
  }
  NOTREACHED();
  return NULL;
}

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;
    default:
      NOTREACHED();
      return "";
  }
}

#if defined(OS_CHROMEOS)
void RequestUsbDevicesAccessHelper(
    ScopedDeviceVector devices,
    std::vector<scoped_refptr<UsbDevice> >::iterator i,
    int interface_id,
    const base::Callback<void(ScopedDeviceVector result)>& callback,
    bool success) {
  if (success) {
    ++i;
  } else {
    i = devices->erase(i);
  }
  if (i == devices->end()) {
    callback.Run(devices.Pass());
    return;
  }
  (*i)->RequestUsbAcess(interface_id, base::Bind(RequestUsbDevicesAccessHelper,
                                                 base::Passed(devices.Pass()),
                                                 i, interface_id, callback));
}

void RequestUsbDevicesAccess(
    ScopedDeviceVector devices,
    int interface_id,
    const base::Callback<void(ScopedDeviceVector result)>& callback) {
  if (devices->empty()) {
    callback.Run(devices.Pass());
    return;
  }
  std::vector<scoped_refptr<UsbDevice> >::iterator i = devices->begin();
  (*i)->RequestUsbAcess(
      interface_id,
      base::Bind(RequestUsbDevicesAccessHelper, base::Passed(devices.Pass()),
                 i, interface_id, callback));
}
#endif  // OS_CHROMEOS

base::DictionaryValue* CreateTransferInfo(
    UsbTransferStatus status,
    scoped_refptr<net::IOBuffer> data,
    size_t length) {
  base::DictionaryValue* result = new base::DictionaryValue();
  result->SetInteger(kResultCodeKey, status);
  result->Set(kDataKey, base::BinaryValue::CreateWithCopiedBuffer(data->data(),
                                                                  length));
  return result;
}

base::Value* PopulateConnectionHandle(int handle, int vendor_id,
                                      int product_id) {
  ConnectionHandle result;
  result.handle = handle;
  result.vendor_id = vendor_id;
  result.product_id = product_id;
  return result.ToValue().release();
}

base::Value* PopulateDevice(UsbDevice* device) {
  Device result;
  result.device = device->unique_id();
  result.vendor_id = device->vendor_id();
  result.product_id = device->product_id();
  return result.ToValue().release();
}

base::Value* PopulateInterfaceDescriptor(
    int interface_number,
    int alternate_setting,
    int interface_class,
    int interface_subclass,
    int interface_protocol,
    std::vector<linked_ptr<EndpointDescriptor> >* endpoints) {
  InterfaceDescriptor descriptor;
  descriptor.interface_number = interface_number;
  descriptor.alternate_setting = alternate_setting;
  descriptor.interface_class = interface_class;
  descriptor.interface_subclass = interface_subclass;
  descriptor.interface_protocol = interface_protocol;
  descriptor.endpoints = *endpoints;
  return descriptor.ToValue().release();
}

}  // namespace

namespace extensions {

UsbAsyncApiFunction::UsbAsyncApiFunction()
    : manager_(NULL) {
}

UsbAsyncApiFunction::~UsbAsyncApiFunction() {
}

bool UsbAsyncApiFunction::PrePrepare() {
  manager_ = ApiResourceManager<UsbDeviceResource>::Get(browser_context());
  set_work_thread_id(BrowserThread::FILE);
  return manager_ != NULL;
}

bool UsbAsyncApiFunction::Respond() {
  return error_.empty();
}

scoped_refptr<UsbDevice>
UsbAsyncApiFunction::GetDeviceOrOrCompleteWithError(
    const Device& input_device) {
  if (g_device_for_test)
    return g_device_for_test;

  const uint16_t vendor_id = input_device.vendor_id;
  const uint16_t product_id = input_device.product_id;
  UsbDevicePermission::CheckParam param(
      vendor_id, product_id, UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
  if (!PermissionsData::CheckAPIPermissionWithParam(
          GetExtension(), APIPermission::kUsbDevice, &param)) {
    LOG(WARNING) << "Insufficient permissions to access device.";
    CompleteWithError(kErrorPermissionDenied);
    return NULL;
  }

  UsbService* service = UsbService::GetInstance();
  if (!service) {
    CompleteWithError(kErrorInitService);
    return NULL;
  }
  scoped_refptr<UsbDevice> device;

  device = service->GetDeviceById(input_device.device);

  if (!device) {
    CompleteWithError(kErrorNoDevice);
    return NULL;
  }

  if (device->vendor_id() != input_device.vendor_id ||
      device->product_id() != input_device.product_id) {
    // Must act as if there is no such a device.
    // Otherwise can be used to finger print unauthorized devices.
    CompleteWithError(kErrorNoDevice);
    return NULL;
  }

  return device;
}

scoped_refptr<UsbDeviceHandle>
UsbAsyncApiFunction::GetDeviceHandleOrCompleteWithError(
    const ConnectionHandle& input_device_handle) {
  UsbDeviceResource* resource =
      manager_->Get(extension_->id(), input_device_handle.handle);
  if (!resource) {
    CompleteWithError(kErrorNoDevice);
    return NULL;
  }

  if (!resource->device() || !resource->device()->device()) {
    CompleteWithError(kErrorDisconnect);
    manager_->Remove(extension_->id(), input_device_handle.handle);
    return NULL;
  }

  if (resource->device()->device()->vendor_id() !=
          input_device_handle.vendor_id ||
      resource->device()->device()->product_id() !=
          input_device_handle.product_id) {
    CompleteWithError(kErrorNoDevice);
    return NULL;
  }

  return resource->device();
}

void UsbAsyncApiFunction::RemoveUsbDeviceResource(int api_resource_id) {
  manager_->Remove(extension_->id(), api_resource_id);
}

void UsbAsyncApiFunction::CompleteWithError(const std::string& error) {
  SetError(error);
  AsyncWorkCompleted();
}

UsbAsyncApiTransferFunction::UsbAsyncApiTransferFunction() {}

UsbAsyncApiTransferFunction::~UsbAsyncApiTransferFunction() {}

void UsbAsyncApiTransferFunction::OnCompleted(UsbTransferStatus status,
                                              scoped_refptr<net::IOBuffer> data,
                                              size_t length) {
  if (status != USB_TRANSFER_COMPLETED)
    SetError(ConvertTransferStatusToErrorString(status));

  SetResult(CreateTransferInfo(status, data, length));
  AsyncWorkCompleted();
}

bool UsbAsyncApiTransferFunction::ConvertDirectionSafely(
    const Direction& input, UsbEndpointDirection* output) {
  const bool converted = ConvertDirection(input, output);
  if (!converted)
    SetError(kErrorConvertDirection);
  return converted;
}

bool UsbAsyncApiTransferFunction::ConvertRequestTypeSafely(
    const RequestType& input, UsbDeviceHandle::TransferRequestType* output) {
  const bool converted = ConvertRequestType(input, output);
  if (!converted)
    SetError(kErrorConvertRequestType);
  return converted;
}

bool UsbAsyncApiTransferFunction::ConvertRecipientSafely(
    const Recipient& input, UsbDeviceHandle::TransferRecipient* output) {
  const bool converted = ConvertRecipient(input, output);
  if (!converted)
    SetError(kErrorConvertRecipient);
  return converted;
}

UsbFindDevicesFunction::UsbFindDevicesFunction() {}

UsbFindDevicesFunction::~UsbFindDevicesFunction() {}

bool UsbFindDevicesFunction::Prepare() {
  parameters_ = FindDevices::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbFindDevicesFunction::AsyncWorkStart() {
  scoped_ptr<base::ListValue> result(new base::ListValue());

  if (g_device_for_test) {
    UsbDeviceResource* const resource = new UsbDeviceResource(
        extension_->id(),
        g_device_for_test->Open());

    result->Append(PopulateConnectionHandle(manager_->Add(resource), 0, 0));
    SetResult(result.release());
    AsyncWorkCompleted();
    return;
  }

  const uint16_t vendor_id = parameters_->options.vendor_id;
  const uint16_t product_id = parameters_->options.product_id;
  int interface_id = parameters_->options.interface_id.get() ?
      *parameters_->options.interface_id.get() :
      UsbDevicePermissionData::ANY_INTERFACE;
  UsbDevicePermission::CheckParam param(vendor_id, product_id, interface_id);
  if (!PermissionsData::CheckAPIPermissionWithParam(
          GetExtension(), APIPermission::kUsbDevice, &param)) {
    LOG(WARNING) << "Insufficient permissions to access device.";
    CompleteWithError(kErrorPermissionDenied);
    return;
  }

  UsbService *service = UsbService::GetInstance();
  if (!service) {
    CompleteWithError(kErrorInitService);
    return;
  }

  ScopedDeviceVector devices(new DeviceVector());
  service->GetDevices(devices.get());

  for (DeviceVector::iterator it = devices->begin();
      it != devices->end();) {
    if ((*it)->vendor_id() != vendor_id || (*it)->product_id() != product_id) {
      it = devices->erase(it);
    } else {
      ++it;
    }
  }

#if defined(OS_CHROMEOS)
  RequestUsbDevicesAccess(
      devices.Pass(), interface_id,
      base::Bind(&UsbFindDevicesFunction::OpenDevices, this));
#else
  OpenDevices(devices.Pass());
#endif  // OS_CHROMEOS
}

void UsbFindDevicesFunction::OpenDevices(ScopedDeviceVector devices) {
  base::ListValue* result = new base::ListValue();

  for (size_t i = 0; i < devices->size(); ++i) {
    scoped_refptr<UsbDeviceHandle> device_handle =
      devices->at(i)->Open();
    if (device_handle)
      device_handles_.push_back(device_handle);
  }

  for (size_t i = 0; i < device_handles_.size(); ++i) {
    UsbDeviceHandle* const device_handle = device_handles_[i].get();
    UsbDeviceResource* const resource =
        new UsbDeviceResource(extension_->id(), device_handle);

    result->Append(PopulateConnectionHandle(manager_->Add(resource),
                                             parameters_->options.vendor_id,
                                             parameters_->options.product_id));
  }

  SetResult(result);
  AsyncWorkCompleted();
}

UsbGetDevicesFunction::UsbGetDevicesFunction() {
}

UsbGetDevicesFunction::~UsbGetDevicesFunction() {
}

void UsbGetDevicesFunction::SetDeviceForTest(UsbDevice* device) {
  g_device_for_test = device;
}

bool UsbGetDevicesFunction::Prepare() {
  parameters_ = GetDevices::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbGetDevicesFunction::AsyncWorkStart() {
  scoped_ptr<base::ListValue> result(new base::ListValue());

  if (g_device_for_test) {
    result->Append(PopulateDevice(g_device_for_test));
    SetResult(result.release());
    AsyncWorkCompleted();
    return;
  }

  const uint16_t vendor_id = parameters_->options.vendor_id;
  const uint16_t product_id = parameters_->options.product_id;
  UsbDevicePermission::CheckParam param(
      vendor_id, product_id, UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
  if (!PermissionsData::CheckAPIPermissionWithParam(
          GetExtension(), APIPermission::kUsbDevice, &param)) {
    LOG(WARNING) << "Insufficient permissions to access device.";
    CompleteWithError(kErrorPermissionDenied);
    return;
  }

  UsbService* service = UsbService::GetInstance();
  if (!service) {
    CompleteWithError(kErrorInitService);
    return;
  }

  DeviceVector devices;
  service->GetDevices(&devices);

  for (DeviceVector::iterator it = devices.begin(); it != devices.end();) {
    if ((*it)->vendor_id() != vendor_id || (*it)->product_id() != product_id) {
      it = devices.erase(it);
    } else {
      ++it;
    }
  }

  for (size_t i = 0; i < devices.size(); ++i) {
    result->Append(PopulateDevice(devices[i].get()));
  }

  SetResult(result.release());
  AsyncWorkCompleted();
}

UsbRequestAccessFunction::UsbRequestAccessFunction() {}

UsbRequestAccessFunction::~UsbRequestAccessFunction() {}

bool UsbRequestAccessFunction::Prepare() {
  parameters_ = RequestAccess::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbRequestAccessFunction::AsyncWorkStart() {
#if defined(OS_CHROMEOS)
  scoped_refptr<UsbDevice> device =
      GetDeviceOrOrCompleteWithError(parameters_->device);
  if (!device) return;

  device->RequestUsbAcess(parameters_->interface_id,
                          base::Bind(&UsbRequestAccessFunction::OnCompleted,
                                     this));
#else
  SetResult(new base::FundamentalValue(false));
  CompleteWithError(kErrorNotSupported);
#endif  // OS_CHROMEOS
}

void UsbRequestAccessFunction::OnCompleted(bool success) {
  SetResult(new base::FundamentalValue(success));
  AsyncWorkCompleted();
}

UsbOpenDeviceFunction::UsbOpenDeviceFunction() {}

UsbOpenDeviceFunction::~UsbOpenDeviceFunction() {}

bool UsbOpenDeviceFunction::Prepare() {
  parameters_ = OpenDevice::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbOpenDeviceFunction::AsyncWorkStart() {
  scoped_refptr<UsbDevice> device =
      GetDeviceOrOrCompleteWithError(parameters_->device);
  if (!device) return;

  handle_ = device->Open();
  if (!handle_) {
    SetError(kErrorOpen);
    AsyncWorkCompleted();
    return;
  }

  SetResult(PopulateConnectionHandle(
      manager_->Add(new UsbDeviceResource(extension_->id(), handle_)),
      handle_->device()->vendor_id(),
      handle_->device()->product_id()));
  AsyncWorkCompleted();
}

UsbListInterfacesFunction::UsbListInterfacesFunction() {}

UsbListInterfacesFunction::~UsbListInterfacesFunction() {}

bool UsbListInterfacesFunction::Prepare() {
  parameters_ = ListInterfaces::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbListInterfacesFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  scoped_refptr<UsbConfigDescriptor> config =
      device_handle->device()->ListInterfaces();

  if (!config) {
    SetError(kErrorCannotListInterfaces);
    AsyncWorkCompleted();
    return;
  }

  result_.reset(new base::ListValue());

  for (size_t i = 0, num_interfaces = config->GetNumInterfaces();
      i < num_interfaces; ++i) {
    scoped_refptr<const UsbInterfaceDescriptor>
        usb_interface(config->GetInterface(i));
    for (size_t j = 0, num_descriptors = usb_interface->GetNumAltSettings();
            j < num_descriptors; ++j) {
      scoped_refptr<const UsbInterfaceAltSettingDescriptor> descriptor
          = usb_interface->GetAltSetting(j);
      std::vector<linked_ptr<EndpointDescriptor> > endpoints;
      for (size_t k = 0, num_endpoints = descriptor->GetNumEndpoints();
          k < num_endpoints; k++) {
        scoped_refptr<const UsbEndpointDescriptor> endpoint
            = descriptor->GetEndpoint(k);
        linked_ptr<EndpointDescriptor> endpoint_desc(new EndpointDescriptor());

        TransferType type;
        Direction direction;
        SynchronizationType synchronization;
        UsageType usage;

        if (!ConvertTransferTypeSafely(endpoint->GetTransferType(), &type) ||
            !ConvertDirectionSafely(endpoint->GetDirection(), &direction) ||
            !ConvertSynchronizationTypeSafely(
                endpoint->GetSynchronizationType(), &synchronization) ||
            !ConvertUsageTypeSafely(endpoint->GetUsageType(), &usage)) {
          SetError(kErrorCannotListInterfaces);
          AsyncWorkCompleted();
          return;
        }

        endpoint_desc->address = endpoint->GetAddress();
        endpoint_desc->type = type;
        endpoint_desc->direction = direction;
        endpoint_desc->maximum_packet_size = endpoint->GetMaximumPacketSize();
        endpoint_desc->synchronization = synchronization;
        endpoint_desc->usage = usage;

        int* polling_interval = new int;
        endpoint_desc->polling_interval.reset(polling_interval);
        *polling_interval = endpoint->GetPollingInterval();

        endpoints.push_back(endpoint_desc);
      }

      result_->Append(PopulateInterfaceDescriptor(
          descriptor->GetInterfaceNumber(),
          descriptor->GetAlternateSetting(),
          descriptor->GetInterfaceClass(),
          descriptor->GetInterfaceSubclass(),
          descriptor->GetInterfaceProtocol(),
          &endpoints));
    }
  }

  SetResult(result_.release());
  AsyncWorkCompleted();
}

bool UsbListInterfacesFunction::ConvertDirectionSafely(
    const UsbEndpointDirection& input,
    usb::Direction* output) {
  const bool converted = ConvertDirectionToApi(input, output);
  if (!converted)
    SetError(kErrorConvertDirection);
  return converted;
}

bool UsbListInterfacesFunction::ConvertSynchronizationTypeSafely(
    const UsbSynchronizationType& input,
    usb::SynchronizationType* output) {
  const bool converted = ConvertSynchronizationTypeToApi(input, output);
  if (!converted)
    SetError(kErrorConvertSynchronizationType);
  return converted;
}

bool UsbListInterfacesFunction::ConvertTransferTypeSafely(
    const UsbTransferType& input,
    usb::TransferType* output) {
  const bool converted = ConvertTransferTypeToApi(input, output);
  if (!converted)
    SetError(kErrorConvertTransferType);
  return converted;
}

bool UsbListInterfacesFunction::ConvertUsageTypeSafely(
    const UsbUsageType& input,
    usb::UsageType* output) {
  const bool converted = ConvertUsageTypeToApi(input, output);
  if (!converted)
    SetError(kErrorConvertUsageType);
  return converted;
}

UsbCloseDeviceFunction::UsbCloseDeviceFunction() {}

UsbCloseDeviceFunction::~UsbCloseDeviceFunction() {}

bool UsbCloseDeviceFunction::Prepare() {
  parameters_ = CloseDevice::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbCloseDeviceFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  device_handle->Close();
  RemoveUsbDeviceResource(parameters_->handle.handle);
  AsyncWorkCompleted();
}

UsbClaimInterfaceFunction::UsbClaimInterfaceFunction() {}

UsbClaimInterfaceFunction::~UsbClaimInterfaceFunction() {}

bool UsbClaimInterfaceFunction::Prepare() {
  parameters_ = ClaimInterface::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbClaimInterfaceFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  bool success = device_handle->ClaimInterface(parameters_->interface_number);

  if (!success)
    SetError(kErrorCannotClaimInterface);
  AsyncWorkCompleted();
}

UsbReleaseInterfaceFunction::UsbReleaseInterfaceFunction() {}

UsbReleaseInterfaceFunction::~UsbReleaseInterfaceFunction() {}

bool UsbReleaseInterfaceFunction::Prepare() {
  parameters_ = ReleaseInterface::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbReleaseInterfaceFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  bool success = device_handle->ReleaseInterface(parameters_->interface_number);
  if (!success)
    SetError(kErrorCannotReleaseInterface);
  AsyncWorkCompleted();
}

UsbSetInterfaceAlternateSettingFunction::
    UsbSetInterfaceAlternateSettingFunction() {}

UsbSetInterfaceAlternateSettingFunction::
    ~UsbSetInterfaceAlternateSettingFunction() {}

bool UsbSetInterfaceAlternateSettingFunction::Prepare() {
  parameters_ = SetInterfaceAlternateSetting::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbSetInterfaceAlternateSettingFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  bool success = device_handle->SetInterfaceAlternateSetting(
      parameters_->interface_number,
      parameters_->alternate_setting);
  if (!success)
    SetError(kErrorCannotSetInterfaceAlternateSetting);

  AsyncWorkCompleted();
}

UsbControlTransferFunction::UsbControlTransferFunction() {}

UsbControlTransferFunction::~UsbControlTransferFunction() {}

bool UsbControlTransferFunction::Prepare() {
  parameters_ = ControlTransfer::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbControlTransferFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  const ControlTransferInfo& transfer = parameters_->transfer_info;

  UsbEndpointDirection direction;
  UsbDeviceHandle::TransferRequestType request_type;
  UsbDeviceHandle::TransferRecipient recipient;
  size_t size = 0;

  if (!ConvertDirectionSafely(transfer.direction, &direction) ||
      !ConvertRequestTypeSafely(transfer.request_type, &request_type) ||
      !ConvertRecipientSafely(transfer.recipient, &recipient)) {
    AsyncWorkCompleted();
    return;
  }

  if (!GetTransferSize(transfer, &size)) {
    CompleteWithError(kErrorInvalidTransferLength);
    return;
  }

  scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(
      transfer, direction, size);
  if (!buffer.get()) {
    CompleteWithError(kErrorMalformedParameters);
    return;
  }

  device_handle->ControlTransfer(
      direction,
      request_type,
      recipient,
      transfer.request,
      transfer.value,
      transfer.index,
      buffer.get(),
      size,
      0,
      base::Bind(&UsbControlTransferFunction::OnCompleted, this));
}

UsbBulkTransferFunction::UsbBulkTransferFunction() {}

UsbBulkTransferFunction::~UsbBulkTransferFunction() {}

bool UsbBulkTransferFunction::Prepare() {
  parameters_ = BulkTransfer::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbBulkTransferFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  const GenericTransferInfo& transfer = parameters_->transfer_info;

  UsbEndpointDirection direction;
  size_t size = 0;

  if (!ConvertDirectionSafely(transfer.direction, &direction)) {
    AsyncWorkCompleted();
    return;
  }

  if (!GetTransferSize(transfer, &size)) {
    CompleteWithError(kErrorInvalidTransferLength);
    return;
  }

  scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(
      transfer, direction, size);
  if (!buffer.get()) {
    CompleteWithError(kErrorMalformedParameters);
    return;
  }

  device_handle->BulkTransfer(
      direction,
      transfer.endpoint,
      buffer.get(),
      size,
      0,
      base::Bind(&UsbBulkTransferFunction::OnCompleted, this));
}

UsbInterruptTransferFunction::UsbInterruptTransferFunction() {}

UsbInterruptTransferFunction::~UsbInterruptTransferFunction() {}

bool UsbInterruptTransferFunction::Prepare() {
  parameters_ = InterruptTransfer::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbInterruptTransferFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  const GenericTransferInfo& transfer = parameters_->transfer_info;

  UsbEndpointDirection direction;
  size_t size = 0;

  if (!ConvertDirectionSafely(transfer.direction, &direction)) {
    AsyncWorkCompleted();
    return;
  }

  if (!GetTransferSize(transfer, &size)) {
    CompleteWithError(kErrorInvalidTransferLength);
    return;
  }

  scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(
      transfer, direction, size);
  if (!buffer.get()) {
    CompleteWithError(kErrorMalformedParameters);
    return;
  }

  device_handle->InterruptTransfer(
      direction,
      transfer.endpoint,
      buffer.get(),
      size,
      0,
      base::Bind(&UsbInterruptTransferFunction::OnCompleted, this));
}

UsbIsochronousTransferFunction::UsbIsochronousTransferFunction() {}

UsbIsochronousTransferFunction::~UsbIsochronousTransferFunction() {}

bool UsbIsochronousTransferFunction::Prepare() {
  parameters_ = IsochronousTransfer::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbIsochronousTransferFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  const IsochronousTransferInfo& transfer = parameters_->transfer_info;
  const GenericTransferInfo& generic_transfer = transfer.transfer_info;

  size_t size = 0;
  UsbEndpointDirection direction;

  if (!ConvertDirectionSafely(generic_transfer.direction, &direction)) {
    AsyncWorkCompleted();
    return;
  }
  if (!GetTransferSize(generic_transfer, &size)) {
    CompleteWithError(kErrorInvalidTransferLength);
    return;
  }
  if (transfer.packets < 0 || transfer.packets >= kMaxPackets) {
    CompleteWithError(kErrorInvalidNumberOfPackets);
    return;
  }
  unsigned int packets = transfer.packets;
  if (transfer.packet_length < 0 ||
      transfer.packet_length >= kMaxPacketLength) {
    CompleteWithError(kErrorInvalidPacketLength);
    return;
  }
  unsigned int packet_length = transfer.packet_length;
  const uint64 total_length = packets * packet_length;
  if (packets > size || total_length > size) {
    CompleteWithError(kErrorTransferLength);
    return;
  }

  scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(
      generic_transfer, direction, size);
  if (!buffer.get()) {
    CompleteWithError(kErrorMalformedParameters);
    return;
  }

  device_handle->IsochronousTransfer(
      direction,
      generic_transfer.endpoint,
      buffer.get(),
      size,
      packets,
      packet_length,
      0,
      base::Bind(&UsbIsochronousTransferFunction::OnCompleted, this));
}

UsbResetDeviceFunction::UsbResetDeviceFunction() {}

UsbResetDeviceFunction::~UsbResetDeviceFunction() {}

bool UsbResetDeviceFunction::Prepare() {
  parameters_ = ResetDevice::Params::Create(*args_);
  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
  return true;
}

void UsbResetDeviceFunction::AsyncWorkStart() {
  scoped_refptr<UsbDeviceHandle> device_handle =
      GetDeviceHandleOrCompleteWithError(parameters_->handle);
  if (!device_handle) return;

  bool success = device_handle->ResetDevice();
  if (!success) {
    device_handle->Close();
    RemoveUsbDeviceResource(parameters_->handle.handle);
    SetResult(new base::FundamentalValue(false));
    CompleteWithError(kErrorResetDevice);
    return;
  }

  SetResult(new base::FundamentalValue(true));
  AsyncWorkCompleted();
}

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */