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

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: 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> {
pfeldman 2013/07/22 08:39:45 I think this tiny bit is worth landing separately.
Bei Zhang 2013/07/23 17:58:44 Done.
63 } 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>();
pfeldman 2013/07/22 18:23:13 You could return NULL.
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) {}
64 168
65 UsbService::~UsbService() {} 169 UsbService::~UsbService() {}
66 170
67 void UsbService::Cleanup() { 171 void UsbService::Shutdown() {
68 event_handler_->Stop(); 172 context_ = NULL;
69 event_handler_ = NULL; 173 for (DeviceMapById::iterator it = devices_by_id_.begin();
174 it != devices_by_id_.end(); ++it) {
175 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second);
176 }
177 devices_.clear();
178 devices_by_id_.clear();
70 } 179 }
71 180
72 void UsbService::FindDevices(const uint16 vendor_id, 181 void UsbService::FindDevices(const uint16 vendor_id,
73 const uint16 product_id, 182 const uint16 product_id,
74 int interface_id, 183 int interface_id,
75 vector<scoped_refptr<UsbDevice> >* devices, 184 vector<int>* devices,
76 const base::Callback<void()>& callback) { 185 const base::Callback<void()>& callback) {
77 DCHECK(event_handler_) << "FindDevices called after event handler stopped."; 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
78 #if defined(OS_CHROMEOS) 187 #if defined(OS_CHROMEOS)
79 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to 188 // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
80 // use permission broker. 189 // use permission broker.
81 if (base::chromeos::IsRunningOnChromeOS()) { 190 if (base::chromeos::IsRunningOnChromeOS()) {
82 chromeos::PermissionBrokerClient* client = 191 chromeos::PermissionBrokerClient* client =
83 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); 192 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
84 DCHECK(client) << "Could not get permission broker client."; 193 DCHECK(client) << "Could not get permission broker client.";
85 if (!client) { 194 if (!client) {
86 callback.Run(); 195 callback.Run();
87 return; 196 return;
88 } 197 }
89 198
90 client->RequestUsbAccess(vendor_id, 199 client->RequestUsbAccess(vendor_id,
pfeldman 2013/07/22 18:23:13 Can you call it on FILE?
Bei Zhang 2013/07/23 17:58:44 Nope, this is a bug. On 2013/07/22 18:23:13, pfel
91 product_id, 200 product_id,
92 interface_id, 201 interface_id,
93 base::Bind(&UsbService::FindDevicesImpl, 202 base::Bind(&UsbService::FindDevicesImpl,
pfeldman 2013/07/22 18:23:13 On what thread will it be called?
Bei Zhang 2013/07/23 17:58:44 If it was working, which I doubt, then it shall be
94 base::Unretained(this), 203 base::Unretained(this),
95 vendor_id, 204 vendor_id,
96 product_id, 205 product_id,
97 devices, 206 devices,
98 callback)); 207 callback));
99 } else { 208 } else {
100 FindDevicesImpl(vendor_id, product_id, devices, callback, true); 209 FindDevicesImpl(vendor_id, product_id, devices, callback, true);
101 } 210 }
102 #else 211 #else
103 FindDevicesImpl(vendor_id, product_id, devices, callback, true); 212 FindDevicesImpl(vendor_id, product_id, devices, callback, true);
104 #endif // defined(OS_CHROMEOS) 213 #endif // defined(OS_CHROMEOS)
105 } 214 }
106 215
107 void UsbService::EnumerateDevices( 216 scoped_refptr<UsbDevice> UsbService::OpenDevice(int device) {
108 std::vector<scoped_refptr<UsbDevice> >* devices) { 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
218 RefreshDevices();
219 if (ContainsKey(devices_by_id_, device)) {
220 return devices_by_id_[device]->OpenDevice(this);
221 }
222 return NULL;
223 }
224
225 void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
227 if (ContainsKey(devices_by_id_, device->device())) {
228 return devices_by_id_[device->device()]->CloseDeviceHandle(device);
229 }
230 }
231
232 void UsbService::GetDevices(std::vector<int>* devices) {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109 devices->clear(); 234 devices->clear();
110 235 RefreshDevices();
111 DeviceVector enumerated_devices; 236 for (DeviceMapById::iterator it = devices_by_id_.begin();
112 EnumerateDevicesImpl(&enumerated_devices); 237 it != devices_by_id_.end();
113 238 ++it) {
114 for (DeviceVector::iterator it = enumerated_devices.begin(); 239 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 } 240 }
121 } 241 }
122 242
123 void UsbService::FindDevicesImpl(const uint16 vendor_id, 243 void UsbService::FindDevicesImpl(const uint16 vendor_id,
124 const uint16 product_id, 244 const uint16 product_id,
125 vector<scoped_refptr<UsbDevice> >* devices, 245 vector<int>* devices,
126 const base::Callback<void()>& callback, 246 const base::Callback<void()>& callback,
127 bool success) { 247 bool success) {
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
128 base::ScopedClosureRunner run_callback(callback); 249 base::ScopedClosureRunner run_callback(callback);
129
130 devices->clear(); 250 devices->clear();
131 251
132 // If the permission broker was unable to obtain permission for the specified 252 // 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 253 // devices then there is no point in attempting to enumerate the devices. On
134 // platforms without a permission broker, we assume permission is granted. 254 // platforms without a permission broker, we assume permission is granted.
135 if (!success) 255 if (!success)
136 return; 256 return;
137 257
138 DeviceVector enumerated_devices; 258 RefreshDevices();
139 EnumerateDevicesImpl(&enumerated_devices);
140 259
141 for (DeviceVector::iterator it = enumerated_devices.begin(); 260 for (DeviceMapById::iterator it = devices_by_id_.begin();
142 it != enumerated_devices.end(); ++it) { 261 it != devices_by_id_.end();
143 PlatformUsbDevice device = it->device(); 262 ++it) {
144 if (DeviceMatches(device, vendor_id, product_id)) { 263 if (DeviceMatches(it->second, vendor_id, product_id))
145 UsbDevice* const wrapper = LookupOrCreateDevice(device); 264 devices->push_back(it->first);
146 if (wrapper)
147 devices->push_back(wrapper);
148 }
149 } 265 }
150 } 266 }
151 267
152 void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) { 268 void UsbService::ScheduleRefreshDevices() {
153 DCHECK(event_handler_) << "CloseDevice called after event handler stopped."; 269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
270 if (device_enumeration_scheduled_)
271 return;
272 device_enumeration_scheduled_ = true;
273 BrowserThread::PostTask(
274 BrowserThread::FILE, FROM_HERE,
275 base::Bind(&UsbService::RefreshDevices, base::Unretained(this)));
276 }
154 277
155 PlatformUsbDevice platform_device = libusb_get_device(device->handle()); 278 void UsbService::RefreshDevices() {
156 if (!ContainsKey(devices_, platform_device)) { 279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
157 LOG(WARNING) << "CloseDevice called for device we're not tracking!"; 280 libusb_device** devices = NULL;
158 return; 281 const ssize_t device_count =
282 libusb_get_device_list(context_->context(), &devices);
283
284 set<int> connected_devices;
285 vector<PlatformUsbDevice> disconnected_devices;
286
287 // Populates new devices.
288 for (ssize_t i = 0; i < device_count; ++i) {
289 if (!ContainsKey(devices_, devices[i])) {
290 libusb_device_descriptor descriptor;
291 if (0 != libusb_get_device_descriptor(devices[i], &descriptor))
292 continue;
293 devices_[devices[i]] = new UsbDeviceStub(
294 context_.get(),
295 devices[i],
296 next_unique_id_,
297 descriptor.idVendor,
298 descriptor.idProduct);
299 devices_by_id_[next_unique_id_] = devices_[devices[i]];
300 ++next_unique_id_;
301 }
302 connected_devices.insert(devices_[devices[i]]->unique_id());
159 } 303 }
160 304
161 devices_.erase(platform_device); 305 // Find disconnected devices.
162 libusb_close(device->handle()); 306 for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
163 } 307 if (!ContainsKey(connected_devices, it->second->unique_id())) {
308 disconnected_devices.push_back(it->first);
309 }
310 }
164 311
165 UsbService::RefCountedPlatformUsbDevice::RefCountedPlatformUsbDevice( 312 // Remove disconnected devices from devices_.
166 PlatformUsbDevice device) : device_(device) { 313 for (size_t i = 0; i < disconnected_devices.size(); ++i) {
167 libusb_ref_device(device_); 314 UsbDeviceStub* device = devices_[disconnected_devices[i]];
168 } 315 devices_.erase(disconnected_devices[i]);
316 devices_by_id_.erase(device->unique_id());
169 317
170 UsbService::RefCountedPlatformUsbDevice::RefCountedPlatformUsbDevice( 318 // This should delete those devices and invalidate their handles.
171 const RefCountedPlatformUsbDevice& other) : device_(other.device_) { 319 // It might take long.
172 libusb_ref_device(device_); 320 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 } 321 }
196 322
197 libusb_free_device_list(devices, true); 323 libusb_free_device_list(devices, true);
198 } 324 }
199 325
200 bool UsbService::DeviceMatches(PlatformUsbDevice device, 326 bool UsbService::DeviceMatches(const UsbDeviceStub* device,
201 const uint16 vendor_id, 327 const uint16 vendor_id,
202 const uint16 product_id) { 328 const uint16 product_id) {
203 libusb_device_descriptor descriptor; 329 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 } 330 }
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
« chrome/browser/usb/usb_service.h ('K') | « chrome/browser/usb/usb_service.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698