diff options
author | rockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-05 18:40:11 +0000 |
---|---|---|
committer | rockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-05 18:40:11 +0000 |
commit | 6e84e2c187647c30f3f4563aac671ba9416b4182 (patch) | |
tree | 7443a12f1dbaca77ca6ab5a51b433fb91d3854ff /extensions | |
parent | 304219bc9ecaba2bc4055bae084dfbd85319cca5 (diff) | |
download | chromium_src-6e84e2c187647c30f3f4563aac671ba9416b4182.zip chromium_src-6e84e2c187647c30f3f4563aac671ba9416b4182.tar.gz chromium_src-6e84e2c187647c30f3f4563aac671ba9416b4182.tar.bz2 |
Move chrome.usb to //extensions
This moves the chrome.usb API implementation into the
core set of extensions APIs.
BUG=369391
R=rpaquay
TBR=yoz for extensions
TBR=sky for +chrome/browser/ui in test DEPS
Review URL: https://codereview.chromium.org/268713013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268231 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/DEPS | 3 | ||||
-rw-r--r-- | extensions/browser/api/usb/DEPS | 3 | ||||
-rw-r--r-- | extensions/browser/api/usb/OWNERS | 4 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_api.cc | 1208 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_api.h | 317 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_apitest.cc | 275 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_device_resource.cc | 45 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_device_resource.h | 50 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_manual_apitest.cc | 18 | ||||
-rw-r--r-- | extensions/common/api/_api_features.json | 4 | ||||
-rw-r--r-- | extensions/common/api/_permission_features.json | 30 | ||||
-rw-r--r-- | extensions/common/api/api.gyp | 1 | ||||
-rw-r--r-- | extensions/common/api/usb.idl | 307 | ||||
-rw-r--r-- | extensions/extensions.gyp | 8 |
14 files changed, 2272 insertions, 1 deletions
diff --git a/extensions/DEPS b/extensions/DEPS index bb58d4f..11611da 100644 --- a/extensions/DEPS +++ b/extensions/DEPS @@ -26,9 +26,12 @@ specific_include_rules = { # Temporarily allowed testing includes. See above. # TODO(jamescook): Remove these. http://crbug.com/162530 + "+chrome/browser/extensions/api/permissions/permissions_api.h", "+chrome/browser/extensions/extension_api_unittest.h", + "+chrome/browser/extensions/extension_apitest.h", "+chrome/browser/extensions/extension_service_unittest.h", "+chrome/browser/extensions/test_extension_system.h", + "+chrome/browser/ui/browser.h", "+chrome/common/chrome_paths.h", "+chrome/common/extensions/features/feature_channel.h", "+chrome/common/extensions/manifest_tests/extension_manifest_test.h", diff --git a/extensions/browser/api/usb/DEPS b/extensions/browser/api/usb/DEPS new file mode 100644 index 0000000..70ac41d --- /dev/null +++ b/extensions/browser/api/usb/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/usb_service", +] diff --git a/extensions/browser/api/usb/OWNERS b/extensions/browser/api/usb/OWNERS new file mode 100644 index 0000000..38fa250 --- /dev/null +++ b/extensions/browser/api/usb/OWNERS @@ -0,0 +1,4 @@ +# USB API members. For core API change, check with owners in +# chrome/browser/extensions/OWNERS +rockot@chromium.org +rpaquay@chromium.org diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc new file mode 100644 index 0000000..93ac6c1 --- /dev/null +++ b/extensions/browser/api/usb/usb_api.cc @@ -0,0 +1,1208 @@ +// Copyright 2014 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 "extensions/browser/api/usb/usb_api.h" + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop_proxy.h" +#include "components/usb_service/usb_device_handle.h" +#include "components/usb_service/usb_service.h" +#include "extensions/browser/api/usb/usb_device_resource.h" +#include "extensions/browser/extension_system.h" +#include "extensions/common/api/usb.h" +#include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/permissions/usb_device_permission.h" + +namespace usb = extensions::core_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; +using usb_service::UsbConfigDescriptor; +using usb_service::UsbDevice; +using usb_service::UsbDeviceHandle; +using usb_service::UsbEndpointDescriptor; +using usb_service::UsbEndpointDirection; +using usb_service::UsbInterfaceAltSettingDescriptor; +using usb_service::UsbInterfaceDescriptor; +using usb_service::UsbService; +using usb_service::UsbSynchronizationType; +using usb_service::UsbTransferStatus; +using usb_service::UsbTransferType; +using usb_service::UsbUsageType; + +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; + +bool ConvertDirectionToApi(const UsbEndpointDirection& input, + Direction* output) { + switch (input) { + case usb_service::USB_DIRECTION_INBOUND: + *output = usb::DIRECTION_IN; + return true; + case usb_service::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_service::USB_SYNCHRONIZATION_NONE: + *output = usb::SYNCHRONIZATION_TYPE_NONE; + return true; + case usb_service::USB_SYNCHRONIZATION_ASYNCHRONOUS: + *output = usb::SYNCHRONIZATION_TYPE_ASYNCHRONOUS; + return true; + case usb_service::USB_SYNCHRONIZATION_ADAPTIVE: + *output = usb::SYNCHRONIZATION_TYPE_ADAPTIVE; + return true; + case usb_service::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_service::USB_TRANSFER_CONTROL: + *output = usb::TRANSFER_TYPE_CONTROL; + return true; + case usb_service::USB_TRANSFER_INTERRUPT: + *output = usb::TRANSFER_TYPE_INTERRUPT; + return true; + case usb_service::USB_TRANSFER_ISOCHRONOUS: + *output = usb::TRANSFER_TYPE_ISOCHRONOUS; + return true; + case usb_service::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_service::USB_USAGE_DATA: + *output = usb::USAGE_TYPE_DATA; + return true; + case usb_service::USB_USAGE_FEEDBACK: + *output = usb::USAGE_TYPE_FEEDBACK; + return true; + case usb_service::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_service::USB_DIRECTION_INBOUND; + return true; + case usb::DIRECTION_OUT: + *output = usb_service::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_service::USB_DIRECTION_INBOUND) { + return buffer; + } else if (direction == usb_service::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_service::USB_TRANSFER_COMPLETED: + return ""; + case usb_service::USB_TRANSFER_ERROR: + return kErrorGeneric; + case usb_service::USB_TRANSFER_TIMEOUT: + return kErrorTimeout; + case usb_service::USB_TRANSFER_CANCELLED: + return kErrorCancelled; + case usb_service::USB_TRANSFER_STALLED: + return kErrorStalled; + case usb_service::USB_TRANSFER_DISCONNECT: + return kErrorDisconnect; + case usb_service::USB_TRANSFER_OVERFLOW: + return kErrorOverflow; + case usb_service::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) { + 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, ¶m)) { + 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_service::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()); + 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, ¶m)) { + 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() { +} + +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()); + + 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, ¶m)) { + 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 diff --git a/extensions/browser/api/usb/usb_api.h b/extensions/browser/api/usb/usb_api.h new file mode 100644 index 0000000..3dd0d2b --- /dev/null +++ b/extensions/browser/api/usb/usb_api.h @@ -0,0 +1,317 @@ +// Copyright 2014 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. + +#ifndef EXTENSIONS_BROWSER_API_USB_USB_API_H_ +#define EXTENSIONS_BROWSER_API_USB_USB_API_H_ + +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "components/usb_service/usb_device.h" +#include "components/usb_service/usb_device_handle.h" +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/api/async_api_function.h" +#include "extensions/common/api/usb.h" +#include "net/base/io_buffer.h" + +namespace extensions { + +class UsbDeviceResource; + +class UsbAsyncApiFunction : public AsyncApiFunction { + public: + UsbAsyncApiFunction(); + + protected: + virtual ~UsbAsyncApiFunction(); + + virtual bool PrePrepare() OVERRIDE; + virtual bool Respond() OVERRIDE; + + scoped_refptr<usb_service::UsbDevice> GetDeviceOrOrCompleteWithError( + const extensions::core_api::usb::Device& input_device); + + scoped_refptr<usb_service::UsbDeviceHandle> + GetDeviceHandleOrCompleteWithError( + const extensions::core_api::usb::ConnectionHandle& + input_device_handle); + + void RemoveUsbDeviceResource(int api_resource_id); + + void CompleteWithError(const std::string& error); + + ApiResourceManager<UsbDeviceResource>* manager_; +}; + +class UsbAsyncApiTransferFunction : public UsbAsyncApiFunction { + protected: + UsbAsyncApiTransferFunction(); + virtual ~UsbAsyncApiTransferFunction(); + + bool ConvertDirectionSafely(const extensions::core_api::usb::Direction& input, + usb_service::UsbEndpointDirection* output); + bool ConvertRequestTypeSafely( + const extensions::core_api::usb::RequestType& input, + usb_service::UsbDeviceHandle::TransferRequestType* output); + bool ConvertRecipientSafely( + const extensions::core_api::usb::Recipient& input, + usb_service::UsbDeviceHandle::TransferRecipient* output); + + void OnCompleted(usb_service::UsbTransferStatus status, + scoped_refptr<net::IOBuffer> data, + size_t length); +}; + +class UsbFindDevicesFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.findDevices", USB_FINDDEVICES) + + UsbFindDevicesFunction(); + + protected: + virtual ~UsbFindDevicesFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + void OpenDevices( + scoped_ptr<std::vector<scoped_refptr<usb_service::UsbDevice> > > devices); + + std::vector<scoped_refptr<usb_service::UsbDeviceHandle> > device_handles_; + scoped_ptr<extensions::core_api::usb::FindDevices::Params> parameters_; +}; + +class UsbGetDevicesFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.getDevices", USB_GETDEVICES) + + UsbGetDevicesFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + protected: + virtual ~UsbGetDevicesFunction(); + + private: + void EnumerationCompletedFileThread( + scoped_ptr<std::vector<scoped_refptr<usb_service::UsbDevice> > > devices); + + scoped_ptr<extensions::core_api::usb::GetDevices::Params> parameters_; +}; + +class UsbRequestAccessFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.requestAccess", USB_REQUESTACCESS) + + UsbRequestAccessFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + protected: + virtual ~UsbRequestAccessFunction(); + + void OnCompleted(bool success); + + private: + scoped_ptr<extensions::core_api::usb::RequestAccess::Params> parameters_; +}; + +class UsbOpenDeviceFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.openDevice", USB_OPENDEVICE) + + UsbOpenDeviceFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + protected: + virtual ~UsbOpenDeviceFunction(); + + private: + scoped_refptr<usb_service::UsbDeviceHandle> handle_; + scoped_ptr<extensions::core_api::usb::OpenDevice::Params> parameters_; +}; + +class UsbListInterfacesFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.listInterfaces", USB_LISTINTERFACES) + + UsbListInterfacesFunction(); + + protected: + virtual ~UsbListInterfacesFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + bool ConvertDirectionSafely(const usb_service::UsbEndpointDirection& input, + extensions::core_api::usb::Direction* output); + bool ConvertSynchronizationTypeSafely( + const usb_service::UsbSynchronizationType& input, + extensions::core_api::usb::SynchronizationType* output); + bool ConvertTransferTypeSafely( + const usb_service::UsbTransferType& input, + extensions::core_api::usb::TransferType* output); + bool ConvertUsageTypeSafely(const usb_service::UsbUsageType& input, + extensions::core_api::usb::UsageType* output); + + scoped_ptr<base::ListValue> result_; + scoped_ptr<extensions::core_api::usb::ListInterfaces::Params> parameters_; +}; + +class UsbCloseDeviceFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.closeDevice", USB_CLOSEDEVICE) + + UsbCloseDeviceFunction(); + + protected: + virtual ~UsbCloseDeviceFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::CloseDevice::Params> parameters_; +}; + +class UsbClaimInterfaceFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.claimInterface", USB_CLAIMINTERFACE) + + UsbClaimInterfaceFunction(); + + protected: + virtual ~UsbClaimInterfaceFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::ClaimInterface::Params> parameters_; +}; + +class UsbReleaseInterfaceFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.releaseInterface", USB_RELEASEINTERFACE) + + UsbReleaseInterfaceFunction(); + + protected: + virtual ~UsbReleaseInterfaceFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::ReleaseInterface::Params> parameters_; +}; + +class UsbSetInterfaceAlternateSettingFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.setInterfaceAlternateSetting", + USB_SETINTERFACEALTERNATESETTING) + + UsbSetInterfaceAlternateSettingFunction(); + + private: + virtual ~UsbSetInterfaceAlternateSettingFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + scoped_ptr<extensions::core_api::usb::SetInterfaceAlternateSetting::Params> + parameters_; +}; + +class UsbControlTransferFunction : public UsbAsyncApiTransferFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.controlTransfer", USB_CONTROLTRANSFER) + + UsbControlTransferFunction(); + + protected: + virtual ~UsbControlTransferFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::ControlTransfer::Params> parameters_; +}; + +class UsbBulkTransferFunction : public UsbAsyncApiTransferFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.bulkTransfer", USB_BULKTRANSFER) + + UsbBulkTransferFunction(); + + protected: + virtual ~UsbBulkTransferFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::BulkTransfer::Params> parameters_; +}; + +class UsbInterruptTransferFunction : public UsbAsyncApiTransferFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.interruptTransfer", USB_INTERRUPTTRANSFER) + + UsbInterruptTransferFunction(); + + protected: + virtual ~UsbInterruptTransferFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::InterruptTransfer::Params> parameters_; +}; + +class UsbIsochronousTransferFunction : public UsbAsyncApiTransferFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.isochronousTransfer", USB_ISOCHRONOUSTRANSFER) + + UsbIsochronousTransferFunction(); + + protected: + virtual ~UsbIsochronousTransferFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::IsochronousTransfer::Params> + parameters_; +}; + +class UsbResetDeviceFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usb.resetDevice", USB_RESETDEVICE) + + UsbResetDeviceFunction(); + + protected: + virtual ~UsbResetDeviceFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + scoped_ptr<extensions::core_api::usb::ResetDevice::Params> parameters_; +}; +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_USB_USB_API_H_ diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc new file mode 100644 index 0000000..455c663 --- /dev/null +++ b/extensions/browser/api/usb/usb_apitest.cc @@ -0,0 +1,275 @@ +// Copyright 2014 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/extension_apitest.h" +#include "chrome/browser/ui/browser.h" +#include "components/usb_service/usb_service.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_utils.h" +#include "extensions/browser/api/usb/usb_api.h" +#include "net/base/io_buffer.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::AnyNumber; +using testing::_; +using testing::Return; +using content::BrowserThread; +using usb_service::UsbConfigDescriptor; +using usb_service::UsbDevice; +using usb_service::UsbDeviceHandle; +using usb_service::UsbEndpointDirection; +using usb_service::UsbService; +using usb_service::UsbTransferCallback; + +namespace { + +ACTION_TEMPLATE(InvokeUsbTransferCallback, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p1)) { + ::std::tr1::get<k>(args).Run(p1, new net::IOBuffer(1), 1); +} + +// MSVC erroneously thinks that at least one of the arguments for the transfer +// methods differ by const or volatility and emits a warning about the old +// standards-noncompliant behaviour of their compiler. +#if defined(OS_WIN) +#pragma warning(push) +#pragma warning(disable : 4373) +#endif + +class MockUsbDeviceHandle : public UsbDeviceHandle { + public: + MockUsbDeviceHandle() : UsbDeviceHandle() {} + + MOCK_METHOD0(Close, void()); + + MOCK_METHOD10(ControlTransfer, + void(const UsbEndpointDirection direction, + const TransferRequestType request_type, + const TransferRecipient recipient, + const uint8 request, + const uint16 value, + const uint16 index, + net::IOBuffer* buffer, + const size_t length, + const unsigned int timeout, + const UsbTransferCallback& callback)); + + MOCK_METHOD6(BulkTransfer, + void(const UsbEndpointDirection direction, + const uint8 endpoint, + net::IOBuffer* buffer, + const size_t length, + const unsigned int timeout, + const UsbTransferCallback& callback)); + + MOCK_METHOD6(InterruptTransfer, + void(const UsbEndpointDirection direction, + const uint8 endpoint, + net::IOBuffer* buffer, + const size_t length, + const unsigned int timeout, + const UsbTransferCallback& callback)); + + MOCK_METHOD8(IsochronousTransfer, + void(const UsbEndpointDirection direction, + const uint8 endpoint, + net::IOBuffer* buffer, + const size_t length, + const unsigned int packets, + const unsigned int packet_length, + const unsigned int timeout, + const UsbTransferCallback& callback)); + + MOCK_METHOD0(ResetDevice, bool()); + + void set_device(UsbDevice* device) { device_ = device; } + + protected: + virtual ~MockUsbDeviceHandle() {} +}; + +class MockUsbConfigDescriptor : public UsbConfigDescriptor { + public: + MOCK_CONST_METHOD0(GetNumInterfaces, size_t()); + + protected: + virtual ~MockUsbConfigDescriptor() {} +}; + +class MockUsbDevice : public UsbDevice { + public: + explicit MockUsbDevice(MockUsbDeviceHandle* mock_handle) + : UsbDevice(), mock_handle_(mock_handle) { + mock_handle->set_device(this); + } + + virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE { + return mock_handle_; + } + + virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE { + EXPECT_TRUE(false) << "Should not be reached"; + return false; + } + +#if defined(OS_CHROMEOS) + virtual void RequestUsbAcess( + int interface_id, + const base::Callback<void(bool success)>& callback) OVERRIDE { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, base::Bind(callback, true)); + } +#endif // OS_CHROMEOS + + MOCK_METHOD0(ListInterfaces, scoped_refptr<UsbConfigDescriptor>()); + + private: + MockUsbDeviceHandle* mock_handle_; + virtual ~MockUsbDevice() {} +}; + +class MockUsbService : public UsbService { + public: + explicit MockUsbService(scoped_refptr<UsbDevice> device) : device_(device) {} + + protected: + virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) OVERRIDE { + EXPECT_EQ(unique_id, 0U); + return device_; + } + + virtual void GetDevices( + std::vector<scoped_refptr<UsbDevice> >* devices) OVERRIDE { + STLClearObject(devices); + devices->push_back(device_); + } + + scoped_refptr<UsbDevice> device_; +}; + +#if defined(OS_WIN) +#pragma warning(pop) +#endif + +class UsbApiTest : public ExtensionApiTest { + public: + virtual void SetUpOnMainThread() OVERRIDE { + mock_device_handle_ = new MockUsbDeviceHandle(); + mock_device_ = new MockUsbDevice(mock_device_handle_.get()); + scoped_refptr<content::MessageLoopRunner> runner = + new content::MessageLoopRunner; + BrowserThread::PostTaskAndReply(BrowserThread::FILE, + FROM_HERE, + base::Bind(&UsbApiTest::SetUpService, this), + runner->QuitClosure()); + runner->Run(); + } + + void SetUpService() { + UsbService::SetInstanceForTest(new MockUsbService(mock_device_)); + } + + virtual void CleanUpOnMainThread() OVERRIDE { + scoped_refptr<content::MessageLoopRunner> runner = + new content::MessageLoopRunner; + UsbService* service = NULL; + BrowserThread::PostTaskAndReply( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&UsbService::SetInstanceForTest, service), + runner->QuitClosure()); + runner->Run(); + } + + protected: + scoped_refptr<MockUsbDeviceHandle> mock_device_handle_; + scoped_refptr<MockUsbDevice> mock_device_; +}; + +} // namespace + +IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) { + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(4); + ASSERT_TRUE(RunExtensionTest("usb/device_handling")); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, ResetDevice) { + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(2); + EXPECT_CALL(*mock_device_handle_.get(), ResetDevice()) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL( + *mock_device_handle_.get(), + InterruptTransfer(usb_service::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _)) + .WillOnce( + InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED)); + ASSERT_TRUE(RunExtensionTest("usb/reset_device")); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, ListInterfaces) { + scoped_refptr<MockUsbConfigDescriptor> mock_descriptor = + new MockUsbConfigDescriptor(); + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber()); + EXPECT_CALL(*mock_descriptor.get(), GetNumInterfaces()).WillOnce(Return(0)); + EXPECT_CALL(*mock_device_.get(), ListInterfaces()) + .WillOnce(Return(mock_descriptor)); + ASSERT_TRUE(RunExtensionTest("usb/list_interfaces")); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) { + EXPECT_CALL(*mock_device_handle_.get(), + ControlTransfer(usb_service::USB_DIRECTION_OUTBOUND, + UsbDeviceHandle::STANDARD, + UsbDeviceHandle::DEVICE, + 1, + 2, + 3, + _, + 1, + _, + _)) + .WillOnce( + InvokeUsbTransferCallback<9>(usb_service::USB_TRANSFER_COMPLETED)); + EXPECT_CALL(*mock_device_handle_.get(), + BulkTransfer(usb_service::USB_DIRECTION_OUTBOUND, 1, _, 1, _, _)) + .WillOnce( + InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED)); + EXPECT_CALL( + *mock_device_handle_.get(), + InterruptTransfer(usb_service::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _)) + .WillOnce( + InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED)); + EXPECT_CALL(*mock_device_handle_.get(), + IsochronousTransfer( + usb_service::USB_DIRECTION_OUTBOUND, 3, _, 1, 1, 1, _, _)) + .WillOnce( + InvokeUsbTransferCallback<7>(usb_service::USB_TRANSFER_COMPLETED)); + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber()); + ASSERT_TRUE(RunExtensionTest("usb/transfer_event")); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) { + EXPECT_CALL(*mock_device_handle_.get(), BulkTransfer(_, _, _, 0, _, _)) + .WillOnce( + InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED)); + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber()); + ASSERT_TRUE(RunExtensionTest("usb/zero_length_transfer")); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) { + EXPECT_CALL(*mock_device_handle_.get(), BulkTransfer(_, _, _, _, _, _)) + .WillOnce( + InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED)) + .WillOnce(InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_ERROR)) + .WillOnce( + InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_TIMEOUT)); + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber()); + ASSERT_TRUE(RunExtensionTest("usb/transfer_failure")); +} + +IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) { + EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber()); + ASSERT_TRUE(RunExtensionTest("usb/invalid_length_transfer")); +} diff --git a/extensions/browser/api/usb/usb_device_resource.cc b/extensions/browser/api/usb/usb_device_resource.cc new file mode 100644 index 0000000..030de7b --- /dev/null +++ b/extensions/browser/api/usb/usb_device_resource.cc @@ -0,0 +1,45 @@ +// Copyright 2014 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 "extensions/browser/api/usb/usb_device_resource.h" + +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/synchronization/lock.h" +#include "components/usb_service/usb_device_handle.h" +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/api/api_resource.h" +#include "extensions/common/api/usb.h" + +using content::BrowserThread; +using usb_service::UsbDeviceHandle; + +namespace extensions { + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<ApiResourceManager<UsbDeviceResource> > > + g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +template <> +BrowserContextKeyedAPIFactory<ApiResourceManager<UsbDeviceResource> >* +ApiResourceManager<UsbDeviceResource>::GetFactoryInstance() { + return g_factory.Pointer(); +} + +UsbDeviceResource::UsbDeviceResource(const std::string& owner_extension_id, + scoped_refptr<UsbDeviceHandle> device) + : ApiResource(owner_extension_id), device_(device) { +} + +UsbDeviceResource::~UsbDeviceResource() { + BrowserThread::PostTask(BrowserThread::FILE, + FROM_HERE, + base::Bind(&UsbDeviceHandle::Close, device_)); +} + +} // namespace extensions diff --git a/extensions/browser/api/usb/usb_device_resource.h b/extensions/browser/api/usb/usb_device_resource.h new file mode 100644 index 0000000..cb6cbbe --- /dev/null +++ b/extensions/browser/api/usb/usb_device_resource.h @@ -0,0 +1,50 @@ +// Copyright 2014 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. + +#ifndef EXTENSIONS_BROWSER_API_USB_USB_DEVICE_RESOURCE_H_ +#define EXTENSIONS_BROWSER_API_USB_USB_DEVICE_RESOURCE_H_ + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "components/usb_service/usb_device_handle.h" +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/api/api_resource.h" +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/common/api/usb.h" + +namespace net { +class IOBuffer; +} // namespace net + +namespace extensions { + +// A UsbDeviceResource is an ApiResource wrapper for a UsbDevice. +class UsbDeviceResource : public ApiResource { + public: + UsbDeviceResource(const std::string& owner_extension_id, + scoped_refptr<usb_service::UsbDeviceHandle> device); + virtual ~UsbDeviceResource(); + + scoped_refptr<usb_service::UsbDeviceHandle> device() { return device_; } + + static const content::BrowserThread::ID kThreadId = + content::BrowserThread::FILE; + + private: + friend class ApiResourceManager<UsbDeviceResource>; + static const char* service_name() { return "UsbDeviceResourceManager"; } + + scoped_refptr<usb_service::UsbDeviceHandle> device_; + + DISALLOW_COPY_AND_ASSIGN(UsbDeviceResource); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_USB_USB_DEVICE_RESOURCE_H_ diff --git a/extensions/browser/api/usb/usb_manual_apitest.cc b/extensions/browser/api/usb/usb_manual_apitest.cc new file mode 100644 index 0000000..d28be9d --- /dev/null +++ b/extensions/browser/api/usb/usb_manual_apitest.cc @@ -0,0 +1,18 @@ +// Copyright 2014 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/permissions/permissions_api.h" +#include "chrome/browser/extensions/extension_apitest.h" + +namespace { + +class UsbManualApiTest : public ExtensionApiTest {}; + +} // namespace + +IN_PROC_BROWSER_TEST_F(UsbManualApiTest, MANUAL_ListInterfaces) { + extensions::PermissionsRequestFunction::SetIgnoreUserGestureForTests(true); + extensions::PermissionsRequestFunction::SetAutoConfirmForTests(true); + ASSERT_TRUE(RunExtensionTest("usb_manual/list_interfaces")); +} diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index cae93b1..4ea9b9e 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -39,5 +39,9 @@ "channel": "stable", "extension_types": "all", "contexts": ["blessed_extension", "unblessed_extension", "content_script"] + }, + "usb": { + "dependencies": ["permission:usb"], + "contexts": ["blessed_extension"] } } diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json index a1e975b..3447aac 100644 --- a/extensions/common/api/_permission_features.json +++ b/extensions/common/api/_permission_features.json @@ -53,5 +53,33 @@ "channel": "stable", "extension_types": ["extension", "legacy_packaged_app", "platform_app"], "min_manifest_version": 2 - } + }, + "usb": [ + { + "channel": "stable", + "extension_types": ["platform_app"] + }, + { + "channel": "stable", + "extension_types": ["extension"], + "whitelist": [ + "496B6890097EB6E19809ADEADD095A8721FBB2E0", // FIDO U2F APIs + "E24F1786D842E91E74C27929B0B3715A4689A473" // CryptoToken + ] + } + ], + "usbDevices": [ + { + "channel": "stable", + "extension_types": ["platform_app"] + }, + { + "channel": "stable", + "extension_types": ["extension"], + "whitelist": [ + "496B6890097EB6E19809ADEADD095A8721FBB2E0", // FIDO U2F APIs + "E24F1786D842E91E74C27929B0B3715A4689A473" // CryptoToken + ] + } + ] } diff --git a/extensions/common/api/api.gyp b/extensions/common/api/api.gyp index 4e3c2b0..b124637 100644 --- a/extensions/common/api/api.gyp +++ b/extensions/common/api/api.gyp @@ -30,6 +30,7 @@ 'sockets_udp.idl', 'storage.json', 'test.json', + 'usb.idl', ], 'cc_dir': 'extensions/common/api', 'root_namespace': 'extensions::core_api', diff --git a/extensions/common/api/usb.idl b/extensions/common/api/usb.idl new file mode 100644 index 0000000..278171e --- /dev/null +++ b/extensions/common/api/usb.idl @@ -0,0 +1,307 @@ +// Copyright 2014 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. + +// Use the <code>chrome.usb</code> API to interact with connected USB +// devices. This API provides access to USB operations from within the context +// of an app. Using this API, apps can function as drivers for hardware devices. +namespace usb { + + // Direction, Recipient, RequestType, and TransferType all map to their + // namesakes within the USB specification. + enum Direction {in, out}; + enum Recipient {device, _interface, endpoint, other}; + enum RequestType {standard, class, vendor, reserved}; + enum TransferType {control, interrupt, isochronous, bulk}; + + // For isochronous mode, SynchronizationType and UsageType map to their + // namesakes within the USB specification. + enum SynchronizationType {asynchronous, adaptive, synchronous}; + enum UsageType {data, feedback, explicitFeedback}; + + // Returned by |getDevices| to identify a connected USB device. + dictionary Device { + // The id of the USB device. It remains unchanged until the device is + // unplugged. + long device; + long vendorId; + long productId; + }; + + // Returned by |openDevice| to be used for USB communication. + // Every time a device is opened, a new connection handle is created. + // + // A connection handle represents the underlying data structure that contains + // all the data we need to communicate with a USB device, including the status + // of interfaces, the pending transfers, the descriptors, and etc. A connectin + // handle id is different from a USB device id. + // + // All connection handles can work together if the device allows it. + // The connection handle will be automatically closed when the app is reloaded + // or suspended. + // + // When a connection handle is closed, all the interfaces it claimed will be + // released and all the transfers in progress will be canceled immediately. + dictionary ConnectionHandle { + // The id of the USB connection handle. + long handle; + long vendorId; + long productId; + }; + + dictionary EndpointDescriptor { + long address; + TransferType type; + Direction direction; + long maximumPacketSize; + + // Used for isochronous mode. + SynchronizationType? synchronization; + UsageType? usage; + + // If this is an interrupt endpoint, this will be 1-255. + long? pollingInterval; + }; + + dictionary InterfaceDescriptor { + long interfaceNumber; + long alternateSetting; + long interfaceClass; + long interfaceSubclass; + long interfaceProtocol; + DOMString? description; + EndpointDescriptor[] endpoints; + }; + + // ControlTransferInfo represents that parameters to a single USB control + // transfer. + dictionary ControlTransferInfo { + // The direction of this transfer. + Direction direction; + + // The intended recipient for this transfer. + Recipient recipient; + + // The type of this request. + RequestType requestType; + + long request; + long value; + long index; + + // If this transfer is an input transfer, then this field must be set to + // indicate the expected data length. If this is an output transfer, then + // this field is ignored. + long? length; + + // The data payload carried by this transfer. If this is an output transfer + // then this field must be set. + ArrayBuffer? data; + }; + + // GenericTransferInfo is used by both bulk and interrupt transfers to + // specify the parameters of the transfer. + dictionary GenericTransferInfo { + // The direction of this transfer. + Direction direction; + + long endpoint; + + // If this is an input transfer then this field indicates the size of the + // input buffer. If this is an output transfer then this field is ignored. + long? length; + + // If this is an output transfer then this field must be populated. + // Otherwise, it will be ignored. + ArrayBuffer? data; + }; + + // IsochronousTransferInfo describes a single multi-packet isochronous + // transfer. + dictionary IsochronousTransferInfo { + // All of the normal transfer parameters are encapsulated in the + // transferInfo parameters. Note that the data specified in this parameter + // block is split along packetLength boundaries to form the individual + // packets of the transfer. + GenericTransferInfo transferInfo; + + // The total number of packets in this transfer. + long packets; + + // The length of each of the packets in this transfer. + long packetLength; + }; + + dictionary TransferResultInfo { + // A value of 0 indicates that the transfer was a success. Other values + // indicate failure. + long? resultCode; + + // If the transfer was an input transfer then this field will contain all + // of the input data requested. + ArrayBuffer? data; + }; + + // Describes the properties of devices which are found via |getDevices|. + dictionary EnumerateDevicesOptions { + long vendorId; + long productId; + }; + + // Describes the properties of devices which are found via |findDevices|. + dictionary EnumerateDevicesAndRequestAccessOptions { + long vendorId; + long productId; + // The interface id to request access against. + // Only available on ChromeOS. It has no effect on other platforms. + long? interfaceId; + }; + + callback VoidCallback = void (); + callback GetDevicesCallback = void (Device[] devices); + callback RequestAccessCallback = void (boolean sucess); + callback OpenDeviceCallback = void (ConnectionHandle handle); + callback FindDevicesCallback = void (ConnectionHandle[] handles); + callback ListInterfacesCallback = void (InterfaceDescriptor[] descriptors); + callback CloseDeviceCallback = void (); + callback TransferCallback = void (TransferResultInfo info); + callback ResetDeviceCallback = void(boolean result); + + interface Functions { + // Lists USB devices specified by vendorId/productId/interfaceId tuple. + // |options|: The properties to search for on target devices. + // |callback|: Invoked with a list of |Device|s on complete. + static void getDevices(EnumerateDevicesOptions options, + GetDevicesCallback callback); + + // This method is ChromeOS specific. Calling this method on other platforms + // will fail. + // Requests access from the permission broker to an OS claimed device if the + // given interface on the device is not claimed. + // + // |device|: The device to request access to. + // |interfaceId|: + static void requestAccess(Device device, + long interfaceId, + RequestAccessCallback callback); + + // Opens a USB device returned by |getDevices|. + // |device|: The device to open. + // |callback|: Invoked with the created ConnectionHandle on complete. + static void openDevice(Device device, OpenDeviceCallback callback); + + // Finds USB devices specified by the vendorId/productId/interfaceId tuple + // and, if permissions allow, opens them for use. + // + // On Chrome OS, you can specify the interfaceId. In that case the method + // will request access from permission broker in the same way as in + // |requestUsbAcess|. + // + // If the access request is rejected, or the device is failed to be opened, + // its connection handle will not be created or returned. + // + // Calling this method is equivalent to calling |getDevices| followed by + // a series of |requestAccess| (if it is on ChromeOs) and |openDevice| + // calls, and returning all the successfully opened connection handles. + // + // |options|: The properties to search for on target devices. + // |callback|: Invoked with the opened ConnectionHandle on complete. + static void findDevices(EnumerateDevicesAndRequestAccessOptions options, + FindDevicesCallback callback); + + // Closes a connection handle. Invoking operations on a device after it + // has been closed is a safe operation, but causes no action to be taken. + // |handle|: The connection handle to close. + // |callback|: The callback to invoke once the device is closed. + static void closeDevice(ConnectionHandle handle, + optional CloseDeviceCallback callback); + + // Lists all the interfaces on the USB device. + // |handle|: The device from which the interfaces should be listed. + // |callback|: The callback to invoke when the interfaces are enumerated. + static void listInterfaces(ConnectionHandle handle, + ListInterfacesCallback callback); + + // Claims an interface on the specified USB device. + // Before you can transfer data with endpoints, you must claim their parent + // interfaces. Only one connection handle on the same host can claim each + // interface. If the interface is already claimed, this call will fail. + // + // You shall call releaseInterface when the interface is not needed anymore. + // + // |handle|: The device on which the interface is to be claimed. + // |interface|: The interface number to be claimed. + // |callback|: The callback to invoke once the interface is claimed. + static void claimInterface(ConnectionHandle handle, long interfaceNumber, + VoidCallback callback); + + // Releases a claim to an interface on the provided device. + // |handle|: The device on which the interface is to be released. + // |interface|: The interface number to be released. + // |callback|: The callback to invoke once the interface is released. + static void releaseInterface(ConnectionHandle handle, long interfaceNumber, + VoidCallback callback); + + // Selects an alternate setting on a previously claimed interface on a + // device. + // |handle|: The device on which the interface settings are to be set. + // |interface|: The interface number to be set. + // |alternateSetting|: The alternate setting to set. + // |callback|: The callback to invoke once the interface setting is set. + static void setInterfaceAlternateSetting(ConnectionHandle handle, + long interfaceNumber, + long alternateSetting, + VoidCallback callback); + + // Performs a control transfer on the specified device. See the + // ControlTransferInfo structure for the parameters required to make a + // transfer. + // + // Conceptually control transfer talks to the device itself. You do not need + // to claim interface 0 to perform a control transfer. + // + // |handle|: A connection handle to make the transfer on. + // |transferInfo|: The parameters to the transfer. See ControlTransferInfo. + // |callback|: Invoked once the transfer has completed. + static void controlTransfer(ConnectionHandle handle, + ControlTransferInfo transferInfo, + TransferCallback callback); + + // Performs a bulk transfer on the specified device. + // |handle|: A connection handle to make the transfer on. + // |transferInfo|: The parameters to the transfer. See GenericTransferInfo. + // |callback|: Invoked once the transfer has completed. + static void bulkTransfer(ConnectionHandle handle, + GenericTransferInfo transferInfo, + TransferCallback callback); + + // Performs an interrupt transfer on the specified device. + // |handle|: A connection handle to make the transfer on. + // |transferInfo|: The parameters to the transfer. See GenericTransferInfo. + // |callback|: Invoked once the transfer has completed. + static void interruptTransfer(ConnectionHandle handle, + GenericTransferInfo transferInfo, + TransferCallback callback); + + // Performs an isochronous transfer on the specific device. + // |handle|: A connection handle to make the transfer on. + // |transferInfo|: The parameters to the transfer. See + // IsochronousTransferInfo. + // |callback|: Invoked once the transfer has been completed. + static void isochronousTransfer(ConnectionHandle handle, + IsochronousTransferInfo transferInfo, + TransferCallback callback); + + // Tries to reset the USB device and restores it to the previous status. + // If the reset fails, the given connection handle will be closed and the + // USB device will appear to be disconnected then reconnected. + // In that case you must call |getDevices| or |findDevices| again to acquire + // the device. + // + // |handle|: A connection handle to reset. + // |callback|: Invoked once the device is reset with a boolean indicating + // whether the reset is completed successfully. + static void resetDevice(ConnectionHandle handle, + ResetDeviceCallback callback); + }; +}; diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 4411dae6..faaa42bc 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -221,6 +221,7 @@ 'type': 'static_library', 'dependencies': [ '../components/components.gyp:keyed_service_content', + '../components/components.gyp:usb_service', '../content/content.gyp:content_browser', '../skia/skia.gyp:skia', '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase', @@ -292,6 +293,10 @@ 'browser/api/storage/weak_unlimited_settings_storage.h', 'browser/api/test/test_api.cc', 'browser/api/test/test_api.h', + 'browser/api/usb/usb_api.cc', + 'browser/api/usb/usb_api.h', + 'browser/api/usb/usb_device_resource.cc', + 'browser/api/usb/usb_device_resource.h', 'browser/api_activity_monitor.h', 'browser/app_sorting.h', 'browser/blacklist_state.h', @@ -405,6 +410,9 @@ 'browser/browser_context_keyed_service_factories.cc', 'browser/browser_context_keyed_service_factories.h', ], + 'dependencies!': [ + '../components/components.gyp:usb_service', + ], }], ], # Disable c4267 warnings until we fix size_t to int truncations. |