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

Side by Side Diff: device/usb/usb_device_handle_usbfs.cc

Issue 1877503003: Replace libusb in the Linux/Chrome OS USB I/O path. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 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
OLDNEW
(Empty)
1 // Copyright 2016 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 "device/usb/usb_device_handle_usbfs.h"
6
7 #if defined(OS_ANDROID) && __ANDROID_API__ < 21
8 #include <linux/usb_ch9.h>
9 #else
10 #include <linux/usb/ch9.h>
11 #endif
12
13 #include <linux/usbdevice_fs.h>
14 #include <sys/ioctl.h>
15
16 #include <numeric>
17
18 #include "base/bind.h"
19 #include "base/cancelable_callback.h"
20 #include "base/logging.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/message_loop/message_pump_libevent.h"
23 #include "base/posix/eintr_wrapper.h"
24 #include "base/thread_task_runner_handle.h"
25 #include "base/threading/thread_restrictions.h"
26 #include "components/device_event_log/device_event_log.h"
27 #include "device/usb/usb_device_impl.h"
28 #include "net/base/io_buffer.h"
29
30 namespace device {
31
32 namespace {
33
34 uint8_t ConvertEndpointDirection(UsbEndpointDirection direction) {
35 switch (direction) {
36 case USB_DIRECTION_INBOUND:
37 return USB_DIR_IN;
38 case USB_DIRECTION_OUTBOUND:
39 return USB_DIR_OUT;
40 }
41 NOTREACHED();
42 return 0;
juncai 2016/04/13 00:22:32 Noticed that USB_DIR_OUT has value 0: Maybe can re
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 At this point something terrible has gone wrong so
43 }
44
45 uint8_t ConvertRequestType(UsbDeviceHandle::TransferRequestType request_type) {
46 switch (request_type) {
47 case UsbDeviceHandle::STANDARD:
48 return USB_TYPE_STANDARD;
49 case UsbDeviceHandle::CLASS:
50 return USB_TYPE_CLASS;
51 case UsbDeviceHandle::VENDOR:
52 return USB_TYPE_VENDOR;
53 case UsbDeviceHandle::RESERVED:
54 return USB_TYPE_RESERVED;
55 }
56 NOTREACHED();
57 return 0;
juncai 2016/04/13 00:22:32 ditto. USB_TYPE_STANDARD has value 0.
58 }
59
60 uint8_t ConvertRecipient(UsbDeviceHandle::TransferRecipient recipient) {
61 switch (recipient) {
62 case UsbDeviceHandle::DEVICE:
63 return USB_RECIP_DEVICE;
64 case UsbDeviceHandle::INTERFACE:
65 return USB_RECIP_INTERFACE;
66 case UsbDeviceHandle::ENDPOINT:
67 return USB_RECIP_ENDPOINT;
68 case UsbDeviceHandle::OTHER:
69 return USB_RECIP_OTHER;
70 }
71 NOTREACHED();
72 return 0;
juncai 2016/04/13 00:22:32 ditto. USB_RECIP_DEVICE has value 0.
73 }
74
75 scoped_refptr<net::IOBuffer> BuildControlTransferBuffer(
76 UsbEndpointDirection direction,
77 UsbDeviceHandle::TransferRequestType request_type,
78 UsbDeviceHandle::TransferRecipient recipient,
79 uint8_t request,
80 uint16_t value,
81 uint16_t index,
82 scoped_refptr<net::IOBuffer> original_buffer,
83 size_t length) {
84 scoped_refptr<net::IOBuffer> new_buffer(
85 new net::IOBuffer(length + sizeof(struct usb_ctrlrequest)));
juncai 2016/04/13 00:22:32 "struct" may be omitted.
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Done.
86 struct usb_ctrlrequest* setup =
juncai 2016/04/13 00:22:32 ditto.
87 reinterpret_cast<struct usb_ctrlrequest*>(new_buffer->data());
juncai 2016/04/13 00:22:32 ditto.
88 setup->bRequestType = ConvertEndpointDirection(direction) |
89 ConvertRequestType(request_type) |
90 ConvertRecipient(recipient);
91 setup->bRequest = request;
92 setup->wValue = value;
93 setup->wIndex = index;
94 setup->wLength = length;
95 memcpy(new_buffer->data() + sizeof(struct usb_ctrlrequest),
juncai 2016/04/13 00:22:32 ditto.
96 original_buffer->data(), length);
97 return new_buffer;
98 }
99
100 uint8_t ConvertTransferType(UsbTransferType type) {
101 switch (type) {
102 case USB_TRANSFER_CONTROL:
103 return USBDEVFS_URB_TYPE_CONTROL;
104 case USB_TRANSFER_ISOCHRONOUS:
105 return USBDEVFS_URB_TYPE_ISO;
106 case USB_TRANSFER_BULK:
107 return USBDEVFS_URB_TYPE_BULK;
108 case USB_TRANSFER_INTERRUPT:
109 return USBDEVFS_URB_TYPE_INTERRUPT;
110 }
111 NOTREACHED();
112 return 0;
juncai 2016/04/13 00:22:32 USBDEVFS_URB_TYPE_ISO has value 0. Maybe can retur
113 }
114
115 UsbTransferStatus ConvertTransferResult(int rc) {
116 switch (rc) {
117 case 0:
118 return USB_TRANSFER_COMPLETED;
119 case EPIPE:
120 return USB_TRANSFER_STALLED;
121 case ENODEV:
122 case ESHUTDOWN:
123 return USB_TRANSFER_DISCONNECT;
124 default:
125 // TODO(reillyg): Add a specific error message whenever one of the cases
126 // above fails to match.
127 USB_LOG(ERROR) << "Unknown system error: "
128 << logging::SystemErrorCodeToString(rc);
129 return USB_TRANSFER_ERROR;
130 }
131 }
132
133 } // namespace
134
135 class UsbDeviceHandleUsbfs::FileThreadHelper
136 : public base::MessagePumpLibevent::Watcher,
137 public base::MessageLoop::DestructionObserver {
138 public:
139 FileThreadHelper(int fd,
140 const scoped_refptr<UsbDeviceHandleUsbfs>& device_handle,
141 const scoped_refptr<base::SequencedTaskRunner>& task_runner);
juncai 2016/04/13 00:22:32 Maybe the above two parameters can just use: scope
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 There's been some discussion of this on the mailin
142 ~FileThreadHelper() override;
143
144 static void Start(scoped_ptr<FileThreadHelper> self);
145
146 // base::MessagePumpLibevent::Watcher:
147 void OnFileCanReadWithoutBlocking(int fd) override;
148 void OnFileCanWriteWithoutBlocking(int fd) override;
149
150 // base::MessageLoop::DestructionObserver:
151 void WillDestroyCurrentMessageLoop() override;
152
153 private:
154 int fd_;
155 scoped_refptr<UsbDeviceHandleUsbfs> device_handle_;
156 scoped_refptr<base::SequencedTaskRunner> task_runner_;
157 base::MessagePumpLibevent::FileDescriptorWatcher file_watcher_;
158 base::ThreadChecker thread_checker_;
159
160 DISALLOW_COPY_AND_ASSIGN(FileThreadHelper);
161 };
162
163 UsbDeviceHandleUsbfs::FileThreadHelper::FileThreadHelper(
164 int fd,
165 const scoped_refptr<UsbDeviceHandleUsbfs>& device_handle,
166 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
167 : fd_(fd), device_handle_(device_handle), task_runner_(task_runner) {}
168
169 UsbDeviceHandleUsbfs::FileThreadHelper::~FileThreadHelper() {
170 DCHECK(thread_checker_.CalledOnValidThread());
171 base::MessageLoop::current()->RemoveDestructionObserver(this);
172 }
173
174 // static
175 void UsbDeviceHandleUsbfs::FileThreadHelper::Start(
176 scoped_ptr<FileThreadHelper> self) {
177 base::ThreadRestrictions::AssertIOAllowed();
178 self->thread_checker_.DetachFromThread();
179
180 // Linux indicates that URBs are available to reap by marking the file
181 // descriptor writable.
182 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
183 self->fd_, true, base::MessageLoopForIO::WATCH_WRITE,
184 &self->file_watcher_, self.get())) {
185 USB_LOG(ERROR) << "Failed to start watching device file descriptor.";
186 }
187
188 // |self| is now owned by the current message loop.
189 base::MessageLoop::current()->AddDestructionObserver(self.release());
190 }
191
192 void UsbDeviceHandleUsbfs::FileThreadHelper::OnFileCanReadWithoutBlocking(
193 int fd) {
194 NOTREACHED(); // Only listening for writability.
195 }
196
197 void UsbDeviceHandleUsbfs::FileThreadHelper::OnFileCanWriteWithoutBlocking(
198 int fd) {
199 DCHECK(thread_checker_.CalledOnValidThread());
200 DCHECK_EQ(fd_, fd);
201
202 const size_t MAX_URBS_PER_EVENT = 10;
203 std::vector<usbdevfs_urb*> urbs;
204 urbs.reserve(MAX_URBS_PER_EVENT);
205 for (size_t i = 0; i < MAX_URBS_PER_EVENT; ++i) {
206 usbdevfs_urb* urb;
207 int rc = HANDLE_EINTR(ioctl(fd_, USBDEVFS_REAPURBNDELAY, &urb));
208 if (rc) {
209 if (errno == EAGAIN)
210 break;
211 USB_PLOG(DEBUG) << "Failed to reap urbs";
212 } else {
213 urbs.push_back(urb);
214 }
215 }
216
217 task_runner_->PostTask(
218 FROM_HERE,
219 base::Bind(&UsbDeviceHandleUsbfs::ReapedUrbs, device_handle_, urbs));
220 }
221
222 void UsbDeviceHandleUsbfs::FileThreadHelper::WillDestroyCurrentMessageLoop() {
223 DCHECK(thread_checker_.CalledOnValidThread());
224 delete this;
225 }
226
227 struct UsbDeviceHandleUsbfs::Transfer {
228 Transfer() = delete;
229 Transfer(scoped_refptr<net::IOBuffer> buffer,
230 const TransferCallback& callback);
231 Transfer(scoped_refptr<net::IOBuffer> buffer,
232 const IsochronousTransferCallback& callback);
233 ~Transfer();
234
235 void* operator new(std::size_t size, size_t number_of_iso_packets);
236
237 scoped_refptr<net::IOBuffer> control_transfer_buffer;
238 scoped_refptr<net::IOBuffer> buffer;
239 TransferCallback callback;
240 IsochronousTransferCallback isoc_callback;
241 base::CancelableClosure timeout_closure;
242 bool timed_out = false;
243
244 // The |urb| field must be the last in the struct so that the extra space
245 // allocated by the overridden new function above extends the length of its
246 // |iso_frame_desc| field.
247 usbdevfs_urb urb;
248
249 private:
250 DISALLOW_COPY_AND_ASSIGN(Transfer);
251 };
252
253 UsbDeviceHandleUsbfs::Transfer::Transfer(scoped_refptr<net::IOBuffer> buffer,
254 const TransferCallback& callback)
255 : buffer(buffer), callback(callback) {
256 memset(&urb, 0, sizeof(urb));
257 urb.usercontext = this;
258 urb.buffer = buffer->data();
259 }
260
261 UsbDeviceHandleUsbfs::Transfer::Transfer(
262 scoped_refptr<net::IOBuffer> buffer,
263 const IsochronousTransferCallback& callback)
264 : buffer(buffer), isoc_callback(callback) {
265 memset(&urb, 0, sizeof(urb) +
266 sizeof(usbdevfs_iso_packet_desc) * urb.number_of_packets);
267 urb.usercontext = this;
268 urb.buffer = buffer->data();
269 }
270
271 UsbDeviceHandleUsbfs::Transfer::~Transfer() = default;
272
273 void* UsbDeviceHandleUsbfs::Transfer::operator new(
274 std::size_t size,
275 size_t number_of_iso_packets) {
276 void* p = ::operator new(
277 size + sizeof(usbdevfs_iso_packet_desc) * number_of_iso_packets);
278 Transfer* transfer = static_cast<Transfer*>(p);
279 transfer->urb.number_of_packets = number_of_iso_packets;
280 return p;
281 }
282
283 UsbDeviceHandleUsbfs::UsbDeviceHandleUsbfs(
284 scoped_refptr<UsbDevice> device,
285 base::ScopedFD fd,
286 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
287 : device_(device),
288 fd_(std::move(fd)),
289 blocking_task_runner_(blocking_task_runner) {
290 DCHECK(device_);
291 DCHECK(fd_.is_valid());
292 DCHECK(blocking_task_runner_);
293
294 task_runner_ = base::ThreadTaskRunnerHandle::Get();
295
296 scoped_ptr<FileThreadHelper> helper(
297 new FileThreadHelper(fd_.get(), this, task_runner_));
298 helper_ = helper.get();
299 blocking_task_runner_->PostTask(
300 FROM_HERE, base::Bind(&FileThreadHelper::Start, base::Passed(&helper)));
301 }
302
303 scoped_refptr<UsbDevice> UsbDeviceHandleUsbfs::GetDevice() const {
304 return device_;
305 }
306
307 void UsbDeviceHandleUsbfs::Close() {
308 // On the |task_runner_| thread check |device_| to see if the handle is
309 // closed. On the |blocking_task_runner_| thread check |fd_.is_valid()| to
310 // see if the handle is closed.
311 DCHECK(device_);
312 device_ = nullptr;
313 blocking_task_runner_->PostTask(
314 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::CloseBlocking, this));
315 }
316
317 void UsbDeviceHandleUsbfs::SetConfiguration(int configuration_value,
318 const ResultCallback& callback) {
319 // USBDEVFS_SETCONFIGURATION synchronously issues a SET_CONFIGURATION request
320 // to
juncai 2016/04/13 00:22:32 rearrange the comments here.
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Fixed.
321 // the device so it must be performed on a thread where it is okay to block.
322 blocking_task_runner_->PostTask(
323 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::SetConfigurationBlocking,
324 this, configuration_value, callback));
325 }
326
327 void UsbDeviceHandleUsbfs::ClaimInterface(int interface_number,
328 const ResultCallback& callback) {
329 if (!device_) {
330 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
331 return;
332 }
333
334 if (ContainsKey(interfaces_, interface_number)) {
juncai 2016/04/13 00:22:32 Add #include "base/stl_util.h" for ContainsKey().
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Done.
335 USB_LOG(DEBUG) << "Interface " << interface_number << " already claimed.";
336 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
337 return;
338 }
339
340 // It appears safe to assume that this ioctl will not block.
341 int rc = HANDLE_EINTR(
342 ioctl(fd_.get(), USBDEVFS_CLAIMINTERFACE, &interface_number));
343 if (rc) {
344 USB_PLOG(DEBUG) << "Failed to claim interface " << interface_number;
345 } else {
346 interfaces_[interface_number].alternate_setting = 0;
347 RefreshEndpointInfo();
348 }
349 task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
350 }
351
352 void UsbDeviceHandleUsbfs::ReleaseInterface(int interface_number,
353 const ResultCallback& callback) {
354 // USBDEVFS_RELEASEINTERFACE may issue a SET_INTERFACE request to the
355 // device to restore alternate setting 0 so it must be performed on a thread
356 // where it is okay to block.
357 blocking_task_runner_->PostTask(
358 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::ReleaseInterfaceBlocking,
359 this, interface_number, callback));
360 }
361
362 void UsbDeviceHandleUsbfs::SetInterfaceAlternateSetting(
363 int interface_number,
364 int alternate_setting,
365 const ResultCallback& callback) {
366 // USBDEVFS_SETINTERFACE is synchronous because it issues a SET_INTERFACE
367 // request to the device so it must be performed on a thread where it is okay
368 // to block.
369 blocking_task_runner_->PostTask(
370 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::SetInterfaceBlocking, this,
371 interface_number, alternate_setting, callback));
372 }
373
374 void UsbDeviceHandleUsbfs::ResetDevice(const ResultCallback& callback) {
375 // USBDEVFS_RESET is synchronous because it waits for the port to be reset
376 // and the device re-enumerated so it must be performed on a thread where it
377 // is okay to block.
378 blocking_task_runner_->PostTask(
379 FROM_HERE,
380 base::Bind(&UsbDeviceHandleUsbfs::ResetDeviceBlocking, this, callback));
381 }
382
383 void UsbDeviceHandleUsbfs::ClearHalt(uint8_t endpoint_address,
384 const ResultCallback& callback) {
385 // USBDEVFS_CLEAR_HALT is synchronous because it issues a CLEAR_FEATURE
386 // request to the device so it must be performed on a thread where it is okay
387 // to block.
388 blocking_task_runner_->PostTask(
389 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::ClearHaltBlocking, this,
390 endpoint_address, callback));
391 }
392
393 void UsbDeviceHandleUsbfs::ControlTransfer(UsbEndpointDirection direction,
394 TransferRequestType request_type,
395 TransferRecipient recipient,
396 uint8_t request,
397 uint16_t value,
398 uint16_t index,
399 scoped_refptr<net::IOBuffer> buffer,
400 size_t length,
401 unsigned int timeout,
402 const TransferCallback& callback) {
403 if (!device_) {
404 task_runner_->PostTask(
405 FROM_HERE, base::Bind(callback, USB_TRANSFER_DISCONNECT, nullptr, 0));
406 return;
407 }
408
409 scoped_ptr<Transfer> transfer(new (0) Transfer(buffer, callback));
410 transfer->control_transfer_buffer =
411 BuildControlTransferBuffer(direction, request_type, recipient, request,
412 value, index, buffer, length);
413 transfer->urb.type = USBDEVFS_URB_TYPE_CONTROL;
414 transfer->urb.endpoint = 0;
415 transfer->urb.buffer = transfer->control_transfer_buffer->data();
416 transfer->urb.buffer_length = 8 + length;
417
418 // USBDEVFS_SUBMITURB appears to be non-blocking as completion is reported
419 // by USBDEVFS_REAPURBNDELAY.
420 int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SUBMITURB, &transfer->urb));
421 if (rc) {
422 rc = logging::GetLastSystemErrorCode();
423 USB_PLOG(DEBUG) << "Failed to submit control transfer";
424 task_runner_->PostTask(
425 FROM_HERE, base::Bind(callback, ConvertTransferResult(rc), nullptr, 0));
426 } else {
427 SetUpTimeoutCallback(transfer.get(), timeout);
428 transfers_.push_back(std::move(transfer));
429 }
430 }
431
432 void UsbDeviceHandleUsbfs::IsochronousTransferIn(
433 uint8_t endpoint_number,
434 const std::vector<uint32_t>& packet_lengths,
435 unsigned int timeout,
436 const IsochronousTransferCallback& callback) {
437 uint8_t endpoint_address = USB_DIR_IN | endpoint_number;
438 size_t total_length =
439 std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
440 IsochronousTransferInternal(endpoint_address, new net::IOBuffer(total_length),
441 total_length, packet_lengths, timeout, callback);
juncai 2016/04/13 00:22:32 |total_length| is size_t, but the parameter at Iso
Reilly Grant (use Gerrit) 2016/04/13 20:21:12 Changed the parameter type to size_t.
442 }
443
444 void UsbDeviceHandleUsbfs::IsochronousTransferOut(
445 uint8_t endpoint_number,
446 scoped_refptr<net::IOBuffer> buffer,
447 const std::vector<uint32_t>& packet_lengths,
448 unsigned int timeout,
449 const IsochronousTransferCallback& callback) {
450 uint8_t endpoint_address = USB_DIR_OUT | endpoint_number;
451 size_t total_length =
452 std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
453 IsochronousTransferInternal(endpoint_address, buffer, total_length,
juncai 2016/04/13 00:22:32 ditto.
454 packet_lengths, timeout, callback);
455 }
456
457 void UsbDeviceHandleUsbfs::GenericTransfer(UsbEndpointDirection direction,
458 uint8_t endpoint_number,
459 scoped_refptr<net::IOBuffer> buffer,
460 size_t length,
461 unsigned int timeout,
462 const TransferCallback& callback) {
463 if (!device_) {
464 task_runner_->PostTask(
465 FROM_HERE, base::Bind(callback, USB_TRANSFER_DISCONNECT, nullptr, 0));
466 return;
467 }
468
469 uint8_t endpoint_address =
470 ConvertEndpointDirection(direction) | endpoint_number;
471 auto it = endpoints_.find(endpoint_address);
472 if (it == endpoints_.end()) {
473 USB_LOG(USER) << "Endpoint address " << (int)endpoint_address
juncai 2016/04/13 00:22:32 use static_cast<int> instead of (int)?
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Done.
474 << " is not part of a claimed interface.";
475 task_runner_->PostTask(
476 FROM_HERE, base::Bind(callback, USB_TRANSFER_ERROR, nullptr, 0));
477 return;
478 }
479
480 scoped_ptr<Transfer> transfer(new (0) Transfer(buffer, callback));
481 transfer->urb.endpoint = endpoint_address;
482 transfer->urb.buffer_length = length;
483 transfer->urb.type = ConvertTransferType(it->second.type);
484
485 // USBDEVFS_SUBMITURB appears to be non-blocking as completion is reported
486 // by USBDEVFS_REAPURBNDELAY. This code assumes a recent kernel that can
487 // accept arbitrarily large transfer requests, hopefully also using a scatter-
488 // gather list.
489 int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SUBMITURB, &transfer->urb));
490 if (rc) {
491 rc = logging::GetLastSystemErrorCode();
492 USB_PLOG(DEBUG) << "Failed to submit transfer";
493 task_runner_->PostTask(
494 FROM_HERE, base::Bind(callback, ConvertTransferResult(rc), nullptr, 0));
495 } else {
496 SetUpTimeoutCallback(transfer.get(), timeout);
497 transfers_.push_back(std::move(transfer));
498 }
499 }
500
501 const UsbInterfaceDescriptor* UsbDeviceHandleUsbfs::FindInterfaceByEndpoint(
502 uint8_t endpoint_address) {
503 auto it = endpoints_.find(endpoint_address);
504 if (it != endpoints_.end())
505 return it->second.interface;
506 return nullptr;
507 }
508
509 UsbDeviceHandleUsbfs::~UsbDeviceHandleUsbfs() {
510 DCHECK(!device_) << "Handle must be closed before it is destroyed.";
511 }
512
513 void UsbDeviceHandleUsbfs::CloseBlocking() {
514 fd_.reset(-1);
515 delete helper_;
516 }
517
518 void UsbDeviceHandleUsbfs::SetConfigurationBlocking(
519 int configuration_value,
520 const ResultCallback& callback) {
521 if (!fd_.is_valid()) {
522 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
523 return;
524 }
525
526 int rc = HANDLE_EINTR(
527 ioctl(fd_.get(), USBDEVFS_SETCONFIGURATION, &configuration_value));
528 if (rc)
529 USB_PLOG(DEBUG) << "Failed to set configuration " << configuration_value;
530 task_runner_->PostTask(
531 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::SetConfigurationComplete,
532 this, configuration_value, rc == 0, callback));
533 }
534
535 void UsbDeviceHandleUsbfs::SetConfigurationComplete(
536 int configuration_value,
537 bool success,
538 const ResultCallback& callback) {
539 if (success && device_) {
540 #if !defined(OS_ANDROID)
541 static_cast<UsbDeviceImpl*>(device_.get())
542 ->ActiveConfigurationChanged(configuration_value);
543 #endif
544 // TODO(reillyg): If all interfaces are unclaimed before a new configuration
545 // is set then this will do nothing. Investigate.
546 RefreshEndpointInfo();
547 }
548 callback.Run(success);
549 }
550
551 void UsbDeviceHandleUsbfs::ReleaseInterfaceBlocking(
552 int interface_number,
553 const ResultCallback& callback) {
554 if (!fd_.is_valid()) {
555 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
556 return;
557 }
558
559 int rc = HANDLE_EINTR(
560 ioctl(fd_.get(), USBDEVFS_RELEASEINTERFACE, &interface_number));
561 if (rc) {
562 USB_PLOG(DEBUG) << "Failed to release interface " << interface_number;
563 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
564 } else {
565 task_runner_->PostTask(
566 FROM_HERE, base::Bind(&UsbDeviceHandleUsbfs::ReleaseInterfaceComplete,
567 this, interface_number, callback));
568 }
569 }
570
571 void UsbDeviceHandleUsbfs::ReleaseInterfaceComplete(
572 int interface_number,
573 const ResultCallback& callback) {
574 auto it = interfaces_.find(interface_number);
575 DCHECK(it != interfaces_.end());
576 interfaces_.erase(it);
577 RefreshEndpointInfo();
578 callback.Run(true);
579 }
580
581 void UsbDeviceHandleUsbfs::SetInterfaceBlocking(
582 int interface_number,
583 int alternate_setting,
584 const ResultCallback& callback) {
585 if (!fd_.is_valid()) {
586 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
587 return;
588 }
589
590 struct usbdevfs_setinterface cmd = {0};
juncai 2016/04/13 00:22:32 "struct" may be omitted.
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Done.
591 cmd.interface = interface_number;
592 cmd.altsetting = alternate_setting;
593 int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SETINTERFACE, &cmd));
594 if (rc) {
595 USB_PLOG(DEBUG) << "Failed to set interface " << interface_number
596 << " to alternate setting " << alternate_setting;
597 }
598 task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
599 }
600
601 void UsbDeviceHandleUsbfs::ResetDeviceBlocking(const ResultCallback& callback) {
602 if (!fd_.is_valid()) {
603 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
604 return;
605 }
606
607 // TODO(reillyg): libusb releases interfaces before and then reclaims
608 // interfaces after a reset. We should probably do this too or document that
609 // callers have to call ClaimInterface as well.
610 int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_RESET, nullptr));
611 if (rc)
612 USB_PLOG(DEBUG) << "Failed to reset the device";
613 task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
614 }
615
616 void UsbDeviceHandleUsbfs::ClearHaltBlocking(uint8_t endpoint_address,
617 const ResultCallback& callback) {
618 if (!fd_.is_valid()) {
619 task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
620 return;
621 }
622
623 int tmp_endpoint = endpoint_address;
624 int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_CLEAR_HALT, &tmp_endpoint));
625 if (rc)
juncai 2016/04/13 00:22:32 use {} here for this if statement.
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Done.
626 USB_PLOG(DEBUG) << "Failed to clear the stall condition on endpoint "
627 << endpoint_address;
628 task_runner_->PostTask(FROM_HERE, base::Bind(callback, rc == 0));
629 }
630
631 void UsbDeviceHandleUsbfs::IsochronousTransferInternal(
632 uint8_t endpoint_address,
633 scoped_refptr<net::IOBuffer> buffer,
634 int length,
635 const std::vector<uint32_t>& packet_lengths,
636 unsigned int timeout,
637 const IsochronousTransferCallback& callback) {
638 if (!device_) {
639 ReportIsochronousError(packet_lengths, callback, USB_TRANSFER_DISCONNECT);
640 return;
641 }
642
643 auto it = endpoints_.find(endpoint_address);
644 if (it == endpoints_.end()) {
645 USB_LOG(USER) << "Endpoint address " << (int)endpoint_address
juncai 2016/04/13 00:22:32 use static_cast<int> instead of (int)?
Reilly Grant (use Gerrit) 2016/04/13 20:21:13 Done.
646 << " is not part of a claimed interface.";
647 ReportIsochronousError(packet_lengths, callback, USB_TRANSFER_ERROR);
648 return;
649 }
650
651 scoped_ptr<Transfer> transfer(new (packet_lengths.size())
652 Transfer(buffer, callback));
653 transfer->urb.type = USBDEVFS_URB_TYPE_ISO;
654 transfer->urb.endpoint = endpoint_address;
655 transfer->urb.buffer_length = length;
656
657 for (size_t i = 0; i < packet_lengths.size(); ++i)
658 transfer->urb.iso_frame_desc[i].length = packet_lengths[i];
659
660 // USBDEVFS_SUBMITURB appears to be non-blocking as completion is reported
661 // by USBDEVFS_REAPURBNDELAY. This code assumes a recent kernel that can
662 // accept arbitrarily large transfer requests, hopefully also using a scatter-
663 // gather list.
664 int rc = HANDLE_EINTR(ioctl(fd_.get(), USBDEVFS_SUBMITURB, &transfer->urb));
665 if (rc) {
666 rc = logging::GetLastSystemErrorCode();
667 USB_PLOG(DEBUG) << "Failed to submit transfer";
668 ReportIsochronousError(packet_lengths, callback, ConvertTransferResult(rc));
669 } else {
670 SetUpTimeoutCallback(transfer.get(), timeout);
671 transfers_.push_back(std::move(transfer));
672 }
673 }
674
675 void UsbDeviceHandleUsbfs::ReapedUrbs(std::vector<usbdevfs_urb*> urbs) {
676 for (const auto& urb : urbs) {
677 Transfer* this_transfer = static_cast<Transfer*>(urb->usercontext);
678 DCHECK_EQ(urb, &this_transfer->urb);
679 auto it = std::find_if(
680 transfers_.begin(), transfers_.end(),
681 [this_transfer](const scoped_ptr<Transfer>& transfer) -> bool {
682 return transfer.get() == this_transfer;
683 });
684 DCHECK(it != transfers_.end());
685 scoped_ptr<Transfer> transfer = std::move(*it);
686 transfers_.erase(it);
687 TransferComplete(std::move(transfer));
688 }
689 }
690
691 void UsbDeviceHandleUsbfs::TransferComplete(scoped_ptr<Transfer> transfer) {
692 if (transfer->timed_out)
693 return;
694
695 // The transfer will soon be freed. Cancel the timeout callback so that the
696 // raw pointer it holds to |transfer| is not used.
697 transfer->timeout_closure.Cancel();
698
699 if (transfer->urb.type == USBDEVFS_URB_TYPE_ISO) {
700 std::vector<IsochronousPacket> packets(transfer->urb.number_of_packets);
701 for (size_t i = 0; i < packets.size(); ++i) {
702 packets[i].length = transfer->urb.iso_frame_desc[i].length;
703 packets[i].transferred_length =
704 transfer->urb.iso_frame_desc[i].actual_length;
705 packets[i].status = ConvertTransferResult(
706 transfer->urb.status == 0 ? transfer->urb.iso_frame_desc[i].status
707 : transfer->urb.status);
708 }
709
710 transfer->isoc_callback.Run(transfer->buffer, packets);
711 } else {
712 if (transfer->urb.status == 0 &&
713 transfer->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
714 // Copy the result of the control transfer back into the original buffer.
715 memcpy(transfer->buffer->data(),
716 transfer->control_transfer_buffer->data() + 8,
717 transfer->urb.actual_length);
718 }
719
720 transfer->callback.Run(ConvertTransferResult(-transfer->urb.status),
721 transfer->buffer, transfer->urb.actual_length);
722 }
723 }
724
725 void UsbDeviceHandleUsbfs::RefreshEndpointInfo() {
726 endpoints_.clear();
727
728 const UsbConfigDescriptor* config = device_->GetActiveConfiguration();
729 if (!config)
730 return;
731
732 for (const auto& entry : interfaces_) {
733 auto interface_it = std::find_if(
734 config->interfaces.begin(), config->interfaces.end(),
735 [entry](const UsbInterfaceDescriptor& interface) {
736 uint8_t interface_number = entry.first;
737 uint8_t alternate_setting = entry.second.alternate_setting;
738 return interface.interface_number == interface_number &&
739 interface.alternate_setting == alternate_setting;
740 });
741 DCHECK(interface_it != config->interfaces.end());
742
743 for (const auto& endpoint : interface_it->endpoints) {
744 EndpointInfo& info = endpoints_[endpoint.address];
745 info.type = endpoint.transfer_type;
746 info.interface = &*interface_it;
747 }
748 }
749 }
750
751 void UsbDeviceHandleUsbfs::ReportIsochronousError(
752 const std::vector<uint32_t> packet_lengths,
753 const UsbDeviceHandle::IsochronousTransferCallback& callback,
754 UsbTransferStatus status) {
755 std::vector<UsbDeviceHandle::IsochronousPacket> packets(
756 packet_lengths.size());
757 for (size_t i = 0; i < packet_lengths.size(); ++i) {
758 packets[i].length = packet_lengths[i];
759 packets[i].transferred_length = 0;
760 packets[i].status = status;
761 }
762 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr, packets));
763 }
764
765 void UsbDeviceHandleUsbfs::SetUpTimeoutCallback(Transfer* transfer,
766 unsigned int timeout) {
767 if (timeout > 0) {
768 transfer->timeout_closure.Reset(
769 base::Bind(&UsbDeviceHandleUsbfs::TransferTimedOut, transfer));
770 task_runner_->PostDelayedTask(FROM_HERE,
771 transfer->timeout_closure.callback(),
772 base::TimeDelta::FromMilliseconds(timeout));
773 }
774 }
775
776 // static
777 void UsbDeviceHandleUsbfs::TransferTimedOut(Transfer* transfer) {
778 // |transfer| must stay in |transfers_| as it is still being processed by the
779 // kernel and will be reaped later. Just tell the caller that the timeout
780 // elapsed.
781 transfer->timed_out = true;
782 if (transfer->urb.type == USBDEVFS_URB_TYPE_ISO) {
783 std::vector<IsochronousPacket> packets(transfer->urb.number_of_packets);
784 for (size_t i = 0; i < packets.size(); ++i) {
785 packets[i].length = transfer->urb.iso_frame_desc[i].length;
786 packets[i].transferred_length = 0;
787 packets[i].status = USB_TRANSFER_TIMEOUT;
788 }
789 transfer->isoc_callback.Run(transfer->buffer, packets);
790 } else {
791 transfer->callback.Run(USB_TRANSFER_TIMEOUT, transfer->buffer, 0);
792 }
793 }
794
795 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698