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

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

Issue 16316004: Separate usb device handle from usb device. (deprecate) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix the threading mess 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
« no previous file with comments | « chrome/browser/usb/usb_device_handle.h ('k') | chrome/browser/usb/usb_interface.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/usb/usb_device_handle.h"
6
7 #include <vector>
8
9 #include "base/stl_util.h"
10 #include "base/synchronization/lock.h"
11 #include "chrome/browser/usb/usb_interface.h"
12 #include "chrome/browser/usb/usb_service.h"
13 #include "content/public/browser/browser_thread.h"
14
15 using content::BrowserThread;
16
17 #define CHECK_DEVICE(callback, ...) \
18 do { \
19 if (handle_ == NULL) { \
20 DVLOG(1) << "Device is disconnected: "; \
21 callback.Run(__VA_ARGS__); \
22 return; \
23 } \
24 } while (0)
25
26 #define CHECK_DEVICE_OR_RETURN \
27 do { \
28 if (handle_ == NULL) { \
29 DVLOG(1) << "Device is disconnected: "; \
30 return; \
31 } \
32 } while (0)
33
34 namespace {
35
36 uint8 ConvertTransferDirection(const UsbEndpointDirection direction) {
37 switch (direction) {
38 case USB_DIRECTION_INBOUND:
39 return LIBUSB_ENDPOINT_IN;
40 case USB_DIRECTION_OUTBOUND:
41 return LIBUSB_ENDPOINT_OUT;
42 default:
43 NOTREACHED();
44 return LIBUSB_ENDPOINT_IN;
45 }
46 }
47
48 uint8 CreateRequestType(
49 const UsbEndpointDirection direction,
50 const UsbDeviceHandle::TransferRequestType request_type,
51 const UsbDeviceHandle::TransferRecipient recipient) {
52 uint8 result = ConvertTransferDirection(direction);
53
54 switch (request_type) {
55 case UsbDeviceHandle::STANDARD:
56 result |= LIBUSB_REQUEST_TYPE_STANDARD;
57 break;
58 case UsbDeviceHandle::CLASS:
59 result |= LIBUSB_REQUEST_TYPE_CLASS;
60 break;
61 case UsbDeviceHandle::VENDOR:
62 result |= LIBUSB_REQUEST_TYPE_VENDOR;
63 break;
64 case UsbDeviceHandle::RESERVED:
65 result |= LIBUSB_REQUEST_TYPE_RESERVED;
66 break;
67 }
68
69 switch (recipient) {
70 case UsbDeviceHandle::DEVICE:
71 result |= LIBUSB_RECIPIENT_DEVICE;
72 break;
73 case UsbDeviceHandle::INTERFACE:
74 result |= LIBUSB_RECIPIENT_INTERFACE;
75 break;
76 case UsbDeviceHandle::ENDPOINT:
77 result |= LIBUSB_RECIPIENT_ENDPOINT;
78 break;
79 case UsbDeviceHandle::OTHER:
80 result |= LIBUSB_RECIPIENT_OTHER;
81 break;
82 }
83
84 return result;
85 }
86
87 UsbTransferStatus ConvertTransferStatus(const libusb_transfer_status status) {
88 switch (status) {
89 case LIBUSB_TRANSFER_COMPLETED:
90 return USB_TRANSFER_COMPLETED;
91 case LIBUSB_TRANSFER_ERROR:
92 return USB_TRANSFER_ERROR;
93 case LIBUSB_TRANSFER_TIMED_OUT:
94 return USB_TRANSFER_TIMEOUT;
95 case LIBUSB_TRANSFER_STALL:
96 return USB_TRANSFER_STALLED;
97 case LIBUSB_TRANSFER_NO_DEVICE:
98 return USB_TRANSFER_DISCONNECT;
99 case LIBUSB_TRANSFER_OVERFLOW:
100 return USB_TRANSFER_OVERFLOW;
101 case LIBUSB_TRANSFER_CANCELLED:
102 return USB_TRANSFER_CANCELLED;
103 default:
104 NOTREACHED();
105 return USB_TRANSFER_ERROR;
106 }
107 }
108
109 } // namespace
110
111 UsbDeviceHandle::Transfer::Transfer()
112 : transfer_type(USB_TRANSFER_CONTROL), length(0) {}
113
114 UsbDeviceHandle::Transfer::~Transfer() {}
115
116 UsbDeviceHandle::UsbDeviceHandle(UsbService* service, const int device,
117 const uint16 vendor_id,
118 const uint16 product_id,
119 PlatformUsbDeviceHandle handle)
120 : service_(service),
121 device_(device),
122 vendor_id_(vendor_id),
123 product_id_(product_id),
124 handle_(handle) {
125 DCHECK(handle) << "Cannot create device with NULL handle.";
126 }
127
128 UsbDeviceHandle::UsbDeviceHandle()
129 : service_(NULL), device_(0), vendor_id_(0), product_id_(0), handle_(NULL) {
130 DCHECK(thread_checker_.CalledOnValidThread());
131 }
132
133 UsbDeviceHandle::~UsbDeviceHandle() {
134 DCHECK(thread_checker_.CalledOnValidThread());
135 InternalClose();
136 }
137
138 void UsbDeviceHandle::Close(const base::Callback<void()>& callback) {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 if (handle_ == NULL) return;
141 service_->CloseDeviceHandle(this);
142 callback.Run();
143 }
144
145 void UsbDeviceHandle::HandleTransferCompletionFileThread(
146 PlatformUsbTransferHandle transfer) {
147 UsbDeviceHandle* const device =
148 reinterpret_cast<UsbDeviceHandle*>(transfer->user_data);
149 if (device) device->TransferComplete(transfer);
150 // We should free the transfer even if the device is removed.
151 libusb_free_transfer(transfer);
152 }
153
154 // This function dispatches a completed transfer to its handle.
155 // It is called from UsbEventDispatcher using libusb_handle_events_timeout.
156 void LIBUSB_CALL UsbDeviceHandle::HandleTransferCompletion(
157 PlatformUsbTransferHandle transfer) {
158 BrowserThread::PostTask(
159 BrowserThread::FILE, FROM_HERE,
160 base::Bind(&HandleTransferCompletionFileThread, transfer));
161 }
162
163 void UsbDeviceHandle::TransferComplete(PlatformUsbTransferHandle handle) {
164 DCHECK(thread_checker_.CalledOnValidThread());
165 DCHECK(handle_ != NULL) << "handle_ can only be reset after "
166 "transfers are unregistered";
167
168 Transfer transfer = transfers_[handle];
169 transfers_.erase(handle);
170
171 if (handle->status != LIBUSB_TRANSFER_COMPLETED &&
172 handle->status != LIBUSB_TRANSFER_CANCELLED) {
173 service_->ScheduleEnumerateDevice();
174 }
175
176 DCHECK_GE(handle->actual_length, 0) << "Negative actual length received";
177 size_t actual_length =
178 static_cast<size_t>(std::max(handle->actual_length, 0));
179
180 DCHECK(transfer.length >= actual_length)
181 << "data too big for our buffer (libusb failure?)";
182
183 scoped_refptr<net::IOBuffer> buffer = transfer.buffer;
184 switch (transfer.transfer_type) {
185 case USB_TRANSFER_CONTROL:
186 // If the transfer is a control transfer we do not expose the control
187 // setup header to the caller. This logic strips off the header if
188 // present before invoking the callback provided with the transfer.
189 if (actual_length > 0) {
190 CHECK(transfer.length >= LIBUSB_CONTROL_SETUP_SIZE)
191 << "buffer was not correctly set: too small for the control header";
192
193 if (transfer.length >= actual_length &&
194 actual_length >= LIBUSB_CONTROL_SETUP_SIZE) {
195 // If the payload is zero bytes long, pad out the allocated buffer
196 // size to one byte so that an IOBuffer of that size can be allocated.
197 scoped_refptr<net::IOBuffer> resized_buffer = new net::IOBuffer(
198 std::max(actual_length, static_cast<size_t>(1)));
199 memcpy(resized_buffer->data(),
200 buffer->data() + LIBUSB_CONTROL_SETUP_SIZE, actual_length);
201 buffer = resized_buffer;
202 }
203 }
204 break;
205
206 case USB_TRANSFER_ISOCHRONOUS:
207 // Isochronous replies might carry data in the different isoc packets even
208 // if the transfer actual_data value is zero. Furthermore, not all of the
209 // received packets might contain data, so we need to calculate how many
210 // data bytes we are effectively providing and pack the results.
211 if (actual_length == 0) {
212 size_t packet_buffer_start = 0;
213 for (int i = 0; i < handle->num_iso_packets; ++i) {
214 PlatformUsbIsoPacketDescriptor packet = &handle->iso_packet_desc[i];
215 if (packet->actual_length > 0) {
216 // We don't need to copy as long as all packets until now provide
217 // all the data the packet can hold.
218 if (actual_length < packet_buffer_start) {
219 CHECK(packet_buffer_start + packet->actual_length <=
220 transfer.length);
221 memmove(buffer->data() + actual_length,
222 buffer->data() + packet_buffer_start,
223 packet->actual_length);
224 }
225 actual_length += packet->actual_length;
226 }
227
228 packet_buffer_start += packet->length;
229 }
230 }
231 break;
232
233 case USB_TRANSFER_BULK:
234 case USB_TRANSFER_INTERRUPT:
235 break;
236
237 default:
238 NOTREACHED() << "Invalid usb transfer type";
239 break;
240 }
241
242 transfer.callback
243 .Run(ConvertTransferStatus(handle->status), buffer, actual_length);
244 }
245
246 void UsbDeviceHandle::ListInterfaces(UsbConfigDescriptor* config,
247 const UsbInterfaceCallback& callback) {
248 DCHECK(thread_checker_.CalledOnValidThread());
249 CHECK_DEVICE(callback, false);
250
251 PlatformUsbDevice device = libusb_get_device(handle_);
252
253 PlatformUsbConfigDescriptor platform_config;
254 const int list_result =
255 libusb_get_active_config_descriptor(device, &platform_config);
256 if (list_result == 0) {
257 config->Reset(platform_config);
258 }
259 callback.Run(list_result == 0);
260 }
261
262 void UsbDeviceHandle::ClaimInterface(const int interface_number,
263 const UsbInterfaceCallback& callback) {
264 DCHECK(thread_checker_.CalledOnValidThread());
265 CHECK_DEVICE(callback, false);
266
267 const int claim_result = libusb_claim_interface(handle_, interface_number);
268 callback.Run(claim_result == 0);
269 }
270
271 void UsbDeviceHandle::ReleaseInterface(const int interface_number,
272 const UsbInterfaceCallback& callback) {
273 DCHECK(thread_checker_.CalledOnValidThread());
274 CHECK_DEVICE(callback, false);
275
276 const int release_result =
277 libusb_release_interface(handle_, interface_number);
278 callback.Run(release_result == 0);
279 }
280
281 void UsbDeviceHandle::SetInterfaceAlternateSetting(
282 const int interface_number, const int alternate_setting,
283 const UsbInterfaceCallback& callback) {
284 DCHECK(thread_checker_.CalledOnValidThread());
285 CHECK_DEVICE(callback, false);
286
287 const int setting_result = libusb_set_interface_alt_setting(
288 handle_, interface_number, alternate_setting);
289
290 callback.Run(setting_result == 0);
291 }
292
293 void UsbDeviceHandle::ControlTransfer(const UsbEndpointDirection direction,
294 const TransferRequestType request_type,
295 const TransferRecipient recipient,
296 const uint8 request, const uint16 value,
297 const uint16 index, net::IOBuffer* buffer,
298 const size_t length,
299 const unsigned int timeout,
300 const UsbTransferCallback& callback) {
301 CHECK_DEVICE(callback, USB_TRANSFER_DISCONNECT,
302 scoped_refptr<net::IOBuffer>(), 0);
303
304 const size_t resized_length = LIBUSB_CONTROL_SETUP_SIZE + length;
305 scoped_refptr<net::IOBuffer> resized_buffer(
306 new net::IOBufferWithSize(resized_length));
307 memcpy(resized_buffer->data() + LIBUSB_CONTROL_SETUP_SIZE, buffer->data(),
308 length);
309
310 struct libusb_transfer* const transfer = libusb_alloc_transfer(0);
311 const uint8 converted_type =
312 CreateRequestType(direction, request_type, recipient);
313 libusb_fill_control_setup(reinterpret_cast<uint8*>(resized_buffer->data()),
314 converted_type, request, value, index, length);
315 libusb_fill_control_transfer(transfer, handle_,
316 reinterpret_cast<uint8*>(resized_buffer->data()),
317 HandleTransferCompletion, this, timeout);
318 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
319 base::Bind(&UsbDeviceHandle::SubmitTransfer, this,
320 transfer, USB_TRANSFER_CONTROL,
321 resized_buffer, resized_length, callback));
322 }
323
324 void UsbDeviceHandle::BulkTransfer(const UsbEndpointDirection direction,
325 const uint8 endpoint, net::IOBuffer* buffer,
326 const size_t length,
327 const unsigned int timeout,
328 const UsbTransferCallback& callback) {
329 CHECK_DEVICE(callback, USB_TRANSFER_DISCONNECT,
330 scoped_refptr<net::IOBuffer>(), 0);
331
332 struct libusb_transfer* const transfer = libusb_alloc_transfer(0);
333 const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint;
334 libusb_fill_bulk_transfer(transfer, handle_, new_endpoint,
335 reinterpret_cast<uint8*>(buffer->data()), length,
336 HandleTransferCompletion, this, timeout);
337 BrowserThread::PostTask(
338 BrowserThread::FILE, FROM_HERE,
339 base::Bind(&UsbDeviceHandle::SubmitTransfer, this, transfer,
340 USB_TRANSFER_BULK, make_scoped_refptr(buffer), length,
341 callback));
342 }
343
344 void UsbDeviceHandle::InterruptTransfer(const UsbEndpointDirection direction,
345 const uint8 endpoint,
346 net::IOBuffer* buffer,
347 const size_t length,
348 const unsigned int timeout,
349 const UsbTransferCallback& callback) {
350 CHECK_DEVICE(callback, USB_TRANSFER_DISCONNECT,
351 scoped_refptr<net::IOBuffer>(), 0);
352
353 struct libusb_transfer* const transfer = libusb_alloc_transfer(0);
354 const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint;
355 libusb_fill_interrupt_transfer(
356 transfer, handle_, new_endpoint, reinterpret_cast<uint8*>(buffer->data()),
357 length, HandleTransferCompletion, this, timeout);
358 BrowserThread::PostTask(
359 BrowserThread::FILE, FROM_HERE,
360 base::Bind(&UsbDeviceHandle::SubmitTransfer, this, transfer,
361 USB_TRANSFER_INTERRUPT, make_scoped_refptr(buffer), length,
362 callback));
363 }
364
365 void UsbDeviceHandle::IsochronousTransfer(
366 const UsbEndpointDirection direction, const uint8 endpoint,
367 net::IOBuffer* buffer, const size_t length, const unsigned int packets,
368 const unsigned int packet_length, const unsigned int timeout,
369 const UsbTransferCallback& callback) {
370 CHECK_DEVICE(callback, USB_TRANSFER_DISCONNECT,
371 scoped_refptr<net::IOBuffer>(), 0);
372
373 const uint64 total_length = packets * packet_length;
374 CHECK(packets <= length && total_length <= length)
375 << "transfer length is too small";
376
377 struct libusb_transfer* const transfer = libusb_alloc_transfer(packets);
378 const uint8 new_endpoint = ConvertTransferDirection(direction) | endpoint;
379 libusb_fill_iso_transfer(transfer, handle_, new_endpoint,
380 reinterpret_cast<uint8*>(buffer->data()), length,
381 packets, HandleTransferCompletion, this, timeout);
382 libusb_set_iso_packet_lengths(transfer, packet_length);
383
384 BrowserThread::PostTask(
385 BrowserThread::FILE, FROM_HERE,
386 base::Bind(&UsbDeviceHandle::SubmitTransfer, this, transfer,
387 USB_TRANSFER_ISOCHRONOUS, make_scoped_refptr(buffer), length,
388 callback));
389 }
390
391 void UsbDeviceHandle::ResetDevice(const base::Callback<void(bool)>& callback) {
392 // Blocking operation. Run it on the FILE thread.
393 DCHECK(thread_checker_.CalledOnValidThread());
394 CHECK_DEVICE(callback, false);
395 callback.Run(libusb_reset_device(handle_) == 0);
396 }
397
398 void UsbDeviceHandle::InternalClose() {
399 DCHECK(thread_checker_.CalledOnValidThread());
400 if (handle_ == NULL) return;
401
402 // The following four lines makes this function re-enterable in case the
403 // callbacks call InternalClose again by, e.g., removing the UsbDevice from
404 // UsbService.
405 PlatformUsbDeviceHandle handle = handle_;
406 handle_ = NULL;
407 std::map<PlatformUsbTransferHandle, Transfer> transfers;
408 std::swap(transfers, transfers_);
409
410 // Cancel all the transfers before libusb_close.
411 // Otherwise the callback will not be invoked.
412 for (std::map<PlatformUsbTransferHandle, Transfer>::iterator it =
413 transfers.begin();
414 it != transfers.end(); it++) {
415 it->first->user_data = NULL;
416 it->second.callback
417 .Run(USB_TRANSFER_DISCONNECT, scoped_refptr<net::IOBuffer>(), 0);
418 }
419 transfers_.clear();
420 libusb_close(handle);
421 }
422
423 void UsbDeviceHandle::SubmitTransfer(PlatformUsbTransferHandle handle,
424 UsbTransferType transfer_type,
425 net::IOBuffer* buffer, const size_t length,
426 const UsbTransferCallback& callback) {
427 DCHECK(thread_checker_.CalledOnValidThread());
428 // This check must be done after the lock.
429 if (!handle_) return;
430
431 Transfer transfer;
432 transfer.transfer_type = transfer_type;
433 transfer.buffer = buffer;
434 transfer.length = length;
435 transfer.callback = callback;
436
437 transfers_[handle] = transfer;
438 libusb_submit_transfer(handle);
439 }
OLDNEW
« no previous file with comments | « chrome/browser/usb/usb_device_handle.h ('k') | chrome/browser/usb/usb_interface.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698