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

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

Powered by Google App Engine
This is Rietveld 408576698