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>
This commit is contained in:
Scott Anderson 2012-04-06 12:39:30 -07:00
parent 5383476727
commit 13081c6915
7 changed files with 89 additions and 11 deletions

View File

@ -480,6 +480,8 @@ int fb_execute_queue(usb_handle *usb)
int status = 0;
a = action_list;
if (!a)
return status;
resp[FB_RESPONSE_SZ] = 0;
double start = -1;
@ -516,3 +518,8 @@ int fb_execute_queue(usb_handle *usb)
fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
return status;
}
int fb_queue_is_empty(void)
{
return (action_list == NULL);
}

View File

@ -58,6 +58,7 @@ static const char *product = 0;
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;
@ -163,9 +164,10 @@ int match_fastboot(usb_ifc_info *info)
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;
}
@ -179,8 +181,16 @@ int list_devices_callback(usb_ifc_info *info)
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;
@ -234,7 +244,9 @@ void usage(void)
"\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"
@ -567,6 +579,7 @@ int main(int argc, char **argv)
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;
@ -578,11 +591,6 @@ int main(int argc, char **argv)
return 1;
}
if (!strcmp(*argv, "devices")) {
list_devices();
return 0;
}
if (!strcmp(*argv, "help")) {
usage();
return 0;
@ -608,6 +616,9 @@ int main(int argc, char **argv)
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];
@ -626,6 +637,9 @@ int main(int argc, char **argv)
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]);
@ -721,6 +735,9 @@ int main(int argc, char **argv)
}
}
if (wants_device_list)
list_devices();
if (wants_wipe) {
fb_queue_erase("userdata");
fb_queue_format("userdata", 1);
@ -733,6 +750,9 @@ int main(int argc, char **argv)
fb_queue_command("reboot-bootloader", "rebooting into bootloader");
}
if (fb_queue_is_empty())
return 0;
usb = open_device();
status = fb_execute_queue(usb);

View File

@ -53,6 +53,7 @@ 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);
int fb_execute_queue(usb_handle *usb);
int fb_queue_is_empty(void);
/* util stuff */
void die(const char *fmt, ...);

View File

@ -53,6 +53,7 @@ struct usb_ifc_info
unsigned char writable;
char serial_number[256];
char device_path[256];
};
typedef int (*ifc_match_func)(usb_ifc_info *ifc);

View File

@ -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 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable,
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 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable,
if (dev->iSerialNumber) {
struct usbdevfs_ctrltransfer ctrl;
__u16 buffer[128];
int result;
memset(buffer, 0, sizeof(buffer));
@ -155,6 +158,42 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable,
}
}
/* 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;

View File

@ -264,6 +264,7 @@ static int try_device(io_service_t device, usb_handle *handle) {
SInt32 score;
HRESULT result;
UInt8 serialIndex;
UInt32 locationId;
// Create an intermediate plugin.
kr = IOCreatePlugInInterfaceForService(device,
@ -322,6 +323,13 @@ static int try_device(io_service_t device, usb_handle *handle) {
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) {

View File

@ -311,6 +311,8 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) {
info.serial_number[0] = 0;
}
info.device_path[0] = 0;
if (callback(&info) == 0) {
return 1;
}