Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: device/usb/usb_service_win.cc

Issue 2690383002: Add device path enumeration to the new Windows USB backend. (Closed)
Patch Set: Addressed scheib@'s feedback. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « device/usb/usb_service_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/usb/usb_service_win.h" 5 #include "device/usb/usb_service_win.h"
6 6
7 #include <setupapi.h>
8 #include <stdint.h>
9 #include <usbiodef.h>
10
11 #define INITGUID
12 #include <devpkey.h>
13
14 #include "base/bind.h"
15 #include "base/location.h"
16 #include "base/memory/free_deleter.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/scoped_generic.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/stl_util.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "base/win/scoped_handle.h"
25 #include "components/device_event_log/device_event_log.h"
26 #include "device/usb/usb_descriptors.h"
27 #include "device/usb/usb_device_handle.h"
28 #include "device/usb/webusb_descriptors.h"
29
7 namespace device { 30 namespace device {
8 31
32 namespace {
33
34 struct DevInfoScopedTraits {
35 static HDEVINFO InvalidValue() { return INVALID_HANDLE_VALUE; }
36 static void Free(HDEVINFO h) { SetupDiDestroyDeviceInfoList(h); }
37 };
38
39 using ScopedDevInfo = base::ScopedGeneric<HDEVINFO, DevInfoScopedTraits>;
40
41 bool GetDeviceUint32Property(HDEVINFO dev_info,
42 SP_DEVINFO_DATA* dev_info_data,
43 const DEVPROPKEY& property,
44 uint32_t* property_buffer) {
45 DEVPROPTYPE property_type;
46 if (!SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
47 &property_type,
48 reinterpret_cast<PBYTE>(property_buffer),
49 sizeof(*property_buffer), nullptr, 0) ||
50 property_type != DEVPROP_TYPE_UINT32) {
51 return false;
52 }
53
54 return true;
55 }
56
57 bool GetDeviceStringProperty(HDEVINFO dev_info,
58 SP_DEVINFO_DATA* dev_info_data,
59 const DEVPROPKEY& property,
60 std::string* property_buffer) {
61 DEVPROPTYPE property_type;
62 DWORD required_size;
63 if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
64 &property_type, nullptr, 0, &required_size, 0) ||
65 GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
66 property_type != DEVPROP_TYPE_STRING) {
67 return false;
68 }
69
70 std::wstring wide_buffer;
71 if (!SetupDiGetDeviceProperty(
72 dev_info, dev_info_data, &property, &property_type,
73 reinterpret_cast<PBYTE>(base::WriteInto(&wide_buffer, required_size)),
74 required_size, nullptr, 0)) {
75 return false;
76 }
77
78 *property_buffer = base::SysWideToUTF8(wide_buffer);
79 return true;
80 }
81
82 bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
83 SP_DEVICE_INTERFACE_DATA* device_interface_data,
84 std::string* device_path,
85 uint32_t* port_number,
86 std::string* parent_instance_id) {
87 DWORD required_size;
88 if (SetupDiGetDeviceInterfaceDetail(dev_info, device_interface_data, nullptr,
89 0, &required_size, nullptr) ||
90 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
91 return false;
92 }
93
94 std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
95 device_interface_detail_data(
96 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
97 device_interface_detail_data->cbSize = sizeof(*device_interface_detail_data);
98
99 SP_DEVINFO_DATA dev_info_data;
100 dev_info_data.cbSize = sizeof(dev_info_data);
101
102 if (!SetupDiGetDeviceInterfaceDetail(
103 dev_info, device_interface_data, device_interface_detail_data.get(),
104 required_size, nullptr, &dev_info_data)) {
105 USB_PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail";
106 return false;
107 }
108
109 if (device_path) {
110 *device_path =
111 base::SysWideToUTF8(device_interface_detail_data->DevicePath);
112 }
113
114 if (port_number) {
115 if (!GetDeviceUint32Property(dev_info, &dev_info_data,
116 DEVPKEY_Device_Address, port_number)) {
117 USB_PLOG(ERROR) << "Failed to get device address";
118 return false;
119 }
120 }
121
122 if (parent_instance_id) {
123 if (!GetDeviceStringProperty(dev_info, &dev_info_data,
124 DEVPKEY_Device_Parent, parent_instance_id)) {
125 USB_PLOG(ERROR) << "Failed to get the device parent";
126 return false;
127 }
128 }
129
130 return true;
131 }
132
133 bool GetHubDevicePath(const std::string& instance_id,
134 std::string* device_path) {
135 ScopedDevInfo dev_info(
136 SetupDiGetClassDevsA(&GUID_DEVINTERFACE_USB_HUB, instance_id.c_str(), 0,
137 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
138 if (!dev_info.is_valid()) {
139 USB_PLOG(ERROR) << "SetupDiGetClassDevs";
140 return false;
141 }
142
143 SP_DEVICE_INTERFACE_DATA device_interface_data;
144 device_interface_data.cbSize = sizeof(device_interface_data);
145 if (!SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
146 &GUID_DEVINTERFACE_USB_HUB, 0,
147 &device_interface_data)) {
148 USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces";
149 return false;
150 }
151
152 return GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
153 device_path, nullptr, nullptr);
154 }
155
156 } // namespace
157
158 class UsbServiceWin::BlockingThreadHelper {
159 public:
160 explicit BlockingThreadHelper(base::WeakPtr<UsbServiceWin> service)
161 : service_task_runner_(base::ThreadTaskRunnerHandle::Get()),
162 service_(service) {}
163 ~BlockingThreadHelper() {}
164
165 void EnumerateDevices() {
166 ScopedDevInfo dev_info(
167 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
168 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
169 if (!dev_info.is_valid()) {
170 USB_PLOG(ERROR) << "Failed to set up device enumeration";
171 service_task_runner_->PostTask(
172 FROM_HERE, base::Bind(&UsbServiceWin::HelperStarted, service_));
173 return;
174 }
175
176 SP_DEVICE_INTERFACE_DATA device_interface_data;
177 device_interface_data.cbSize = sizeof(device_interface_data);
178 for (DWORD i = 0; SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
179 &GUID_DEVINTERFACE_USB_DEVICE,
180 i, &device_interface_data);
181 ++i) {
182 std::string device_path;
183 uint32_t port_number;
184 std::string parent_instance_id;
185 if (!GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
186 &device_path, &port_number,
187 &parent_instance_id)) {
188 continue;
189 }
190
191 std::string& hub_path = hub_paths_[parent_instance_id];
192 if (hub_path.empty()) {
193 std::string parent_path;
194 if (!GetHubDevicePath(parent_instance_id, &parent_path))
195 continue;
196
197 hub_path = parent_path;
198 }
199
200 service_task_runner_->PostTask(
201 FROM_HERE, base::Bind(&UsbServiceWin::CreateDeviceObject, service_,
202 device_path, hub_path, port_number));
203 }
204
205 if (GetLastError() != ERROR_NO_MORE_ITEMS)
206 USB_PLOG(ERROR) << "Failed to enumerate devices";
207
208 service_task_runner_->PostTask(
209 FROM_HERE, base::Bind(&UsbServiceWin::HelperStarted, service_));
210 }
211
212 void EnumerateDevicePath(const std::string& device_path) {
213 ScopedDevInfo dev_info(
214 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
215 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
216 if (!dev_info.is_valid()) {
217 USB_PLOG(ERROR) << "Failed to set up device enumeration";
218 return;
219 }
220
221 SP_DEVICE_INTERFACE_DATA device_interface_data;
222 device_interface_data.cbSize = sizeof(device_interface_data);
223 if (!SetupDiOpenDeviceInterfaceA(dev_info.get(), device_path.c_str(), 0,
224 &device_interface_data)) {
225 USB_PLOG(ERROR) << "Failed to add device interface: " << device_path;
226 return;
227 }
228
229 uint32_t port_number;
230 std::string parent_instance_id;
231 if (!GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
232 nullptr, &port_number,
233 &parent_instance_id)) {
234 return;
235 }
236
237 std::string& hub_path = hub_paths_[parent_instance_id];
238 if (hub_path.empty()) {
239 std::string parent_path;
240 if (!GetHubDevicePath(parent_instance_id, &parent_path))
241 return;
242
243 hub_path = parent_path;
244 }
245
246 service_task_runner_->PostTask(
247 FROM_HERE, base::Bind(&UsbServiceWin::CreateDeviceObject, service_,
248 device_path, hub_path, port_number));
249 }
250
251 private:
252 std::unordered_map<std::string, std::string> hub_paths_;
253
254 // Calls back to |service_| must be posted to |task_runner_|, where that
scheib 2017/02/15 22:41:54 to |service_task_runner_|,
255 // object lives.
256 scoped_refptr<base::SingleThreadTaskRunner> service_task_runner_;
257 base::WeakPtr<UsbServiceWin> service_;
258 };
259
9 UsbServiceWin::UsbServiceWin( 260 UsbServiceWin::UsbServiceWin(
10 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) 261 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
11 : UsbService(blocking_task_runner) {} 262 : UsbService(blocking_task_runner),
263 device_observer_(this),
264 weak_factory_(this) {
265 DeviceMonitorWin* device_monitor =
266 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE);
267 if (device_monitor)
268 device_observer_.Add(device_monitor);
269
270 helper_ = new BlockingThreadHelper(weak_factory_.GetWeakPtr());
271 blocking_task_runner->PostTask(
272 FROM_HERE, base::Bind(&BlockingThreadHelper::EnumerateDevices,
273 base::Unretained(helper_)));
274 }
12 275
13 UsbServiceWin::~UsbServiceWin() {} 276 UsbServiceWin::~UsbServiceWin() {}
14 277
278 void UsbServiceWin::GetDevices(const GetDevicesCallback& callback) {
279 DCHECK(CalledOnValidThread());
280 if (enumeration_ready())
281 UsbService::GetDevices(callback);
282 else
283 enumeration_callbacks_.push_back(callback);
284 }
285
286 void UsbServiceWin::OnDeviceAdded(const GUID& class_guid,
287 const std::string& device_path) {
288 blocking_task_runner()->PostTask(
289 FROM_HERE, base::Bind(&BlockingThreadHelper::EnumerateDevicePath,
290 base::Unretained(helper_), device_path));
291 }
292
293 void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid,
294 const std::string& device_path) {
295 DCHECK(CalledOnValidThread());
296 auto by_path_it = devices_by_path_.find(device_path);
297 if (by_path_it == devices_by_path_.end())
298 return;
299
300 scoped_refptr<UsbDeviceWin> device = by_path_it->second;
301 devices_by_path_.erase(by_path_it);
302 device->OnDisconnect();
303
304 auto by_guid_it = devices().find(device->guid());
305 if (by_guid_it != devices().end() && enumeration_ready()) {
306 USB_LOG(USER) << "USB device removed: path=" << device->device_path()
307 << " guid=" << device->guid();
308
309 devices().erase(by_guid_it);
310 NotifyDeviceRemoved(device);
311 }
312 }
313
314 void UsbServiceWin::HelperStarted() {
315 DCHECK(CalledOnValidThread());
316 helper_started_ = true;
317 if (enumeration_ready()) {
318 std::vector<scoped_refptr<UsbDevice>> result;
319 result.reserve(devices().size());
320 for (const auto& map_entry : devices())
321 result.push_back(map_entry.second);
322 for (const auto& callback : enumeration_callbacks_)
323 callback.Run(result);
324 enumeration_callbacks_.clear();
325 }
326 }
327
328 void UsbServiceWin::CreateDeviceObject(const std::string& device_path,
329 const std::string& hub_path,
330 int port_number) {
331 // Devices that appear during initial enumeration are gathered into the first
332 // result returned by GetDevices() and prevent device add/remove notifications
333 // from being sent.
334 if (!enumeration_ready())
335 ++first_enumeration_countdown_;
336
337 scoped_refptr<UsbDeviceWin> device(new UsbDeviceWin(
338 device_path, hub_path, port_number, blocking_task_runner()));
339 devices_by_path_[device->device_path()] = device;
340
341 // TODO(reillyg): Read device descriptors.
342 DeviceReady(device, true);
343 }
344
345 void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
346 bool success) {
347 DCHECK(CalledOnValidThread());
348
349 bool enumeration_became_ready = false;
350 if (!enumeration_ready()) {
351 DCHECK_GT(first_enumeration_countdown_, 0u);
352 first_enumeration_countdown_--;
353 if (enumeration_ready())
354 enumeration_became_ready = true;
355 }
356
357 // If |device| was disconnected while descriptors were being read then it
358 // will have been removed from |devices_by_path_|.
359 auto it = devices_by_path_.find(device->device_path());
360 if (it == devices_by_path_.end()) {
361 success = false;
362 } else if (success) {
363 DCHECK(!base::ContainsKey(devices(), device->guid()));
364 devices()[device->guid()] = device;
365
366 USB_LOG(USER) << "USB device added: path=" << device->device_path()
367 << " vendor=" << device->vendor_id() << " \""
368 << device->manufacturer_string()
369 << "\", product=" << device->product_id() << " \""
370 << device->product_string() << "\", serial=\""
371 << device->serial_number() << "\", guid=" << device->guid();
372 } else {
373 devices_by_path_.erase(it);
374 }
375
376 if (enumeration_became_ready) {
377 std::vector<scoped_refptr<UsbDevice>> result;
378 result.reserve(devices().size());
379 for (const auto& map_entry : devices())
380 result.push_back(map_entry.second);
381 for (const auto& callback : enumeration_callbacks_)
382 callback.Run(result);
383 enumeration_callbacks_.clear();
384 } else if (success && enumeration_ready()) {
385 NotifyDeviceAdded(device);
386 }
387 }
388
15 } // namespace device 389 } // namespace device
OLDNEW
« no previous file with comments | « device/usb/usb_service_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698