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.
5 #include "device/usb/usb_service_impl.h"
10 #include "base/location.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "components/device_event_log/device_event_log.h"
16 #include "device/usb/usb_error.h"
22 #include "base/scoped_observer.h"
23 #include "base/strings/string_util.h"
24 #include "device/core/device_monitor_win.h"
33 // Wrapper around a HDEVINFO that automatically destroys it.
34 class ScopedDeviceInfoList
{
36 explicit ScopedDeviceInfoList(HDEVINFO handle
) : handle_(handle
) {}
38 ~ScopedDeviceInfoList() {
40 SetupDiDestroyDeviceInfoList(handle_
);
44 bool valid() { return handle_
!= INVALID_HANDLE_VALUE
; }
46 HDEVINFO
get() { return handle_
; }
51 DISALLOW_COPY_AND_ASSIGN(ScopedDeviceInfoList
);
54 // Wrapper around an SP_DEVINFO_DATA that initializes it properly and
55 // automatically deletes it.
56 class ScopedDeviceInfo
{
59 memset(&dev_info_data_
, 0, sizeof(dev_info_data_
));
60 dev_info_data_
.cbSize
= sizeof(dev_info_data_
);
64 if (dev_info_set_
!= INVALID_HANDLE_VALUE
) {
65 SetupDiDeleteDeviceInfo(dev_info_set_
, &dev_info_data_
);
69 // Once the SP_DEVINFO_DATA has been populated it must be freed using the
70 // HDEVINFO it was created from.
71 void set_valid(HDEVINFO dev_info_set
) {
72 DCHECK(dev_info_set_
== INVALID_HANDLE_VALUE
);
73 DCHECK(dev_info_set
!= INVALID_HANDLE_VALUE
);
74 dev_info_set_
= dev_info_set
;
77 PSP_DEVINFO_DATA
get() { return &dev_info_data_
; }
80 HDEVINFO dev_info_set_
= INVALID_HANDLE_VALUE
;
81 SP_DEVINFO_DATA dev_info_data_
;
86 // This class lives on the application main thread so that it can listen for
87 // device change notification window messages. It registers for notifications
88 // that may indicate new devices that the UsbService will enumerate.
89 class UsbServiceImpl::UIThreadHelper final
90 : private DeviceMonitorWin::Observer
{
92 UIThreadHelper(base::WeakPtr
<UsbServiceImpl
> usb_service
)
93 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
94 usb_service_(usb_service
),
95 device_observer_(this) {}
100 DeviceMonitorWin
* device_monitor
= DeviceMonitorWin::GetForAllInterfaces();
101 if (device_monitor
) {
102 device_observer_
.Add(device_monitor
);
107 void OnDeviceAdded(const GUID
& class_guid
,
108 const std::string
& device_path
) override
{
109 // Only the root node of a composite USB device has the class GUID
110 // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded.
111 // This first pass filter will catch anything that's sitting on the USB bus
112 // (including devices on 3rd party USB controllers) to avoid the more
113 // expensive driver check that needs to be done on the FILE thread.
114 if (device_path
.find("usb") != std::string::npos
) {
115 task_runner_
->PostTask(
116 FROM_HERE
, base::Bind(&UsbServiceImpl::RefreshDevicesIfWinUsbDevice
,
117 usb_service_
, device_path
));
121 void OnDeviceRemoved(const GUID
& class_guid
,
122 const std::string
& device_path
) override
{
123 // The root USB device node is removed last
124 if (class_guid
== GUID_DEVINTERFACE_USB_DEVICE
) {
125 task_runner_
->PostTask(
126 FROM_HERE
, base::Bind(&UsbServiceImpl::RefreshDevices
, usb_service_
));
130 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
131 base::WeakPtr
<UsbServiceImpl
> usb_service_
;
132 ScopedObserver
<DeviceMonitorWin
, DeviceMonitorWin::Observer
> device_observer_
;
138 UsbService
* UsbServiceImpl::Create(
139 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
140 PlatformUsbContext context
= NULL
;
141 const int rv
= libusb_init(&context
);
142 if (rv
!= LIBUSB_SUCCESS
) {
143 USB_LOG(ERROR
) << "Failed to initialize libusb: "
144 << ConvertPlatformUsbErrorToString(rv
);
151 return new UsbServiceImpl(context
, ui_task_runner
);
154 scoped_refptr
<UsbDevice
> UsbServiceImpl::GetDeviceById(uint32 unique_id
) {
155 DCHECK(CalledOnValidThread());
157 DeviceMap::iterator it
= devices_
.find(unique_id
);
158 if (it
!= devices_
.end()) {
164 void UsbServiceImpl::GetDevices(
165 std::vector
<scoped_refptr
<UsbDevice
> >* devices
) {
166 DCHECK(CalledOnValidThread());
167 STLClearObject(devices
);
169 if (!hotplug_enabled_
) {
173 for (const auto& map_entry
: devices_
) {
174 devices
->push_back(map_entry
.second
);
178 UsbServiceImpl::UsbServiceImpl(
179 PlatformUsbContext context
,
180 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
181 : context_(new UsbContext(context
)),
182 ui_task_runner_(ui_task_runner
),
184 hotplug_enabled_(false),
185 weak_factory_(this) {
186 task_runner_
= base::ThreadTaskRunnerHandle::Get();
187 int rv
= libusb_hotplug_register_callback(
189 static_cast<libusb_hotplug_event
>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
|
190 LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT
),
191 LIBUSB_HOTPLUG_ENUMERATE
, LIBUSB_HOTPLUG_MATCH_ANY
,
192 LIBUSB_HOTPLUG_MATCH_ANY
, LIBUSB_HOTPLUG_MATCH_ANY
,
193 &UsbServiceImpl::HotplugCallback
, this, &hotplug_handle_
);
194 if (rv
== LIBUSB_SUCCESS
) {
195 hotplug_enabled_
= true;
198 ui_thread_helper_
= new UIThreadHelper(weak_factory_
.GetWeakPtr());
199 ui_task_runner_
->PostTask(FROM_HERE
,
200 base::Bind(&UIThreadHelper::Start
,
201 base::Unretained(ui_thread_helper_
)));
206 UsbServiceImpl::~UsbServiceImpl() {
207 if (hotplug_enabled_
) {
208 libusb_hotplug_deregister_callback(context_
->context(), hotplug_handle_
);
211 if (ui_thread_helper_
) {
212 ui_task_runner_
->DeleteSoon(FROM_HERE
, ui_thread_helper_
);
215 for (const auto& map_entry
: devices_
) {
216 map_entry
.second
->OnDisconnect();
220 void UsbServiceImpl::RefreshDevices() {
221 DCHECK(CalledOnValidThread());
223 libusb_device
** platform_devices
= NULL
;
224 const ssize_t device_count
=
225 libusb_get_device_list(context_
->context(), &platform_devices
);
226 if (device_count
< 0) {
227 USB_LOG(ERROR
) << "Failed to get device list: "
228 << ConvertPlatformUsbErrorToString(device_count
);
231 std::set
<UsbDevice
*> connected_devices
;
232 std::vector
<PlatformUsbDevice
> disconnected_devices
;
234 // Populates new devices.
235 for (ssize_t i
= 0; i
< device_count
; ++i
) {
236 if (!ContainsKey(platform_devices_
, platform_devices
[i
])) {
237 scoped_refptr
<UsbDeviceImpl
> new_device
= AddDevice(platform_devices
[i
]);
239 connected_devices
.insert(new_device
.get());
242 connected_devices
.insert(platform_devices_
[platform_devices
[i
]].get());
246 // Find disconnected devices.
247 for (const auto& map_entry
: platform_devices_
) {
248 PlatformUsbDevice platform_device
= map_entry
.first
;
249 scoped_refptr
<UsbDeviceImpl
> device
= map_entry
.second
;
250 if (!ContainsKey(connected_devices
, device
.get())) {
251 disconnected_devices
.push_back(platform_device
);
252 devices_
.erase(device
->unique_id());
254 NotifyDeviceRemoved(device
);
255 device
->OnDisconnect();
259 // Remove disconnected devices from platform_devices_.
260 for (const PlatformUsbDevice
& platform_device
: disconnected_devices
) {
261 // UsbDevice will be destroyed after this. The corresponding
262 // PlatformUsbDevice will be unref'ed during this process.
263 platform_devices_
.erase(platform_device
);
266 libusb_free_device_list(platform_devices
, true);
270 void UsbServiceImpl::RefreshDevicesIfWinUsbDevice(
271 const std::string
& device_path
) {
272 ScopedDeviceInfoList
dev_info_list(SetupDiCreateDeviceInfoList(NULL
, NULL
));
273 if (!dev_info_list
.valid()) {
274 USB_PLOG(ERROR
) << "Failed to create a device information set";
278 // This will add the device to |dev_info_list| so we can query driver info.
279 if (!SetupDiOpenDeviceInterfaceA(dev_info_list
.get(), device_path
.c_str(), 0,
281 USB_PLOG(ERROR
) << "Failed to get device interface data for "
286 ScopedDeviceInfo dev_info
;
287 if (!SetupDiEnumDeviceInfo(dev_info_list
.get(), 0, dev_info
.get())) {
288 USB_PLOG(ERROR
) << "Failed to get device info for " << device_path
;
291 dev_info
.set_valid(dev_info_list
.get());
295 if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list
.get(), dev_info
.get(),
296 SPDRP_SERVICE
, ®_data_type
,
297 &buffer
[0], sizeof buffer
, NULL
)) {
298 USB_PLOG(ERROR
) << "Failed to get device service property";
301 if (reg_data_type
!= REG_SZ
) {
302 USB_LOG(ERROR
) << "Unexpected data type for driver service: "
307 USB_LOG(DEBUG
) << "Driver for " << device_path
<< " is " << buffer
<< ".";
308 if (base::strncasecmp("WinUSB", (const char*)&buffer
[0], sizeof "WinUSB") ==
315 scoped_refptr
<UsbDeviceImpl
> UsbServiceImpl::AddDevice(
316 PlatformUsbDevice platform_device
) {
317 libusb_device_descriptor descriptor
;
318 int rv
= libusb_get_device_descriptor(platform_device
, &descriptor
);
319 if (rv
== LIBUSB_SUCCESS
) {
322 unique_id
= ++next_unique_id_
;
323 } while (devices_
.find(unique_id
) != devices_
.end());
325 scoped_refptr
<UsbDeviceImpl
> new_device(new UsbDeviceImpl(
326 context_
, ui_task_runner_
, platform_device
, descriptor
.idVendor
,
327 descriptor
.idProduct
, unique_id
));
328 platform_devices_
[platform_device
] = new_device
;
329 devices_
[unique_id
] = new_device
;
330 NotifyDeviceAdded(new_device
);
333 USB_LOG(EVENT
) << "Failed to get device descriptor: "
334 << ConvertPlatformUsbErrorToString(rv
);
340 int LIBUSB_CALL
UsbServiceImpl::HotplugCallback(libusb_context
* context
,
341 PlatformUsbDevice device
,
342 libusb_hotplug_event event
,
344 // It is safe to access the UsbServiceImpl* here because libusb takes a lock
345 // around registering, deregistering and calling hotplug callback functions
346 // and so guarantees that this function will not be called by the event
347 // processing thread after it has been deregistered.
348 UsbServiceImpl
* self
= reinterpret_cast<UsbServiceImpl
*>(user_data
);
350 case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
:
351 libusb_ref_device(device
); // Released in OnDeviceAdded.
352 if (self
->task_runner_
->BelongsToCurrentThread()) {
353 self
->OnDeviceAdded(device
);
355 self
->task_runner_
->PostTask(
356 FROM_HERE
, base::Bind(&UsbServiceImpl::OnDeviceAdded
,
357 base::Unretained(self
), device
));
360 case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT
:
361 libusb_ref_device(device
); // Released in OnDeviceRemoved.
362 if (self
->task_runner_
->BelongsToCurrentThread()) {
363 self
->OnDeviceRemoved(device
);
365 self
->task_runner_
->PostTask(
366 FROM_HERE
, base::Bind(&UsbServiceImpl::OnDeviceRemoved
,
367 base::Unretained(self
), device
));
377 void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device
) {
378 DCHECK(CalledOnValidThread());
379 DCHECK(!ContainsKey(platform_devices_
, platform_device
));
381 AddDevice(platform_device
);
382 libusb_unref_device(platform_device
);
385 void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device
) {
386 DCHECK(CalledOnValidThread());
388 PlatformDeviceMap::iterator it
= platform_devices_
.find(platform_device
);
389 if (it
!= platform_devices_
.end()) {
390 scoped_refptr
<UsbDeviceImpl
> device
= it
->second
;
391 DeviceMap::iterator dev_it
= devices_
.find(device
->unique_id());
392 if (dev_it
!= devices_
.end()) {
393 devices_
.erase(dev_it
);
397 platform_devices_
.erase(it
);
399 NotifyDeviceRemoved(device
);
400 device
->OnDisconnect();
405 libusb_unref_device(platform_device
);
408 } // namespace device