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