fastboot: Add ability to specify device path
For manufacturing and testing, there is a need to talk to
whatever device is connected to a given port on the host. This
change modifies fastboot's "-s" option to take either a serial
number or a device path. The device paths of the connected
devices can be listed using "fastboot -l devices" whose output
will resemble:
016B75D60A00600D usb:2-5 fastboot
AD3C12020173 usb:1-4.3 fastboot
The second column lists the device paths. If the -l option is
not given, the output from "fastboot devices" will be the same as
it used to be (i.e. the paths will not be printed).
Finally, note that the format of the device paths are platform
dependent. The example above is from Linux. On OS-X, the paths
will be "usb:" followed by hex digits. For Windows, the device
paths will be printed as "????????????" and the -s option will
not be able to select a device until someone implements the
underlying functionality in usb_windows.c.
Change-Id: I1f01b8f47acd32edb0ac18db107316a2c923bbde
Signed-off-by: Scott Anderson <saa@android.com>
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c
index 1ba87e6..19be51e 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>
@@ -108,6 +109,9 @@
unsigned i;
unsigned e;
+ struct stat st;
+ int result;
+
if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
return -1;
dev = (void*) ptr;
@@ -132,7 +136,6 @@
if (dev->iSerialNumber) {
struct usbdevfs_ctrltransfer ctrl;
__u16 buffer[128];
- int result;
memset(buffer, 0, sizeof(buffer));
@@ -155,6 +158,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;