void do_update(char *fn, int erase_first)
{
    void *zdata;
    unsigned zsize;
    void *data;
    unsigned sz;
    zipfile_t zip;

    queue_info_dump();

    fb_queue_query_save("product", cur_product, sizeof(cur_product));

    zdata = load_file(fn, &zsize);
    if (zdata == 0) die("failed to load '%s': %s", fn, strerror(errno));

    zip = init_zipfile(zdata, zsize);
    if(zip == 0) die("failed to access zipdata in '%s'");

    data = unzip_file(zip, "android-info.txt", &sz);
    if (data == 0) {
        char *tmp;
            /* fallback for older zipfiles */
        data = unzip_file(zip, "android-product.txt", &sz);
        if ((data == 0) || (sz < 1)) {
            die("update package has no android-info.txt or android-product.txt");
        }
        tmp = malloc(sz + 128);
        if (tmp == 0) die("out of memory");
        sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data);
        data = tmp;
        sz = strlen(tmp);
    }

    setup_requirements(data, sz);

    data = unzip_file(zip, "boot.img", &sz);
    if (data == 0) die("update package missing boot.img");
    do_update_signature(zip, "boot.sig");
    if (erase_first && needs_erase("boot")) {
        fb_queue_erase("boot");
    }
    fb_queue_flash("boot", data, sz);

    data = unzip_file(zip, "recovery.img", &sz);
    if (data != 0) {
        do_update_signature(zip, "recovery.sig");
        if (erase_first && needs_erase("recovery")) {
            fb_queue_erase("recovery");
        }
        fb_queue_flash("recovery", data, sz);
    }

    data = unzip_file(zip, "system.img", &sz);
    if (data == 0) die("update package missing system.img");
    do_update_signature(zip, "system.sig");
    if (erase_first && needs_erase("system")) {
        fb_queue_erase("system");
    }
    fb_queue_flash("system", data, sz);
}
void do_flashall(usb_handle *usb, int erase_first)
{
    queue_info_dump();

    fb_queue_query_save("product", cur_product, sizeof(cur_product));

    char* fname = find_item("info", product);
    if (fname == 0) die("cannot find android-info.txt");

    unsigned sz;
    void* data = load_file(fname, &sz);
    if (data == 0) die("could not load android-info.txt: %s", strerror(errno));

    setup_requirements(reinterpret_cast<char*>(data), sz);

    for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
        fname = find_item(images[i].part_name, product);
        fastboot_buffer buf;
        if (load_buf(usb, fname, &buf)) {
            if (images[i].is_optional)
                continue;
            die("could not load %s\n", images[i].img_name);
        }
        do_send_signature(fname);
        if (erase_first && needs_erase(usb, images[i].part_name)) {
            fb_queue_erase(images[i].part_name);
        }
        flash_buf(images[i].part_name, &buf);
    }
}
void do_update(usb_handle *usb, char *fn, int erase_first)
{
    void *zdata;
    unsigned zsize;
    void *data;
    unsigned sz;
    zipfile_t zip;
    int fd;
    int rc;
    struct fastboot_buffer buf;
    size_t i;

    queue_info_dump();

    fb_queue_query_save("product", cur_product, sizeof(cur_product));

    zdata = load_file(fn, &zsize);
    if (zdata == 0) die("failed to load '%s': %s", fn, strerror(errno));

    zip = init_zipfile(zdata, zsize);
    if(zip == 0) die("failed to access zipdata in '%s'");

    data = unzip_file(zip, "android-info.txt", &sz);
    if (data == 0) {
        char *tmp;
        /* fallback for older zipfiles */
        data = unzip_file(zip, "android-product.txt", &sz);
        if ((data == 0) || (sz < 1)) {
            die("update package has no android-info.txt or android-product.txt");
        }
        tmp = malloc(sz + 128);
        if (tmp == 0) die("out of memory");
        sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data);
        data = tmp;
        sz = strlen(tmp);
    }

    setup_requirements(data, sz);

    for (i = 0; i < ARRAY_SIZE(images); i++) {
        fd = unzip_to_file(zip, images[i].img_name);
        if (fd < 0) {
            if (images[i].is_optional)
                continue;
            die("update package missing %s", images[i].img_name);
        }
        rc = load_buf_fd(usb, fd, &buf);
        if (rc) die("cannot load %s from flash", images[i].img_name);
        do_update_signature(zip, images[i].sig_name);
        if (erase_first && needs_erase(images[i].part_name)) {
            fb_queue_erase(images[i].part_name);
        }
        flash_buf(images[i].part_name, &buf);
        /* not closing the fd here since the sparse code keeps the fd around
         * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
         * program exits.
         */
    }
}
void do_flashall(int erase_first)
{
    char *fname;
    void *data;
    unsigned sz;

    queue_info_dump();

    fb_queue_query_save("product", cur_product, sizeof(cur_product));

    fname = find_item("info", product);
    if (fname == 0) die("cannot find android-info.txt");
    data = load_file(fname, &sz);
    if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
    setup_requirements(data, sz);

    fname = find_item("boot", product);
    data = load_file(fname, &sz);
    if (data == 0) die("could not load boot.img: %s", strerror(errno));
    do_send_signature(fname);
    if (erase_first && needs_erase("boot")) {
        fb_queue_erase("boot");
    }
    fb_queue_flash("boot", data, sz);

    fname = find_item("recovery", product);
    data = load_file(fname, &sz);
    if (data != 0) {
        do_send_signature(fname);
        if (erase_first && needs_erase("recovery")) {
            fb_queue_erase("recovery");
        }
        fb_queue_flash("recovery", data, sz);
    }

    fname = find_item("system", product);
    data = load_file(fname, &sz);
    if (data == 0) die("could not load system.img: %s", strerror(errno));
    do_send_signature(fname);
    if (erase_first && needs_erase("system")) {
        fb_queue_erase("system");
    }
    fb_queue_flash("system", data, sz);
}
void do_update(usb_handle *usb, const char *filename, int erase_first)
{
    queue_info_dump();

    fb_queue_query_save("product", cur_product, sizeof(cur_product));

    ZipArchiveHandle zip;
    int error = OpenArchive(filename, &zip);
    if (error != 0) {
        CloseArchive(zip);
        die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
    }

    unsigned sz;
    void* data = unzip_file(zip, "android-info.txt", &sz);
    if (data == 0) {
        CloseArchive(zip);
        die("update package '%s' has no android-info.txt", filename);
    }

    setup_requirements(reinterpret_cast<char*>(data), sz);

    for (size_t i = 0; i < ARRAY_SIZE(images); ++i) {
        int fd = unzip_to_file(zip, images[i].img_name);
        if (fd == -1) {
            if (images[i].is_optional) {
                continue;
            }
            CloseArchive(zip);
            exit(1); // unzip_to_file already explained why.
        }
        fastboot_buffer buf;
        int rc = load_buf_fd(usb, fd, &buf);
        if (rc) die("cannot load %s from flash", images[i].img_name);
        do_update_signature(zip, images[i].sig_name);
        if (erase_first && needs_erase(usb, images[i].part_name)) {
            fb_queue_erase(images[i].part_name);
        }
        flash_buf(images[i].part_name, &buf);
        /* not closing the fd here since the sparse code keeps the fd around
         * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
         * program exits.
         */
    }

    CloseArchive(zip);
}
int main(int argc, char **argv)
{
    int wants_wipe = 0;
    int wants_reboot = 0;
    int wants_reboot_bootloader = 0;
    int erase_first = 1;
    void *data;
    unsigned sz;
    int status;
    int c;
    int longindex;

    const struct option longopts[] = {
        {"base", required_argument, 0, 'b'},
        {"kernel_offset", required_argument, 0, 'k'},
        {"page_size", required_argument, 0, 'n'},
        {"ramdisk_offset", required_argument, 0, 'r'},
        {"tags_offset", required_argument, 0, 't'},
        {"help", no_argument, 0, 'h'},
        {"unbuffered", no_argument, 0, 0},
        {"version", no_argument, 0, 0},
        {0, 0, 0, 0}
    };

    serial = getenv("ANDROID_SERIAL");

    while (1) {
        c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
        if (c < 0) {
            break;
        }
        /* Alphabetical cases */
        switch (c) {
        case 'b':
            base_addr = strtoul(optarg, 0, 16);
            break;
        case 'c':
            cmdline = optarg;
            break;
        case 'h':
            usage();
            return 1;
        case 'i': {
                char *endptr = NULL;
                unsigned long val;

                val = strtoul(optarg, &endptr, 0);
                if (!endptr || *endptr != '\0' || (val & ~0xffff))
                    die("invalid vendor id '%s'", optarg);
                vendor_id = (unsigned short)val;
                break;
            }
        case 'k':
            kernel_offset = strtoul(optarg, 0, 16);
            break;
        case 'l':
            long_listing = 1;
            break;
        case 'n':
            page_size = (unsigned)strtoul(optarg, NULL, 0);
            if (!page_size) die("invalid page size");
            break;
        case 'p':
            product = optarg;
            break;
        case 'r':
            ramdisk_offset = strtoul(optarg, 0, 16);
            break;
        case 't':
            tags_offset = strtoul(optarg, 0, 16);
            break;
        case 's':
            serial = optarg;
            break;
        case 'S':
            sparse_limit = parse_num(optarg);
            if (sparse_limit < 0) {
                    die("invalid sparse limit");
            }
            break;
        case 'u':
            erase_first = 0;
            break;
        case 'w':
            wants_wipe = 1;
            break;
        case '?':
            return 1;
        case 0:
            if (strcmp("unbuffered", longopts[longindex].name) == 0) {
                setvbuf(stdout, NULL, _IONBF, 0);
                setvbuf(stderr, NULL, _IONBF, 0);
            } else if (strcmp("version", longopts[longindex].name) == 0) {
                fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
                return 0;
            }
            break;
        default:
            abort();
        }
    }

    argc -= optind;
    argv += optind;

    if (argc == 0 && !wants_wipe) {
        usage();
        return 1;
    }

    if (argc > 0 && !strcmp(*argv, "devices")) {
        skip(1);
        list_devices();
        return 0;
    }

    if (argc > 0 && !strcmp(*argv, "help")) {
        usage();
        return 0;
    }

    usb_handle* usb = open_device();

    while (argc > 0) {
        if(!strcmp(*argv, "getvar")) {
            require(2);
            fb_queue_display(argv[1], argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "erase")) {
            require(2);

            if (fb_format_supported(usb, argv[1], NULL)) {
                fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
            }

            fb_queue_erase(argv[1]);
            skip(2);
        } else if(!strncmp(*argv, "format", strlen("format"))) {
            char *overrides;
            char *type_override = NULL;
            char *size_override = NULL;
            require(2);
            /*
             * Parsing for: "format[:[type][:[size]]]"
             * Some valid things:
             *  - select ontly the size, and leave default fs type:
             *    format::0x4000000 userdata
             *  - default fs type and size:
             *    format userdata
             *    format:: userdata
             */
            overrides = strchr(*argv, ':');
            if (overrides) {
                overrides++;
                size_override = strchr(overrides, ':');
                if (size_override) {
                    size_override[0] = '\0';
                    size_override++;
                }
                type_override = overrides;
            }
            if (type_override && !type_override[0]) type_override = NULL;
            if (size_override && !size_override[0]) size_override = NULL;
            if (erase_first && needs_erase(usb, argv[1])) {
                fb_queue_erase(argv[1]);
            }
            fb_perform_format(usb, argv[1], 0, type_override, size_override);
            skip(2);
        } else if(!strcmp(*argv, "signature")) {
            require(2);
            data = load_file(argv[1], &sz);
            if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
            if (sz != 256) die("signature must be 256 bytes");
            fb_queue_download("signature", data, sz);
            fb_queue_command("signature", "installing signature");
            skip(2);
        } else if(!strcmp(*argv, "reboot")) {
            wants_reboot = 1;
            skip(1);
            if (argc > 0) {
                if (!strcmp(*argv, "bootloader")) {
                    wants_reboot = 0;
                    wants_reboot_bootloader = 1;
                    skip(1);
                }
            }
            require(0);
        } else if(!strcmp(*argv, "reboot-bootloader")) {
            wants_reboot_bootloader = 1;
            skip(1);
        } else if (!strcmp(*argv, "continue")) {
            fb_queue_command("continue", "resuming boot");
            skip(1);
        } else if(!strcmp(*argv, "boot")) {
            char *kname = 0;
            char *rname = 0;
            skip(1);
            if (argc > 0) {
                kname = argv[0];
                skip(1);
            }
            if (argc > 0) {
                rname = argv[0];
                skip(1);
            }
            data = load_bootable_image(kname, rname, &sz, cmdline);
            if (data == 0) return 1;
            fb_queue_download("boot.img", data, sz);
            fb_queue_command("boot", "booting");
        } else if(!strcmp(*argv, "flash")) {
            char *pname = argv[1];
            char *fname = 0;
            require(2);
            if (argc > 2) {
                fname = argv[2];
                skip(3);
            } else {
                fname = find_item(pname, product);
                skip(2);
            }
            if (fname == 0) die("cannot determine image filename for '%s'", pname);
            if (erase_first && needs_erase(usb, pname)) {
                fb_queue_erase(pname);
            }
            do_flash(usb, pname, fname);
        } else if(!strcmp(*argv, "flash:raw")) {
            char *pname = argv[1];
            char *kname = argv[2];
            char *rname = 0;
            require(3);
            if(argc > 3) {
                rname = argv[3];
                skip(4);
            } else {
                skip(3);
            }
            data = load_bootable_image(kname, rname, &sz, cmdline);
            if (data == 0) die("cannot load bootable image");
            fb_queue_flash(pname, data, sz);
        } else if(!strcmp(*argv, "flashall")) {
            skip(1);
            do_flashall(usb, erase_first);
            wants_reboot = 1;
        } else if(!strcmp(*argv, "update")) {
            if (argc > 1) {
                do_update(usb, argv[1], erase_first);
                skip(2);
            } else {
                do_update(usb, "update.zip", erase_first);
                skip(1);
            }
            wants_reboot = 1;
        } else if(!strcmp(*argv, "oem")) {
            argc = do_oem_command(argc, argv);
        } else if(!strcmp(*argv, "flashing") && argc == 2) {
            if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock")
               || !strcmp(*(argv+1), "unlock_critical")
               || !strcmp(*(argv+1), "lock_critical")
               || !strcmp(*(argv+1), "get_unlock_ability")) {
              argc = do_oem_command(argc, argv);
            } else {
              usage();
              return 1;
            }
        } else {
            usage();
            return 1;
        }
    }

    if (wants_wipe) {
        fb_queue_erase("userdata");
        fb_perform_format(usb, "userdata", 1, NULL, NULL);
        fb_queue_erase("cache");
        fb_perform_format(usb, "cache", 1, NULL, NULL);
    }
    if (wants_reboot) {
        fb_queue_reboot();
        fb_queue_wait_for_disconnect();
    } else if (wants_reboot_bootloader) {
        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
        fb_queue_wait_for_disconnect();
    }

    if (fb_queue_is_empty())
        return 0;

    status = fb_execute_queue(usb);
    return (status) ? 1 : 0;
}
Exemple #7
0
int main(int argc, char **argv)
{
    int wants_reboot = 0;
    int wants_reboot_bootloader = 0;
    void *data;
    unsigned sz;
    int status;

    skip(1);
    if (argc == 0) {
        usage();
        return 1;
    }
#if HAVE_COMPATIBILITY
    if (argc == 1 && (!strcmp(*argv, "-o") || !strcmp(*argv, "--old"))) {
        usage();
        return 1;
    }
#endif

    if (!strcmp(*argv, "-h") || !strcmp(*argv, "--help")) {
        usage();
        return 0;
    } else if (!strcmp(*argv, "-v") || !strcmp(*argv, "--version")) {
	    fprintf(stdout, PACKAGE_STRING "\n");
	    return 0;
    }

    while (argc > 0) {
        if(!access(*argv, R_OK)) {
            /* all-in-one file */
            do_flashall(*argv);
            wants_reboot = 1;
            skip(1);
        } else if(!strcmp(*argv, "-s") || !strcmp(*argv, "--serial")) {
            require(2);
            serial = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-i") || !strcmp(*argv, "--id")) {
            char *endptr = NULL;
            unsigned long val;
            require(2);
            val = strtoul(argv[1], &endptr, 0);
            if (!endptr || *endptr != '\0' || (val & ~0xffff))
                die("invalid vendor id '%s'", argv[1]);
            vendor_id = (unsigned short)val;
            skip(2);
        } else if(!strcmp(*argv, "getvar")) {
            /* when argc == 1, just list all available variables */
            if (argc == 1) {
                fb_queue_display("","");
                skip(1);
            } else {
                require(2);
                fb_queue_display(argv[1], argv[1]);
                skip(2);
            }
        } else if(!strcmp(*argv, "erase")) {
            if (argc == 1) {
                fb_queue_erase("");
                skip(1);
            } else {
                require(2);
                fb_queue_erase(argv[1]);
                skip(2);
            }
        } else if(!strcmp(*argv, "reboot")) {
            wants_reboot = 1;
            skip(1);
        } else if(!strcmp(*argv, "reboot-bootloader")) {
            wants_reboot_bootloader = 1;
            skip(1);
        } else if(!strcmp(*argv, "flash")) {
            if (argc == 1) {
                fb_queue_stream_flash("", NULL, 0);
                skip(1);
            } else {
                char *pname = argv[1];
                char *fname = 0;
                require(3);
                fname = argv[2];
                skip(3);
                data = load_file(fname, &sz);
                if (data == 0) {
                    int fd = open(fname, O_RDONLY);
                    if (fd == -1) die("cannot open %s for read: %m\n", fname);
                    sz = lseek(fd, 0, SEEK_END);
                    data = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
                    if (data == MAP_FAILED) die("cannot map '%s': %s\n", fname, strerror(errno));
                }
                fb_queue_stream_flash(pname, data, sz);
            }
        } else if(!strcmp(*argv, "flashall")) {
            require(2);
            do_flashall(argv[1]);
            skip(2);
            wants_reboot = 1;
        } else if(!strcmp(*argv, "oem")) {
            argc = do_oem_command(argc, argv);
            if (argc)
                return argc;
        } else if (!strcmp(*argv, "devices")) {
            list_devices();
            return 0;
#if HAVE_COMPATIBILITY
        } else if (!strcmp(*argv, "-o") || !strcmp(*argv, "--old")) {
            old_preos = 1;
            skip(1);
#endif
        } else {
            usage();
            return 1;
        }
    }

    if (wants_reboot) {
        fb_queue_command("reboot", "rebooting");
    } else if (wants_reboot_bootloader) {
        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
    }

    usb = open_device();

    status = fb_execute_queue(usb);
    return (status) ? 1 : 0;
}
Exemple #8
0
int main(int argc, char **argv)
{
    int wants_wipe = 0;
    int wants_reboot = 0;
    int wants_reboot_bootloader = 0;
    void *data;
    unsigned sz;
    unsigned page_size = 2048;
    int status;

    skip(1);
    if (argc == 0) {
        usage();
        return 1;
    }

    if (!strcmp(*argv, "devices")) {
        list_devices();
        return 0;
    }

    if (!strcmp(*argv, "help")) {
        usage();
        return 0;
    }


    serial = getenv("ANDROID_SERIAL");

    while (argc > 0) {
        if(!strcmp(*argv, "-w")) {
            wants_wipe = 1;
            skip(1);
        } else if(!strcmp(*argv, "-b")) {
            require(2);
            base_addr = strtoul(argv[1], 0, 16);
            skip(2);
        } else if(!strcmp(*argv, "-n")) {
            require(2);
            page_size = (unsigned)strtoul(argv[1], NULL, 0);
            if (!page_size) die("invalid page size");
            skip(2);
        } else if(!strcmp(*argv, "-s")) {
            require(2);
            serial = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-p")) {
            require(2);
            product = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-c")) {
            require(2);
            cmdline = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-i")) {
            char *endptr = NULL;
            unsigned long val;

            require(2);
            val = strtoul(argv[1], &endptr, 0);
            if (!endptr || *endptr != '\0' || (val & ~0xffff))
                die("invalid vendor id '%s'", argv[1]);
            vendor_id = (unsigned short)val;
            skip(2);
        } else if(!strcmp(*argv, "getvar")) {
            require(2);
            fb_queue_display(argv[1], argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "erase")) {
            require(2);
            fb_queue_erase(argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "signature")) {
            require(2);
            data = load_file(argv[1], &sz);
            if (data == 0) die("could not load '%s'", argv[1]);
            if (sz != 256) die("signature must be 256 bytes");
            fb_queue_download("signature", data, sz);
            fb_queue_command("signature", "installing signature");
            skip(2);
        } else if(!strcmp(*argv, "reboot")) {
            wants_reboot = 1;
            skip(1);
        } else if(!strcmp(*argv, "reboot-bootloader")) {
            wants_reboot_bootloader = 1;
            skip(1);
        } else if (!strcmp(*argv, "continue")) {
            fb_queue_command("continue", "resuming boot");
            skip(1);
        } else if(!strcmp(*argv, "boot")) {
            char *kname = 0;
            char *rname = 0;
            skip(1);
            if (argc > 0) {
                kname = argv[0];
                skip(1);
            }
            if (argc > 0) {
                rname = argv[0];
                skip(1);
            }
            data = load_bootable_image(page_size, kname, rname, &sz, cmdline);
            if (data == 0) return 1;
            fb_queue_download("boot.img", data, sz);
            fb_queue_command("boot", "booting");
        } else if(!strcmp(*argv, "flash")) {
            char *pname = argv[1];
            char *fname = 0;
            require(2);
            if (argc > 2) {
                fname = argv[2];
                skip(3);
            } else {
                fname = find_item(pname, product);
                skip(2);
            }
            if (fname == 0) die("cannot determine image filename for '%s'", pname);
            data = load_file(fname, &sz);
            if (data == 0) die("cannot load '%s'\n", fname);
            fb_queue_flash(pname, data, sz);
        } else if(!strcmp(*argv, "flash:raw")) {
            char *pname = argv[1];
            char *kname = argv[2];
            char *rname = 0;
            require(3);
            if(argc > 3) {
                rname = argv[3];
                skip(4);
            } else {
                skip(3);
            }
            data = load_bootable_image(page_size, kname, rname, &sz, cmdline);
            if (data == 0) die("cannot load bootable image");
            fb_queue_flash(pname, data, sz);
        } else if(!strcmp(*argv, "flashall")) {
            skip(1);
            do_flashall();
            wants_reboot = 1;
        } else if(!strcmp(*argv, "update")) {
            if (argc > 1) {
                do_update(argv[1]);
                skip(2);
            } else {
                do_update("update.zip");
                skip(1);
            }
            wants_reboot = 1;
        } else if(!strcmp(*argv, "oem")) {
            argc = do_oem_command(argc, argv);
        } else {
            usage();
            return 1;
        }
    }

    if (wants_wipe) {
        fb_queue_erase("userdata");
        fb_queue_erase("cache");
    }
    if (wants_reboot) {
        fb_queue_reboot();
    } else if (wants_reboot_bootloader) {
        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
    }

    usb = open_device();

    status = fb_execute_queue(usb);
    return (status) ? 1 : 0;
}
int main(int argc, char **argv)
{
    int wants_wipe = 0;
    int wants_reboot = 0;
    int wants_reboot_bootloader = 0;
    int erase_first = 1;
    void *data;
    unsigned sz;
    unsigned page_size = 2048;
    int status;
    int c;
    int r;

    const struct option longopts = { 0, 0, 0, 0 };

    serial = getenv("ANDROID_SERIAL");

    while (1) {
        c = getopt_long(argc, argv, "wub:n:s:S:lp:c:i:m:ht:", &longopts, NULL);
        if (c < 0) {
            break;
        }

        switch (c) {
        case 'w':
            wants_wipe = 1;
            break;
        case 'u':
            erase_first = 0;
            break;
        case 'b':
            base_addr = strtoul(optarg, 0, 16);
            break;
        case 'n':
            page_size = (unsigned)strtoul(optarg, NULL, 0);
            if (!page_size) die("invalid page size");
            break;
        case 's':
            serial = optarg;
            break;
        case 'S':
            sparse_limit = parse_num(optarg);
            if (sparse_limit < 0) {
                    die("invalid sparse limit");
            }
            break;
        case 'l':
            long_listing = 1;
            break;
        case 'p':
            product = optarg;
            break;
        case 'c':
            cmdline = optarg;
            break;
        case 'i': {
                char *endptr = NULL;
                unsigned long val;

                val = strtoul(optarg, &endptr, 0);
                if (!endptr || *endptr != '\0' || (val & ~0xffff))
                    die("invalid vendor id '%s'", optarg);
                vendor_id = (unsigned short)val;
                break;
            }
        case 't':
            host = optarg;
            break;
        case 'h':
            usage();
            return 1;
        case '?':
            return 1;
        default:
            abort();
        }
    }

    argc -= optind;
    argv += optind;

    if (argc == 0 && !wants_wipe) {
        usage();
        return 1;
    }

    if (argc > 0 && !strcmp(*argv, "devices")) {
        skip(1);
        list_devices();
        return 0;
    }

    if (argc > 0 && !strcmp(*argv, "help")) {
        usage();
        return 0;
    }

    if(host) {
        transport.userdata = open_device_tcp();
        transport.close = tcp_close;
        transport.read = tcp_read;
        transport.write = tcp_write;
    } else {
        transport.userdata = open_device();
        transport.close = usb_close;
        transport.read = usb_read;
        transport.write = usb_write;
    }

    while (argc > 0) {
        if(!strcmp(*argv, "getvar")) {
            require(2);
            fb_queue_display(argv[1], argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "erase")) {
            require(2);
            fb_queue_erase(argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "format")) {
            require(2);
            fb_queue_format(argv[1], 0);
            skip(2);
        } else if(!strcmp(*argv, "signature")) {
            require(2);
            data = load_file(argv[1], &sz);
            if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
            if (sz != 256) die("signature must be 256 bytes");
            fb_queue_download("signature", data, sz);
            fb_queue_command("signature", "installing signature");
            skip(2);
        } else if(!strcmp(*argv, "reboot")) {
            wants_reboot = 1;
            skip(1);
        } else if(!strcmp(*argv, "reboot-bootloader")) {
            wants_reboot_bootloader = 1;
            skip(1);
        } else if (!strcmp(*argv, "continue")) {
            fb_queue_command("continue", "resuming boot");
            skip(1);
        } else if(!strcmp(*argv, "boot")) {
            char *kname = 0;
            char *rname = 0;
            skip(1);
            if (argc > 0) {
                kname = argv[0];
                skip(1);
            }
            if (argc > 0) {
                rname = argv[0];
                skip(1);
            }
            data = load_bootable_image(page_size, kname, rname, &sz, cmdline);
            if (data == 0) return 1;
            fb_queue_download("boot.img", data, sz);
            fb_queue_command("boot", "booting");
        } else if(!strcmp(*argv, "flash")) {
            char *pname = argv[1];
            char *fname = 0;
            require(2);
            if (argc > 2) {
                fname = argv[2];
                skip(3);
            } else {
                fname = find_item(pname, product);
                skip(2);
            }
            if (fname == 0) die("cannot determine image filename for '%s'", pname);
            if (erase_first && needs_erase(pname)) {
                fb_queue_erase(pname);
            }
            data = load_file(fname, &sz);
            if (data == 0) die("cannot load '%s': %s\n", fname, strerror(errno));
            fb_queue_flash(pname, data, sz);
        } else if(!strcmp(*argv, "flash:raw")) {
            char *pname = argv[1];
            char *kname = argv[2];
            char *rname = 0;
            require(3);
            if(argc > 3) {
                rname = argv[3];
                skip(4);
            } else {
                skip(3);
            }
            data = load_bootable_image(page_size, kname, rname, &sz, cmdline);
            if (data == 0) die("cannot load bootable image");
            fb_queue_flash(pname, data, sz);
        } else if(!strcmp(*argv, "flashall")) {
            skip(1);
            do_flashall(erase_first);
            wants_reboot = 1;
        } else if(!strcmp(*argv, "update")) {
            if (argc > 1) {
                do_update(argv[1], erase_first);
                skip(2);
            } else {
                do_update("update.zip", erase_first);
                skip(1);
            }
            wants_reboot = 1;
        } else if(!strcmp(*argv, "oem")) {
            argc = do_oem_command(argc, argv);
        } else {
            usage();
            return 1;
        }
    }

    if (wants_wipe) {
        fb_queue_erase("userdata");
        fb_queue_format("userdata", 1);
        fb_queue_erase("cache");
        fb_queue_format("cache", 1);
    }
    if (wants_reboot) {
        fb_queue_reboot();
    } else if (wants_reboot_bootloader) {
        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
    }

    if (fb_queue_is_empty())
        return 0;

    status = fb_execute_queue(&transport);
    return (status) ? 1 : 0;
}
int main(int argc, char **argv)
{
    int wants_wipe = 0;
    int wants_reboot = 0;
    int wants_reboot_bootloader = 0;
    int erase_first = 1;
    void *data;
    unsigned sz;
    int status;
    int c;
    int r;

    const struct option longopts[] = {
        {"base", required_argument, 0, 'b'},
        {"kernel_offset", required_argument, 0, 'k'},
        {"page_size", required_argument, 0, 'n'},
        {"ramdisk_offset", required_argument, 0, 'r'},
        {"help", 0, 0, 'h'},
        {0, 0, 0, 0}
    };

    serial = getenv("ANDROID_SERIAL");

    while (1) {
        int option_index = 0;
        c = getopt_long(argc, argv, "wub:k:n:r:s:S:lp:c:i:m:h", longopts, NULL);
        if (c < 0) {
            break;
        }
        /* Alphabetical cases */
        switch (c) {
        case 'b':
            base_addr = strtoul(optarg, 0, 16);
            break;
        case 'c':
            cmdline = optarg;
            break;
        case 'h':
            usage();
            return 1;
        case 'i': {
                char *endptr = NULL;
                unsigned long val;

                val = strtoul(optarg, &endptr, 0);
                if (!endptr || *endptr != '\0' || (val & ~0xffff))
                    die("invalid vendor id '%s'", optarg);
                vendor_id = (unsigned short)val;
                break;
            }
        case 'k':
            kernel_offset = strtoul(optarg, 0, 16);
            break;
        case 'l':
            long_listing = 1;
            break;
        case 'n':
            page_size = (unsigned)strtoul(optarg, NULL, 0);
            if (!page_size) die("invalid page size");
            break;
        case 'p':
            product = optarg;
            break;
        case 'r':
            ramdisk_offset = strtoul(optarg, 0, 16);
            break;
        case 's':
            serial = optarg;
            break;
        case 'S':
            sparse_limit = parse_num(optarg);
            if (sparse_limit < 0) {
                    die("invalid sparse limit");
            }
            break;
        case 'u':
            erase_first = 0;
            break;
        case 'w':
            wants_wipe = 1;
            break;
        case '?':
            return 1;
        default:
            abort();
        }
    }

    argc -= optind;
    argv += optind;

    if (argc == 0 && !wants_wipe) {
        usage();
        return 1;
    }

    if (argc > 0 && !strcmp(*argv, "devices")) {
        skip(1);
        list_devices();
        return 0;
    }

    if (argc > 0 && !strcmp(*argv, "help")) {
        usage();
        return 0;
    }

    usb = open_device();

    while (argc > 0) {
        if(!strcmp(*argv, "getvar")) {
            require(2);
            fb_queue_display(argv[1], argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "erase")) {
            require(2);

            if (fb_format_supported(usb, argv[1])) {
                fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
            }

            fb_queue_erase(argv[1]);
            skip(2);
        } else if(!strcmp(*argv, "format")) {
            require(2);
            if (erase_first && needs_erase(argv[1])) {
                fb_queue_erase(argv[1]);
            }
            fb_queue_format(argv[1], 0);
            skip(2);
        } else if(!strcmp(*argv, "signature")) {
            require(2);
            data = load_file(argv[1], &sz);
            if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
            if (sz != 256) die("signature must be 256 bytes");
            fb_queue_download("signature", data, sz);
            fb_queue_command("signature", "installing signature");
            skip(2);
        } else if(!strcmp(*argv, "reboot")) {
            wants_reboot = 1;
            skip(1);
        } else if(!strcmp(*argv, "reboot-bootloader")) {
            wants_reboot_bootloader = 1;
            skip(1);
        } else if (!strcmp(*argv, "continue")) {
            fb_queue_command("continue", "resuming boot");
            skip(1);
        } else if(!strcmp(*argv, "boot")) {
            char *kname = 0;
            char *rname = 0;
            skip(1);
            if (argc > 0) {
                kname = argv[0];
                skip(1);
            }
            if (argc > 0) {
                rname = argv[0];
                skip(1);
            }
            data = load_bootable_image(kname, rname, &sz, cmdline);
            if (data == 0) return 1;
            fb_queue_download("boot.img", data, sz);
            fb_queue_command("boot", "booting");
        } else if(!strcmp(*argv, "flash")) {
            char *pname = argv[1];
            char *fname = 0;
            require(2);
            if (argc > 2) {
                fname = argv[2];
                skip(3);
            } else {
                fname = find_item(pname, product);
                skip(2);
            }
            if (fname == 0) die("cannot determine image filename for '%s'", pname);
            if (erase_first && needs_erase(pname)) {
                fb_queue_erase(pname);
            }
            do_flash(usb, pname, fname);
        } else if(!strcmp(*argv, "flash:raw")) {
            char *pname = argv[1];
            char *kname = argv[2];
            char *rname = 0;
            require(3);
            if(argc > 3) {
                rname = argv[3];
                skip(4);
            } else {
                skip(3);
            }
            data = load_bootable_image(kname, rname, &sz, cmdline);
            if (data == 0) die("cannot load bootable image");
            fb_queue_flash(pname, data, sz);
        } else if(!strcmp(*argv, "flashall")) {
            skip(1);
            do_flashall(usb, erase_first);
            wants_reboot = 1;
        } else if(!strcmp(*argv, "update")) {
            if (argc > 1) {
                do_update(usb, argv[1], erase_first);
                skip(2);
            } else {
                do_update(usb, "update.zip", erase_first);
                skip(1);
            }
            wants_reboot = 1;
        } else if(!strcmp(*argv, "oem")) {
            argc = do_oem_command(argc, argv);
        } else {
            usage();
            return 1;
        }
    }

    if (wants_wipe) {
        fb_queue_erase("userdata");
        fb_queue_format("userdata", 1);
        fb_queue_erase("cache");
        fb_queue_format("cache", 1);
    }
    if (wants_reboot) {
        fb_queue_reboot();
    } else if (wants_reboot_bootloader) {
        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
    }

    if (fb_queue_is_empty())
        return 0;

    status = fb_execute_queue(usb);
    return (status) ? 1 : 0;
}