diff options
Diffstat (limited to 'fastboot')
-rw-r--r-- | fastboot/Android.mk | 5 | ||||
-rw-r--r-- | fastboot/engine.c | 238 | ||||
-rw-r--r-- | fastboot/fastboot.c | 98 | ||||
-rw-r--r-- | fastboot/fastboot.h | 5 | ||||
-rw-r--r-- | fastboot/fs.c | 56 | ||||
-rw-r--r-- | fastboot/fs.h | 12 | ||||
-rw-r--r-- | fastboot/usb.h | 2 | ||||
-rw-r--r-- | fastboot/usb_linux.c | 226 | ||||
-rw-r--r-- | fastboot/usb_osx.c | 5 | ||||
-rw-r--r-- | fastboot/usb_windows.c | 5 | ||||
-rw-r--r-- | fastboot/util.c | 69 |
11 files changed, 378 insertions, 343 deletions
diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 1189e1f..05ddf2a 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -18,9 +18,10 @@ include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ $(LOCAL_PATH)/../../extras/ext4_utils -LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c +LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c util.c fs.c LOCAL_MODULE := fastboot LOCAL_MODULE_TAGS := debug +LOCAL_CFLAGS += -std=gnu99 ifeq ($(HOST_OS),linux) LOCAL_SRC_FILES += usb_linux.c util_linux.c @@ -69,7 +70,7 @@ $(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) include $(CLEAR_VARS) -LOCAL_SRC_FILES := usbtest.c usb_linux.c +LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c LOCAL_MODULE := usbtest include $(BUILD_HOST_EXECUTABLE) endif diff --git a/fastboot/engine.c b/fastboot/engine.c index b07e742..0fab703 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -27,7 +27,7 @@ */ #include "fastboot.h" -#include "make_ext4fs.h" +#include "fs.h" #include <errno.h> #include <stdio.h> @@ -36,7 +36,6 @@ #include <stdbool.h> #include <string.h> #include <sys/stat.h> -#include <sys/time.h> #include <sys/types.h> #include <unistd.h> @@ -48,34 +47,12 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) -double now() -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; -} - -char *mkmsg(const char *fmt, ...) -{ - char buf[256]; - char *s; - va_list ap; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - - s = strdup(buf); - if (s == 0) die("out of memory"); - return s; -} - #define OP_DOWNLOAD 1 #define OP_COMMAND 2 #define OP_QUERY 3 #define OP_NOTICE 4 -#define OP_FORMAT 5 -#define OP_DOWNLOAD_SPARSE 6 +#define OP_DOWNLOAD_SPARSE 5 +#define OP_WAIT_FOR_DISCONNECT 6 typedef struct Action Action; @@ -101,14 +78,7 @@ static Action *action_list = 0; static Action *action_last = 0; -struct image_data { - long long partition_size; - long long image_size; // real size of image file - void *buffer; -}; -void generate_ext4_image(struct image_data *image); -void cleanup_image(struct image_data *image); int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) { @@ -124,24 +94,6 @@ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) return fb_command_response(usb, cmd, response); } -struct generator { - char *fs_type; - - /* generate image and return it as image->buffer. - * size of the buffer returned as image->image_size. - * - * image->partition_size specifies what is the size of the - * file partition we generate image for. - */ - void (*generate)(struct image_data *image); - - /* it cleans the buffer allocated during image creation. - * this function probably does free() or munmap(). - */ - void (*cleanup)(struct image_data *image); -} generators[] = { - { "ext4", generate_ext4_image, cleanup_image } -}; /* Return true if this partition is supported by the fastboot format command. * It is also used to determine if we should first erase a partition before @@ -152,8 +104,7 @@ struct generator { */ int fb_format_supported(usb_handle *usb, const char *partition) { - char response[FB_RESPONSE_SZ+1]; - struct generator *generator = NULL; + char response[FB_RESPONSE_SZ + 1] = {0,}; int status; unsigned int i; @@ -162,18 +113,7 @@ int fb_format_supported(usb_handle *usb, const char *partition) return 0; } - for (i = 0; i < ARRAY_SIZE(generators); i++) { - if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { - generator = &generators[i]; - break; - } - } - - if (generator) { - return 1; - } - - return 0; + return !!fs_get_generator(response); } static int cb_default(Action *a, int status, char *resp) @@ -227,163 +167,6 @@ void fb_queue_erase(const char *ptn) a->msg = mkmsg("erasing '%s'", ptn); } -/* Loads file content into buffer. Returns NULL on error. */ -static void *load_buffer(int fd, off_t size) -{ - void *buffer; - -#ifdef USE_MINGW - ssize_t count = 0; - - // mmap is more efficient but mingw does not support it. - // In this case we read whole image into memory buffer. - buffer = malloc(size); - if (!buffer) { - perror("malloc"); - return NULL; - } - - lseek(fd, 0, SEEK_SET); - while(count < size) { - ssize_t actually_read = read(fd, (char*)buffer+count, size-count); - - if (actually_read == 0) { - break; - } - if (actually_read < 0) { - if (errno == EINTR) { - continue; - } - perror("read"); - free(buffer); - return NULL; - } - - count += actually_read; - } -#else - buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (buffer == MAP_FAILED) { - perror("mmap"); - return NULL; - } -#endif - - return buffer; -} - -void cleanup_image(struct image_data *image) -{ -#ifdef USE_MINGW - free(image->buffer); -#else - munmap(image->buffer, image->image_size); -#endif -} - -void generate_ext4_image(struct image_data *image) -{ - int fd; - struct stat st; - - fd = fileno(tmpfile()); - make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL); - - fstat(fd, &st); - image->image_size = st.st_size; - image->buffer = load_buffer(fd, st.st_size); - - close(fd); -} - -int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) -{ - const char *partition = a->cmd; - char response[FB_RESPONSE_SZ+1]; - int status = 0; - struct image_data image; - struct generator *generator = NULL; - int fd; - unsigned i; - char cmd[CMD_SIZE]; - - status = fb_getvar(usb, response, "partition-type:%s", partition); - if (status) { - if (skip_if_not_supported) { - fprintf(stderr, - "Erase successful, but not automatically formatting.\n"); - fprintf(stderr, - "Can't determine partition type.\n"); - return 0; - } - fprintf(stderr,"FAILED (%s)\n", fb_get_error()); - return status; - } - - for (i = 0; i < ARRAY_SIZE(generators); i++) { - if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { - generator = &generators[i]; - break; - } - } - if (!generator) { - if (skip_if_not_supported) { - fprintf(stderr, - "Erase successful, but not automatically formatting.\n"); - fprintf(stderr, - "File system type %s not supported.\n", response); - return 0; - } - fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n", - response); - return -1; - } - - status = fb_getvar(usb, response, "partition-size:%s", partition); - if (status) { - if (skip_if_not_supported) { - fprintf(stderr, - "Erase successful, but not automatically formatting.\n"); - fprintf(stderr, "Unable to get partition size\n."); - return 0; - } - fprintf(stderr,"FAILED (%s)\n", fb_get_error()); - return status; - } - image.partition_size = strtoll(response, (char **)NULL, 16); - - generator->generate(&image); - if (!image.buffer) { - fprintf(stderr,"Cannot generate image.\n"); - return -1; - } - - // Following piece of code is similar to fb_queue_flash() but executes - // actions directly without queuing - fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024); - status = fb_download_data(usb, image.buffer, image.image_size); - if (status) goto cleanup; - - fprintf(stderr, "writing '%s'...\n", partition); - snprintf(cmd, sizeof(cmd), "flash:%s", partition); - status = fb_command(usb, cmd); - if (status) goto cleanup; - -cleanup: - generator->cleanup(&image); - - return status; -} - -void fb_queue_format(const char *partition, int skip_if_not_supported) -{ - Action *a; - - a = queue_action(OP_FORMAT, partition); - a->data = (void*)skip_if_not_supported; - a->msg = mkmsg("formatting '%s' partition", partition); -} - void fb_queue_flash(const char *ptn, void *data, unsigned sz) { Action *a; @@ -573,6 +356,11 @@ void fb_queue_notice(const char *notice) a->data = (void*) notice; } +void fb_queue_wait_for_disconnect(void) +{ + queue_action(OP_WAIT_FOR_DISCONNECT, ""); +} + int fb_execute_queue(usb_handle *usb) { Action *a; @@ -606,14 +394,12 @@ int fb_execute_queue(usb_handle *usb) if (status) break; } else if (a->op == OP_NOTICE) { fprintf(stderr,"%s\n",(char*)a->data); - } else if (a->op == OP_FORMAT) { - status = fb_format(a, usb, (int)a->data); - status = a->func(a, status, status ? fb_get_error() : ""); - if (status) break; } else if (a->op == OP_DOWNLOAD_SPARSE) { status = fb_download_data_sparse(usb, a->data); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; + } else if (a->op == OP_WAIT_FOR_DISCONNECT) { + usb_wait_for_disconnect(usb); } else { die("bogus action"); } diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index f186c93..4fd1a8e 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -30,7 +30,6 @@ #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> #include <stdbool.h> #include <stdint.h> #include <string.h> @@ -50,6 +49,7 @@ #include <zipfile/zipfile.h> #include "fastboot.h" +#include "fs.h" #ifndef O_BINARY #define O_BINARY 0 @@ -106,17 +106,6 @@ static struct { {"system.img", "system.sig", "system", false}, }; -void die(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr,"error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr,"\n"); - va_end(ap); - exit(1); -} - void get_my_path(char *path); char *find_item(const char *item, const char *product) @@ -503,7 +492,13 @@ static int setup_requirement_line(char *name) for(n = 0; n < count; n++) { out[n] = strdup(strip(val[n])); - if (out[n] == 0) return -1; + if (out[n] == 0) { + for(size_t i = 0; i < n; ++i) { + free((char*) out[i]); + } + free(out); + return -1; + } } fb_queue_require(prod, name, invert, n, out); @@ -628,10 +623,13 @@ static int load_buf_fd(usb_handle *usb, int fd, void *data; int64_t limit; + sz64 = file_size(fd); if (sz64 < 0) { return -1; } + + lseek(fd, 0, SEEK_SET); limit = get_sparse_limit(usb, sz64); if (limit) { struct sparse_file **s = load_sparse_files(fd, limit); @@ -878,6 +876,73 @@ static int64_t parse_num(const char *arg) return num; } +void fb_perform_format(const char *partition, int skip_if_not_supported) +{ + char pType[FB_RESPONSE_SZ + 1], pSize[FB_RESPONSE_SZ + 1]; + unsigned int limit = INT_MAX; + struct fastboot_buffer buf; + const char *errMsg = NULL; + const struct fs_generator *gen; + uint64_t pSz; + int status; + int fd; + + if (target_sparse_limit > 0 && target_sparse_limit < limit) + limit = target_sparse_limit; + if (sparse_limit > 0 && sparse_limit < limit) + limit = sparse_limit; + + status = fb_getvar(usb, pType, "partition-type:%s", partition); + if (status) { + errMsg = "Can't determine partition type.\n"; + goto failed; + } + + status = fb_getvar(usb, pSize, "partition-size:%s", partition); + if (status) { + errMsg = "Unable to get partition size\n"; + goto failed; + } + + gen = fs_get_generator(pType); + if (!gen) { + if (skip_if_not_supported) { + fprintf(stderr, "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, "File system type %s not supported.\n", pType); + return; + } + fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType); + return; + } + + pSz = strtoll(pSize, (char **)NULL, 16); + + fd = fileno(tmpfile()); + if (fs_generator_generate(gen, fd, pSz)) { + close(fd); + fprintf(stderr, "Cannot generate image.\n"); + return; + } + + if (load_buf_fd(usb, fd, &buf)) { + fprintf(stderr, "Cannot read image.\n"); + close(fd); + return; + } + flash_buf(partition, &buf); + + return; + + +failed: + if (skip_if_not_supported) { + fprintf(stderr, "Erase successful, but not automatically formatting.\n"); + if (errMsg) + fprintf(stderr, "%s", errMsg); + } + fprintf(stderr,"FAILED (%s)\n", fb_get_error()); +} + int main(int argc, char **argv) { int wants_wipe = 0; @@ -1006,7 +1071,7 @@ int main(int argc, char **argv) if (erase_first && needs_erase(argv[1])) { fb_queue_erase(argv[1]); } - fb_queue_format(argv[1], 0); + fb_perform_format(argv[1], 0); skip(2); } else if(!strcmp(*argv, "signature")) { require(2); @@ -1094,14 +1159,15 @@ int main(int argc, char **argv) if (wants_wipe) { fb_queue_erase("userdata"); - fb_queue_format("userdata", 1); + fb_perform_format("userdata", 1); fb_queue_erase("cache"); - fb_queue_format("cache", 1); + fb_perform_format("cache", 1); } if (wants_reboot) { fb_queue_reboot(); } else if (wants_reboot_bootloader) { fb_queue_command("reboot-bootloader", "rebooting into bootloader"); + fb_queue_wait_for_disconnect(); } if (fb_queue_is_empty()) diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index c1b2964..c510a36 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -49,7 +49,7 @@ int fb_format_supported(usb_handle *usb, const char *partition); void fb_queue_flash(const char *ptn, void *data, unsigned sz); void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz); void fb_queue_erase(const char *ptn); -void fb_queue_format(const char *ptn, int skip_if_not_supported); +void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz); void fb_queue_require(const char *prod, const char *var, int invert, unsigned nvalues, const char **value); void fb_queue_display(const char *var, const char *prettyname); @@ -58,10 +58,13 @@ void fb_queue_reboot(void); 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); +void fb_queue_wait_for_disconnect(void); int fb_execute_queue(usb_handle *usb); int fb_queue_is_empty(void); /* util stuff */ +double now(); +char *mkmsg(const char *fmt, ...); void die(const char *fmt, ...); /* Current product */ diff --git a/fastboot/fs.c b/fastboot/fs.c new file mode 100644 index 0000000..6a1f9e6 --- /dev/null +++ b/fastboot/fs.c @@ -0,0 +1,56 @@ +#include "fastboot.h" +#include "make_ext4fs.h" +#include "fs.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sparse/sparse.h> +#include <unistd.h> + +#ifdef USE_MINGW +#include <fcntl.h> +#else +#include <sys/mman.h> +#endif + + + +static int generate_ext4_image(int fd, long long partSize) +{ + make_ext4fs_sparse_fd(fd, partSize, NULL, NULL); + + return 0; +} + +static const struct fs_generator { + + char *fs_type; //must match what fastboot reports for partition type + int (*generate)(int fd, long long partSize); //returns 0 or error value + +} generators[] = { + + { "ext4", generate_ext4_image} + +}; + +const struct fs_generator* fs_get_generator(const char* name) +{ + unsigned i; + + for (i = 0; i < sizeof(generators) / sizeof(*generators); i++) + if (!strcmp(generators[i].fs_type, name)) + return generators + i; + + return NULL; +} + +int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize) +{ + return gen->generate(tmpFileNo, partSize); +} diff --git a/fastboot/fs.h b/fastboot/fs.h new file mode 100644 index 0000000..65b9555 --- /dev/null +++ b/fastboot/fs.h @@ -0,0 +1,12 @@ +#ifndef _FS_H_ +#define _FH_H_ + +#include <stdint.h> + +struct fs_generator; + +const struct fs_generator* fs_get_generator(const char* name); +int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize); + +#endif + diff --git a/fastboot/usb.h b/fastboot/usb.h index d504ee2..17cf0a9 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -62,6 +62,6 @@ usb_handle *usb_open(ifc_match_func callback); int usb_close(usb_handle *h); int usb_read(usb_handle *h, void *_data, int len); int usb_write(usb_handle *h, const void *_data, int len); - +int usb_wait_for_disconnect(usb_handle *h); #endif diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index b7a9ca3..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) @@ -75,10 +82,18 @@ struct usb_handle unsigned char ep_out; }; +/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices. + * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'. + * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1'). + * The name must also start with a digit, to disallow '.' and '..' + */ static inline int badname(const char *name) { - while(*name) { - if(!isdigit(*name++)) return 1; + if (!isdigit(*name)) + return 1; + while(*++name) { + if(!isdigit(*name) && *name != '.' && *name != '-') + return 1; } return 0; } @@ -95,7 +110,8 @@ static int check(void *_desc, int len, unsigned type, int size) return 0; } -static int filter_usb_device(int fd, char *ptr, int len, int writable, +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) { @@ -131,69 +147,35 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, info.dev_protocol = dev->bDeviceProtocol; info.writable = writable; - // read device serial number (if there is one) - info.serial_number[0] = 0; - if (dev->iSerialNumber) { - struct usbdevfs_ctrltransfer ctrl; - // Keep it short enough because some bootloaders are borked if the URB len is > 255 - // 128 is too big by 1. - __u16 buffer[127]; - - memset(buffer, 0, sizeof(buffer)); - - ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; - ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; - ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber; - //language ID (en-us) for serial number string - ctrl.wIndex = 0x0409; - ctrl.wLength = sizeof(buffer); - ctrl.data = buffer; - ctrl.timeout = 50; - - result = ioctl(fd, USBDEVFS_CONTROL, &ctrl); - if (result > 0) { - int i; - // skip first word, and copy the rest to the serial string, changing shorts to bytes. - result /= 2; - for (i = 1; i < result; i++) - info.serial_number[i - 1] = buffer[i]; - info.serial_number[i - 1] = 0; - } - } + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name); - /* 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). + /* Read device serial number (if there is one). + * We read the serial number from sysfs, since it's faster and more + * reliable than issuing a control pipe read, and also won't + * cause problems for devices which don't like getting descriptor + * requests while they're in the middle of flashing. */ - 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); + info.serial_number[0] = '\0'; + if (dev->iSerialNumber) { + char path[80]; + int fd; + + snprintf(path, sizeof(path), + "/sys/bus/usb/devices/%s/serial", sysfs_name); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_RDONLY); + if (fd >= 0) { + int chars_read = read(fd, info.serial_number, + sizeof(info.serial_number) - 1); + close(fd); + + if (chars_read <= 0) + info.serial_number[0] = '\0'; + else if (info.serial_number[chars_read - 1] == '\n') { + // strip trailing newline + info.serial_number[chars_read - 1] = '\0'; + } } } @@ -241,14 +223,73 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, return -1; } +static int read_sysfs_string(const char *sysfs_name, const char *sysfs_node, + char* buf, int bufsize) +{ + char path[80]; + int fd, n; + + snprintf(path, sizeof(path), + "/sys/bus/usb/devices/%s/%s", sysfs_name, sysfs_node); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + + n = read(fd, buf, bufsize - 1); + close(fd); + + if (n < 0) + return -1; + + buf[n] = '\0'; + + return n; +} + +static int read_sysfs_number(const char *sysfs_name, const char *sysfs_node) +{ + char buf[16]; + int value; + + if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0) + return -1; + + if (sscanf(buf, "%d", &value) != 1) + return -1; + + return value; +} + +/* Given the name of a USB device in sysfs, get the name for the same + * device in devfs. Returns 0 for success, -1 for failure. + */ +static int convert_to_devfs_name(const char* sysfs_name, + char* devname, int devname_size) +{ + int busnum, devnum; + + busnum = read_sysfs_number(sysfs_name, "busnum"); + if (busnum < 0) + return -1; + + devnum = read_sysfs_number(sysfs_name, "devnum"); + if (devnum < 0) + return -1; + + snprintf(devname, devname_size, "/dev/bus/usb/%03d/%03d", busnum, devnum); + return 0; +} + static usb_handle *find_usb_device(const char *base, ifc_match_func callback) { usb_handle *usb = 0; - char busname[64], devname[64]; + char devname[64]; char desc[1024]; int n, in, out, ifc; - DIR *busdir, *devdir; + DIR *busdir; struct dirent *de; int fd; int writable; @@ -259,15 +300,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) while((de = readdir(busdir)) && (usb == 0)) { if(badname(de->d_name)) continue; - sprintf(busname, "%s/%s", base, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - -// DBG("[ scanning %s ]\n", busname); - while((de = readdir(devdir)) && (usb == 0)) { - - if(badname(de->d_name)) continue; - sprintf(devname, "%s/%s", busname, de->d_name); + if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) { // DBG("[ scanning %s ]\n", devname); writable = 1; @@ -282,7 +315,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) n = read(fd, desc, sizeof(desc)); - if(filter_usb_device(fd, 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); @@ -301,7 +334,6 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback) close(fd); } } - closedir(devdir); } closedir(busdir); @@ -315,26 +347,11 @@ int usb_write(usb_handle *h, const void *_data, int len) 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; @@ -353,7 +370,7 @@ int usb_write(usb_handle *h, const void *_data, int len) count += xfer; len -= xfer; data += xfer; - } + } while(len > 0); return count; } @@ -365,7 +382,7 @@ int usb_read(usb_handle *h, void *_data, int len) struct usbdevfs_bulktransfer bulk; int n, retry; - if(h->ep_in == 0) { + if(h->ep_in == 0 || h->desc == -1) { return -1; } @@ -431,5 +448,20 @@ int usb_close(usb_handle *h) usb_handle *usb_open(ifc_match_func callback) { - return find_usb_device("/dev/bus/usb", callback); + 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; } diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index 1548ba8..0f55e0d 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -467,6 +467,11 @@ int usb_close(usb_handle *h) { return 0; } +int usb_wait_for_disconnect(usb_handle *usb) { + /* TODO: Punt for now */ + return 0; +} + int usb_read(usb_handle *h, void *data, int len) { IOReturn result; UInt32 numBytes = len; diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 7aa36b2..07f7be2 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -269,6 +269,11 @@ int usb_close(usb_handle* handle) { return 0; } +int usb_wait_for_disconnect(usb_handle *usb) { + /* TODO: Punt for now */ + return 0; +} + int recognized_device(usb_handle* handle, ifc_match_func callback) { struct usb_ifc_info info; USB_DEVICE_DESCRIPTOR device_desc; diff --git a/fastboot/util.c b/fastboot/util.c new file mode 100644 index 0000000..f2bbd34 --- /dev/null +++ b/fastboot/util.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/time.h> + +#include "fastboot.h" + +double now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; +} + +char *mkmsg(const char *fmt, ...) +{ + char buf[256]; + char *s; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + s = strdup(buf); + if (s == 0) die("out of memory"); + return s; +} + +void die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr,"error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr,"\n"); + va_end(ap); + exit(1); +} |