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 "services/device/usb/mojo/device_impl.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <numeric>
12 #include <utility>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/callback.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/ref_counted_memory.h"
19 #include "base/stl_util.h"
20 #include "services/device/public/cpp/usb/usb_utils.h"
21 #include "services/device/usb/usb_descriptors.h"
22 #include "services/device/usb/usb_device.h"
23
24 namespace device {
25
26 using mojom::UsbControlTransferParamsPtr;
27 using mojom::UsbControlTransferRecipient;
28 using mojom::UsbIsochronousPacketPtr;
29 using mojom::UsbTransferDirection;
30 using mojom::UsbTransferStatus;
31
32 namespace usb {
33
34 namespace {
35
OnTransferIn(mojom::UsbDevice::GenericTransferInCallback callback,UsbTransferStatus status,scoped_refptr<base::RefCountedBytes> buffer,size_t buffer_size)36 void OnTransferIn(mojom::UsbDevice::GenericTransferInCallback callback,
37 UsbTransferStatus status,
38 scoped_refptr<base::RefCountedBytes> buffer,
39 size_t buffer_size) {
40 std::vector<uint8_t> data;
41 if (buffer) {
42 // TODO(rockot/reillyg): Take advantage of the ability to access the
43 // std::vector<uint8_t> within a base::RefCountedBytes to move instead of
44 // copy.
45 data.resize(buffer_size);
46 std::copy(buffer->front(), buffer->front() + buffer_size, data.begin());
47 }
48
49 std::move(callback).Run(mojo::ConvertTo<mojom::UsbTransferStatus>(status),
50 data);
51 }
52
OnTransferOut(mojom::UsbDevice::GenericTransferOutCallback callback,UsbTransferStatus status,scoped_refptr<base::RefCountedBytes> buffer,size_t buffer_size)53 void OnTransferOut(mojom::UsbDevice::GenericTransferOutCallback callback,
54 UsbTransferStatus status,
55 scoped_refptr<base::RefCountedBytes> buffer,
56 size_t buffer_size) {
57 std::move(callback).Run(mojo::ConvertTo<mojom::UsbTransferStatus>(status));
58 }
59
OnIsochronousTransferIn(mojom::UsbDevice::IsochronousTransferInCallback callback,scoped_refptr<base::RefCountedBytes> buffer,std::vector<UsbIsochronousPacketPtr> packets)60 void OnIsochronousTransferIn(
61 mojom::UsbDevice::IsochronousTransferInCallback callback,
62 scoped_refptr<base::RefCountedBytes> buffer,
63 std::vector<UsbIsochronousPacketPtr> packets) {
64 std::vector<uint8_t> data;
65 if (buffer) {
66 // TODO(rockot/reillyg): Take advantage of the ability to access the
67 // std::vector<uint8_t> within a base::RefCountedBytes to move instead of
68 // copy.
69 uint32_t buffer_size = std::accumulate(
70 packets.begin(), packets.end(), 0u,
71 [](const uint32_t& a, const UsbIsochronousPacketPtr& packet) {
72 return a + packet->length;
73 });
74 data.resize(buffer_size);
75 std::copy(buffer->front(), buffer->front() + buffer_size, data.begin());
76 }
77 std::move(callback).Run(data, std::move(packets));
78 }
79
OnIsochronousTransferOut(mojom::UsbDevice::IsochronousTransferOutCallback callback,scoped_refptr<base::RefCountedBytes> buffer,std::vector<UsbIsochronousPacketPtr> packets)80 void OnIsochronousTransferOut(
81 mojom::UsbDevice::IsochronousTransferOutCallback callback,
82 scoped_refptr<base::RefCountedBytes> buffer,
83 std::vector<UsbIsochronousPacketPtr> packets) {
84 std::move(callback).Run(std::move(packets));
85 }
86
87 } // namespace
88
89 // static
Create(scoped_refptr<device::UsbDevice> device,mojo::PendingReceiver<mojom::UsbDevice> receiver,mojo::PendingRemote<mojom::UsbDeviceClient> client)90 void DeviceImpl::Create(scoped_refptr<device::UsbDevice> device,
91 mojo::PendingReceiver<mojom::UsbDevice> receiver,
92 mojo::PendingRemote<mojom::UsbDeviceClient> client) {
93 auto* device_impl = new DeviceImpl(std::move(device), std::move(client));
94 device_impl->receiver_ = mojo::MakeSelfOwnedReceiver(
95 base::WrapUnique(device_impl), std::move(receiver));
96 }
97
~DeviceImpl()98 DeviceImpl::~DeviceImpl() {
99 CloseHandle();
100 }
101
DeviceImpl(scoped_refptr<device::UsbDevice> device,mojo::PendingRemote<mojom::UsbDeviceClient> client)102 DeviceImpl::DeviceImpl(scoped_refptr<device::UsbDevice> device,
103 mojo::PendingRemote<mojom::UsbDeviceClient> client)
104 : device_(std::move(device)), observer_(this), client_(std::move(client)) {
105 DCHECK(device_);
106 observer_.Add(device_.get());
107
108 if (client_) {
109 client_.set_disconnect_handler(base::BindOnce(
110 &DeviceImpl::OnClientConnectionError, weak_factory_.GetWeakPtr()));
111 }
112 }
113
CloseHandle()114 void DeviceImpl::CloseHandle() {
115 if (device_handle_) {
116 device_handle_->Close();
117 if (client_)
118 client_->OnDeviceClosed();
119 }
120 device_handle_ = nullptr;
121 }
122
HasControlTransferPermission(UsbControlTransferRecipient recipient,uint16_t index)123 bool DeviceImpl::HasControlTransferPermission(
124 UsbControlTransferRecipient recipient,
125 uint16_t index) {
126 DCHECK(device_handle_);
127
128 if (recipient != UsbControlTransferRecipient::INTERFACE &&
129 recipient != UsbControlTransferRecipient::ENDPOINT) {
130 return true;
131 }
132
133 const mojom::UsbConfigurationInfo* config = device_->GetActiveConfiguration();
134 if (!config)
135 return false;
136
137 const mojom::UsbInterfaceInfo* interface = nullptr;
138 if (recipient == UsbControlTransferRecipient::ENDPOINT) {
139 interface = device_handle_->FindInterfaceByEndpoint(index & 0xff);
140 } else {
141 auto interface_it =
142 std::find_if(config->interfaces.begin(), config->interfaces.end(),
143 [index](const mojom::UsbInterfaceInfoPtr& this_iface) {
144 return this_iface->interface_number == (index & 0xff);
145 });
146 if (interface_it != config->interfaces.end())
147 interface = interface_it->get();
148 }
149
150 return interface != nullptr;
151 }
152
153 // static
OnOpen(base::WeakPtr<DeviceImpl> self,OpenCallback callback,scoped_refptr<UsbDeviceHandle> handle)154 void DeviceImpl::OnOpen(base::WeakPtr<DeviceImpl> self,
155 OpenCallback callback,
156 scoped_refptr<UsbDeviceHandle> handle) {
157 if (!self) {
158 if (handle)
159 handle->Close();
160 return;
161 }
162
163 self->device_handle_ = std::move(handle);
164 if (self->device_handle_ && self->client_)
165 self->client_->OnDeviceOpened();
166
167 std::move(callback).Run(self->device_handle_
168 ? mojom::UsbOpenDeviceError::OK
169 : mojom::UsbOpenDeviceError::ACCESS_DENIED);
170 }
171
OnPermissionGrantedForOpen(OpenCallback callback,bool granted)172 void DeviceImpl::OnPermissionGrantedForOpen(OpenCallback callback,
173 bool granted) {
174 if (granted) {
175 device_->Open(base::BindOnce(
176 &DeviceImpl::OnOpen, weak_factory_.GetWeakPtr(), std::move(callback)));
177 } else {
178 std::move(callback).Run(mojom::UsbOpenDeviceError::ACCESS_DENIED);
179 }
180 }
181
Open(OpenCallback callback)182 void DeviceImpl::Open(OpenCallback callback) {
183 if (device_handle_) {
184 std::move(callback).Run(mojom::UsbOpenDeviceError::ALREADY_OPEN);
185 return;
186 }
187
188 if (!device_->permission_granted()) {
189 device_->RequestPermission(
190 base::BindOnce(&DeviceImpl::OnPermissionGrantedForOpen,
191 weak_factory_.GetWeakPtr(), std::move(callback)));
192 return;
193 }
194
195 device_->Open(base::BindOnce(&DeviceImpl::OnOpen, weak_factory_.GetWeakPtr(),
196 std::move(callback)));
197 }
198
Close(CloseCallback callback)199 void DeviceImpl::Close(CloseCallback callback) {
200 CloseHandle();
201 std::move(callback).Run();
202 }
203
SetConfiguration(uint8_t value,SetConfigurationCallback callback)204 void DeviceImpl::SetConfiguration(uint8_t value,
205 SetConfigurationCallback callback) {
206 if (!device_handle_) {
207 std::move(callback).Run(false);
208 return;
209 }
210
211 device_handle_->SetConfiguration(value, std::move(callback));
212 }
213
ClaimInterface(uint8_t interface_number,ClaimInterfaceCallback callback)214 void DeviceImpl::ClaimInterface(uint8_t interface_number,
215 ClaimInterfaceCallback callback) {
216 if (!device_handle_) {
217 std::move(callback).Run(false);
218 return;
219 }
220
221 const mojom::UsbConfigurationInfo* config = device_->GetActiveConfiguration();
222 if (!config) {
223 std::move(callback).Run(false);
224 return;
225 }
226
227 auto interface_it = std::find_if(
228 config->interfaces.begin(), config->interfaces.end(),
229 [interface_number](const mojom::UsbInterfaceInfoPtr& interface) {
230 return interface->interface_number == interface_number;
231 });
232 if (interface_it == config->interfaces.end()) {
233 std::move(callback).Run(false);
234 return;
235 }
236
237 device_handle_->ClaimInterface(interface_number, std::move(callback));
238 }
239
ReleaseInterface(uint8_t interface_number,ReleaseInterfaceCallback callback)240 void DeviceImpl::ReleaseInterface(uint8_t interface_number,
241 ReleaseInterfaceCallback callback) {
242 if (!device_handle_) {
243 std::move(callback).Run(false);
244 return;
245 }
246
247 device_handle_->ReleaseInterface(interface_number, std::move(callback));
248 }
249
SetInterfaceAlternateSetting(uint8_t interface_number,uint8_t alternate_setting,SetInterfaceAlternateSettingCallback callback)250 void DeviceImpl::SetInterfaceAlternateSetting(
251 uint8_t interface_number,
252 uint8_t alternate_setting,
253 SetInterfaceAlternateSettingCallback callback) {
254 if (!device_handle_) {
255 std::move(callback).Run(false);
256 return;
257 }
258
259 device_handle_->SetInterfaceAlternateSetting(
260 interface_number, alternate_setting, std::move(callback));
261 }
262
Reset(ResetCallback callback)263 void DeviceImpl::Reset(ResetCallback callback) {
264 if (!device_handle_) {
265 std::move(callback).Run(false);
266 return;
267 }
268
269 device_handle_->ResetDevice(std::move(callback));
270 }
271
ClearHalt(uint8_t endpoint,ClearHaltCallback callback)272 void DeviceImpl::ClearHalt(uint8_t endpoint, ClearHaltCallback callback) {
273 if (!device_handle_) {
274 std::move(callback).Run(false);
275 return;
276 }
277
278 device_handle_->ClearHalt(endpoint, std::move(callback));
279 }
280
ControlTransferIn(UsbControlTransferParamsPtr params,uint32_t length,uint32_t timeout,ControlTransferInCallback callback)281 void DeviceImpl::ControlTransferIn(UsbControlTransferParamsPtr params,
282 uint32_t length,
283 uint32_t timeout,
284 ControlTransferInCallback callback) {
285 if (!device_handle_) {
286 std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, {});
287 return;
288 }
289
290 if (HasControlTransferPermission(params->recipient, params->index)) {
291 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(length);
292 device_handle_->ControlTransfer(
293 UsbTransferDirection::INBOUND, params->type, params->recipient,
294 params->request, params->value, params->index, buffer, timeout,
295 base::BindOnce(&OnTransferIn, std::move(callback)));
296 } else {
297 std::move(callback).Run(mojom::UsbTransferStatus::PERMISSION_DENIED, {});
298 }
299 }
300
ControlTransferOut(UsbControlTransferParamsPtr params,const std::vector<uint8_t> & data,uint32_t timeout,ControlTransferOutCallback callback)301 void DeviceImpl::ControlTransferOut(UsbControlTransferParamsPtr params,
302 const std::vector<uint8_t>& data,
303 uint32_t timeout,
304 ControlTransferOutCallback callback) {
305 if (!device_handle_) {
306 std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR);
307 return;
308 }
309
310 if (HasControlTransferPermission(params->recipient, params->index)) {
311 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(data);
312 device_handle_->ControlTransfer(
313 UsbTransferDirection::OUTBOUND, params->type, params->recipient,
314 params->request, params->value, params->index, buffer, timeout,
315 base::BindOnce(&OnTransferOut, std::move(callback)));
316 } else {
317 std::move(callback).Run(mojom::UsbTransferStatus::PERMISSION_DENIED);
318 }
319 }
320
GenericTransferIn(uint8_t endpoint_number,uint32_t length,uint32_t timeout,GenericTransferInCallback callback)321 void DeviceImpl::GenericTransferIn(uint8_t endpoint_number,
322 uint32_t length,
323 uint32_t timeout,
324 GenericTransferInCallback callback) {
325 if (!device_handle_) {
326 std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, {});
327 return;
328 }
329
330 uint8_t endpoint_address = endpoint_number | 0x80;
331 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(length);
332 device_handle_->GenericTransfer(
333 UsbTransferDirection::INBOUND, endpoint_address, buffer, timeout,
334 base::BindOnce(&OnTransferIn, std::move(callback)));
335 }
336
GenericTransferOut(uint8_t endpoint_number,const std::vector<uint8_t> & data,uint32_t timeout,GenericTransferOutCallback callback)337 void DeviceImpl::GenericTransferOut(uint8_t endpoint_number,
338 const std::vector<uint8_t>& data,
339 uint32_t timeout,
340 GenericTransferOutCallback callback) {
341 if (!device_handle_) {
342 std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR);
343 return;
344 }
345
346 uint8_t endpoint_address = endpoint_number;
347 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(data);
348 device_handle_->GenericTransfer(
349 UsbTransferDirection::OUTBOUND, endpoint_address, buffer, timeout,
350 base::BindOnce(&OnTransferOut, std::move(callback)));
351 }
352
IsochronousTransferIn(uint8_t endpoint_number,const std::vector<uint32_t> & packet_lengths,uint32_t timeout,IsochronousTransferInCallback callback)353 void DeviceImpl::IsochronousTransferIn(
354 uint8_t endpoint_number,
355 const std::vector<uint32_t>& packet_lengths,
356 uint32_t timeout,
357 IsochronousTransferInCallback callback) {
358 if (!device_handle_) {
359 std::move(callback).Run(
360 {}, BuildIsochronousPacketArray(
361 packet_lengths, mojom::UsbTransferStatus::TRANSFER_ERROR));
362 return;
363 }
364
365 uint8_t endpoint_address = endpoint_number | 0x80;
366 device_handle_->IsochronousTransferIn(
367 endpoint_address, packet_lengths, timeout,
368 base::BindOnce(&OnIsochronousTransferIn, std::move(callback)));
369 }
370
IsochronousTransferOut(uint8_t endpoint_number,const std::vector<uint8_t> & data,const std::vector<uint32_t> & packet_lengths,uint32_t timeout,IsochronousTransferOutCallback callback)371 void DeviceImpl::IsochronousTransferOut(
372 uint8_t endpoint_number,
373 const std::vector<uint8_t>& data,
374 const std::vector<uint32_t>& packet_lengths,
375 uint32_t timeout,
376 IsochronousTransferOutCallback callback) {
377 if (!device_handle_) {
378 std::move(callback).Run(BuildIsochronousPacketArray(
379 packet_lengths, mojom::UsbTransferStatus::TRANSFER_ERROR));
380 return;
381 }
382
383 uint8_t endpoint_address = endpoint_number;
384 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(data);
385 device_handle_->IsochronousTransferOut(
386 endpoint_address, buffer, packet_lengths, timeout,
387 base::BindOnce(&OnIsochronousTransferOut, std::move(callback)));
388 }
389
OnDeviceRemoved(scoped_refptr<device::UsbDevice> device)390 void DeviceImpl::OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) {
391 DCHECK_EQ(device_, device);
392 receiver_->Close();
393 }
394
OnClientConnectionError()395 void DeviceImpl::OnClientConnectionError() {
396 // Close the connection with Blink when WebUsbServiceImpl notifies the
397 // permission revocation from settings UI.
398 receiver_->Close();
399 }
400
401 } // namespace usb
402 } // namespace device
403