1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/usb/web_usb_device_impl.h"
8 #include "base/callback.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/child/scoped_web_callbacks.h"
11 #include "content/renderer/usb/type_converters.h"
12 #include "device/devices_app/public/cpp/constants.h"
13 #include "mojo/application/public/cpp/connect.h"
14 #include "mojo/application/public/interfaces/shell.mojom.h"
15 #include "third_party/WebKit/public/platform/WebVector.h"
16 #include "third_party/WebKit/public/platform/modules/webusb/WebUSBDeviceInfo.h"
17 #include "third_party/WebKit/public/platform/modules/webusb/WebUSBTransferInfo.h"
23 const char kClaimInterfaceFailed
[] = "Unable to claim interface.";
24 const char kClearHaltFailed
[] = "Unable to clear endpoint.";
25 const char kDeviceNoAccess
[] = "Access denied.";
26 const char kDeviceNotOpened
[] = "Device not opened.";
27 const char kDeviceUnavailable
[] = "Device unavailable.";
28 const char kDeviceResetFailed
[] = "Unable to reset the device.";
29 const char kReleaseInterfaceFailed
[] = "Unable to release interface.";
30 const char kSetConfigurationFailed
[] = "Unable to set device configuration.";
31 const char kSetInterfaceFailed
[] = "Unable to set device interface.";
32 const char kTransferFailed
[] = "Transfer failed.";
34 // Generic default rejection handler for any WebUSB callbacks type. Assumes
35 // |CallbacksType| is a blink::WebCallbacks<T, const blink::WebUSBError&>
37 template <typename CallbacksType
>
38 void RejectWithError(const blink::WebUSBError
& error
,
39 scoped_ptr
<CallbacksType
> callbacks
) {
40 callbacks
->onError(error
);
43 template <typename CallbacksType
>
44 void RejectWithDeviceError(const std::string
& message
,
45 scoped_ptr
<CallbacksType
> callbacks
) {
46 RejectWithError(blink::WebUSBError(blink::WebUSBError::Error::Device
,
47 base::UTF8ToUTF16(message
)),
51 template <typename CallbacksType
>
52 void RejectWithTransferError(scoped_ptr
<CallbacksType
> callbacks
) {
53 RejectWithError(blink::WebUSBError(blink::WebUSBError::Error::Transfer
,
54 base::UTF8ToUTF16(kTransferFailed
)),
58 // Create a new ScopedWebCallbacks for WebUSB device callbacks, defaulting to
59 // a "device unavailable" rejection.
60 template <typename CallbacksType
>
61 ScopedWebCallbacks
<CallbacksType
> MakeScopedUSBCallbacks(
62 CallbacksType
* callbacks
) {
63 return make_scoped_web_callbacks(
65 base::Bind(&RejectWithError
<CallbacksType
>,
66 blink::WebUSBError(blink::WebUSBError::Error::Device
,
67 base::UTF8ToUTF16(kDeviceUnavailable
))));
71 ScopedWebCallbacks
<blink::WebUSBDeviceOpenCallbacks
> callbacks
,
72 device::usb::OpenDeviceError error
) {
73 auto scoped_callbacks
= callbacks
.PassCallbacks();
75 case device::usb::OPEN_DEVICE_ERROR_OK
:
76 scoped_callbacks
->onSuccess();
78 case device::usb::OPEN_DEVICE_ERROR_ACCESS_DENIED
:
79 scoped_callbacks
->onError(blink::WebUSBError(
80 blink::WebUSBError::Error::Device
,
81 base::UTF8ToUTF16(kDeviceNoAccess
)));
89 ScopedWebCallbacks
<blink::WebUSBDeviceCloseCallbacks
> callbacks
) {
90 callbacks
.PassCallbacks()->onSuccess();
93 void HandlePassFailDeviceOperation(
94 ScopedWebCallbacks
<blink::WebCallbacks
<void, const blink::WebUSBError
&>>
96 const std::string
& failure_message
,
98 auto scoped_callbacks
= callbacks
.PassCallbacks();
100 scoped_callbacks
->onSuccess();
102 RejectWithDeviceError(failure_message
, scoped_callbacks
.Pass());
106 ScopedWebCallbacks
<blink::WebUSBDeviceControlTransferCallbacks
> callbacks
,
107 device::usb::TransferStatus status
,
108 mojo::Array
<uint8_t> data
) {
109 auto scoped_callbacks
= callbacks
.PassCallbacks();
110 if (status
!= device::usb::TRANSFER_STATUS_COMPLETED
) {
111 RejectWithTransferError(scoped_callbacks
.Pass());
114 scoped_ptr
<blink::WebUSBTransferInfo
> info(new blink::WebUSBTransferInfo());
115 info
->status
= blink::WebUSBTransferInfo::Status::Ok
;
116 info
->data
.assign(data
);
117 scoped_callbacks
->onSuccess(adoptWebPtr(info
.release()));
121 ScopedWebCallbacks
<blink::WebUSBDeviceControlTransferCallbacks
> callbacks
,
122 size_t bytes_written
,
123 device::usb::TransferStatus status
) {
124 auto scoped_callbacks
= callbacks
.PassCallbacks();
125 scoped_ptr
<blink::WebUSBTransferInfo
> info(new blink::WebUSBTransferInfo());
127 case device::usb::TRANSFER_STATUS_COMPLETED
:
128 info
->status
= blink::WebUSBTransferInfo::Status::Ok
;
130 case device::usb::TRANSFER_STATUS_STALLED
:
131 info
->status
= blink::WebUSBTransferInfo::Status::Stall
;
133 case device::usb::TRANSFER_STATUS_BABBLE
:
134 info
->status
= blink::WebUSBTransferInfo::Status::Babble
;
137 RejectWithTransferError(scoped_callbacks
.Pass());
141 // TODO(rockot): Device::ControlTransferOut should expose the number of bytes
142 // actually transferred so we can send it from here.
143 info
->bytesWritten
= bytes_written
;
144 scoped_callbacks
->onSuccess(adoptWebPtr(info
.release()));
149 WebUSBDeviceImpl::WebUSBDeviceImpl(device::usb::DevicePtr device
,
150 const blink::WebUSBDeviceInfo
& device_info
)
151 : device_(device
.Pass()), device_info_(device_info
), weak_factory_(this) {}
153 WebUSBDeviceImpl::~WebUSBDeviceImpl() {}
155 const blink::WebUSBDeviceInfo
& WebUSBDeviceImpl::info() const {
159 void WebUSBDeviceImpl::open(blink::WebUSBDeviceOpenCallbacks
* callbacks
) {
160 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
161 device_
->Open(base::Bind(&OnOpenDevice
, base::Passed(&scoped_callbacks
)));
164 void WebUSBDeviceImpl::close(blink::WebUSBDeviceCloseCallbacks
* callbacks
) {
165 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
167 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
170 base::Bind(&OnDeviceClosed
, base::Passed(&scoped_callbacks
)));
174 void WebUSBDeviceImpl::setConfiguration(
175 uint8_t configuration_value
,
176 blink::WebUSBDeviceSetConfigurationCallbacks
* callbacks
) {
177 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
179 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
181 device_
->SetConfiguration(
183 base::Bind(&HandlePassFailDeviceOperation
,
184 base::Passed(&scoped_callbacks
), kSetConfigurationFailed
));
188 void WebUSBDeviceImpl::claimInterface(
189 uint8_t interface_number
,
190 blink::WebUSBDeviceClaimInterfaceCallbacks
* callbacks
) {
191 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
193 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
195 device_
->ClaimInterface(
197 base::Bind(&HandlePassFailDeviceOperation
,
198 base::Passed(&scoped_callbacks
), kClaimInterfaceFailed
));
202 void WebUSBDeviceImpl::releaseInterface(
203 uint8_t interface_number
,
204 blink::WebUSBDeviceReleaseInterfaceCallbacks
* callbacks
) {
205 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
207 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
209 device_
->ReleaseInterface(
211 base::Bind(&HandlePassFailDeviceOperation
,
212 base::Passed(&scoped_callbacks
), kReleaseInterfaceFailed
));
216 void WebUSBDeviceImpl::setInterface(
217 uint8_t interface_number
,
218 uint8_t alternate_setting
,
219 blink::WebUSBDeviceSetInterfaceAlternateSettingCallbacks
* callbacks
) {
220 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
222 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
224 device_
->SetInterfaceAlternateSetting(
225 interface_number
, alternate_setting
,
226 base::Bind(&HandlePassFailDeviceOperation
,
227 base::Passed(&scoped_callbacks
), kSetInterfaceFailed
));
231 void WebUSBDeviceImpl::clearHalt(
232 uint8_t endpoint_number
,
233 blink::WebUSBDeviceClearHaltCallbacks
* callbacks
) {
234 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
236 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
240 base::Bind(&HandlePassFailDeviceOperation
,
241 base::Passed(&scoped_callbacks
), kClearHaltFailed
));
245 void WebUSBDeviceImpl::controlTransfer(
246 const blink::WebUSBDevice::ControlTransferParameters
& parameters
,
249 unsigned int timeout
,
250 blink::WebUSBDeviceControlTransferCallbacks
* callbacks
) {
251 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
253 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
255 device::usb::ControlTransferParamsPtr params
=
256 device::usb::ControlTransferParams::From(parameters
);
257 switch (parameters
.direction
) {
258 case WebUSBDevice::TransferDirection::In
:
259 device_
->ControlTransferIn(params
.Pass(), data_size
, timeout
,
260 base::Bind(&OnTransferIn
, base::Passed(&scoped_callbacks
)));
262 case WebUSBDevice::TransferDirection::Out
: {
263 std::vector
<uint8_t> bytes
;
265 bytes
.assign(data
, data
+ data_size
);
266 mojo::Array
<uint8_t> mojo_bytes
;
267 mojo_bytes
.Swap(&bytes
);
268 device_
->ControlTransferOut(params
.Pass(), mojo_bytes
.Pass(), timeout
,
269 base::Bind(&OnTransferOut
, base::Passed(&scoped_callbacks
),
279 void WebUSBDeviceImpl::transfer(
280 blink::WebUSBDevice::TransferDirection direction
,
281 uint8_t endpoint_number
,
284 unsigned int timeout
,
285 blink::WebUSBDeviceBulkTransferCallbacks
* callbacks
) {
286 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
288 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
291 case WebUSBDevice::TransferDirection::In
:
292 device_
->GenericTransferIn(
293 endpoint_number
, data_size
, timeout
,
294 base::Bind(&OnTransferIn
, base::Passed(&scoped_callbacks
)));
296 case WebUSBDevice::TransferDirection::Out
: {
297 std::vector
<uint8_t> bytes
;
299 bytes
.assign(data
, data
+ data_size
);
300 mojo::Array
<uint8_t> mojo_bytes
;
301 mojo_bytes
.Swap(&bytes
);
302 device_
->GenericTransferOut(
303 endpoint_number
, mojo_bytes
.Pass(), timeout
,
304 base::Bind(&OnTransferOut
, base::Passed(&scoped_callbacks
),
314 void WebUSBDeviceImpl::reset(blink::WebUSBDeviceResetCallbacks
* callbacks
) {
315 auto scoped_callbacks
= MakeScopedUSBCallbacks(callbacks
);
317 RejectWithDeviceError(kDeviceNotOpened
, scoped_callbacks
.PassCallbacks());
320 base::Bind(&HandlePassFailDeviceOperation
,
321 base::Passed(&scoped_callbacks
), kDeviceResetFailed
));
325 } // namespace content