Wait for device to disappear after reboot-bootloader.

(Linux only for now) With fastboot reading serial numbers from sysfs, it had
become possible for a fastboot command issued immediately after rebooting
the bootloader to fail, because sysfs still thought the device was online.
To prevent this, after reboot-bootloader we wait for the device to disconnect.

Also made usb_read and usb_write fail immediately if the descriptor has been
closed; this prevents an incorrect error message ("Bad file descriptor")
when errors from fb_getvar are ignored (e.g., by fb_format_supported).

Also removed unused fd param from filter_usb_device, and simplified logic
in usb_write by using do/while instead of a special case for len == 0.

Change-Id: I799b857eab411fd8ad25f5777fc61c685152ea86
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 1189e1f..f339988 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,7 +18,7 @@
 
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
   $(LOCAL_PATH)/../../extras/ext4_utils
-LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c
+LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c
 LOCAL_MODULE := fastboot
 LOCAL_MODULE_TAGS := debug
 
@@ -69,7 +69,7 @@
 
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := usbtest.c usb_linux.c
+LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c
 LOCAL_MODULE := usbtest
 include $(BUILD_HOST_EXECUTABLE)
 endif
diff --git a/fastboot/engine.c b/fastboot/engine.c
index 8d46991..84ea984 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.c
@@ -36,7 +36,6 @@
 #include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -48,34 +47,13 @@
 
 #define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
 
-double now()
-{
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
-}
-
-char *mkmsg(const char *fmt, ...)
-{
-    char buf[256];
-    char *s;
-    va_list ap;
-
-    va_start(ap, fmt);
-    vsprintf(buf, fmt, ap);
-    va_end(ap);
-
-    s = strdup(buf);
-    if (s == 0) die("out of memory");
-    return s;
-}
-
 #define OP_DOWNLOAD   1
 #define OP_COMMAND    2
 #define OP_QUERY      3
 #define OP_NOTICE     4
 #define OP_FORMAT     5
 #define OP_DOWNLOAD_SPARSE 6
+#define OP_WAIT_FOR_DISCONNECT 7
 
 typedef struct Action Action;
 
@@ -587,6 +565,11 @@
     a->data = (void*) notice;
 }
 
+void fb_queue_wait_for_disconnect(void)
+{
+    queue_action(OP_WAIT_FOR_DISCONNECT, "");
+}
+
 int fb_execute_queue(usb_handle *usb)
 {
     Action *a;
@@ -628,6 +611,8 @@
             status = fb_download_data_sparse(usb, a->data);
             status = a->func(a, status, status ? fb_get_error() : "");
             if (status) break;
+        } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
+            usb_wait_for_disconnect(usb);
         } else {
             die("bogus action");
         }
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index 447b257..70b838f 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -30,7 +30,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
@@ -82,17 +81,6 @@
 unsigned tags_offset    = 0x00000100;
 
 
-void die(const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    fprintf(stderr,"error: ");
-    vfprintf(stderr, fmt, ap);
-    fprintf(stderr,"\n");
-    va_end(ap);
-    exit(1);
-}
-
 void get_my_path(char *path);
 
 char *find_item(const char *item, const char *product)
@@ -1031,6 +1019,7 @@
         fb_queue_reboot();
     } else if (wants_reboot_bootloader) {
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
+        fb_queue_wait_for_disconnect();
     }
 
     if (fb_queue_is_empty())
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index c1b2964..976c397 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -58,10 +58,13 @@
 void fb_queue_command(const char *cmd, const char *msg);
 void fb_queue_download(const char *name, void *data, unsigned size);
 void fb_queue_notice(const char *notice);
+void fb_queue_wait_for_disconnect(void);
 int fb_execute_queue(usb_handle *usb);
 int fb_queue_is_empty(void);
 
 /* util stuff */
+double now();
+char *mkmsg(const char *fmt, ...);
 void die(const char *fmt, ...);
 
 /* Current product */
diff --git a/fastboot/usb.h b/fastboot/usb.h
index d504ee2..17cf0a9 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -62,6 +62,6 @@
 int usb_close(usb_handle *h);
 int usb_read(usb_handle *h, void *_data, int len);
 int usb_write(usb_handle *h, const void *_data, int len);
-
+int usb_wait_for_disconnect(usb_handle *h);
 
 #endif
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index 9153c8d..f2ce226 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.c
@@ -50,10 +50,17 @@
 #endif
 #include <asm/byteorder.h>
 
