1// Copyright 2014 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 "components/usb_service/usb_device_impl.h"
6
7#include <algorithm>
8
9#include "base/stl_util.h"
10#include "components/usb_service/usb_context.h"
11#include "components/usb_service/usb_device_handle_impl.h"
12#include "components/usb_service/usb_error.h"
13#include "components/usb_service/usb_interface_impl.h"
14#include "content/public/browser/browser_thread.h"
15#include "third_party/libusb/src/libusb/libusb.h"
16
17#if defined(OS_CHROMEOS)
18#include "base/sys_info.h"
19#include "chromeos/dbus/dbus_thread_manager.h"
20#include "chromeos/dbus/permission_broker_client.h"
21#endif  // defined(OS_CHROMEOS)
22
23using content::BrowserThread;
24
25namespace {
26
27#if defined(OS_CHROMEOS)
28void OnRequestUsbAccessReplied(
29    const base::Callback<void(bool success)>& callback,
30    bool success) {
31  BrowserThread::PostTask(
32      BrowserThread::FILE, FROM_HERE, base::Bind(callback, success));
33}
34#endif  // defined(OS_CHROMEOS)
35
36}  // namespace
37
38namespace usb_service {
39
40UsbDeviceImpl::UsbDeviceImpl(scoped_refptr<UsbContext> context,
41                             PlatformUsbDevice platform_device,
42                             uint16 vendor_id,
43                             uint16 product_id,
44                             uint32 unique_id)
45    : UsbDevice(vendor_id, product_id, unique_id),
46      platform_device_(platform_device),
47      context_(context) {
48  CHECK(platform_device) << "platform_device cannot be NULL";
49  libusb_ref_device(platform_device);
50}
51
52UsbDeviceImpl::~UsbDeviceImpl() {
53  DCHECK(thread_checker_.CalledOnValidThread());
54  for (HandlesVector::iterator it = handles_.begin(); it != handles_.end();
55       ++it) {
56    (*it)->InternalClose();
57  }
58  STLClearObject(&handles_);
59  libusb_unref_device(platform_device_);
60}
61
62#if defined(OS_CHROMEOS)
63
64void UsbDeviceImpl::RequestUsbAcess(
65    int interface_id,
66    const base::Callback<void(bool success)>& callback) {
67  DCHECK(thread_checker_.CalledOnValidThread());
68
69  // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
70  // use permission broker.
71  if (base::SysInfo::IsRunningOnChromeOS()) {
72    chromeos::PermissionBrokerClient* client =
73        chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
74    DCHECK(client) << "Could not get permission broker client.";
75    if (!client) {
76      callback.Run(false);
77      return;
78    }
79
80    BrowserThread::PostTask(
81        BrowserThread::UI,
82        FROM_HERE,
83        base::Bind(&chromeos::PermissionBrokerClient::RequestUsbAccess,
84                   base::Unretained(client),
85                   vendor_id(),
86                   product_id(),
87                   interface_id,
88                   base::Bind(&OnRequestUsbAccessReplied, callback)));
89  }
90}
91
92#endif
93
94scoped_refptr<UsbDeviceHandle> UsbDeviceImpl::Open() {
95  DCHECK(thread_checker_.CalledOnValidThread());
96  PlatformUsbDeviceHandle handle;
97  const int rv = libusb_open(platform_device_, &handle);
98  if (LIBUSB_SUCCESS == rv) {
99    scoped_refptr<UsbConfigDescriptor> interfaces = ListInterfaces();
100    if (!interfaces)
101      return NULL;
102    scoped_refptr<UsbDeviceHandleImpl> device_handle =
103        new UsbDeviceHandleImpl(context_, this, handle, interfaces);
104    handles_.push_back(device_handle);
105    return device_handle;
106  } else {
107    LOG(ERROR) << "Failed to open device: " << ConvertErrorToString(rv);
108    return NULL;
109  }
110}
111
112bool UsbDeviceImpl::Close(scoped_refptr<UsbDeviceHandle> handle) {
113  DCHECK(thread_checker_.CalledOnValidThread());
114
115  for (HandlesVector::iterator it = handles_.begin(); it != handles_.end();
116       ++it) {
117    if (*it == handle) {
118      (*it)->InternalClose();
119      handles_.erase(it);
120      return true;
121    }
122  }
123  return false;
124}
125
126scoped_refptr<UsbConfigDescriptor> UsbDeviceImpl::ListInterfaces() {
127  DCHECK(thread_checker_.CalledOnValidThread());
128
129  PlatformUsbConfigDescriptor platform_config;
130  const int rv =
131      libusb_get_active_config_descriptor(platform_device_, &platform_config);
132  if (rv == LIBUSB_SUCCESS) {
133    return new UsbConfigDescriptorImpl(platform_config);
134  } else {
135    LOG(ERROR) << "Failed to get config descriptor: "
136        << ConvertErrorToString(rv);
137    return NULL;
138  }
139}
140
141void UsbDeviceImpl::OnDisconnect() {
142  DCHECK(thread_checker_.CalledOnValidThread());
143  HandlesVector handles;
144  swap(handles, handles_);
145  for (HandlesVector::iterator it = handles.begin(); it != handles.end(); ++it)
146    (*it)->InternalClose();
147}
148
149}  // namespace usb_service
150