blob: d63c493dd632f59533966550ff1ae3441f1f1c14 [file] [log] [blame]
[email protected]b5508682014-04-22 17:10:011// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]1c39a6b2012-04-27 16:09:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
reillyg45919812015-01-22 02:39:595#include "device/usb/usb_service_impl.h"
[email protected]1c39a6b2012-04-27 16:09:576
[email protected]d165a6442013-08-08 19:38:117#include <set>
[email protected]22ac24e2012-10-03 17:56:058
reillyg0ae3a9992015-01-05 20:12:429#include "base/bind.h"
reillyg45919812015-01-22 02:39:5910#include "base/location.h"
reillyg257f43092015-01-08 00:48:1011#include "base/memory/weak_ptr.h"
reillyge471fab2014-08-29 01:58:4312#include "base/single_thread_task_runner.h"
[email protected]1c39a6b2012-04-27 16:09:5713#include "base/stl_util.h"
reillyg0ae3a9992015-01-05 20:12:4214#include "base/thread_task_runner_handle.h"
reillyg0f2dc632015-02-23 20:02:0815#include "components/device_event_log/device_event_log.h"
reillygd77718d2014-09-04 00:57:5616#include "device/usb/usb_error.h"
[email protected]1c39a6b2012-04-27 16:09:5717
reillyg257f43092015-01-08 00:48:1018#if defined(OS_WIN)
19#include <usbiodef.h>
20
21#include "base/scoped_observer.h"
22#include "device/core/device_monitor_win.h"
23#endif // OS_WIN
24
reillygd77718d2014-09-04 00:57:5625namespace device {
[email protected]b5508682014-04-22 17:10:0126
reillyg257f43092015-01-08 00:48:1027#if defined(OS_WIN)
28// This class lives on the application main thread so that it can listen for
29// device change notification window messages. It registers for notifications
30// regarding devices implementating the "UsbDevice" interface, which represents
31// most of the devices the UsbService will enumerate.
reillygf8330b72015-01-13 02:48:2032class UsbServiceImpl::UIThreadHelper final
33 : private DeviceMonitorWin::Observer {
reillyg257f43092015-01-08 00:48:1034 public:
35 UIThreadHelper(base::WeakPtr<UsbServiceImpl> usb_service)
36 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
37 usb_service_(usb_service),
38 device_observer_(this) {}
39
40 ~UIThreadHelper() {}
41
42 void Start() {
43 DeviceMonitorWin* device_monitor =
44 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE);
45 if (device_monitor) {
46 device_observer_.Add(device_monitor);
47 }
48 }
49
50 private:
51 void OnDeviceAdded(const std::string& device_path) override {
52 task_runner_->PostTask(
53 FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevices, usb_service_));
54 }
55
56 void OnDeviceRemoved(const std::string& device_path) override {
57 task_runner_->PostTask(
58 FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevices, usb_service_));
59 }
60
61 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
62 base::WeakPtr<UsbServiceImpl> usb_service_;
63 ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_;
64};
65#endif
66
reillyg45919812015-01-22 02:39:5967// static
68UsbService* UsbServiceImpl::Create(
69 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
70 PlatformUsbContext context = NULL;
71 const int rv = libusb_init(&context);
72 if (rv != LIBUSB_SUCCESS) {
reillyg0f2dc632015-02-23 20:02:0873 USB_LOG(ERROR) << "Failed to initialize libusb: "
74 << ConvertPlatformUsbErrorToString(rv);
reillyg45919812015-01-22 02:39:5975 return nullptr;
76 }
77 if (!context) {
78 return nullptr;
79 }
80
81 return new UsbServiceImpl(context, ui_task_runner);
82}
83
[email protected]ad2263f2014-05-05 13:57:5084scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
[email protected]e1954872014-04-13 17:43:2985 DCHECK(CalledOnValidThread());
86 RefreshDevices();
reillyg89b276f2014-09-19 15:00:0187 DeviceMap::iterator it = devices_.find(unique_id);
88 if (it != devices_.end()) {
89 return it->second;
[email protected]e1954872014-04-13 17:43:2990 }
91 return NULL;
[email protected]96816d22013-07-26 11:33:1792}
93
[email protected]ad2263f2014-05-05 13:57:5094void UsbServiceImpl::GetDevices(
95 std::vector<scoped_refptr<UsbDevice> >* devices) {
[email protected]e1954872014-04-13 17:43:2996 DCHECK(CalledOnValidThread());
[email protected]d165a6442013-08-08 19:38:1197 STLClearObject(devices);
[email protected]62269732013-07-02 19:51:3198
reillyg0ae3a9992015-01-05 20:12:4299 if (!hotplug_enabled_) {
100 RefreshDevices();
101 }
102
103 for (const auto& map_entry : devices_) {
104 devices->push_back(map_entry.second);
[email protected]62269732013-07-02 19:51:31105 }
106}
107
reillyge471fab2014-08-29 01:58:43108UsbServiceImpl::UsbServiceImpl(
109 PlatformUsbContext context,
110 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
111 : context_(new UsbContext(context)),
112 ui_task_runner_(ui_task_runner),
reillyg0ae3a9992015-01-05 20:12:42113 next_unique_id_(0),
reillyg257f43092015-01-08 00:48:10114 hotplug_enabled_(false),
115 weak_factory_(this) {
reillyg0ae3a9992015-01-05 20:12:42116 task_runner_ = base::ThreadTaskRunnerHandle::Get();
117 int rv = libusb_hotplug_register_callback(
118 context_->context(),
119 static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
120 LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
121 LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,
122 LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
123 &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_);
124 if (rv == LIBUSB_SUCCESS) {
125 hotplug_enabled_ = true;
reillyg257f43092015-01-08 00:48:10126 } else {
127#if defined(OS_WIN)
128 ui_thread_helper_ = new UIThreadHelper(weak_factory_.GetWeakPtr());
129 ui_task_runner_->PostTask(FROM_HERE,
130 base::Bind(&UIThreadHelper::Start,
131 base::Unretained(ui_thread_helper_)));
132#endif // OS_WIN
reillyg0ae3a9992015-01-05 20:12:42133 }
[email protected]e1954872014-04-13 17:43:29134}
135
[email protected]ad2263f2014-05-05 13:57:50136UsbServiceImpl::~UsbServiceImpl() {
reillyg0ae3a9992015-01-05 20:12:42137 if (hotplug_enabled_) {
138 libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
139 }
reillyg257f43092015-01-08 00:48:10140#if defined(OS_WIN)
141 if (ui_thread_helper_) {
142 ui_task_runner_->DeleteSoon(FROM_HERE, ui_thread_helper_);
143 }
144#endif // OS_WIN
reillyg0ae3a9992015-01-05 20:12:42145 for (const auto& map_entry : devices_) {
146 map_entry.second->OnDisconnect();
[email protected]22ac24e2012-10-03 17:56:05147 }
[email protected]22ac24e2012-10-03 17:56:05148}
149
[email protected]ad2263f2014-05-05 13:57:50150void UsbServiceImpl::RefreshDevices() {
[email protected]e1954872014-04-13 17:43:29151 DCHECK(CalledOnValidThread());
[email protected]d165a6442013-08-08 19:38:11152
153 libusb_device** platform_devices = NULL;
154 const ssize_t device_count =
155 libusb_get_device_list(context_->context(), &platform_devices);
[email protected]3782a822014-06-20 18:13:59156 if (device_count < 0) {
reillyg0f2dc632015-02-23 20:02:08157 USB_LOG(ERROR) << "Failed to get device list: "
158 << ConvertPlatformUsbErrorToString(device_count);
[email protected]3782a822014-06-20 18:13:59159 }
[email protected]d165a6442013-08-08 19:38:11160
161 std::set<UsbDevice*> connected_devices;
[email protected]e1954872014-04-13 17:43:29162 std::vector<PlatformUsbDevice> disconnected_devices;
[email protected]d165a6442013-08-08 19:38:11163
164 // Populates new devices.
165 for (ssize_t i = 0; i < device_count; ++i) {
reillyg89b276f2014-09-19 15:00:01166 if (!ContainsKey(platform_devices_, platform_devices[i])) {
reillyg0ae3a9992015-01-05 20:12:42167 scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]);
168 if (new_device) {
169 connected_devices.insert(new_device.get());
[email protected]3782a822014-06-20 18:13:59170 }
[email protected]d165a6442013-08-08 19:38:11171 } else {
reillyg89b276f2014-09-19 15:00:01172 connected_devices.insert(platform_devices_[platform_devices[i]].get());
[email protected]d165a6442013-08-08 19:38:11173 }
[email protected]1c39a6b2012-04-27 16:09:57174 }
[email protected]22ac24e2012-10-03 17:56:05175
[email protected]d165a6442013-08-08 19:38:11176 // Find disconnected devices.
reillyg4ebb03e02015-01-06 20:58:08177 for (const auto& map_entry : platform_devices_) {
178 PlatformUsbDevice platform_device = map_entry.first;
179 scoped_refptr<UsbDeviceImpl> device = map_entry.second;
180 if (!ContainsKey(connected_devices, device.get())) {
181 disconnected_devices.push_back(platform_device);
182 devices_.erase(device->unique_id());
183
184 NotifyDeviceRemoved(device);
185 device->OnDisconnect();
[email protected]d165a6442013-08-08 19:38:11186 }
[email protected]22ac24e2012-10-03 17:56:05187 }
188
reillyg89b276f2014-09-19 15:00:01189 // Remove disconnected devices from platform_devices_.
reillyg4ebb03e02015-01-06 20:58:08190 for (const PlatformUsbDevice& platform_device : disconnected_devices) {
[email protected]d165a6442013-08-08 19:38:11191 // UsbDevice will be destroyed after this. The corresponding
192 // PlatformUsbDevice will be unref'ed during this process.
reillyg4ebb03e02015-01-06 20:58:08193 platform_devices_.erase(platform_device);
[email protected]d165a6442013-08-08 19:38:11194 }
195
196 libusb_free_device_list(platform_devices, true);
[email protected]22ac24e2012-10-03 17:56:05197}
[email protected]b5508682014-04-22 17:10:01198
reillyg0ae3a9992015-01-05 20:12:42199scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice(
200 PlatformUsbDevice platform_device) {
201 libusb_device_descriptor descriptor;
202 int rv = libusb_get_device_descriptor(platform_device, &descriptor);
203 if (rv == LIBUSB_SUCCESS) {
204 uint32 unique_id;
205 do {
206 unique_id = ++next_unique_id_;
207 } while (devices_.find(unique_id) != devices_.end());
208
209 scoped_refptr<UsbDeviceImpl> new_device(new UsbDeviceImpl(
210 context_, ui_task_runner_, platform_device, descriptor.idVendor,
211 descriptor.idProduct, unique_id));
212 platform_devices_[platform_device] = new_device;
213 devices_[unique_id] = new_device;
reillyg4ebb03e02015-01-06 20:58:08214 NotifyDeviceAdded(new_device);
reillyg0ae3a9992015-01-05 20:12:42215 return new_device;
216 } else {
reillyg0f2dc632015-02-23 20:02:08217 USB_LOG(EVENT) << "Failed to get device descriptor: "
218 << ConvertPlatformUsbErrorToString(rv);
reillyg0ae3a9992015-01-05 20:12:42219 return nullptr;
220 }
221}
222
223// static
224int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
225 PlatformUsbDevice device,
226 libusb_hotplug_event event,
227 void* user_data) {
228 // It is safe to access the UsbServiceImpl* here because libusb takes a lock
229 // around registering, deregistering and calling hotplug callback functions
230 // and so guarantees that this function will not be called by the event
231 // processing thread after it has been deregistered.
232 UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data);
233 switch (event) {
234 case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
235 libusb_ref_device(device); // Released in OnDeviceAdded.
236 if (self->task_runner_->BelongsToCurrentThread()) {
237 self->OnDeviceAdded(device);
238 } else {
239 self->task_runner_->PostTask(
240 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded,
241 base::Unretained(self), device));
242 }
243 break;
244 case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
245 libusb_ref_device(device); // Released in OnDeviceRemoved.
246 if (self->task_runner_->BelongsToCurrentThread()) {
247 self->OnDeviceRemoved(device);
248 } else {
249 self->task_runner_->PostTask(
250 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved,
251 base::Unretained(self), device));
252 }
253 break;
254 default:
255 NOTREACHED();
256 }
257
258 return 0;
259}
260
261void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) {
262 DCHECK(CalledOnValidThread());
263 DCHECK(!ContainsKey(platform_devices_, platform_device));
264
265 AddDevice(platform_device);
266 libusb_unref_device(platform_device);
267}
268
269void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) {
270 DCHECK(CalledOnValidThread());
271
272 PlatformDeviceMap::iterator it = platform_devices_.find(platform_device);
273 if (it != platform_devices_.end()) {
274 scoped_refptr<UsbDeviceImpl> device = it->second;
275 DeviceMap::iterator dev_it = devices_.find(device->unique_id());
276 if (dev_it != devices_.end()) {
277 devices_.erase(dev_it);
278 } else {
279 NOTREACHED();
280 }
reillyg0ae3a9992015-01-05 20:12:42281 platform_devices_.erase(it);
reillyg4ebb03e02015-01-06 20:58:08282
283 NotifyDeviceRemoved(device);
284 device->OnDisconnect();
reillyg0ae3a9992015-01-05 20:12:42285 } else {
286 NOTREACHED();
287 }
288
289 libusb_unref_device(platform_device);
290}
291
reillygd77718d2014-09-04 00:57:56292} // namespace device