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:
parent
5383476727
commit
13081c6915
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue