Add derived UsbTransport class with USB reset method

For testing there needs to be a way to simulate unplugging and
replugging a device. This change adds support for a USB reset
method that does this.

Also add timeouts, so USB reads/writes don't block forever
on an unresponsive device.

Test: glinux, fastboot tool still works
Test: Reset confirmed working via wireshark Linux URB captures

Change-Id: I7213a2395d4ef1c0238810e4929ab966e78c8b55
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 5b44468..96eb934 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -52,6 +52,13 @@
     char device_path[256];
 };
 
+class UsbTransport : public Transport {
+    // Resets the underlying transport.  Returns 0 on success.
+    // This effectively simulates unplugging and replugging
+    virtual int Reset() = 0;
+};
+
 typedef int (*ifc_match_func)(usb_ifc_info *ifc);
 
-Transport* usb_open(ifc_match_func callback);
+// 0 is non blocking
+UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 386dd30..9b779dd 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -52,7 +52,7 @@
 
 using namespace std::chrono_literals;
 
-#define MAX_RETRIES 5
+#define MAX_RETRIES 2
 
 /* Timeout in seconds for usb_wait_for_disconnect.
  * It doesn't usually take long for a device to disconnect (almost always
@@ -91,18 +91,21 @@
     unsigned char ep_out;
 };
 
-class LinuxUsbTransport : public Transport {
+class LinuxUsbTransport : public UsbTransport {
   public:
-    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
+        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
     ~LinuxUsbTransport() override = default;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
     int WaitForDisconnect() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
+    const uint32_t ms_timeout_;
 
     DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport);
 };
@@ -402,7 +405,7 @@
         bulk.ep = handle_->ep_out;
         bulk.len = xfer;
         bulk.data = data;
-        bulk.timeout = 0;
+        bulk.timeout = ms_timeout_;
 
         n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
         if(n != xfer) {
@@ -436,7 +439,7 @@
         bulk.ep = handle_->ep_in;
         bulk.len = xfer;
         bulk.data = data;
-        bulk.timeout = 0;
+        bulk.timeout = ms_timeout_;
         retry = 0;
 
         do {
@@ -447,7 +450,7 @@
             if (n < 0) {
                 DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
                 if (++retry > MAX_RETRIES) return -1;
-                std::this_thread::sleep_for(1s);
+                std::this_thread::sleep_for(100ms);
             }
         } while (n < 0);
 
@@ -477,10 +480,19 @@
     return 0;
 }
 
-Transport* usb_open(ifc_match_func callback)
-{
+int LinuxUsbTransport::Reset() {
+    int ret = 0;
+    // We reset the USB connection
+    if ((ret = ioctl(handle_->desc, USBDEVFS_RESET, 0))) {
+        return ret;
+    }
+
+    return 0;
+}
+
+UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
     std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
-    return handle ? new LinuxUsbTransport(std::move(handle)) : nullptr;
+    return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr;
 }
 
 /* Wait for the system to notice the device is gone, so that a subsequent
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index e95b049..442dea5 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -65,17 +65,20 @@
     unsigned int zero_mask;
 };
 
-class OsxUsbTransport : public Transport {
+class OsxUsbTransport : public UsbTransport {
   public:
-    OsxUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
+    OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout)
+        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
     ~OsxUsbTransport() override = default;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
+    const uint32_t ms_timeout_;
 
     DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);
 };
@@ -456,7 +459,7 @@
  * Definitions of this file's public functions.
  */
 
-Transport* usb_open(ifc_match_func callback) {
+UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
     std::unique_ptr<usb_handle> handle;
 
     if (init_usb(callback, &handle) < 0) {
@@ -464,7 +467,7 @@
         return nullptr;
     }
 
-    return new OsxUsbTransport(std::move(handle));
+    return new OsxUsbTransport(std::move(handle), timeout_ms);
 }
 
 int OsxUsbTransport::Close() {
@@ -472,6 +475,17 @@
     return 0;
 }
 
+int OsxUsbTransport::Reset() {
+    IOReturn result = (*handle_->interface)->ResetDevice(handle_->interface);
+
+    if (result == 0) {
+        return 0;
+    } else {
+        ERR("usb_reset failed with status %x\n", result);
+        return -1;
+    }
+}
+
 ssize_t OsxUsbTransport::Read(void* data, size_t len) {
     IOReturn result;
     UInt32 numBytes = len;
@@ -494,7 +508,9 @@
         return -1;
     }
 
-    result = (*handle_->interface)->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);
+    result = (*handle_->interface)
+                     ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,
+                                  USB_TRANSACTION_TIMEOUT, USB_TRANSACTION_TIMEOUT);
 
     if (result == 0) {
         return (int) numBytes;
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 0e5fba1..8c60a71 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -66,7 +66,7 @@
     std::string interface_name;
 };
 
-class WindowsUsbTransport : public Transport {
+class WindowsUsbTransport : public UsbTransport {
   public:
     WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
     ~WindowsUsbTransport() override = default;
@@ -74,6 +74,7 @@
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
     int Close() override;
+    int Reset() override;
 
   private:
     std::unique_ptr<usb_handle> handle_;
@@ -261,6 +262,12 @@
     return 0;
 }
 
+int WindowsUsbTransport::Reset() {
+    DBG("usb_reset currently unsupported\n\n");
+    // TODO, this is a bit complicated since it is using ADB
+    return -1;
+}
+
 int recognized_device(usb_handle* handle, ifc_match_func callback) {
     struct usb_ifc_info info;
     USB_DEVICE_DESCRIPTOR device_desc;
@@ -366,8 +373,7 @@
     return handle;
 }
 
-Transport* usb_open(ifc_match_func callback)
-{
+UsbTransport* usb_open(ifc_match_func callback, uint32_t) {
     std::unique_ptr<usb_handle> handle = find_usb_device(callback);
     return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
 }