1 // Copyright 2017 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/usb_service_win.h"
6
7 #include <objbase.h>
8 #include <setupapi.h>
9 #include <stdint.h>
10 #include <usbiodef.h>
11
12 #define INITGUID
13 #include <devpkey.h>
14
15 #include "base/bind.h"
16 #include "base/location.h"
17 #include "base/memory/free_deleter.h"
18 #include "base/memory/ptr_util.h"
19 #include "base/scoped_generic.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/stl_util.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/sys_string_conversions.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/threading/thread_task_runner_handle.h"
27 #include "base/win/registry.h"
28 #include "base/win/scoped_handle.h"
29 #include "components/device_event_log/device_event_log.h"
30 #include "services/device/usb/usb_descriptors.h"
31 #include "services/device/usb/usb_device_handle.h"
32 #include "services/device/usb/webusb_descriptors.h"
33
34 namespace device {
35
36 namespace {
37
38 struct DevInfoScopedTraits {
InvalidValuedevice::__anonefc0a5860111::DevInfoScopedTraits39 static HDEVINFO InvalidValue() { return INVALID_HANDLE_VALUE; }
Freedevice::__anonefc0a5860111::DevInfoScopedTraits40 static void Free(HDEVINFO h) { SetupDiDestroyDeviceInfoList(h); }
41 };
42
43 using ScopedDevInfo = base::ScopedGeneric<HDEVINFO, DevInfoScopedTraits>;
44
GetDeviceUint32Property(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,const DEVPROPKEY & property,uint32_t * property_buffer)45 bool GetDeviceUint32Property(HDEVINFO dev_info,
46 SP_DEVINFO_DATA* dev_info_data,
47 const DEVPROPKEY& property,
48 uint32_t* property_buffer) {
49 DEVPROPTYPE property_type;
50 if (!SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
51 &property_type,
52 reinterpret_cast<PBYTE>(property_buffer),
53 sizeof(*property_buffer), nullptr, 0) ||
54 property_type != DEVPROP_TYPE_UINT32) {
55 return false;
56 }
57
58 return true;
59 }
60
GetDeviceStringProperty(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,const DEVPROPKEY & property,base::string16 * buffer)61 bool GetDeviceStringProperty(HDEVINFO dev_info,
62 SP_DEVINFO_DATA* dev_info_data,
63 const DEVPROPKEY& property,
64 base::string16* buffer) {
65 DEVPROPTYPE property_type;
66 DWORD required_size;
67 if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
68 &property_type, nullptr, 0, &required_size, 0) ||
69 GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
70 property_type != DEVPROP_TYPE_STRING) {
71 return false;
72 }
73
74 if (!SetupDiGetDeviceProperty(
75 dev_info, dev_info_data, &property, &property_type,
76 reinterpret_cast<PBYTE>(base::WriteInto(buffer, required_size)),
77 required_size, nullptr, 0)) {
78 return false;
79 }
80
81 return true;
82 }
83
GetDeviceStringListProperty(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,const DEVPROPKEY & property,std::vector<base::string16> * result)84 bool GetDeviceStringListProperty(HDEVINFO dev_info,
85 SP_DEVINFO_DATA* dev_info_data,
86 const DEVPROPKEY& property,
87 std::vector<base::string16>* result) {
88 DEVPROPTYPE property_type;
89 DWORD required_size;
90 if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
91 &property_type, nullptr, 0, &required_size, 0) ||
92 GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
93 property_type != DEVPROP_TYPE_STRING_LIST) {
94 return false;
95 }
96
97 base::string16 buffer;
98 if (!SetupDiGetDeviceProperty(
99 dev_info, dev_info_data, &property, &property_type,
100 reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
101 required_size, nullptr, 0)) {
102 return false;
103 }
104
105 // Windows string list properties use a NUL character as the delimiter.
106 *result = base::SplitString(buffer, base::StringPiece16(L"\0", 1),
107 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
108 return true;
109 }
110
GetServiceName(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,base::string16 * service_name)111 bool GetServiceName(HDEVINFO dev_info,
112 SP_DEVINFO_DATA* dev_info_data,
113 base::string16* service_name) {
114 base::string16 buffer;
115 if (!GetDeviceStringProperty(dev_info, dev_info_data, DEVPKEY_Device_Service,
116 &buffer)) {
117 return false;
118 }
119
120 // Windows pads this string with a variable number of NUL bytes for no
121 // discernible reason.
122 *service_name = base::TrimString(buffer, base::StringPiece16(L"\0", 1),
123 base::TRIM_TRAILING)
124 .as_string();
125 return true;
126 }
127
GetDeviceInterfaceDetails(HDEVINFO dev_info,SP_DEVICE_INTERFACE_DATA * device_interface_data,base::string16 * device_path,uint32_t * bus_number,uint32_t * port_number,base::string16 * parent_instance_id,std::vector<base::string16> * child_instance_ids,base::string16 * service_name)128 bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
129 SP_DEVICE_INTERFACE_DATA* device_interface_data,
130 base::string16* device_path,
131 uint32_t* bus_number,
132 uint32_t* port_number,
133 base::string16* parent_instance_id,
134 std::vector<base::string16>* child_instance_ids,
135 base::string16* service_name) {
136 DWORD required_size = 0;
137 if (SetupDiGetDeviceInterfaceDetail(dev_info, device_interface_data, nullptr,
138 0, &required_size, nullptr) ||
139 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
140 return false;
141 }
142
143 std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
144 device_interface_detail_data(
145 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
146 device_interface_detail_data->cbSize = sizeof(*device_interface_detail_data);
147
148 SP_DEVINFO_DATA dev_info_data = {};
149 dev_info_data.cbSize = sizeof(dev_info_data);
150
151 if (!SetupDiGetDeviceInterfaceDetail(
152 dev_info, device_interface_data, device_interface_detail_data.get(),
153 required_size, nullptr, &dev_info_data)) {
154 USB_PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail";
155 return false;
156 }
157
158 if (device_path) {
159 *device_path = base::string16(device_interface_detail_data->DevicePath);
160 }
161
162 if (bus_number) {
163 if (!GetDeviceUint32Property(dev_info, &dev_info_data,
164 DEVPKEY_Device_BusNumber, bus_number)) {
165 USB_PLOG(ERROR) << "Failed to get device bus number";
166 return false;
167 }
168 }
169
170 if (port_number) {
171 if (!GetDeviceUint32Property(dev_info, &dev_info_data,
172 DEVPKEY_Device_Address, port_number)) {
173 USB_PLOG(ERROR) << "Failed to get device address";
174 return false;
175 }
176 }
177
178 if (parent_instance_id) {
179 if (!GetDeviceStringProperty(dev_info, &dev_info_data,
180 DEVPKEY_Device_Parent, parent_instance_id)) {
181 USB_PLOG(ERROR) << "Failed to get the device parent";
182 return false;
183 }
184 }
185
186 if (child_instance_ids) {
187 if (!GetDeviceStringListProperty(dev_info, &dev_info_data,
188 DEVPKEY_Device_Children,
189 child_instance_ids) &&
190 GetLastError() != ERROR_NOT_FOUND) {
191 USB_PLOG(ERROR) << "Failed to get device children";
192 return false;
193 }
194 }
195
196 if (service_name) {
197 if (!GetServiceName(dev_info, &dev_info_data, service_name)) {
198 USB_PLOG(ERROR) << "Failed to get device driver name";
199 return false;
200 }
201 }
202
203 return true;
204 }
205
GetDevicePath(const base::string16 & instance_id,const GUID & device_interface_guid,base::string16 * device_path)206 bool GetDevicePath(const base::string16& instance_id,
207 const GUID& device_interface_guid,
208 base::string16* device_path) {
209 ScopedDevInfo dev_info(
210 SetupDiGetClassDevs(&device_interface_guid, instance_id.c_str(), 0,
211 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
212 if (!dev_info.is_valid()) {
213 USB_PLOG(ERROR) << "SetupDiGetClassDevs";
214 return false;
215 }
216
217 SP_DEVICE_INTERFACE_DATA device_interface_data = {};
218 device_interface_data.cbSize = sizeof(device_interface_data);
219 if (!SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
220 &device_interface_guid, 0,
221 &device_interface_data)) {
222 USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces";
223 return false;
224 }
225
226 return GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
227 device_path, nullptr, nullptr, nullptr,
228 nullptr, nullptr);
229 }
230
GetWinUsbDevicePath(const base::string16 & instance_id,base::string16 * device_path)231 bool GetWinUsbDevicePath(const base::string16& instance_id,
232 base::string16* device_path) {
233 ScopedDevInfo dev_info(SetupDiCreateDeviceInfoList(nullptr, nullptr));
234 if (!dev_info.is_valid()) {
235 USB_PLOG(ERROR) << "SetupDiCreateDeviceInfoList";
236 return false;
237 }
238
239 SP_DEVINFO_DATA dev_info_data = {};
240 dev_info_data.cbSize = sizeof(dev_info_data);
241 if (!SetupDiOpenDeviceInfo(dev_info.get(), instance_id.c_str(), nullptr, 0,
242 &dev_info_data)) {
243 USB_PLOG(ERROR) << "SetupDiOpenDeviceInfo";
244 return false;
245 }
246
247 base::string16 service_name;
248 if (!GetServiceName(dev_info.get(), &dev_info_data, &service_name)) {
249 USB_PLOG(ERROR) << "Could not get child device's service name";
250 return false;
251 }
252
253 if (!base::EqualsCaseInsensitiveASCII(service_name, L"winusb"))
254 return false;
255
256 // There is no standard device interface GUID for USB functions and so we
257 // must discover the set of GUIDs that have been set in the registry by
258 // the INF file or Microsoft OS Compatibility descriptors before
259 // SetupDiGetDeviceInterfaceDetail() can be used to get the device path.
260 HKEY key = SetupDiOpenDevRegKey(dev_info.get(), &dev_info_data,
261 DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
262 if (key == INVALID_HANDLE_VALUE) {
263 USB_PLOG(ERROR) << "Could not open device registry key";
264 return false;
265 }
266 base::win::RegKey scoped_key(key);
267
268 std::vector<std::wstring> device_interface_guids;
269 LONG result =
270 scoped_key.ReadValues(L"DeviceInterfaceGUIDs", &device_interface_guids);
271 if (result != ERROR_SUCCESS) {
272 USB_LOG(ERROR) << "Could not read device interface GUIDs: "
273 << logging::SystemErrorCodeToString(result);
274 return false;
275 }
276
277 for (const auto& guid_string : device_interface_guids) {
278 GUID guid;
279 if (FAILED(CLSIDFromString(guid_string.c_str(), &guid))) {
280 USB_LOG(ERROR) << "Failed to parse device interface GUID: "
281 << guid_string;
282 continue;
283 }
284
285 if (GetDevicePath(instance_id, guid, device_path))
286 return true;
287 }
288
289 return false;
290 }
291
292 } // namespace
293
294 class UsbServiceWin::BlockingTaskRunnerHelper {
295 public:
BlockingTaskRunnerHelper(base::WeakPtr<UsbServiceWin> service)296 explicit BlockingTaskRunnerHelper(base::WeakPtr<UsbServiceWin> service)
297 : service_task_runner_(base::ThreadTaskRunnerHandle::Get()),
298 service_(service) {}
~BlockingTaskRunnerHelper()299 ~BlockingTaskRunnerHelper() {}
300
EnumerateDevices()301 void EnumerateDevices() {
302 ScopedDevInfo dev_info(
303 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
304 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
305 if (!dev_info.is_valid()) {
306 USB_PLOG(ERROR) << "Failed to set up device enumeration";
307 service_task_runner_->PostTask(
308 FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_));
309 return;
310 }
311
312 SP_DEVICE_INTERFACE_DATA device_interface_data = {};
313 device_interface_data.cbSize = sizeof(device_interface_data);
314 for (DWORD i = 0; SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
315 &GUID_DEVINTERFACE_USB_DEVICE,
316 i, &device_interface_data);
317 ++i) {
318 EnumerateDevice(dev_info.get(), &device_interface_data, base::nullopt);
319 }
320
321 if (GetLastError() != ERROR_NO_MORE_ITEMS)
322 USB_PLOG(ERROR) << "Failed to enumerate devices";
323
324 service_task_runner_->PostTask(
325 FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_));
326 }
327
EnumerateDevicePath(const base::string16 & device_path)328 void EnumerateDevicePath(const base::string16& device_path) {
329 ScopedDevInfo dev_info(
330 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
331 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
332 if (!dev_info.is_valid()) {
333 USB_PLOG(ERROR) << "Failed to set up device enumeration";
334 return;
335 }
336
337 SP_DEVICE_INTERFACE_DATA device_interface_data = {};
338 device_interface_data.cbSize = sizeof(device_interface_data);
339 if (!SetupDiOpenDeviceInterface(dev_info.get(), device_path.c_str(), 0,
340 &device_interface_data)) {
341 USB_PLOG(ERROR) << "Failed to add device interface: " << device_path;
342 return;
343 }
344
345 EnumerateDevice(dev_info.get(), &device_interface_data, device_path);
346 }
347
EnumerateDevice(HDEVINFO dev_info,SP_DEVICE_INTERFACE_DATA * device_interface_data,const base::Optional<base::string16> & opt_device_path)348 void EnumerateDevice(HDEVINFO dev_info,
349 SP_DEVICE_INTERFACE_DATA* device_interface_data,
350 const base::Optional<base::string16>& opt_device_path) {
351 base::string16 device_path;
352 base::string16* device_path_ptr = &device_path;
353 if (opt_device_path) {
354 device_path = *opt_device_path;
355 device_path_ptr = nullptr;
356 }
357
358 uint32_t bus_number;
359 uint32_t port_number;
360 base::string16 parent_instance_id;
361 std::vector<base::string16> child_instance_ids;
362 base::string16 service_name;
363 if (!GetDeviceInterfaceDetails(dev_info, device_interface_data,
364 device_path_ptr, &bus_number, &port_number,
365 &parent_instance_id, &child_instance_ids,
366 &service_name)) {
367 return;
368 }
369
370 // For composite devices Windows loads the usbccgp driver, which creates
371 // child device notes for each of the device functions. It is these device
372 // paths for these children which must be opened in order to communicate
373 // with the WinUSB driver.
374 std::vector<base::string16> child_device_paths;
375 if (base::EqualsCaseInsensitiveASCII(service_name, L"usbccgp")) {
376 for (const base::string16& instance_id : child_instance_ids) {
377 base::string16 child_device_path;
378 if (GetWinUsbDevicePath(instance_id, &child_device_path))
379 child_device_paths.push_back(std::move(child_device_path));
380 }
381 }
382
383 base::string16& hub_path = hub_paths_[parent_instance_id];
384 if (hub_path.empty()) {
385 base::string16 parent_path;
386 if (!GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_HUB,
387 &parent_path)) {
388 return;
389 }
390 hub_path = parent_path;
391 }
392
393 service_task_runner_->PostTask(
394 FROM_HERE, base::BindOnce(&UsbServiceWin::CreateDeviceObject, service_,
395 device_path, hub_path, child_device_paths,
396 bus_number, port_number, service_name));
397 }
398
399 private:
400 std::unordered_map<base::string16, base::string16> hub_paths_;
401
402 // Calls back to |service_| must be posted to |service_task_runner_|, which
403 // runs tasks on the thread where that object lives.
404 scoped_refptr<base::SingleThreadTaskRunner> service_task_runner_;
405 base::WeakPtr<UsbServiceWin> service_;
406 };
407
UsbServiceWin()408 UsbServiceWin::UsbServiceWin()
409 : UsbService(),
410 blocking_task_runner_(CreateBlockingTaskRunner()),
411 helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)),
412 device_observer_(this) {
413 DeviceMonitorWin* device_monitor =
414 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE);
415 if (device_monitor)
416 device_observer_.Add(device_monitor);
417
418 helper_.reset(new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr()));
419 blocking_task_runner_->PostTask(
420 FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::EnumerateDevices,
421 base::Unretained(helper_.get())));
422 }
423
~UsbServiceWin()424 UsbServiceWin::~UsbServiceWin() {
425 NotifyWillDestroyUsbService();
426 }
427
GetDevices(GetDevicesCallback callback)428 void UsbServiceWin::GetDevices(GetDevicesCallback callback) {
429 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
430 if (enumeration_ready())
431 UsbService::GetDevices(std::move(callback));
432 else
433 enumeration_callbacks_.push_back(std::move(callback));
434 }
435
OnDeviceAdded(const GUID & class_guid,const base::string16 & device_path)436 void UsbServiceWin::OnDeviceAdded(const GUID& class_guid,
437 const base::string16& device_path) {
438 blocking_task_runner_->PostTask(
439 FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::EnumerateDevicePath,
440 base::Unretained(helper_.get()), device_path));
441 }
442
OnDeviceRemoved(const GUID & class_guid,const base::string16 & device_path)443 void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid,
444 const base::string16& device_path) {
445 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
446 auto by_path_it = devices_by_path_.find(device_path);
447 if (by_path_it == devices_by_path_.end())
448 return;
449
450 scoped_refptr<UsbDeviceWin> device = by_path_it->second;
451 devices_by_path_.erase(by_path_it);
452 device->OnDisconnect();
453
454 auto by_guid_it = devices().find(device->guid());
455 if (by_guid_it != devices().end() && enumeration_ready()) {
456 USB_LOG(USER) << "USB device removed: path=" << device->device_path()
457 << " guid=" << device->guid();
458
459 devices().erase(by_guid_it);
460 NotifyDeviceRemoved(device);
461 }
462 }
463
HelperStarted()464 void UsbServiceWin::HelperStarted() {
465 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
466 helper_started_ = true;
467 if (enumeration_ready()) {
468 std::vector<scoped_refptr<UsbDevice>> result;
469 result.reserve(devices().size());
470 for (const auto& map_entry : devices())
471 result.push_back(map_entry.second);
472 for (auto& callback : enumeration_callbacks_)
473 std::move(callback).Run(result);
474 enumeration_callbacks_.clear();
475 }
476 }
477
CreateDeviceObject(const base::string16 & device_path,const base::string16 & hub_path,const std::vector<base::string16> & child_device_paths,uint32_t bus_number,uint32_t port_number,const base::string16 & driver_name)478 void UsbServiceWin::CreateDeviceObject(
479 const base::string16& device_path,
480 const base::string16& hub_path,
481 const std::vector<base::string16>& child_device_paths,
482 uint32_t bus_number,
483 uint32_t port_number,
484 const base::string16& driver_name) {
485 // Devices that appear during initial enumeration are gathered into the first
486 // result returned by GetDevices() and prevent device add/remove notifications
487 // from being sent.
488 if (!enumeration_ready())
489 ++first_enumeration_countdown_;
490
491 auto device = base::MakeRefCounted<UsbDeviceWin>(
492 device_path, hub_path, child_device_paths, bus_number, port_number,
493 driver_name);
494 devices_by_path_[device->device_path()] = device;
495 device->ReadDescriptors(base::BindOnce(&UsbServiceWin::DeviceReady,
496 weak_factory_.GetWeakPtr(), device));
497 }
498
DeviceReady(scoped_refptr<UsbDeviceWin> device,bool success)499 void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
500 bool success) {
501 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
502
503 bool enumeration_became_ready = false;
504 if (!enumeration_ready()) {
505 DCHECK_GT(first_enumeration_countdown_, 0u);
506 first_enumeration_countdown_--;
507 if (enumeration_ready())
508 enumeration_became_ready = true;
509 }
510
511 // If |device| was disconnected while descriptors were being read then it
512 // will have been removed from |devices_by_path_|.
513 auto it = devices_by_path_.find(device->device_path());
514 if (it == devices_by_path_.end()) {
515 success = false;
516 } else if (success) {
517 DCHECK(!base::Contains(devices(), device->guid()));
518 devices()[device->guid()] = device;
519
520 USB_LOG(USER) << "USB device added: path=" << device->device_path()
521 << " vendor=" << device->vendor_id() << " \""
522 << device->manufacturer_string()
523 << "\", product=" << device->product_id() << " \""
524 << device->product_string() << "\", serial=\""
525 << device->serial_number() << "\", driver=\""
526 << device->driver_name() << "\", children=["
527 << base::JoinString(device->child_device_paths(), L", ")
528 << "], guid=" << device->guid();
529 } else {
530 devices_by_path_.erase(it);
531 }
532
533 if (enumeration_became_ready) {
534 std::vector<scoped_refptr<UsbDevice>> result;
535 result.reserve(devices().size());
536 for (const auto& map_entry : devices())
537 result.push_back(map_entry.second);
538 for (auto& callback : enumeration_callbacks_)
539 std::move(callback).Run(result);
540 enumeration_callbacks_.clear();
541 } else if (success && enumeration_ready()) {
542 NotifyDeviceAdded(device);
543 }
544 }
545
546 } // namespace device
547