Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / usb / web_usb_device_impl.cc
blob8ccebdc6bbb1c8b53509a072b0d68695c51bd160
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"
7 #include "base/bind.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"
19 namespace content {
21 namespace {
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&>
36 // for any type |T|.
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)),
48 callbacks.Pass());
51 template <typename CallbacksType>
52 void RejectWithTransferError(scoped_ptr<CallbacksType> callbacks) {
53 RejectWithError(blink::WebUSBError(blink::WebUSBError::Error::Transfer,
54 base::UTF8ToUTF16(kTransferFailed)),
55 callbacks.Pass());
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(
64 callbacks,
65 base::Bind(&RejectWithError<CallbacksType>,
66 blink::WebUSBError(blink::WebUSBError::Error::Device,
67 base::UTF8ToUTF16(kDeviceUnavailable))));
70 void OnOpenDevice(
71 ScopedWebCallbacks<blink::WebUSBDeviceOpenCallbacks> callbacks,
72 device::usb::OpenDeviceError error) {
73 auto scoped_callbacks = callbacks.PassCallbacks();
74 switch(error) {
75 case device::usb::OPEN_DEVICE_ERROR_OK:
76 scoped_callbacks->onSuccess();
77 break;
78 case device::usb::OPEN_DEVICE_ERROR_ACCESS_DENIED:
79 scoped_callbacks->onError(blink::WebUSBError(
80 blink::WebUSBError::Error::Device,
81 base::UTF8ToUTF16(kDeviceNoAccess)));
82 break;
83 default:
84 NOTREACHED();
88 void OnDeviceClosed(
89 ScopedWebCallbacks<blink::WebUSBDeviceCloseCallbacks> callbacks) {
90 callbacks.PassCallbacks()->onSuccess();
93 void HandlePassFailDeviceOperation(
94 ScopedWebCallbacks<blink::WebCallbacks<void, const blink::WebUSBError&>>
95 callbacks,
96 const std::string& failure_message,
97 bool success) {
98 auto scoped_callbacks = callbacks.PassCallbacks();
99 if (success)
100 scoped_callbacks->onSuccess();
101 else
102 RejectWithDeviceError(failure_message, scoped_callbacks.Pass());
105 void OnTransferIn(
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());
112 return;
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()));
120 void OnTransferOut(
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());
126 switch (status) {
127 case device::usb::TRANSFER_STATUS_COMPLETED:
128 info->status = blink::WebUSBTransferInfo::Status::Ok;
129 break;
130 case device::usb::TRANSFER_STATUS_STALLED:
131 info->status = blink::WebUSBTransferInfo::Status::Stall;
132 break;
133 case device::usb::TRANSFER_STATUS_BABBLE:
134 info->status = blink::WebUSBTransferInfo::Status::Babble;
135 break;
136 default:
137 RejectWithTransferError(scoped_callbacks.Pass());
138 return;
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()));
147 } // namespace
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 {
156 return device_info_;
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);
166 if (!device_) {
167 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
168 } else {
169 device_->Close(
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);
178 if (!device_) {
179 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
180 } else {
181 device_->SetConfiguration(
182 configuration_value,
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);
192 if (!device_) {
193 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
194 } else {
195 device_->ClaimInterface(
196 interface_number,
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);
206 if (!device_) {
207 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
208 } else {
209 device_->ReleaseInterface(
210 interface_number,
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);
221 if (!device_) {
222 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
223 } else {
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);
235 if (!device_) {
236 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
237 } else {
238 device_->ClearHalt(
239 endpoint_number,
240 base::Bind(&HandlePassFailDeviceOperation,
241 base::Passed(&scoped_callbacks), kClearHaltFailed));
245 void WebUSBDeviceImpl::controlTransfer(
246 const blink::WebUSBDevice::ControlTransferParameters& parameters,
247 uint8_t* data,
248 size_t data_size,
249 unsigned int timeout,
250 blink::WebUSBDeviceControlTransferCallbacks* callbacks) {
251 auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks);
252 if (!device_) {
253 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
254 } else {
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)));
261 break;
262 case WebUSBDevice::TransferDirection::Out: {
263 std::vector<uint8_t> bytes;
264 if (data)
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),
270 data_size));
271 break;
273 default:
274 NOTREACHED();
279 void WebUSBDeviceImpl::transfer(
280 blink::WebUSBDevice::TransferDirection direction,
281 uint8_t endpoint_number,
282 uint8_t* data,
283 size_t data_size,
284 unsigned int timeout,
285 blink::WebUSBDeviceBulkTransferCallbacks* callbacks) {
286 auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks);
287 if (!device_) {
288 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
289 } else {
290 switch (direction) {
291 case WebUSBDevice::TransferDirection::In:
292 device_->GenericTransferIn(
293 endpoint_number, data_size, timeout,
294 base::Bind(&OnTransferIn, base::Passed(&scoped_callbacks)));
295 break;
296 case WebUSBDevice::TransferDirection::Out: {
297 std::vector<uint8_t> bytes;
298 if (data)
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),
305 data_size));
306 break;
308 default:
309 NOTREACHED();
314 void WebUSBDeviceImpl::reset(blink::WebUSBDeviceResetCallbacks* callbacks) {
315 auto scoped_callbacks = MakeScopedUSBCallbacks(callbacks);
316 if (!device_) {
317 RejectWithDeviceError(kDeviceNotOpened, scoped_callbacks.PassCallbacks());
318 } else {
319 device_->Reset(
320 base::Bind(&HandlePassFailDeviceOperation,
321 base::Passed(&scoped_callbacks), kDeviceResetFailed));
325 } // namespace content