OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "device/devices_app/usb/device_impl.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <algorithm> | |
10 #include <numeric> | |
11 #include <utility> | |
12 #include <vector> | |
13 | |
14 #include "base/bind.h" | |
15 #include "base/callback.h" | |
16 #include "base/stl_util.h" | |
17 #include "device/devices_app/usb/type_converters.h" | |
18 #include "device/usb/usb_descriptors.h" | |
19 #include "device/usb/usb_device.h" | |
20 #include "net/base/io_buffer.h" | |
21 | |
22 namespace device { | |
23 namespace usb { | |
24 | |
25 namespace { | |
26 | |
27 using MojoTransferInCallback = | |
28 mojo::Callback<void(TransferStatus, mojo::Array<uint8_t>)>; | |
29 | |
30 using MojoTransferOutCallback = mojo::Callback<void(TransferStatus)>; | |
31 | |
32 template <typename... Args> | |
33 void CallMojoCallback(scoped_ptr<mojo::Callback<void(Args...)>> callback, | |
34 Args... args) { | |
35 callback->Run(args...); | |
36 } | |
37 | |
38 // Generic wrapper to convert a Mojo callback to something we can rebind and | |
39 // pass around. This is only usable for callbacks with no move-only arguments. | |
40 template <typename... Args> | |
41 base::Callback<void(Args...)> WrapMojoCallback( | |
42 const mojo::Callback<void(Args...)>& callback) { | |
43 // mojo::Callback is not thread safe. By wrapping |callback| in a scoped_ptr | |
44 // we guarantee that it will be freed when CallMojoCallback is run and not | |
45 // retained until the base::Callback is destroyed, which could happen on any | |
46 // thread. This pattern is also used below in places where this generic | |
47 // wrapper is not used. | |
48 auto callback_ptr = | |
49 make_scoped_ptr(new mojo::Callback<void(Args...)>(callback)); | |
50 return base::Bind(&CallMojoCallback<Args...>, base::Passed(&callback_ptr)); | |
51 } | |
52 | |
53 void OnPermissionCheckComplete( | |
54 const base::Callback<void(bool)>& callback, | |
55 const base::Callback<void(const base::Callback<void(bool)>&)>& action, | |
56 bool allowed) { | |
57 if (allowed) | |
58 action.Run(callback); | |
59 else | |
60 callback.Run(false); | |
61 } | |
62 | |
63 scoped_refptr<net::IOBuffer> CreateTransferBuffer(size_t size) { | |
64 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer( | |
65 std::max(static_cast<size_t>(1u), static_cast<size_t>(size))); | |
66 return buffer; | |
67 } | |
68 | |
69 void OnTransferIn(scoped_ptr<MojoTransferInCallback> callback, | |
70 UsbTransferStatus status, | |
71 scoped_refptr<net::IOBuffer> buffer, | |
72 size_t buffer_size) { | |
73 mojo::Array<uint8_t> data; | |
74 if (buffer) { | |
75 // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a | |
76 // std::vector<uint8_t> instead of net::IOBuffer. Then we could move | |
77 // instead of copy. | |
78 std::vector<uint8_t> bytes(buffer_size); | |
79 std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); | |
80 data.Swap(&bytes); | |
81 } | |
82 callback->Run(mojo::ConvertTo<TransferStatus>(status), std::move(data)); | |
83 } | |
84 | |
85 void OnControlTransferInPermissionCheckComplete( | |
86 scoped_refptr<UsbDeviceHandle> device_handle, | |
87 ControlTransferParamsPtr params, | |
88 int length, | |
89 int timeout, | |
90 scoped_ptr<Device::ControlTransferInCallback> callback, | |
91 bool allowed) { | |
92 if (allowed) { | |
93 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(length); | |
94 device_handle->ControlTransfer( | |
95 USB_DIRECTION_INBOUND, | |
96 mojo::ConvertTo<UsbDeviceHandle::TransferRequestType>(params->type), | |
97 mojo::ConvertTo<UsbDeviceHandle::TransferRecipient>(params->recipient), | |
98 params->request, params->value, params->index, buffer, length, timeout, | |
99 base::Bind(&OnTransferIn, base::Passed(&callback))); | |
100 } else { | |
101 mojo::Array<uint8_t> data; | |
102 callback->Run(TransferStatus::PERMISSION_DENIED, std::move(data)); | |
103 } | |
104 } | |
105 | |
106 void OnTransferOut(scoped_ptr<MojoTransferOutCallback> callback, | |
107 UsbTransferStatus status, | |
108 scoped_refptr<net::IOBuffer> buffer, | |
109 size_t buffer_size) { | |
110 callback->Run(mojo::ConvertTo<TransferStatus>(status)); | |
111 } | |
112 | |
113 void OnControlTransferOutPermissionCheckComplete( | |
114 scoped_refptr<UsbDeviceHandle> device_handle, | |
115 ControlTransferParamsPtr params, | |
116 mojo::Array<uint8_t> data, | |
117 int timeout, | |
118 scoped_ptr<Device::ControlTransferOutCallback> callback, | |
119 bool allowed) { | |
120 if (allowed) { | |
121 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); | |
122 const std::vector<uint8_t>& storage = data.storage(); | |
123 std::copy(storage.begin(), storage.end(), buffer->data()); | |
124 device_handle->ControlTransfer( | |
125 USB_DIRECTION_OUTBOUND, | |
126 mojo::ConvertTo<UsbDeviceHandle::TransferRequestType>(params->type), | |
127 mojo::ConvertTo<UsbDeviceHandle::TransferRecipient>(params->recipient), | |
128 params->request, params->value, params->index, buffer, data.size(), | |
129 timeout, base::Bind(&OnTransferOut, base::Passed(&callback))); | |
130 } else { | |
131 callback->Run(TransferStatus::PERMISSION_DENIED); | |
132 } | |
133 } | |
134 | |
135 mojo::Array<IsochronousPacketPtr> BuildIsochronousPacketArray( | |
136 mojo::Array<uint32_t> packet_lengths, | |
137 TransferStatus status) { | |
138 mojo::Array<IsochronousPacketPtr> packets(packet_lengths.size()); | |
139 for (size_t i = 0; i < packet_lengths.size(); ++i) { | |
140 packets[i] = IsochronousPacket::New(); | |
141 packets[i]->length = packet_lengths[i]; | |
142 packets[i]->status = status; | |
143 } | |
144 return packets; | |
145 } | |
146 | |
147 void OnIsochronousTransferIn( | |
148 scoped_ptr<Device::IsochronousTransferInCallback> callback, | |
149 scoped_refptr<net::IOBuffer> buffer, | |
150 const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) { | |
151 mojo::Array<uint8_t> data; | |
152 if (buffer) { | |
153 // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a | |
154 // std::vector<uint8_t> instead of net::IOBuffer. Then we could move | |
155 // instead of copy. | |
156 uint32_t buffer_size = | |
157 std::accumulate(packets.begin(), packets.end(), 0u, | |
158 [](const uint32_t& a, | |
159 const UsbDeviceHandle::IsochronousPacket& packet) { | |
160 return a + packet.length; | |
161 }); | |
162 std::vector<uint8_t> bytes(buffer_size); | |
163 std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin()); | |
164 data.Swap(&bytes); | |
165 } | |
166 callback->Run(std::move(data), | |
167 mojo::Array<IsochronousPacketPtr>::From(packets)); | |
168 } | |
169 | |
170 void OnIsochronousTransferOut( | |
171 scoped_ptr<Device::IsochronousTransferOutCallback> callback, | |
172 scoped_refptr<net::IOBuffer> buffer, | |
173 const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) { | |
174 callback->Run(mojo::Array<IsochronousPacketPtr>::From(packets)); | |
175 } | |
176 | |
177 } // namespace | |
178 | |
179 DeviceImpl::DeviceImpl(scoped_refptr<UsbDevice> device, | |
180 PermissionProviderPtr permission_provider, | |
181 mojo::InterfaceRequest<Device> request) | |
182 : binding_(this, std::move(request)), | |
183 device_(device), | |
184 permission_provider_(std::move(permission_provider)), | |
185 weak_factory_(this) { | |
186 // This object owns itself and will be destroyed if either the message pipe | |
187 // it is bound to is closed or the PermissionProvider it depends on is | |
188 // unavailable. | |
189 binding_.set_connection_error_handler([this]() { delete this; }); | |
190 permission_provider_.set_connection_error_handler([this]() { delete this; }); | |
191 } | |
192 | |
193 DeviceImpl::~DeviceImpl() { | |
194 CloseHandle(); | |
195 } | |
196 | |
197 void DeviceImpl::CloseHandle() { | |
198 if (device_handle_) | |
199 device_handle_->Close(); | |
200 device_handle_ = nullptr; | |
201 } | |
202 | |
203 void DeviceImpl::HasControlTransferPermission( | |
204 ControlTransferRecipient recipient, | |
205 uint16_t index, | |
206 const base::Callback<void(bool)>& callback) { | |
207 DCHECK(device_handle_); | |
208 const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); | |
209 | |
210 if (recipient == ControlTransferRecipient::INTERFACE || | |
211 recipient == ControlTransferRecipient::ENDPOINT) { | |
212 if (!config) { | |
213 callback.Run(false); | |
214 return; | |
215 } | |
216 | |
217 uint8_t interface_number = index & 0xff; | |
218 if (recipient == ControlTransferRecipient::ENDPOINT) { | |
219 if (!device_handle_->FindInterfaceByEndpoint(index & 0xff, | |
220 &interface_number)) { | |
221 callback.Run(false); | |
222 return; | |
223 } | |
224 } | |
225 | |
226 permission_provider_->HasInterfacePermission( | |
227 interface_number, config->configuration_value, | |
228 DeviceInfo::From(*device_), callback); | |
229 } else if (config) { | |
230 permission_provider_->HasConfigurationPermission( | |
231 config->configuration_value, DeviceInfo::From(*device_), callback); | |
232 } else { | |
233 // Client must already have device permission to have gotten this far. | |
234 callback.Run(true); | |
235 } | |
236 } | |
237 | |
238 void DeviceImpl::OnOpen(const OpenCallback& callback, | |
239 scoped_refptr<UsbDeviceHandle> handle) { | |
240 device_handle_ = handle; | |
241 callback.Run(handle ? OpenDeviceError::OK : OpenDeviceError::ACCESS_DENIED); | |
242 } | |
243 | |
244 void DeviceImpl::GetDeviceInfo(const GetDeviceInfoCallback& callback) { | |
245 callback.Run(DeviceInfo::From(*device_)); | |
246 } | |
247 | |
248 void DeviceImpl::GetConfiguration(const GetConfigurationCallback& callback) { | |
249 const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); | |
250 callback.Run(config ? config->configuration_value : 0); | |
251 } | |
252 | |
253 void DeviceImpl::Open(const OpenCallback& callback) { | |
254 device_->Open( | |
255 base::Bind(&DeviceImpl::OnOpen, weak_factory_.GetWeakPtr(), callback)); | |
256 } | |
257 | |
258 void DeviceImpl::Close(const CloseCallback& callback) { | |
259 CloseHandle(); | |
260 callback.Run(); | |
261 } | |
262 | |
263 void DeviceImpl::SetConfiguration(uint8_t value, | |
264 const SetConfigurationCallback& callback) { | |
265 if (!device_handle_) { | |
266 callback.Run(false); | |
267 return; | |
268 } | |
269 | |
270 auto set_configuration = | |
271 base::Bind(&UsbDeviceHandle::SetConfiguration, device_handle_, value); | |
272 permission_provider_->HasConfigurationPermission( | |
273 value, DeviceInfo::From(*device_), | |
274 base::Bind(&OnPermissionCheckComplete, WrapMojoCallback(callback), | |
275 set_configuration)); | |
276 } | |
277 | |
278 void DeviceImpl::ClaimInterface(uint8_t interface_number, | |
279 const ClaimInterfaceCallback& callback) { | |
280 if (!device_handle_) { | |
281 callback.Run(false); | |
282 return; | |
283 } | |
284 | |
285 const UsbConfigDescriptor* config = device_->GetActiveConfiguration(); | |
286 if (!config) { | |
287 callback.Run(false); | |
288 return; | |
289 } | |
290 | |
291 auto claim_interface = base::Bind(&UsbDeviceHandle::ClaimInterface, | |
292 device_handle_, interface_number); | |
293 permission_provider_->HasInterfacePermission( | |
294 interface_number, config->configuration_value, DeviceInfo::From(*device_), | |
295 base::Bind(&OnPermissionCheckComplete, WrapMojoCallback(callback), | |
296 claim_interface)); | |
297 } | |
298 | |
299 void DeviceImpl::ReleaseInterface(uint8_t interface_number, | |
300 const ReleaseInterfaceCallback& callback) { | |
301 if (!device_handle_) { | |
302 callback.Run(false); | |
303 return; | |
304 } | |
305 | |
306 device_handle_->ReleaseInterface(interface_number, | |
307 WrapMojoCallback(callback)); | |
308 } | |
309 | |
310 void DeviceImpl::SetInterfaceAlternateSetting( | |
311 uint8_t interface_number, | |
312 uint8_t alternate_setting, | |
313 const SetInterfaceAlternateSettingCallback& callback) { | |
314 if (!device_handle_) { | |
315 callback.Run(false); | |
316 return; | |
317 } | |
318 | |
319 device_handle_->SetInterfaceAlternateSetting( | |
320 interface_number, alternate_setting, WrapMojoCallback(callback)); | |
321 } | |
322 | |
323 void DeviceImpl::Reset(const ResetCallback& callback) { | |
324 if (!device_handle_) { | |
325 callback.Run(false); | |
326 return; | |
327 } | |
328 | |
329 device_handle_->ResetDevice(WrapMojoCallback(callback)); | |
330 } | |
331 | |
332 void DeviceImpl::ClearHalt(uint8_t endpoint, | |
333 const ClearHaltCallback& callback) { | |
334 if (!device_handle_) { | |
335 callback.Run(false); | |
336 return; | |
337 } | |
338 | |
339 device_handle_->ClearHalt(endpoint, WrapMojoCallback(callback)); | |
340 } | |
341 | |
342 void DeviceImpl::ControlTransferIn(ControlTransferParamsPtr params, | |
343 uint32_t length, | |
344 uint32_t timeout, | |
345 const ControlTransferInCallback& callback) { | |
346 if (!device_handle_) { | |
347 callback.Run(TransferStatus::TRANSFER_ERROR, mojo::Array<uint8_t>()); | |
348 return; | |
349 } | |
350 | |
351 auto callback_ptr = make_scoped_ptr(new ControlTransferInCallback(callback)); | |
352 ControlTransferRecipient recipient = params->recipient; | |
353 uint16_t index = params->index; | |
354 HasControlTransferPermission( | |
355 recipient, index, | |
356 base::Bind(&OnControlTransferInPermissionCheckComplete, device_handle_, | |
357 base::Passed(¶ms), length, timeout, | |
358 base::Passed(&callback_ptr))); | |
359 } | |
360 | |
361 void DeviceImpl::ControlTransferOut( | |
362 ControlTransferParamsPtr params, | |
363 mojo::Array<uint8_t> data, | |
364 uint32_t timeout, | |
365 const ControlTransferOutCallback& callback) { | |
366 if (!device_handle_) { | |
367 callback.Run(TransferStatus::TRANSFER_ERROR); | |
368 return; | |
369 } | |
370 | |
371 auto callback_ptr = make_scoped_ptr(new ControlTransferOutCallback(callback)); | |
372 ControlTransferRecipient recipient = params->recipient; | |
373 uint16_t index = params->index; | |
374 HasControlTransferPermission( | |
375 recipient, index, | |
376 base::Bind(&OnControlTransferOutPermissionCheckComplete, device_handle_, | |
377 base::Passed(¶ms), base::Passed(&data), timeout, | |
378 base::Passed(&callback_ptr))); | |
379 } | |
380 | |
381 void DeviceImpl::GenericTransferIn(uint8_t endpoint_number, | |
382 uint32_t length, | |
383 uint32_t timeout, | |
384 const GenericTransferInCallback& callback) { | |
385 if (!device_handle_) { | |
386 callback.Run(TransferStatus::TRANSFER_ERROR, mojo::Array<uint8_t>()); | |
387 return; | |
388 } | |
389 | |
390 auto callback_ptr = make_scoped_ptr(new GenericTransferInCallback(callback)); | |
391 uint8_t endpoint_address = endpoint_number | 0x80; | |
392 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(length); | |
393 device_handle_->GenericTransfer( | |
394 USB_DIRECTION_INBOUND, endpoint_address, buffer, length, timeout, | |
395 base::Bind(&OnTransferIn, base::Passed(&callback_ptr))); | |
396 } | |
397 | |
398 void DeviceImpl::GenericTransferOut( | |
399 uint8_t endpoint_number, | |
400 mojo::Array<uint8_t> data, | |
401 uint32_t timeout, | |
402 const GenericTransferOutCallback& callback) { | |
403 if (!device_handle_) { | |
404 callback.Run(TransferStatus::TRANSFER_ERROR); | |
405 return; | |
406 } | |
407 | |
408 auto callback_ptr = make_scoped_ptr(new GenericTransferOutCallback(callback)); | |
409 uint8_t endpoint_address = endpoint_number; | |
410 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); | |
411 const std::vector<uint8_t>& storage = data.storage(); | |
412 std::copy(storage.begin(), storage.end(), buffer->data()); | |
413 device_handle_->GenericTransfer( | |
414 USB_DIRECTION_OUTBOUND, endpoint_address, buffer, data.size(), timeout, | |
415 base::Bind(&OnTransferOut, base::Passed(&callback_ptr))); | |
416 } | |
417 | |
418 void DeviceImpl::IsochronousTransferIn( | |
419 uint8_t endpoint_number, | |
420 mojo::Array<uint32_t> packet_lengths, | |
421 uint32_t timeout, | |
422 const IsochronousTransferInCallback& callback) { | |
423 if (!device_handle_) { | |
424 callback.Run(mojo::Array<uint8_t>(), | |
425 BuildIsochronousPacketArray(std::move(packet_lengths), | |
426 TransferStatus::TRANSFER_ERROR)); | |
427 return; | |
428 } | |
429 | |
430 auto callback_ptr = | |
431 make_scoped_ptr(new IsochronousTransferInCallback(callback)); | |
432 uint8_t endpoint_address = endpoint_number | 0x80; | |
433 device_handle_->IsochronousTransferIn( | |
434 endpoint_address, packet_lengths.storage(), timeout, | |
435 base::Bind(&OnIsochronousTransferIn, base::Passed(&callback_ptr))); | |
436 } | |
437 | |
438 void DeviceImpl::IsochronousTransferOut( | |
439 uint8_t endpoint_number, | |
440 mojo::Array<uint8_t> data, | |
441 mojo::Array<uint32_t> packet_lengths, | |
442 uint32_t timeout, | |
443 const IsochronousTransferOutCallback& callback) { | |
444 if (!device_handle_) { | |
445 callback.Run(BuildIsochronousPacketArray(std::move(packet_lengths), | |
446 TransferStatus::TRANSFER_ERROR)); | |
447 return; | |
448 } | |
449 | |
450 auto callback_ptr = | |
451 make_scoped_ptr(new IsochronousTransferOutCallback(callback)); | |
452 uint8_t endpoint_address = endpoint_number; | |
453 scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size()); | |
454 { | |
455 const std::vector<uint8_t>& storage = data.storage(); | |
456 std::copy(storage.begin(), storage.end(), buffer->data()); | |
457 } | |
458 device_handle_->IsochronousTransferOut( | |
459 endpoint_address, buffer, packet_lengths.storage(), timeout, | |
460 base::Bind(&OnIsochronousTransferOut, base::Passed(&callback_ptr))); | |
461 } | |
462 | |
463 } // namespace usb | |
464 } // namespace device | |
OLD | NEW |