diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp index 37bb304ea..b7fd5ed33 100644 --- a/fastboot/usb_linux.cpp +++ b/fastboot/usb_linux.cpp @@ -404,34 +404,84 @@ ssize_t LinuxUsbTransport::Write(const void* _data, size_t len) { unsigned char *data = (unsigned char*) _data; unsigned count = 0; - struct usbdevfs_bulktransfer bulk; - int n; + struct usbdevfs_urb urb[2] = {}; + bool pending[2] = {}; if (handle_->ep_out == 0 || handle_->desc == -1) { return -1; } - do { - int xfer; - xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; + auto submit_urb = [&](size_t i) { + int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len; - bulk.ep = handle_->ep_out; - bulk.len = xfer; - bulk.data = data; - bulk.timeout = ms_timeout_; + urb[i].type = USBDEVFS_URB_TYPE_BULK; + urb[i].endpoint = handle_->ep_out; + urb[i].buffer_length = xfer; + urb[i].buffer = data; + urb[i].usercontext = (void *)i; - n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk); - if(n != xfer) { - DBG("ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); - return -1; + int n = ioctl(handle_->desc, USBDEVFS_SUBMITURB, &urb[i]); + if (n != 0) { + DBG("ioctl(USBDEVFS_SUBMITURB) failed\n"); + return false; } + pending[i] = true; count += xfer; len -= xfer; data += xfer; - } while(len > 0); + return true; + }; + + auto reap_urb = [&](size_t i) { + while (pending[i]) { + struct usbdevfs_urb *urbp; + int res = ioctl(handle_->desc, USBDEVFS_REAPURB, &urbp); + if (res != 0) { + DBG("ioctl(USBDEVFS_REAPURB) failed\n"); + return false; + } + size_t done = (size_t)urbp->usercontext; + if (done >= 2 || !pending[done]) { + DBG("unexpected urb\n"); + return false; + } + if (urbp->status != 0 || urbp->actual_length != urbp->buffer_length) { + DBG("urb returned error\n"); + return false; + } + pending[done] = false; + } + return true; + }; + + if (!submit_urb(0)) { + return -1; + } + while (len > 0) { + if (!submit_urb(1)) { + return -1; + } + if (!reap_urb(0)) { + return -1; + } + if (len <= 0) { + if (!reap_urb(1)) { + return -1; + } + return count; + } + if (!submit_urb(0)) { + return -1; + } + if (!reap_urb(1)) { + return -1; + } + } + if (!reap_urb(0)) { + return -1; + } return count; }