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

Side by Side Diff: chrome/browser/usb/usb_service.cc

Issue 18593002: Usb backend refactor step 2: Separate Usb Device Handle and Usb Device (deprecate) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Separate Usb Device Handle and Usb Device Created 7 years, 5 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 | Annotate | Revision Log
OLDNEW
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 <set>
7 #include <vector> 8 #include <vector>
8 9
9 #include "base/bind.h" 10 #include "base/bind.h"
10 #include "base/bind_helpers.h" 11 #include "base/bind_helpers.h"
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "base/stl_util.h" 13 #include "base/stl_util.h"
13 #include "chrome/browser/usb/usb_device.h" 14 #include "chrome/browser/usb/usb_device.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "third_party/libusb/src/libusb/interrupt.h"
14 #include "third_party/libusb/src/libusb/libusb.h" 17 #include "third_party/libusb/src/libusb/libusb.h"
15 18
16 #if defined(OS_CHROMEOS) 19 #if defined(OS_CHROMEOS)
17 #include "base/chromeos/chromeos_version.h" 20 #include "base/chromeos/chromeos_version.h"
18 #include "chromeos/dbus/dbus_thread_manager.h" 21 #include "chromeos/dbus/dbus_thread_manager.h"
19 #include "chromeos/dbus/permission_broker_client.h" 22 #include "chromeos/dbus/permission_broker_client.h"
20 #endif // defined(OS_CHROMEOS) 23 #endif // defined(OS_CHROMEOS)
21 24
25 using std::set;
22 using std::vector; 26 using std::vector;
27 using content::BrowserThread;
23 28
24 // The UsbEventHandler works around a design flaw in the libusb interface. There 29 // The UsbEventHandler dispatches USB events on separate thread. There is
25 // is currently no way to signal to libusb that any caller into one of the event 30 // currently no way to signal to libusb that any caller into one of the event
26 // handler calls should return without handling any events. 31 // handler calls should return without handling any events.
32 //
33 // This class manages the polling thread and assures the thread exits safely.
34 // This class is only visible to UsbContext. UsbContext manages its life cycle.
27 class UsbEventHandler : public base::PlatformThread::Delegate { 35 class UsbEventHandler : public base::PlatformThread::Delegate {
28 public: 36 public:
29 explicit UsbEventHandler(PlatformUsbContext context) 37 explicit UsbEventHandler(PlatformUsbContext context)
30 : running_(true), context_(context) { 38 : running_(true), context_(context), thread_handle_(0) {
31 base::PlatformThread::CreateNonJoinable(0, this); 39 base::PlatformThread::Create(0, this, &thread_handle_);
32 } 40 }
33 41
34 virtual ~UsbEventHandler() {} 42 virtual ~UsbEventHandler() {}
35 43
36 virtual void ThreadMain() OVERRIDE { 44 virtual void ThreadMain() OVERRIDE {
37 base::PlatformThread::SetName("UsbEventHandler"); 45 base::PlatformThread::SetName("UsbEventHandler");
38 46
39 DLOG(INFO) << "UsbEventHandler started."; 47 VLOG(1) << "UsbEventHandler started.";
40 while (running_) { 48
49 while (running_)
41 libusb_handle_events(context_); 50 libusb_handle_events(context_);
42 }
43 DLOG(INFO) << "UsbEventHandler shutting down.";
44 libusb_exit(context_);
45 51
46 delete this; 52 VLOG(1) << "UsbEventHandler shutting down.";
47 } 53 }
48 54
49 void Stop() { 55 void Stop() {
50 running_ = false; 56 running_ = false;
57 base::subtle::MemoryBarrier();
58 libusb_interrupt_handle_event(context_);
59 base::PlatformThread::Join(thread_handle_);
51 } 60 }
52 61
53 private: 62 private:
54 bool running_; 63 volatile bool running_;
55 PlatformUsbContext context_; 64 PlatformUsbContext context_;
65 base::PlatformThreadHandle thread_handle_;
56 66
57 DISALLOW_EVIL_CONSTRUCTORS(UsbEventHandler); 67 DISALLOW_COPY_AND_ASSIGN(UsbEventHandler);
58 }; 68 };
59 69
60 UsbService::UsbService() { 70 // Ref-counted wrapper for PlatformUsbContext.
61 libusb_init(&context_); 71 // It also manages the life-cycle of UsbEventHandler
62 event_handler_ = new UsbEventHandler(context_); 72 class UsbContext : public base::RefCountedThreadSafe<UsbContext> {
73 public:
74 UsbContext() : context_(NULL) {
75 CHECK_EQ(0, libusb_init(&context_)) << "Cannot initialize libusb";
76 event_handler_.reset(new UsbEventHandler(context_));
77 }
78 PlatformUsbContext context() const { return context_; }
79
80 private:
81 friend class base::RefCountedThreadSafe<UsbContext>;
82
83 virtual ~UsbContext() {
84 event_handler_->Stop();
85 event_handler_.reset(NULL);
86 libusb_exit(context_);
87 }
88 PlatformUsbContext context_;
89 scoped_ptr<UsbEventHandler> event_handler_;
90 };
91
92 class UsbDeviceStub : public base::NonThreadSafe {
93 public:
94 explicit UsbDeviceStub(UsbContext* context, PlatformUsbDevice device,
95 const int unique_id, const uint16 vendor_id,
96 const uint16 product_id)
97 : context_(context),
98 device_(device),
99 unique_id_(unique_id),
100 vendor_id_(vendor_id),
101 product_id_(product_id) {
102 DCHECK(CalledOnValidThread());
103 libusb_ref_device(device_);
104 }
105
106 virtual ~UsbDeviceStub() {
107 DCHECK(CalledOnValidThread());
108 libusb_unref_device(device_);
109
110 // Device is lost.
111 // Invalidates all the opened handle.
112 for (vector<scoped_refptr<UsbDevice> >::iterator it = handles_.begin();
113 it != handles_.end(); ++it) {
114 it->get()->InternalClose();
115 }
116 STLClearObject(&handles_);
117 }
118
119 PlatformUsbDevice device() const { return device_; }
120 int unique_id() const { return unique_id_; }
121 int vendor_id() const { return vendor_id_; }
122 int product_id() const { return product_id_; }
123
124 scoped_refptr<UsbDevice> OpenDevice(UsbService* service) {
125 DCHECK(CalledOnValidThread());
126 PlatformUsbDeviceHandle handle;
127
128 if (0 == libusb_open(device_, &handle)) {
129 scoped_refptr<UsbDevice> wrapper =
130 make_scoped_refptr(new UsbDevice(
131 service,
132 unique_id_,
133 handle));
134 handles_.push_back(wrapper);
135 return wrapper;
136 }
137 return scoped_refptr<UsbDevice>();
138 }
139
140 void CloseDeviceHandle(UsbDevice* device) {
141 DCHECK(CalledOnValidThread());
142 device->InternalClose();
143 for (vector<scoped_refptr<UsbDevice> >::iterator it = handles_.begin();
144 it != handles_.end(); ++it) {
145 if (it->get() == device) {
146 handles_.erase(it);
147 return;
148 }
149 }
150 }
151
152 private:
153 // Retain the context so it will not be release before the destruction
154 // of the UsbDevice object.
155 scoped_refptr<UsbContext> context_;
156 vector<scoped_refptr<UsbDevice> > handles_;
157 const PlatformUsbDevice device_;
158 const uint16 unique_id_;
159 const uint16 vendor_id_;
160 const int product_id_;
161 DISALLOW_COPY_AND_ASSIGN(UsbDeviceStub);
162 };
163
164 UsbService::UsbService()
165 : context_(new UsbContext()),
166 next_unique_id_(1),
167 device_enumeration_scheduled_(false) {
168 // This class will be consequently used on FILE thread.
169 DetachFromThread();
63 } 170 }
64 171
65 UsbService::~UsbService() {} 172 UsbService::~UsbService() {
173 // The destructor will be called on UI thread.
174 DetachFromThread();
175 }
66 176
67 void UsbService::Cleanup() { 177 void UsbService::Shutdown() {
68 event_handler_->Stop(); 178 context_ = NULL;
69 event_handler_ = NULL; 179 for (DeviceMapById::iterator it = devices_by_id_.begin();
180 it != devices_by_id_.end(); ++it) {
181 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second);
182 }
183 devices_.clear();
184 devices_by_id_.clear();
70 } 185 }
71 186
72 void UsbService::FindDevices(const uint16 vendor_id, 187 void UsbService::FindDevices(const uint16 vendor_id,
73 const uint16 product_id, 188 const uint16 product_id,
74 int interface_id, 189 int interface_id,
75 vector<scoped_refptr<UsbDevice> >* devices, 190 vector<int>* devices,
76 const base::Callback<void()>& callback) { 191 const base::Callback<void()>& callback) {
77 DCHECK(event_handler_) << "FindDevices called after event handler stopped."; 192 DCHECK(CalledOnValidThread());
78 #if defined(OS_CHROMEOS) 193 #if defined(OS_CHROMEOS)
79 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to 194 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
80 // use permission broker. 195 // use permission broker.
81 if (base::chromeos::IsRunningOnChromeOS()) { 196 if (base::chromeos::IsRunningOnChromeOS()) {
82 chromeos::PermissionBrokerClient* client = 197 chromeos::PermissionBrokerClient* client =
83 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); 198 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
84 DCHECK(client) << "Could not get permission broker client."; 199 DCHECK(client) << "Could not get permission broker client.";
85 if (!client) { 200 if (!client) {
86 callback.Run(); 201 callback.Run();
87 return; 202 return;
88 } 203 }
89 204
90 client->RequestUsbAccess(vendor_id, 205 client->RequestUsbAccess(vendor_id,
91 product_id, 206 product_id,
92 interface_id, 207 interface_id,
93 base::Bind(&UsbService::FindDevicesImpl, 208 base::Bind(&UsbService::FindDevicesImpl,
94 base::Unretained(this), 209 base::Unretained(this),
95 vendor_id, 210 vendor_id,
96 product_id, 211 product_id,
97 devices, 212 devices,
98 callback)); 213 callback));
99 } else { 214 } else {
100 FindDevicesImpl(vendor_id, product_id, devices, callback, true); 215 FindDevicesImpl(vendor_id, product_id, devices, callback, true);
101 } 216 }
102 #else 217 #else
103 FindDevicesImpl(vendor_id, product_id, devices, callback, true); 218 FindDevicesImpl(vendor_id, product_id, devices, callback, true);
104 #endif // defined(OS_CHROMEOS) 219 #endif // defined(OS_CHROMEOS)
105 } 220 }
106 221
107 void UsbService::EnumerateDevices( 222 scoped_refptr<UsbDevice> UsbService::OpenDevice(int device) {
108 std::vector<scoped_refptr<UsbDevice> >* devices) { 223 DCHECK(CalledOnValidThread());
224 RefreshDevices();
225 if (ContainsKey(devices_by_id_, device)) {
226 return devices_by_id_[device]->OpenDevice(this);
227 }
228 return NULL;
229 }
230
231 void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) {
232 DCHECK(CalledOnValidThread());
233 if (ContainsKey(devices_by_id_, device->device())) {
234 return devices_by_id_[device->device()]->CloseDeviceHandle(device);
235 }
236 }
237
238 void UsbService::GetDevices(std::vector<int>* devices) {
239 DCHECK(CalledOnValidThread());
109 devices->clear(); 240 devices->clear();
110 241 RefreshDevices();
111 DeviceVector enumerated_devices; 242 for (DeviceMapById::iterator it = devices_by_id_.begin();
112 EnumerateDevicesImpl(&enumerated_devices); 243 it != devices_by_id_.end();
113 244 ++it) {
114 for (DeviceVector::iterator it = enumerated_devices.begin(); 245 devices->push_back(it->first);
115 it != enumerated_devices.end(); ++it) {
116 PlatformUsbDevice device = it->device();
117 UsbDevice* const wrapper = LookupOrCreateDevice(device);
118 if (wrapper)
119 devices->push_back(wrapper);
120 } 246 }
121 } 247 }
122 248
123 void UsbService::FindDevicesImpl(const uint16 vendor_id, 249 void UsbService::FindDevicesImpl(const uint16 vendor_id,
124 const uint16 product_id, 250 const uint16 product_id,
125 vector<scoped_refptr<UsbDevice> >* devices, 251 vector<int>* devices,
126 const base::Callback<void()>& callback, 252 const base::Callback<void()>& callback,
127 bool success) { 253 bool success) {
254 DCHECK(CalledOnValidThread());
128 base::ScopedClosureRunner run_callback(callback); 255 base::ScopedClosureRunner run_callback(callback);
129
130 devices->clear(); 256 devices->clear();
131 257
132 // If the permission broker was unable to obtain permission for the specified 258 // If the permission broker was unable to obtain permission for the specified
133 // devices then there is no point in attempting to enumerate the devices. On 259 // devices then there is no point in attempting to enumerate the devices. On
134 // platforms without a permission broker, we assume permission is granted. 260 // platforms without a permission broker, we assume permission is granted.
135 if (!success) 261 if (!success)
136 return; 262 return;
137 263
138 DeviceVector enumerated_devices; 264 RefreshDevices();
139 EnumerateDevicesImpl(&enumerated_devices);
140 265
141 for (DeviceVector::iterator it = enumerated_devices.begin(); 266 for (DeviceMapById::iterator it = devices_by_id_.begin();
142 it != enumerated_devices.end(); ++it) { 267 it != devices_by_id_.end();
143 PlatformUsbDevice device = it->device(); 268 ++it) {
144 if (DeviceMatches(device, vendor_id, product_id)) { 269 if (DeviceMatches(it->second, vendor_id, product_id))
145 UsbDevice* const wrapper = LookupOrCreateDevice(device); 270 devices->push_back(it->first);
146 if (wrapper)
147 devices->push_back(wrapper);
148 }
149 } 271 }
150 } 272 }
151 273
152 void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) { 274 void UsbService::ScheduleRefreshDevices() {
153 DCHECK(event_handler_) << "CloseDevice called after event handler stopped."; 275 DCHECK(CalledOnValidThread());
276 if (device_enumeration_scheduled_)
277 return;
278 device_enumeration_scheduled_ = true;
279 BrowserThread::PostTask(
280 BrowserThread::FILE, FROM_HERE,
281 base::Bind(&UsbService::RefreshDevices, base::Unretained(this)));
282 }
154 283
155 PlatformUsbDevice platform_device = libusb_get_device(device->handle()); 284 void UsbService::RefreshDevices() {
156 if (!ContainsKey(devices_, platform_device)) { 285 DCHECK(CalledOnValidThread());
157 LOG(WARNING) << "CloseDevice called for device we're not tracking!"; 286 libusb_device** devices = NULL;
158 return; 287 const ssize_t device_count =
288 libusb_get_device_list(context_->context(), &devices);
289
290 set<int> connected_devices;
291 vector<PlatformUsbDevice> disconnected_devices;
292
293 // Populates new devices.
294 for (ssize_t i = 0; i < device_count; ++i) {
295 if (!ContainsKey(devices_, devices[i])) {
296 libusb_device_descriptor descriptor;
297 if (0 != libusb_get_device_descriptor(devices[i], &descriptor))
298 continue;
299 devices_[devices[i]] = new UsbDeviceStub(
300 context_.get(),
301 devices[i],
302 next_unique_id_,
303 descriptor.idVendor,
304 descriptor.idProduct);
305 devices_by_id_[next_unique_id_] = devices_[devices[i]];
306 ++next_unique_id_;
307 }
308 connected_devices.insert(devices_[devices[i]]->unique_id());
159 } 309 }
160 310
161 devices_.erase(platform_device); 311 // Find disconnected devices.
162 libusb_close(device->handle()); 312 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
163 } 313 if (!ContainsKey(connected_devices, it->second->unique_id())) {
314 disconnected_devices.push_back(it->first);
315 }
316 }
164 317
165 UsbService::RefCountedPlatformUsbDevice::RefCountedPlatformUsbDevice( 318 // Remove disconnected devices from devices_.
166 PlatformUsbDevice device) : device_(device) { 319 for (size_t i = 0; i < disconnected_devices.size(); ++i) {
167 libusb_ref_device(device_); 320 UsbDeviceStub* device = devices_[disconnected_devices[i]];
168 } 321 devices_.erase(disconnected_devices[i]);
322 devices_by_id_.erase(device->unique_id());
169 323
170 UsbService::RefCountedPlatformUsbDevice::RefCountedPlatformUsbDevice( 324 // This should delete those devices and invalidate their handles.
171 const RefCountedPlatformUsbDevice& other) : device_(other.device_) { 325 // It might take long.
172 libusb_ref_device(device_); 326 delete device;
173 }
174
175 UsbService::RefCountedPlatformUsbDevice::~RefCountedPlatformUsbDevice() {
176 libusb_unref_device(device_);
177 }
178
179 PlatformUsbDevice UsbService::RefCountedPlatformUsbDevice::device() {
180 return device_;
181 }
182
183 void UsbService::EnumerateDevicesImpl(DeviceVector* output) {
184 STLClearObject(output);
185
186 libusb_device** devices = NULL;
187 const ssize_t device_count = libusb_get_device_list(context_, &devices);
188 if (device_count < 0)
189 return;
190
191 for (int i = 0; i < device_count; ++i) {
192 libusb_device* device = devices[i];
193 libusb_ref_device(device);
194 output->push_back(RefCountedPlatformUsbDevice(device));
195 } 327 }
196 328
197 libusb_free_device_list(devices, true); 329 libusb_free_device_list(devices, true);
198 } 330 }
199 331
200 bool UsbService::DeviceMatches(PlatformUsbDevice device, 332 bool UsbService::DeviceMatches(const UsbDeviceStub* device,
201 const uint16 vendor_id, 333 const uint16 vendor_id,
202 const uint16 product_id) { 334 const uint16 product_id) {
203 libusb_device_descriptor descriptor; 335 return device->vendor_id() == vendor_id && device->product_id() == product_id;
204 if (libusb_get_device_descriptor(device, &descriptor))
205 return false;
206 return descriptor.idVendor == vendor_id && descriptor.idProduct == product_id;
207 } 336 }
208
209 UsbDevice* UsbService::LookupOrCreateDevice(PlatformUsbDevice device) {
210 if (!ContainsKey(devices_, device)) {
211 libusb_device_handle* handle = NULL;
212 if (libusb_open(device, &handle)) {
213 LOG(WARNING) << "Could not open device.";
214 return NULL;
215 }
216
217 UsbDevice* wrapper = new UsbDevice(this, handle);
218 devices_[device] = wrapper;
219 }
220 return devices_[device].get();
221 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698