resolved conflicts for merge of 13081c69 to master

Change-Id: I631e466fc53e9b0f4a39f24f959b4ae9b626bdda
diff --git a/fastboot/engine.c b/fastboot/engine.c
index 994eeae..a1b6539 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.c
@@ -520,6 +520,8 @@
     int status = 0;
 
     a = action_list;
+    if (!a)
+        return status;
     resp[FB_RESPONSE_SZ] = 0;
 
     double start = -1;
@@ -556,3 +558,8 @@
     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
     return status;
 }
+
+int fb_queue_is_empty(void)
+{
+    return (action_list == NULL);
+}
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index af61f88..b96d93b 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -58,6 +58,7 @@
 static const char *cmdline = 0;
 static int wipe_data = 0;
 static unsigned short vendor_id = 0;
+static int long_listing = 0;
 
 static unsigned base_addr = 0x10000000;
 
@@ -167,9 +168,10 @@
     if(info->ifc_class != 0xff) return -1;
     if(info->ifc_subclass != 0x42) return -1;
     if(info->ifc_protocol != 0x03) return -1;
-    // require matching serial number if a serial number is specified
+    // require matching serial number or device path if requested
     // at the command line with the -s option.
-    if (serial && strcmp(serial, info->serial_number) != 0) return -1;
+    if (serial && (strcmp(serial, info->serial_number) != 0 &&
+                   strcmp(serial, info->device_path) != 0)) return -1;
     return 0;
 }
 
@@ -183,8 +185,16 @@
         if (!serial[0]) {
             serial = "????????????";
         }
-        // output compatible with "adb devices"
-        printf("%s\tfastboot\n", serial);
+        if (!long_listing) {
+            // output compatible with "adb devices"
+            printf("%s\tfastboot\n", serial);
+        } else {
+            char* device_path = info->device_path;
+            if (!device_path[0]) {
+                device_path = "????????????";
+            }
+            printf("%s\t%s\tfastboot\n", serial, device_path);
+        }
     }
 
     return -1;
@@ -238,7 +248,9 @@
             "\n"
             "options:\n"
             "  -w                                       erase userdata and cache\n"
-            "  -s <serial number>                       specify device serial number\n"
+            "  -s <specific device>                     specify device serial number\n"
+            "                                           or path to device port\n"
+            "  -l                                       with \"devices\", lists device paths\n"
             "  -p <product>                             specify product name\n"
             "  -c <cmdline>                             override kernel commandline\n"
             "  -i <vendor id>                           specify a custom USB vendor id\n"
@@ -571,6 +583,7 @@
     int wants_wipe = 0;
     int wants_reboot = 0;
     int wants_reboot_bootloader = 0;
+    int wants_device_list = 0;
     void *data;
     unsigned sz;
     unsigned page_size = 2048;
@@ -582,11 +595,6 @@
         return 1;
     }
 
-    if (!strcmp(*argv, "devices")) {
-        list_devices();
-        return 0;
-    }
-
     if (!strcmp(*argv, "help")) {
         usage();
         return 0;
@@ -612,6 +620,9 @@
             require(2);
             serial = argv[1];
             skip(2);
+        } else if(!strcmp(*argv, "-l")) {
+            long_listing = 1;
+            skip(1);
         } else if(!strcmp(*argv, "-p")) {
             require(2);
             product = argv[1];
@@ -630,6 +641,9 @@
                 die("invalid vendor id '%s'", argv[1]);
             vendor_id = (unsigned short)val;
             skip(2);
+        } else if (!strcmp(*argv, "devices")) {
+            skip(1);
+            wants_device_list = 1;
         } else if(!strcmp(*argv, "getvar")) {
             require(2);
             fb_queue_display(argv[1], argv[1]);
@@ -725,6 +739,9 @@
         }
     }
 
+    if (wants_device_list)
+        list_devices();
+
     if (wants_wipe) {
         fb_queue_erase("userdata");
         fb_queue_erase("cache");
@@ -735,6 +752,9 @@
         fb_queue_command("reboot-bootloader", "rebooting into bootloader");
     }
 
