OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/usb/usb_service.h" | 5 #include "chrome/browser/usb/usb_service.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/logging.h" |
9 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
10 #include "chrome/browser/usb/usb_device.h" | 11 #include "chrome/browser/usb/usb_device.h" |
11 #include "third_party/libusb/libusb.h" | 12 #include "third_party/libusb/libusb.h" |
12 | 13 |
13 UsbService::UsbService() : running_(true), thread_("UsbThread") { | 14 // The UsbEventHandler works around a design flaw in the libusb interface. There |
| 15 // is currently no way to signal to libusb that any caller into one of the event |
| 16 // handler calls should return without handling any events. |
| 17 class UsbEventHandler : public base::PlatformThread::Delegate { |
| 18 public: |
| 19 explicit UsbEventHandler(PlatformUsbContext context) |
| 20 : running_(true), context_(context) { |
| 21 base::PlatformThread::CreateNonJoinable(0, this); |
| 22 } |
| 23 |
| 24 virtual ~UsbEventHandler() {} |
| 25 |
| 26 virtual void ThreadMain() { |
| 27 base::PlatformThread::SetName("UsbEventHandler"); |
| 28 |
| 29 DLOG(INFO) << "UsbEventHandler started."; |
| 30 while (running_) { |
| 31 libusb_handle_events(context_); |
| 32 } |
| 33 DLOG(INFO) << "UsbEventHandler shutting down."; |
| 34 libusb_exit(context_); |
| 35 |
| 36 delete this; |
| 37 } |
| 38 |
| 39 void Stop() { |
| 40 running_ = false; |
| 41 } |
| 42 |
| 43 private: |
| 44 bool running_; |
| 45 PlatformUsbContext context_; |
| 46 |
| 47 DISALLOW_EVIL_CONSTRUCTORS(UsbEventHandler); |
| 48 }; |
| 49 |
| 50 UsbService::UsbService() { |
14 libusb_init(&context_); | 51 libusb_init(&context_); |
15 thread_.Start(); | 52 event_handler_ = new UsbEventHandler(context_); |
16 PostHandleEventTask(); | |
17 } | 53 } |
18 | 54 |
19 UsbService::~UsbService() {} | 55 UsbService::~UsbService() {} |
20 | 56 |
21 // TODO(gdk): There is currently no clean way to indicate to the event handler | |
22 // thread that it must break out of the handling loop before the event timeout, | |
23 // therefore we currently are at the whim of the event handler timeout before | |
24 // the message handling thread can be joined. | |
25 void UsbService::Cleanup() { | 57 void UsbService::Cleanup() { |
26 running_ = false; | 58 event_handler_->Stop(); |
27 | 59 event_handler_ = NULL; |
28 if (!devices_.empty()) { | |
29 libusb_close(devices_.begin()->second->handle()); | |
30 } else { | |
31 LOG(WARNING) << "UsbService cannot force the USB event-handler thread to " | |
32 << "exit because there are no open devices with which to " | |
33 << "manipulate it. It maybe take up to 60 (!) seconds for the " | |
34 << "thread to join from this point."; | |
35 } | |
36 | |
37 thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
38 &UsbService::PlatformShutdown, base::Unretained(this))); | |
39 } | 60 } |
40 | 61 |
41 UsbDevice* UsbService::FindDevice(const uint16 vendor_id, | 62 UsbDevice* UsbService::FindDevice(const uint16 vendor_id, |
42 const uint16 product_id) { | 63 const uint16 product_id) { |
| 64 DCHECK(event_handler_) << "FindDevice called after event handler stopped."; |
| 65 |
43 const std::pair<uint16, uint16> key = std::make_pair(vendor_id, product_id); | 66 const std::pair<uint16, uint16> key = std::make_pair(vendor_id, product_id); |
44 if (ContainsKey(devices_, key)) { | 67 if (ContainsKey(devices_, key)) { |
45 return devices_[key]; | 68 return devices_[key]; |
46 } | 69 } |
47 | 70 |
48 libusb_device_handle* const handle = libusb_open_device_with_vid_pid( | 71 libusb_device_handle* const handle = libusb_open_device_with_vid_pid( |
49 context_, vendor_id, product_id); | 72 context_, vendor_id, product_id); |
50 if (!handle) { | 73 if (!handle) { |
51 return NULL; | 74 return NULL; |
52 } | 75 } |
53 | 76 |
54 UsbDevice* const device = new UsbDevice(this, handle); | 77 UsbDevice* const device = new UsbDevice(this, handle); |
55 devices_[key] = device; | 78 devices_[key] = device; |
56 | 79 |
57 return device; | 80 return device; |
58 } | 81 } |
59 | 82 |
60 void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) { | 83 void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) { |
61 DCHECK(running_) << "Cannot close device after service has stopped running."; | 84 DCHECK(event_handler_) << "CloseDevice called after event handler stopped."; |
62 | 85 |
63 for (DeviceMap::iterator i = devices_.begin(); i != devices_.end(); ++i) { | 86 for (DeviceMap::iterator i = devices_.begin(); i != devices_.end(); ++i) { |
64 if (i->second.get() == device.get()) { | 87 if (i->second.get() == device.get()) { |
65 devices_.erase(i); | 88 devices_.erase(i); |
66 libusb_close(device->handle()); | 89 libusb_close(device->handle()); |
67 return; | 90 return; |
68 } | 91 } |
69 } | 92 } |
70 } | 93 } |
71 | |
72 void UsbService::PostHandleEventTask() { | |
73 thread_.message_loop()->PostTask(FROM_HERE, base::Bind( | |
74 &UsbService::HandleEvent, base::Unretained(this))); | |
75 } | |
76 | |
77 void UsbService::HandleEvent() { | |
78 libusb_handle_events(context_); | |
79 if (running_) { | |
80 PostHandleEventTask(); | |
81 } | |
82 } | |
83 | |
84 void UsbService::PlatformShutdown() { | |
85 libusb_exit(context_); | |
86 } | |
OLD | NEW |