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 #include "base/strings/string_piece_forward.h"
12
13 #define INITGUID
14 #include <devpkey.h>
15
16 #include "base/bind.h"
17 #include "base/location.h"
18 #include "base/memory/free_deleter.h"
19 #include "base/memory/ptr_util.h"
20 #include "base/scoped_generic.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/sys_string_conversions.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/threading/scoped_blocking_call.h"
29 #include "base/threading/scoped_thread_priority.h"
30 #include "base/threading/thread_task_runner_handle.h"
31 #include "base/win/registry.h"
32 #include "base/win/scoped_devinfo.h"
33 #include "base/win/scoped_handle.h"
34 #include "components/device_event_log/device_event_log.h"
35 #include "services/device/usb/usb_descriptors.h"
36 #include "services/device/usb/usb_device_handle.h"
37 #include "services/device/usb/webusb_descriptors.h"
38 #include "third_party/re2/src/re2/re2.h"
39
40 namespace device {
41
42 namespace {
43
IsCompositeDevice(const std::wstring & service_name)44 bool IsCompositeDevice(const std::wstring& service_name) {
45 // Windows built-in composite device driver
46 return base::EqualsCaseInsensitiveASCII(service_name, L"usbccgp") ||
47 // Samsung Mobile USB Composite device driver
48 base::EqualsCaseInsensitiveASCII(service_name, L"dg_ssudbus");
49 }
50
GetDeviceUint32Property(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,const DEVPROPKEY & property)51 base::Optional<uint32_t> GetDeviceUint32Property(HDEVINFO dev_info,
52 SP_DEVINFO_DATA* dev_info_data,
53 const DEVPROPKEY& property) {
54 // SetupDiGetDeviceProperty() makes an RPC which may block.
55 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
56 base::BlockingType::MAY_BLOCK);
57
58 DEVPROPTYPE property_type;
59 uint32_t buffer;
60 if (!SetupDiGetDeviceProperty(
61 dev_info, dev_info_data, &property, &property_type,
62 reinterpret_cast<PBYTE>(&buffer), sizeof(buffer), nullptr, 0) ||
63 property_type != DEVPROP_TYPE_UINT32) {
64 return base::nullopt;
65 }
66
67 return buffer;
68 }
69
GetDeviceStringProperty(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,const DEVPROPKEY & property)70 base::Optional<std::wstring> GetDeviceStringProperty(
71 HDEVINFO dev_info,
72 SP_DEVINFO_DATA* dev_info_data,
73 const DEVPROPKEY& property) {
74 // SetupDiGetDeviceProperty() makes an RPC which may block.
75 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
76 base::BlockingType::MAY_BLOCK);
77
78 DEVPROPTYPE property_type;
79 DWORD required_size;
80 if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
81 &property_type, nullptr, 0, &required_size, 0) ||
82 GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
83 property_type != DEVPROP_TYPE_STRING) {
84 return base::nullopt;
85 }
86
87 std::wstring buffer;
88 if (!SetupDiGetDeviceProperty(
89 dev_info, dev_info_data, &property, &property_type,
90 reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
91 required_size, nullptr, 0)) {
92 return base::nullopt;
93 }
94
95 return buffer;
96 }
97
GetDeviceStringListProperty(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data,const DEVPROPKEY & property)98 base::Optional<std::vector<std::wstring>> GetDeviceStringListProperty(
99 HDEVINFO dev_info,
100 SP_DEVINFO_DATA* dev_info_data,
101 const DEVPROPKEY& property) {
102 // SetupDiGetDeviceProperty() makes an RPC which may block.
103 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
104 base::BlockingType::MAY_BLOCK);
105
106 DEVPROPTYPE property_type;
107 DWORD required_size;
108 if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
109 &property_type, nullptr, 0, &required_size, 0) ||
110 GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
111 property_type != DEVPROP_TYPE_STRING_LIST) {
112 return base::nullopt;
113 }
114
115 std::wstring buffer;
116 if (!SetupDiGetDeviceProperty(
117 dev_info, dev_info_data, &property, &property_type,
118 reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
119 required_size, nullptr, 0)) {
120 return base::nullopt;
121 }
122
123 // Windows string list properties use a NUL character as the delimiter.
124 return base::SplitString(buffer, base::WStringPiece(L"\0", 1),
125 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
126 }
127
GetServiceName(HDEVINFO dev_info,SP_DEVINFO_DATA * dev_info_data)128 std::wstring GetServiceName(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data) {
129 base::Optional<std::wstring> property =
130 GetDeviceStringProperty(dev_info, dev_info_data, DEVPKEY_Device_Service);
131 if (!property.has_value())
132 return std::wstring();
133
134 // Windows pads this string with a variable number of NUL bytes for no
135 // discernible reason.
136 return std::wstring(base::TrimString(*property, base::WStringPiece(L"\0", 1),
137 base::TRIM_TRAILING));
138 }
139
GetDeviceInterfaceDetails(HDEVINFO dev_info,SP_DEVICE_INTERFACE_DATA * device_interface_data,std::wstring * device_path,uint32_t * bus_number,uint32_t * port_number,std::wstring * instance_id,std::wstring * parent_instance_id,std::vector<std::wstring> * child_instance_ids,std::vector<std::wstring> * hardware_ids,std::wstring * service_name)140 bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
141 SP_DEVICE_INTERFACE_DATA* device_interface_data,
142 std::wstring* device_path,
143 uint32_t* bus_number,
144 uint32_t* port_number,
145 std::wstring* instance_id,
146 std::wstring* parent_instance_id,
147 std::vector<std::wstring>* child_instance_ids,
148 std::vector<std::wstring>* hardware_ids,
149 std::wstring* service_name) {
150 SP_DEVINFO_DATA dev_info_data = {};
151 dev_info_data.cbSize = sizeof(dev_info_data);
152
153 DWORD required_size = 0;
154 std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
155 device_interface_detail_data;
156
157 // Probing for the required size of the SP_DEVICE_INTERFACE_DETAIL_DATA
158 // struct is only required if we are looking for the device path.
159 // Otherwise all the necessary data can be queried from the SP_DEVINFO_DATA.
160 if (device_path) {
161 if (!SetupDiGetDeviceInterfaceDetail(dev_info, device_interface_data,
162 /*DeviceInterfaceDetailData=*/nullptr,
163 /*DeviceInterfaceDetailDataSize=*/0,
164 &required_size,
165 /*DeviceInfoData=*/nullptr) &&
166 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
167 return false;
168 }
169
170 device_interface_detail_data.reset(
171 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
172 device_interface_detail_data->cbSize =
173 sizeof(*device_interface_detail_data);
174 }
175
176 if (!SetupDiGetDeviceInterfaceDetail(
177 dev_info, device_interface_data, device_interface_detail_data.get(),
178 required_size, /*RequiredSize=*/nullptr, &dev_info_data) &&
179 (device_path || GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
180 USB_PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail";
181 return false;
182 }
183
184 if (device_path)
185 *device_path = std::wstring(device_interface_detail_data->DevicePath);
186
187 if (bus_number) {
188 auto result = GetDeviceUint32Property(dev_info, &dev_info_data,
189 DEVPKEY_Device_BusNumber);
190 if (!result.has_value()) {
191 USB_PLOG(ERROR) << "Failed to get device bus number";
192 return false;
193 }
194 *bus_number = result.value();
195 }
196
197 if (port_number) {
198 auto result = GetDeviceUint32Property(dev_info, &dev_info_data,
199 DEVPKEY_Device_Address);
200 if (!result.has_value()) {
201 USB_PLOG(ERROR) << "Failed to get device address";
202 return false;
203 }
204 *port_number = result.value();
205 }
206
207 if (instance_id) {
208 auto result = GetDeviceStringProperty(dev_info, &dev_info_data,
209 DEVPKEY_Device_InstanceId);
210 if (!result.has_value()) {
211 USB_PLOG(ERROR) << "Failed to get the instance ID";
212 return false;
213 }
214 *instance_id = std::move(result.value());
215 }
216
217 if (parent_instance_id) {
218 auto result = GetDeviceStringProperty(dev_info, &dev_info_data,
219 DEVPKEY_Device_Parent);
220 if (!result.has_value()) {
221 USB_PLOG(ERROR) << "Failed to get the device parent";
222 return false;
223 }
224 *parent_instance_id = std::move(result.value());
225 }
226
227 if (child_instance_ids) {
228 auto result = GetDeviceStringListProperty(dev_info, &dev_info_data,
229 DEVPKEY_Device_Children);
230 if (!result.has_value()) {
231 if (GetLastError() == ERROR_NOT_FOUND) {
232 result.emplace();
233 } else {
234 USB_PLOG(ERROR) << "Failed to get device children";
235 return false;
236 }
237 }
238 *child_instance_ids = std::move(result.value());
239 }
240
241 if (hardware_ids) {
242 auto result = GetDeviceStringListProperty(dev_info, &dev_info_data,
243 DEVPKEY_Device_HardwareIds);
244 if (!result.has_value()) {
245 if (GetLastError() == ERROR_NOT_FOUND) {
246 result.emplace();
247 } else {
248 USB_PLOG(ERROR) << "Failed to get hardware IDs";
249 return false;
250 }
251 }
252 *hardware_ids = std::move(result.value());
253 }
254
255 if (service_name) {
256 *service_name = GetServiceName(dev_info, &dev_info_data);
257 if (service_name->empty()) {
258 USB_PLOG(ERROR) << "Failed to get device driver name";
259 return false;
260 }
261 }
262
263 return true;
264 }
265
GetDevicePath(const std::wstring & instance_id,const GUID & device_interface_guid)266 std::wstring GetDevicePath(const std::wstring& instance_id,
267 const GUID& device_interface_guid) {
268 base::win::ScopedDevInfo dev_info(
269 SetupDiGetClassDevs(&device_interface_guid, instance_id.c_str(), 0,
270 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
271 if (!dev_info.is_valid()) {
272 USB_PLOG(ERROR) << "SetupDiGetClassDevs";
273 return std::wstring();
274 }
275
276 SP_DEVICE_INTERFACE_DATA device_interface_data = {};
277 device_interface_data.cbSize = sizeof(device_interface_data);
278 if (!SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
279 &device_interface_guid, 0,
280 &device_interface_data)) {
281 USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces";
282 return std::wstring();
283 }
284
285 std::wstring device_path;
286 if (!GetDeviceInterfaceDetails(
287 dev_info.get(), &device_interface_data, &device_path,
288 /*bus_number=*/nullptr, /*port_number=*/nullptr,
289 /*instance_id=*/nullptr, /*parent_instance_id=*/nullptr,
290 /*child_instance_ids=*/nullptr, /*hardware_ids=*/nullptr,
291 /*service_name=*/nullptr)) {
292 return std::wstring();
293 }
294
295 return device_path;
296 }
297
GetInterfaceNumber(const std::wstring & instance_id,const std::vector<std::wstring> & hardware_ids)298 int GetInterfaceNumber(const std::wstring& instance_id,
299 const std::vector<std::wstring>& hardware_ids) {
300 // According to MSDN the instance IDs for the device nodes created by the
301 // composite driver is in the form "USB\VID_vvvv&PID_dddd&MI_zz" where "zz"
302 // is the interface number.
303 //
304 // https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices
305 RE2 pattern("MI_([0-9a-fA-F]{2})");
306
307 std::string instance_id_ascii = base::WideToASCII(instance_id);
308 std::string match;
309 if (!RE2::PartialMatch(instance_id_ascii, pattern, &match)) {
310 // Alternative composite drivers, such as the one used for Samsung devices,
311 // don't use the standard format for the instance ID, but one of the
312 // hardware IDs will still match the expected pattern.
313 bool found = false;
314 for (const std::wstring& hardware_id : hardware_ids) {
315 std::string hardware_id_ascii = base::WideToASCII(hardware_id);
316 if (RE2::PartialMatch(hardware_id_ascii, pattern, &match)) {
317 found = true;
318 break;
319 }
320 }
321 if (!found)
322 return -1;
323 }
324
325 int interface_number;
326 if (!base::HexStringToInt(match, &interface_number))
327 return -1;
328 return interface_number;
329 }
330
GetFunctionInfo(const std::wstring & instance_id)331 UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) {
332 UsbDeviceWin::FunctionInfo info;
333 info.interface_number = -1;
334
335 base::win::ScopedDevInfo dev_info(
336 SetupDiCreateDeviceInfoList(nullptr, nullptr));
337 if (!dev_info.is_valid()) {
338 USB_PLOG(ERROR) << "SetupDiCreateDeviceInfoList";
339 return info;
340 }
341
342 SP_DEVINFO_DATA dev_info_data = {};
343 dev_info_data.cbSize = sizeof(dev_info_data);
344 if (!SetupDiOpenDeviceInfo(dev_info.get(), instance_id.c_str(), nullptr, 0,
345 &dev_info_data)) {
346 USB_PLOG(ERROR) << "SetupDiOpenDeviceInfo";
347 return info;
348 }
349
350 info.driver = GetServiceName(dev_info.get(), &dev_info_data);
351 if (info.driver.empty()) {
352 USB_PLOG(ERROR) << "Could not get child device's service name";
353 return info;
354 }
355
356 base::Optional<std::vector<std::wstring>> hardware_ids =
357 GetDeviceStringListProperty(dev_info.get(), &dev_info_data,
358 DEVPKEY_Device_HardwareIds);
359 if (!hardware_ids) {
360 USB_PLOG(ERROR) << "Could not get the child device's hardware IDs";
361 return info;
362 }
363
364 info.interface_number = GetInterfaceNumber(instance_id, *hardware_ids);
365
366 if (!base::EqualsCaseInsensitiveASCII(info.driver, L"winusb"))
367 return info;
368
369 // Boost priority while potentially loading Advapi32.dll on a background
370 // thread for the registry functions used below.
371 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
372
373 // There is no standard device interface GUID for USB functions and so we
374 // must discover the set of GUIDs that have been set in the registry by
375 // the INF file or Microsoft OS Compatibility descriptors before
376 // SetupDiGetDeviceInterfaceDetail() can be used to get the device path.
377 HKEY key = SetupDiOpenDevRegKey(dev_info.get(), &dev_info_data,
378 DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
379 if (key == INVALID_HANDLE_VALUE) {
380 USB_PLOG(ERROR) << "Could not open device registry key";
381 return info;
382 }
383 base::win::RegKey scoped_key(key);
384
385 std::vector<std::wstring> device_interface_guids;
386 LONG result =
387 scoped_key.ReadValues(L"DeviceInterfaceGUIDs", &device_interface_guids);
388 if (result != ERROR_SUCCESS) {
389 USB_LOG(ERROR) << "Could not read device interface GUIDs: "
390 << logging::SystemErrorCodeToString(result);
391 return info;
392 }
393
394 for (const auto& guid_string : device_interface_guids) {
395 // Boost priority while potentially loading Ole32.dll on a background
396 // thread for CLSIDFromString().
397 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
398
399 GUID guid;
400 if (FAILED(CLSIDFromString(guid_string.c_str(), &guid))) {
401 USB_LOG(ERROR) << "Failed to parse device interface GUID: "
402 << guid_string;
403 continue;
404 }
405
406 info.path = GetDevicePath(instance_id, guid);
407 if (!info.path.empty())
408 return info;
409 }
410
411 return info;
412 }
413
414 } // namespace
415
416 class UsbServiceWin::BlockingTaskRunnerHelper {
417 public:
BlockingTaskRunnerHelper(base::WeakPtr<UsbServiceWin> service)418 explicit BlockingTaskRunnerHelper(base::WeakPtr<UsbServiceWin> service)
419 : service_task_runner_(base::ThreadTaskRunnerHandle::Get()),
420 service_(service) {}
~BlockingTaskRunnerHelper()421 ~BlockingTaskRunnerHelper() {}
422
EnumerateDevices()423 void EnumerateDevices() {
424 // Boost priority while potentially loading SetupAPI.dll for the following
425 // functions on a background thread.
426 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
427
428 base::win::ScopedDevInfo dev_info(
429 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
430 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
431 if (!dev_info.is_valid()) {
432 USB_PLOG(ERROR) << "Failed to set up device enumeration";
433 service_task_runner_->PostTask(
434 FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_));
435 return;
436 }
437
438 SP_DEVICE_INTERFACE_DATA device_interface_data = {};
439 device_interface_data.cbSize = sizeof(device_interface_data);
440 for (DWORD i = 0; SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
441 &GUID_DEVINTERFACE_USB_DEVICE,
442 i, &device_interface_data);
443 ++i) {
444 EnumerateDevice(dev_info.get(), &device_interface_data, base::nullopt);
445 }
446
447 if (GetLastError() != ERROR_NO_MORE_ITEMS)
448 USB_PLOG(ERROR) << "Failed to enumerate devices";
449
450 service_task_runner_->PostTask(
451 FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_));
452 }
453
OnDeviceAdded(const GUID & guid,const std::wstring & device_path)454 void OnDeviceAdded(const GUID& guid, const std::wstring& device_path) {
455 // Boost priority while potentially loading SetupAPI.dll and Ole32.dll on a
456 // background thread for the following functions.
457 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
458
459 base::win::ScopedDevInfo dev_info(SetupDiGetClassDevs(
460 &guid, nullptr, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
461 if (!dev_info.is_valid()) {
462 USB_PLOG(ERROR) << "Failed to set up device enumeration";
463 return;
464 }
465
466 SP_DEVICE_INTERFACE_DATA device_interface_data = {};
467 device_interface_data.cbSize = sizeof(device_interface_data);
468 if (!SetupDiOpenDeviceInterface(dev_info.get(), device_path.c_str(), 0,
469 &device_interface_data)) {
470 USB_PLOG(ERROR) << "Failed to add device interface: " << device_path;
471 return;
472 }
473
474 if (IsEqualGUID(guid, GUID_DEVINTERFACE_USB_DEVICE)) {
475 EnumerateDevice(dev_info.get(), &device_interface_data, device_path);
476 } else {
477 EnumeratePotentialFunction(dev_info.get(), &device_interface_data,
478 device_path);
479 }
480 }
481
482 private:
EnumerateDevice(HDEVINFO dev_info,SP_DEVICE_INTERFACE_DATA * device_interface_data,const base::Optional<std::wstring> & opt_device_path)483 void EnumerateDevice(HDEVINFO dev_info,
484 SP_DEVICE_INTERFACE_DATA* device_interface_data,
485 const base::Optional<std::wstring>& opt_device_path) {
486 std::wstring device_path;
487 std::wstring* device_path_ptr = &device_path;
488 if (opt_device_path) {
489 device_path = *opt_device_path;
490 device_path_ptr = nullptr;
491 }
492
493 uint32_t bus_number;
494 uint32_t port_number;
495 std::wstring parent_instance_id;
496 std::vector<std::wstring> child_instance_ids;
497 std::wstring service_name;
498 if (!GetDeviceInterfaceDetails(dev_info, device_interface_data,
499 device_path_ptr, &bus_number, &port_number,
500 /*instance_id=*/nullptr, &parent_instance_id,
501 &child_instance_ids,
502 /*hardware_ids=*/nullptr, &service_name)) {
503 return;
504 }
505
506 auto driver_type = UsbDeviceWin::DriverType::kUnsupported;
507 std::vector<std::pair<int, UsbDeviceWin::FunctionInfo>> functions;
508 if (IsCompositeDevice(service_name)) {
509 driver_type = UsbDeviceWin::DriverType::kComposite;
510 // For composite devices Windows a composite device driver (usually the
511 // built-in usbccgp.sys) creates child device nodes for each device
512 // function. The device paths for these children must be opened in order
513 // to communicate with the WinUSB driver.
514 for (const std::wstring& instance_id : child_instance_ids) {
515 UsbDeviceWin::FunctionInfo info = GetFunctionInfo(instance_id);
516 if (info.interface_number != -1) {
517 functions.emplace_back(info.interface_number, info);
518 }
519 }
520 } else if (base::EqualsCaseInsensitiveASCII(service_name, L"winusb")) {
521 driver_type = UsbDeviceWin::DriverType::kWinUSB;
522 // A non-composite device has a single device node for all interfaces. It
523 // may still include multiple functions but they will be ignored.
524 UsbDeviceWin::FunctionInfo info;
525 info.driver = service_name;
526 info.path = device_path;
527 functions.emplace_back(/*interface_number=*/0, info);
528 }
529
530 std::wstring& hub_path = hub_paths_[parent_instance_id];
531 if (hub_path.empty()) {
532 hub_path = GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_HUB);
533 if (hub_path.empty())
534 return;
535 }
536
537 service_task_runner_->PostTask(
538 FROM_HERE, base::BindOnce(&UsbServiceWin::CreateDeviceObject, service_,
539 std::move(device_path), std::move(hub_path),
540 std::move(functions), bus_number, port_number,
541 driver_type, service_name));
542 }
543
EnumeratePotentialFunction(HDEVINFO dev_info,SP_DEVICE_INTERFACE_DATA * device_interface_data,const std::wstring & device_path)544 void EnumeratePotentialFunction(
545 HDEVINFO dev_info,
546 SP_DEVICE_INTERFACE_DATA* device_interface_data,
547 const std::wstring& device_path) {
548 std::wstring instance_id;
549 std::wstring parent_instance_id;
550 std::vector<std::wstring> hardware_ids;
551 std::wstring service_name;
552 if (!GetDeviceInterfaceDetails(
553 dev_info, device_interface_data,
554 /*device_path=*/nullptr, /*bus_number=*/nullptr,
555 /*port_number=*/nullptr, &instance_id, &parent_instance_id,
556 /*child_instance_ids=*/nullptr, &hardware_ids, &service_name)) {
557 return;
558 }
559
560 int interface_number = GetInterfaceNumber(instance_id, hardware_ids);
561 if (interface_number == -1)
562 return;
563
564 std::wstring parent_path =
565 GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_DEVICE);
566 if (parent_path.empty())
567 return;
568
569 UsbDeviceWin::FunctionInfo info;
570 info.driver = service_name;
571 info.path = device_path;
572
573 service_task_runner_->PostTask(
574 FROM_HERE,
575 base::BindOnce(&UsbServiceWin::UpdateFunction, service_,
576 std::move(parent_path), interface_number, info));
577 }
578
579 std::unordered_map<std::wstring, std::wstring> hub_paths_;
580
581 // Calls back to |service_| must be posted to |service_task_runner_|, which
582 // runs tasks on the thread where that object lives.
583 scoped_refptr<base::SingleThreadTaskRunner> service_task_runner_;
584 base::WeakPtr<UsbServiceWin> service_;
585 };
586
UsbServiceWin()587 UsbServiceWin::UsbServiceWin()
588 : UsbService(),
589 blocking_task_runner_(CreateBlockingTaskRunner()),
590 helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)),
591 device_observer_(this) {
592 DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
593 if (device_monitor)
594 device_observer_.Add(device_monitor);
595
596 helper_.reset(new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr()));
597 blocking_task_runner_->PostTask(
598 FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::EnumerateDevices,
599 base::Unretained(helper_.get())));
600 }
601
~UsbServiceWin()602 UsbServiceWin::~UsbServiceWin() {
603 NotifyWillDestroyUsbService();
604 }
605
GetDevices(GetDevicesCallback callback)606 void UsbServiceWin::GetDevices(GetDevicesCallback callback) {
607 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
608 if (enumeration_ready())
609 UsbService::GetDevices(std::move(callback));
610 else
611 enumeration_callbacks_.push_back(std::move(callback));
612 }
613
OnDeviceAdded(const GUID & class_guid,const std::wstring & device_path)614 void UsbServiceWin::OnDeviceAdded(const GUID& class_guid,
615 const std::wstring& device_path) {
616 blocking_task_runner_->PostTask(
617 FROM_HERE,
618 base::BindOnce(&BlockingTaskRunnerHelper::OnDeviceAdded,
619 base::Unretained(helper_.get()), class_guid, device_path));
620 }
621
OnDeviceRemoved(const GUID & class_guid,const std::wstring & device_path)622 void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid,
623 const std::wstring& device_path) {
624 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
625 auto by_path_it = devices_by_path_.find(device_path);
626 if (by_path_it == devices_by_path_.end())
627 return;
628
629 scoped_refptr<UsbDeviceWin> device = by_path_it->second;
630 devices_by_path_.erase(by_path_it);
631 device->OnDisconnect();
632
633 auto by_guid_it = devices().find(device->guid());
634 if (by_guid_it != devices().end() && enumeration_ready()) {
635 USB_LOG(USER) << "USB device removed: path=" << device->device_path()
636 << " guid=" << device->guid();
637
638 devices().erase(by_guid_it);
639 NotifyDeviceRemoved(device);
640 }
641 }
642
HelperStarted()643 void UsbServiceWin::HelperStarted() {
644 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
645 helper_started_ = true;
646 if (enumeration_ready()) {
647 std::vector<scoped_refptr<UsbDevice>> result;
648 result.reserve(devices().size());
649 for (const auto& map_entry : devices())
650 result.push_back(map_entry.second);
651 for (auto& callback : enumeration_callbacks_)
652 std::move(callback).Run(result);
653 enumeration_callbacks_.clear();
654 }
655 }
656
CreateDeviceObject(const std::wstring & device_path,const std::wstring & hub_path,const base::flat_map<int,UsbDeviceWin::FunctionInfo> & functions,uint32_t bus_number,uint32_t port_number,UsbDeviceWin::DriverType driver_type,const std::wstring & driver_name)657 void UsbServiceWin::CreateDeviceObject(
658 const std::wstring& device_path,
659 const std::wstring& hub_path,
660 const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions,
661 uint32_t bus_number,
662 uint32_t port_number,
663 UsbDeviceWin::DriverType driver_type,
664 const std::wstring& driver_name) {
665 // Devices that appear during initial enumeration are gathered into the first
666 // result returned by GetDevices() and prevent device add/remove notifications
667 // from being sent.
668 if (!enumeration_ready())
669 ++first_enumeration_countdown_;
670
671 auto device = base::MakeRefCounted<UsbDeviceWin>(
672 device_path, hub_path, functions, bus_number, port_number, driver_type);
673 devices_by_path_[device->device_path()] = device;
674 device->ReadDescriptors(base::BindOnce(&UsbServiceWin::DeviceReady,
675 weak_factory_.GetWeakPtr(), device,
676 driver_name));
677 }
678
UpdateFunction(const std::wstring & device_path,int interface_number,const UsbDeviceWin::FunctionInfo & function_info)679 void UsbServiceWin::UpdateFunction(
680 const std::wstring& device_path,
681 int interface_number,
682 const UsbDeviceWin::FunctionInfo& function_info) {
683 auto it = devices_by_path_.find(device_path);
684 if (it == devices_by_path_.end())
685 return;
686 const scoped_refptr<UsbDeviceWin>& device = it->second;
687
688 USB_LOG(EVENT) << "USB device function updated: guid=" << device->guid()
689 << ", interface_number=" << interface_number << ", path=\""
690 << function_info.path << "\", driver=\""
691 << function_info.driver << "\"";
692 device->UpdateFunction(interface_number, function_info);
693 }
694
DeviceReady(scoped_refptr<UsbDeviceWin> device,const std::wstring & driver_name,bool success)695 void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
696 const std::wstring& driver_name,
697 bool success) {
698 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
699
700 bool enumeration_became_ready = false;
701 if (!enumeration_ready()) {
702 DCHECK_GT(first_enumeration_countdown_, 0u);
703 first_enumeration_countdown_--;
704 if (enumeration_ready())
705 enumeration_became_ready = true;
706 }
707
708 // If |device| was disconnected while descriptors were being read then it
709 // will have been removed from |devices_by_path_|.
710 auto it = devices_by_path_.find(device->device_path());
711 if (it == devices_by_path_.end()) {
712 success = false;
713 } else if (success) {
714 DCHECK(!base::Contains(devices(), device->guid()));
715 devices()[device->guid()] = device;
716
717 USB_LOG(USER) << "USB device added: path=" << device->device_path()
718 << " vendor=" << device->vendor_id() << " \""
719 << device->manufacturer_string()
720 << "\", product=" << device->product_id() << " \""
721 << device->product_string() << "\", serial=\""
722 << device->serial_number() << "\", driver=\"" << driver_name
723 << "\", guid=" << device->guid();
724 } else {
725 devices_by_path_.erase(it);
726 }
727
728 if (enumeration_became_ready) {
729 std::vector<scoped_refptr<UsbDevice>> result;
730 result.reserve(devices().size());
731 for (const auto& map_entry : devices())
732 result.push_back(map_entry.second);
733 for (auto& callback : enumeration_callbacks_)
734 std::move(callback).Run(result);
735 enumeration_callbacks_.clear();
736 } else if (success && enumeration_ready()) {
737 NotifyDeviceAdded(device);
738 }
739 }
740
741 } // namespace device
742