GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / device / usb / usb_service_impl.cc
blob6bdac2bfdc9744212da07fd7aa5f58871223b60a
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"
7 #include <set>
9 #include "base/bind.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"
18 #if defined(OS_WIN)
19 #include <setupapi.h>
20 #include <usbiodef.h>
22 #include "base/scoped_observer.h"
23 #include "base/strings/string_util.h"
24 #include "device/core/device_monitor_win.h"
25 #endif // OS_WIN
27 namespace device {
29 #if defined(OS_WIN)
31 namespace {
33 // Wrapper around a HDEVINFO that automatically destroys it.
34 class ScopedDeviceInfoList {
35 public:
36 explicit ScopedDeviceInfoList(HDEVINFO handle) : handle_(handle) {}
38 ~ScopedDeviceInfoList() {
39 if (valid()) {
40 SetupDiDestroyDeviceInfoList(handle_);
44 bool valid() { return handle_ != INVALID_HANDLE_VALUE; }
46 HDEVINFO get() { return handle_; }
48 private:
49 HDEVINFO 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 {
57 public:
58 ScopedDeviceInfo() {
59 memset(&dev_info_data_, 0, sizeof(dev_info_data_));
60 dev_info_data_.cbSize = sizeof(dev_info_data_);
63 ~ScopedDeviceInfo() {
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_; }
79 private:
80 HDEVINFO dev_info_set_ = INVALID_HANDLE_VALUE;
81 SP_DEVINFO_DATA dev_info_data_;
84 } // namespace
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 {
91 public:
92 UIThreadHelper(base::WeakPtr<UsbServiceImpl> usb_service)
93 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
94 usb_service_(usb_service),
95 device_observer_(this) {}
97 ~UIThreadHelper() {}
99 void Start() {
100 DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
101 if (device_monitor) {
102 device_observer_.Add(device_monitor);
106 private:
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_;
135 #endif // OS_WIN
137 // static
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);
145 return nullptr;
147 if (!context) {
148 return nullptr;
151 return new UsbServiceImpl(context, ui_task_runner);
154 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
155 DCHECK(CalledOnValidThread());
156 RefreshDevices();
157 DeviceMap::iterator it = devices_.find(unique_id);
158 if (it != devices_.end()) {
159 return it->second;
161 return NULL;
164 void UsbServiceImpl::GetDevices(
165 std::vector<scoped_refptr<UsbDevice> >* devices) {
166 DCHECK(CalledOnValidThread());
167 STLClearObject(devices);
169 if (!hotplug_enabled_) {
170 RefreshDevices();
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),
183 next_unique_id_(0),
184 hotplug_enabled_(false),
185 weak_factory_(this) {
186 task_runner_ = base::ThreadTaskRunnerHandle::Get();
187 int rv = libusb_hotplug_register_callback(
188 context_->context(),
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;
196 } else {
197 #if defined(OS_WIN)
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_)));
202 #endif // OS_WIN
206 UsbServiceImpl::~UsbServiceImpl() {
207 if (hotplug_enabled_) {
208 libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
210 #if defined(OS_WIN)
211 if (ui_thread_helper_) {
212 ui_task_runner_->DeleteSoon(FROM_HERE, ui_thread_helper_);
214 #endif // OS_WIN
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]);
238 if (new_device) {
239 connected_devices.insert(new_device.get());
241 } else {
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);
269 #if defined(OS_WIN)
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";
275 return;
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,
280 NULL)) {
281 USB_PLOG(ERROR) << "Failed to get device interface data for "
282 << device_path;
283 return;
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;
289 return;
291 dev_info.set_valid(dev_info_list.get());
293 DWORD reg_data_type;
294 BYTE buffer[256];
295 if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(),
296 SPDRP_SERVICE, &reg_data_type,
297 &buffer[0], sizeof buffer, NULL)) {
298 USB_PLOG(ERROR) << "Failed to get device service property";
299 return;
301 if (reg_data_type != REG_SZ) {
302 USB_LOG(ERROR) << "Unexpected data type for driver service: "
303 << reg_data_type;
304 return;
307 USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << ".";
308 if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") ==
309 0) {
310 RefreshDevices();
313 #endif // OS_WIN
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) {
320 uint32 unique_id;
321 do {
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);
331 return new_device;
332 } else {
333 USB_LOG(EVENT) << "Failed to get device descriptor: "
334 << ConvertPlatformUsbErrorToString(rv);
335 return nullptr;
339 // static
340 int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
341 PlatformUsbDevice device,
342 libusb_hotplug_event event,
343 void* user_data) {
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);
349 switch (event) {
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);
354 } else {
355 self->task_runner_->PostTask(
356 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded,
357 base::Unretained(self), device));
359 break;
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);
364 } else {
365 self->task_runner_->PostTask(
366 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved,
367 base::Unretained(self), device));
369 break;
370 default:
371 NOTREACHED();
374 return 0;
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);
394 } else {
395 NOTREACHED();
397 platform_devices_.erase(it);
399 NotifyDeviceRemoved(device);
400 device->OnDisconnect();
401 } else {
402 NOTREACHED();
405 libusb_unref_device(platform_device);
408 } // namespace device