summaryrefslogtreecommitdiffstats
path: root/fastboot
diff options
context:
space:
mode:
Diffstat (limited to 'fastboot')
-rw-r--r--fastboot/Android.mk5
-rw-r--r--fastboot/engine.c238
-rw-r--r--fastboot/fastboot.c98
-rw-r--r--fastboot/fastboot.h5
-rw-r--r--fastboot/fs.c56
-rw-r--r--fastboot/fs.h12
-rw-r--r--fastboot/usb.h2
-rw-r--r--fastboot/usb_linux.c226
-rw-r--r--fastboot/usb_osx.c5
-rw-r--r--fastboot/usb_windows.c5
-rw-r--r--fastboot/util.c69
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);
+}