+    if (fb_queue_is_empty())
+        return 0;
+
     usb = open_device();
 
     status = fb_execute_queue(usb);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 7d56ecb..c249a8f 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -53,6 +53,7 @@
 void fb_queue_download(const char *name, void *data, unsigned size);
 void fb_queue_notice(const char *notice);
 int fb_execute_queue(usb_handle *usb);
+int fb_queue_is_empty(void);
 
 /* util stuff */
 void die(const char *fmt, ...);
diff --git a/fastboot/usb.h b/fastboot/usb.h
index df9efde..d504ee2 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -53,6 +53,7 @@
     unsigned char writable;
 
     char serial_number[256];
+    char device_path[256];
 };
 
 typedef int (*ifc_match_func)(usb_ifc_info *ifc);
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index 83c6de9..b7a9ca3 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.c
@@ -32,6 +32,7 @@
 #include <string.h>
 
 #include <sys/ioctl.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <dirent.h>
 #include <fcntl.h>
@@ -107,6 +108,9 @@
     int in, out;
     unsigned i;
     unsigned e;
+    
+    struct stat st;
+    int result;
 
     if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
         return -1;
@@ -134,7 +138,6 @@
         // Keep it short enough because some bootloaders are borked if the URB len is > 255
         // 128 is too big by 1.
         __u16 buffer[127];
-        int result;
 
         memset(buffer, 0, sizeof(buffer));
 
@@ -158,6 +161,42 @@
         }
     }
 
+    /* We need to get a path that represents a particular port on a particular
+     * hub.  We are passed an fd that was obtained by opening an entry under
+     * /dev/bus/usb.  Unfortunately, the names of those entries change each
+     * time devices are plugged and unplugged.  So how to get a repeatable
+     * path?  udevadm provided the inspiration.  We can get the major and
+     * minor of the device file, read the symlink that can be found here:
+     *   /sys/dev/char/<major>:<minor>
+     * and then use the last element of that path.  As a concrete example, I
+     * have an Android device at /dev/bus/usb/001/027 so working with bash:
+     *   $ ls -l /dev/bus/usb/001/027
+     *   crw-rw-r-- 1 root plugdev 189, 26 Apr  9 11:03 /dev/bus/usb/001/027
+     *   $ ls -l /sys/dev/char/189:26
+     *   lrwxrwxrwx 1 root root 0 Apr  9 11:03 /sys/dev/char/189:26 ->
+     *           ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3
+     * So our device_path would be 1-4.2.3 which says my device is connected
+     * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per
+     * http://www.linux-usb.org/FAQ.html).
+     */
+    info.device_path[0] = '\0';
+    result = fstat(fd, &st);
+    if (!result && S_ISCHR(st.st_mode)) {
+        char cdev[128];
+        char link[256];
+        char *slash;
+        ssize_t link_len;
+        snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d",
+                 major(st.st_rdev), minor(st.st_rdev));
+        link_len = readlink(cdev, link, sizeof(link) - 1);
+        if (link_len > 0) {
+            link[link_len] = '\0';
+            slash = strrchr(link, '/');
+            if (slash)
+                snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1);
+        }
+    }
+
     for(i = 0; i < cfg->bNumInterfaces; i++) {
         if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE))
             return -1;
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c
index cbce9bd..1548ba8 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.c
@@ -264,6 +264,7 @@
     SInt32 score;
     HRESULT result;
     UInt8 serialIndex;
+    UInt32 locationId;
 
     // Create an intermediate plugin.
     kr = IOCreatePlugInInterfaceForService(device,
@@ -322,6 +323,13 @@
         goto error;
     }
 
+    kr = (*dev)->GetLocationID(dev, &locationId);
+    if (kr != 0) {
+        ERR("GetLocationId");
+        goto error;
+    }
+    snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId);
+
     kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
 
     if (serialIndex > 0) {
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c
index 99027cc..7aa36b2 100644
--- a/fastboot/usb_windows.c
+++ b/fastboot/usb_windows.c
@@ -311,6 +311,8 @@
         info.serial_number[0] = 0;
     }
 
+    info.device_path[0] = 0;
+
     if (callback(&info) == 0) {
         return 1;
     }