+#include "fastboot.h"
 #include "usb.h"
 
 #define MAX_RETRIES 5
 
+/* Timeout in seconds for usb_wait_for_disconnect.
+ * It doesn't usually take long for a device to disconnect (almost always
+ * under 2 seconds) but we'll time out after 3 seconds just in case.
+ */
+#define WAIT_FOR_DISCONNECT_TIMEOUT  3
+
 #ifdef TRACE_USB
 #define DBG1(x...) fprintf(stderr, x)
 #define DBG(x...) fprintf(stderr, x)
@@ -103,7 +110,7 @@
     return 0;
 }
 
-static int filter_usb_device(int fd, char* sysfs_name,
+static int filter_usb_device(char* sysfs_name,
                              char *ptr, int len, int writable,
                              ifc_match_func callback,
                              int *ept_in_id, int *ept_out_id, int *ifc_id)
@@ -308,7 +315,7 @@
 
             n = read(fd, desc, sizeof(desc));
 
-            if(filter_usb_device(fd, de->d_name, desc, n, writable, callback,
+            if(filter_usb_device(de->d_name, desc, n, writable, callback,
                                  &in, &out, &ifc) == 0) {
                 usb = calloc(1, sizeof(usb_handle));
                 strcpy(usb->fname, devname);
@@ -340,26 +347,11 @@
     struct usbdevfs_bulktransfer bulk;
     int n;
 
-    if(h->ep_out == 0) {
+    if(h->ep_out == 0 || h->desc == -1) {
         return -1;
     }
 
-    if(len == 0) {
-        bulk.ep = h->ep_out;
-        bulk.len = 0;
-        bulk.data = data;
-        bulk.timeout = 0;
-
-        n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
-        if(n != 0) {
-            fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
-                    n, errno, strerror(errno));
-            return -1;
-        }
-        return 0;
-    }
-
-    while(len > 0) {
+    do {
         int xfer;
         xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
 
@@ -378,7 +370,7 @@
         count += xfer;
         len -= xfer;
         data += xfer;
-    }
+    } while(len > 0);
 
     return count;
 }
@@ -390,7 +382,7 @@
     struct usbdevfs_bulktransfer bulk;
     int n, retry;
 
-    if(h->ep_in == 0) {
+    if(h->ep_in == 0 || h->desc == -1) {
         return -1;
     }
 
@@ -458,3 +450,18 @@
 {
     return find_usb_device("/sys/bus/usb/devices", callback);
 }
+
+/* Wait for the system to notice the device is gone, so that a subsequent
+ * fastboot command won't try to access the device before it's rebooted.
+ * Returns 0 for success, -1 for timeout.
+ */
+int usb_wait_for_disconnect(usb_handle *usb)
+{
+  double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;
+  while (now() < deadline) {
+    if (access(usb->fname, F_OK))
+      return 0;
+    usleep(50000);
+  }
+  return -1;
+}
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c
index 1548ba8..0f55e0d 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.c
@@ -467,6 +467,11 @@
     return 0;
 }
 
+int usb_wait_for_disconnect(usb_handle *usb) {
+    /* TODO: Punt for now */
+    return 0;
+}
+
 int usb_read(usb_handle *h, void *data, int len) {
     IOReturn result;
     UInt32 numBytes = len;
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c
index 7aa36b2..07f7be2 100644
--- a/fastboot/usb_windows.c
+++ b/fastboot/usb_windows.c
@@ -269,6 +269,11 @@
     return 0;
 }
 
+int usb_wait_for_disconnect(usb_handle *usb) {
+    /* TODO: Punt for now */
+    return 0;
+}
+
 int recognized_device(usb_handle* handle, ifc_match_func callback) {
     struct usb_ifc_info info;
     USB_DEVICE_DESCRIPTOR device_desc;
diff --git a/fastboot/util.c b/fastboot/util.c
new file mode 100644
index 0000000..f2bbd34
--- /dev/null
+++ b/fastboot/util.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/time.h>
+
+#include "fastboot.h"
+
+double now()
+{
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
+}
+
+char *mkmsg(const char *fmt, ...)
+{
+    char buf[256];
+    char *s;
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsprintf(buf, fmt, ap);
+    va_end(ap);
+
+    s = strdup(buf);
+    if (s == 0) die("out of memory");
+    return s;
+}
+
+void die(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    fprintf(stderr,"error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr,"\n");
+    va_end(ap);
+    exit(1);
+}