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/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;
+}