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

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: 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
« device/usb/usb_service_impl.h ('K') | « 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 GetDeviceStringProperty(HDEVINFO dev_info,
42 SP_DEVINFO_DATA* dev_info_data,
43 const DEVPROPKEY& property,
44 std::string* property_buffer) {
45 DEVPROPTYPE property_type;
46 DWORD required_size;
47 if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
scheib 2017/02/14 19:40:05 When bluetooth windows work was underway doing sim
Reilly Grant (use Gerrit) 2017/02/15 22:03:13 We discussed the test plan offline.
48 &property_type, nullptr, 0, &required_size, 0) ||
49 GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
50 property_type != DEVPROP_TYPE_STRING) {
51 return false;
52 }
53
54 std::wstring wide_buffer;
55 if (!SetupDiGetDeviceProperty(
56 dev_info, dev_info_data, &property, &property_type,
57 reinterpret_cast<PBYTE>(base::WriteInto(&wide_buffer, required_size)),
58 required_size, nullptr, 0)) {
59 return false;
60 }
61
62 *property_buffer = base::SysWideToUTF8(wide_buffer);
63 return true;
64 }
65
66 bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
67 SP_DEVICE_INTERFACE_DATA* device_interface_data,
68 std::string* device_path,
69 uint32_t* port_number,
70 std::string* parent_instance_id) {
71 DWORD required_size;
72 if (SetupDiGetDeviceInterfaceDetail(dev_info, device_interface_data, nullptr,
73 0, &required_size, nullptr) ||
74 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
75 return false;
76 }
77
78 std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
79 device_interface_detail_data(
80 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
81 device_interface_detail_data->cbSize = sizeof(*device_interface_detail_data);
82
83 SP_DEVINFO_DATA dev_info_data;
84 dev_info_data.cbSize = sizeof(dev_info_data);
85
86 if (!SetupDiGetDeviceInterfaceDetail(
87 dev_info, device_interface_data, device_interface_detail_data.get(),
88 required_size, nullptr, &dev_info_data)) {
89 USB_PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail";
90 return false;
91 }
92
93 if (device_path) {
94 *device_path =
95 base::SysWideToUTF8(device_interface_detail_data->DevicePath);
96 }
97
98 if (port_number) {
99 DEVPROPTYPE property_type;
100 if (!SetupDiGetDeviceProperty(dev_info, &dev_info_data,
101 &DEVPKEY_Device_Address, &property_type,
102 reinterpret_cast<PBYTE>(port_number),
103 sizeof(*port_number), nullptr, 0) ||
104 property_type != DEVPROP_TYPE_UINT32) {
105 USB_PLOG(ERROR) << "Failed to get device address";
106 return false;
107 }
108 }
109
110 if (parent_instance_id) {
111 if (!GetDeviceStringProperty(dev_info, &dev_info_data,
112 DEVPKEY_Device_Parent, parent_instance_id)) {
113 USB_PLOG(ERROR) << "Failed to get the device parent";
114 return false;
115 }
116 }
117
118 return true;
119 }
120
121 bool GetHubDevicePath(const std::string& instance_id,
122 std::string* device_path) {
123 ScopedDevInfo dev_info(
124 SetupDiGetClassDevsA(&GUID_DEVINTERFACE_USB_HUB, instance_id.c_str(), 0,
125 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
126 if (!dev_info.is_valid()) {
127 USB_PLOG(ERROR) << "SetupDiGetClassDevs";
128 return false;
129 }
130
131 SP_DEVICE_INTERFACE_DATA device_interface_data;
132 device_interface_data.cbSize = sizeof(device_interface_data);
133 if (!SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
134 &GUID_DEVINTERFACE_USB_HUB, 0,
135 &device_interface_data)) {
136 USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces";
137 return false;
138 }
139
140 return GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
141 device_path, nullptr, nullptr);
142 }
143
144 } // namespace
145
146 class UsbServiceWin::FileThreadHelper {
147 public:
148 explicit FileThreadHelper(base::WeakPtr<UsbServiceWin> service)
149 : task_runner_(base::ThreadTaskRunnerHandle::Get()), service_(service) {}
150 ~FileThreadHelper() {}
151
152 void EnumerateDevices() {
153 ScopedDevInfo dev_info(
154 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
155 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
156 if (!dev_info.is_valid()) {
157 USB_PLOG(ERROR) << "Failed to set up device enumeration";
158 task_runner_->PostTask(
159 FROM_HERE, base::Bind(&UsbServiceWin::HelperStarted, service_));
160 return;
161 }
162
163 SP_DEVICE_INTERFACE_DATA device_interface_data;
164 device_interface_data.cbSize = sizeof(device_interface_data);
165 for (DWORD i = 0; SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
166 &GUID_DEVINTERFACE_USB_DEVICE,
167 i, &device_interface_data);
168 ++i) {
169 std::string device_path;
170 uint32_t port_number;
171 std::string parent_instance_id;
172 if (!GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
173 &device_path, &port_number,
174 &parent_instance_id)) {
175 continue;
176 }
177
178 std::string& hub_path = hub_paths_[parent_instance_id];
179 if (hub_path.empty()) {
180 std::string parent_path;
181 if (!GetHubDevicePath(parent_instance_id, &parent_path))
182 continue;
183
184 hub_path = parent_path;
185 }
186
187 task_runner_->PostTask(FROM_HERE,
188 base::Bind(&UsbServiceWin::AddDevice, service_,
189 device_path, hub_path, port_number));
190 }
191
192 if (GetLastError() != ERROR_NO_MORE_ITEMS)
193 USB_PLOG(ERROR) << "Failed to enumerate devices";
194
195 task_runner_->PostTask(FROM_HERE,
196 base::Bind(&UsbServiceWin::HelperStarted, service_));
197 }
198
199 void EnumerateDevicePath(const std::string& device_path) {
200 ScopedDevInfo dev_info(
201 SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
202 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
203 if (!dev_info.is_valid()) {
204 USB_PLOG(ERROR) << "Failed to set up device enumeration";
205 return;
206 }
207
208 SP_DEVICE_INTERFACE_DATA device_interface_data;
209 device_interface_data.cbSize = sizeof(device_interface_data);
210 if (!SetupDiOpenDeviceInterfaceA(dev_info.get(), device_path.c_str(), 0,
211 &device_interface_data)) {
212 USB_PLOG(ERROR) << "Failed to add device interface: " << device_path;
213 return;
214 }
215
216 uint32_t port_number;
217 std::string parent_instance_id;
218 if (!GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
219 nullptr, &port_number,
220 &parent_instance_id)) {
221 return;
222 }
223
224 std::string& hub_path = hub_paths_[parent_instance_id];
225 if (hub_path.empty()) {
226 std::string parent_path;
227 if (!GetHubDevicePath(parent_instance_id, &parent_path))
228 return;
229
230 hub_path = parent_path;
231 }
232
233 task_runner_->PostTask(
234 FROM_HERE, base::Bind(&UsbServiceWin::AddDevice, service_, device_path,
235 hub_path, port_number));
236 }
237
238 private:
239 std::unordered_map<std::string, std::string> hub_paths_;
240 // Calls back to |service_| must be posted to |task_runner_|, where it is
241 // okay to check the validity of this weak pointer.
242 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
scheib 2017/02/14 19:40:05 I'm still not quickly understanding the hop via ta
Reilly Grant (use Gerrit) 2017/02/15 22:03:13 We discussed naming offline and I've made a number
243 base::WeakPtr<UsbServiceWin> service_;
244 };
245
9 UsbServiceWin::UsbServiceWin( 246 UsbServiceWin::UsbServiceWin(
10 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) 247 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
11 : UsbService(blocking_task_runner) {} 248 : UsbService(blocking_task_runner),
249 device_observer_(this),
250 weak_factory_(this) {
251 DeviceMonitorWin* device_monitor =
252 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE);
253 if (device_monitor)
254 device_observer_.Add(device_monitor);
255
256 helper_ = new FileThreadHelper(weak_factory_.GetWeakPtr());
257 blocking_task_runner->PostTask(FROM_HERE,
258 base::Bind(&FileThreadHelper::EnumerateDevices,
259 base::Unretained(helper_)));
260 }
12 261
13 UsbServiceWin::~UsbServiceWin() {} 262 UsbServiceWin::~UsbServiceWin() {}
14 263
264 void UsbServiceWin::GetDevices(const GetDevicesCallback& callback) {
265 DCHECK(CalledOnValidThread());
266 if (enumeration_ready())
267 UsbService::GetDevices(callback);
268 else
269 enumeration_callbacks_.push_back(callback);
270 }
271
272 void UsbServiceWin::OnDeviceAdded(const GUID& class_guid,
273 const std::string& device_path) {
274 blocking_task_runner()->PostTask(
275 FROM_HERE, base::Bind(&FileThreadHelper::EnumerateDevicePath,
276 base::Unretained(helper_), device_path));
277 }
278
279 void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid,
280 const std::string& device_path) {
281 DCHECK(CalledOnValidThread());
282 auto by_path_it = devices_by_path_.find(device_path);
283 if (by_path_it == devices_by_path_.end())
284 return;
285
286 scoped_refptr<UsbDeviceWin> device = by_path_it->second;
287 devices_by_path_.erase(by_path_it);
288 device->OnDisconnect();
289
290 auto by_guid_it = devices().find(device->guid());
291 if (by_guid_it != devices().end() && enumeration_ready()) {
292 USB_LOG(USER) << "USB device removed: path=" << device->device_path()
293 << " guid=" << device->guid();
294
295 devices().erase(by_guid_it);
296 NotifyDeviceRemoved(device);
297 }
298 }
299
300 void UsbServiceWin::HelperStarted() {
301 DCHECK(CalledOnValidThread());
302 helper_started_ = true;
303 if (enumeration_ready()) {
304 std::vector<scoped_refptr<UsbDevice>> result;
305 result.reserve(devices().size());
306 for (const auto& map_entry : devices())
307 result.push_back(map_entry.second);
308 for (const auto& callback : enumeration_callbacks_)
309 callback.Run(result);
310 enumeration_callbacks_.clear();
311 }
312 }
313
314 void UsbServiceWin::AddDevice(const std::string& device_path,
315 const std::string& hub_path,
316 int port_number) {
317 // Devices that appear during initial enumeration are gathered into the first
318 // result returned by GetDevices() and prevent device add/remove notifications
319 // from being sent.
320 if (!enumeration_ready())
321 ++first_enumeration_countdown_;
322
323 scoped_refptr<UsbDeviceWin> device(new UsbDeviceWin(
324 device_path, hub_path, port_number, blocking_task_runner()));
325 devices_by_path_[device->device_path()] = device;
326
327 // TODO(reillyg): Read device descriptors.
328 DeviceReady(device, true);
329 }
330
331 void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
332 bool success) {
333 DCHECK(CalledOnValidThread());
334
335 bool enumeration_became_ready = false;
336 if (!enumeration_ready()) {
337 DCHECK_GT(first_enumeration_countdown_, 0u);
338 first_enumeration_countdown_--;
339 if (enumeration_ready())
340 enumeration_became_ready = true;
341 }
342
343 // If |device| was disconnected while descriptors were being read then it
344 // will have been removed from |devices_by_path_|.
345 auto it = devices_by_path_.find(device->device_path());
346 if (it == devices_by_path_.end()) {
347 success = false;
348 } else if (success) {
349 DCHECK(!base::ContainsKey(devices(), device->guid()));
350 devices()[device->guid()] = device;
351
352 USB_LOG(USER) << "USB device added: path=" << device->device_path()
353 << " vendor=" << device->vendor_id() << " \""
354 << device->manufacturer_string()
355 << "\", product=" << device->product_id() << " \""
356 << device->product_string() << "\", serial=\""
357 << device->serial_number() << "\", guid=" << device->guid();
358 } else {
359 devices_by_path_.erase(it);
360 }
361
362 if (enumeration_became_ready) {
363 std::vector<scoped_refptr<UsbDevice>> result;
364 result.reserve(devices().size());
365 for (const auto& map_entry : devices())
366 result.push_back(map_entry.second);
367 for (const auto& callback : enumeration_callbacks_)
368 callback.Run(result);
369 enumeration_callbacks_.clear();
370 } else if (success && enumeration_ready()) {
371 NotifyDeviceAdded(device);
372 }
373 }
374
15 } // namespace device 375 } // namespace device
OLDNEW
« device/usb/usb_service_impl.h ('K') | « device/usb/usb_service_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698