blob: a53522b48347e8ef0739bd3472b9a6a8f82ec040 [file] [log] [blame]
/*
* Copyright (C) 2008 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.
*/
/*
* Copyright (c) 2010 Marvell Inc.
* Modified by Lei Wen <leiwen@marvell.com>
*/
#include <common.h>
#include <fastboot.h>
#include <sparse_format.h>
#include <jffs2/load_kernel.h>
#include <linux/mtd/mtd.h>
#include <mmc.h>
#include <malloc.h>
DECLARE_GLOBAL_DATA_PTR;
#define SECTOR_SIZE 512
enum fb_dev {
FB_INVALID = 0,
FB_NOR = MTD_DEV_TYPE_NOR,
FB_NAND = MTD_DEV_TYPE_NAND,
FB_ONENAND = MTD_DEV_TYPE_ONENAND,
FB_MMC,
FB_MAX,
};
#ifndef CONFIG_FB_RESV
#define CONFIG_FB_RESV 64
#endif
#define MAX_NAME_NUM 36
#define UDC_OUT_PACKET_SIZE 0x200
block_dev_desc_t *mmc_dev;
static char commands[CONFIG_SYS_CBSIZE];
/* For each part has four attributes:
* device type, device num, start address, size
*/
struct fb_part {
char name[MAX_NAME_NUM];
#define PART_ATTR_YAFFS 1
#define PART_NUM_MASK 0xE0000000
#define PART_NUM(x) (((x) & PART_NUM_MASK) >> 29)
unsigned int attr;
unsigned long long start;
unsigned long long size;
};
struct fb_parts {
enum fb_dev dev;
int dev_num;
int part_num;
int align_len;
struct fb_part *part;
};
static struct fb_parts *parts;
#ifdef CONFIG_MTD_DEVICE
#ifdef CONFIG_SYS_FB_YAFFS
static char *part_yaffs[] = CONFIG_SYS_FB_YAFFS;
#else
static char *part_yaffs[] = {NULL};
#endif
#endif
static char *rcv_buf;
static unsigned fb_mem, ramdisk_addr, ramdisk_size, kernel_addr, kernel_size;
static int keep_running, is_yaffs, part_num, onfly, bootsp, first_rcv, need_unsparse;
#define BOOT_MAGIC "ANDROID!"
#define BOOT_MAGIC_SIZE 8
#define BOOT_NAME_SIZE 16
#define BOOT_ARGS_SIZE 512
struct boot_img_hdr {
unsigned char magic[BOOT_MAGIC_SIZE];
unsigned kernel_size; /* size in bytes */
unsigned kernel_addr; /* physical load addr */
unsigned ramdisk_size; /* size in bytes */
unsigned ramdisk_addr; /* physical load addr */
unsigned second_size; /* size in bytes */
unsigned second_addr; /* physical load addr */
unsigned tags_addr; /* physical addr for kernel tags */
unsigned page_size; /* flash page size we assume */
unsigned unused[2]; /* future expansion: should be 0 */
unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */
unsigned char cmdline[BOOT_ARGS_SIZE];
unsigned id[8]; /* timestamp / checksum / sha1 / etc */
};
static unsigned rx_addr;
static unsigned rx_length;
static unsigned long long flash_start;
static unsigned long long flash_len;
static int init_boot_linux(void)
{
struct boot_img_hdr *hdr = (void *) fb_mem;
unsigned page_mask = 2047;
unsigned kernel_actual;
unsigned ramdisk_actual;
unsigned second_actual;
if ((kernel_size < 2048)
|| memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
printf("bootimg: bad header\n");
return -1;
}
if (hdr->page_size != 2048) {
printf("bootimg: invalid page size\n");
return -1;
}
kernel_actual = (hdr->kernel_size + page_mask) & (~page_mask);
ramdisk_actual = (hdr->ramdisk_size + page_mask) & (~page_mask);
second_actual = (hdr->second_size + page_mask) & (~page_mask);
if (kernel_size !=
(kernel_actual + ramdisk_actual + second_actual + 2048)) {
printf("bootimg: invalid image size");
return -1;
}
/* XXX process commandline here */
if (hdr->cmdline[0]) {
hdr->cmdline[BOOT_ARGS_SIZE - 1] = 0;
printf("cmdline is: %s\n", hdr->cmdline);
setenv("bootargs", (char *)hdr->cmdline);
}
/* XXX how to validate addresses? */
kernel_addr = (unsigned)hdr + 2048;
kernel_size = hdr->kernel_size;
ramdisk_size = hdr->ramdisk_size;
if (ramdisk_size > 0)
ramdisk_addr = (kernel_addr + 2048 + kernel_size) & (~page_mask);
else
ramdisk_addr = 0;
printf("bootimg: kernel addr=%x size=%x\n",
kernel_addr, kernel_size);
printf("bootimg: ramdisk addr=%x size=%x\n",
ramdisk_addr, ramdisk_size);
return 0;
}
static int composite_command(int is_write)
{
char tmp[CONFIG_SYS_CBSIZE];
ulong start, len;
if (need_unsparse) {
need_unsparse = 0;
sprintf(commands, "mmc dev %d 0; unsparse mmc %d 0x%x 0x%llx 0x%llx",
parts->dev_num, parts->dev_num,
fb_mem, flash_start, parts->part[part_num].size);
goto DIRECT_BURN;
}
start = (parts->dev == FB_MMC)
? flash_start / SECTOR_SIZE : flash_start;
len = (parts->dev == FB_MMC) ? flash_len / SECTOR_SIZE : flash_len;
if (is_write) {
if (is_yaffs)
sprintf(tmp, "write.yaffs 0x%x 0x%lx 0x%lx",
fb_mem, start, len);
else
sprintf(tmp, "write 0x%x 0x%lx 0x%lx",
fb_mem, start, len);
} else
sprintf(tmp, "erase 0x%lx 0x%lx", start, len);
switch (parts->dev) {
case FB_NOR:
sprintf(commands, "sf %s", tmp); break;
case FB_NAND:
sprintf(commands, "nand device %d; nand %s",
parts->dev_num, tmp); break;
case FB_ONENAND:
sprintf(commands, "onenand %s", tmp); break;
case FB_MMC:
sprintf(commands, "mmc dev %d %d; mmc %s; mmc dev %d 0",
parts->dev_num, PART_NUM(parts->part[part_num].attr),
tmp, parts->dev_num);
break;
default:
printf("Err dev!!\n");
return 0;
}
DIRECT_BURN:
printf("command::%s\n", commands);
run_command(commands, 0);
return 0;
}
static void burn_image(int force_burn)
{
int len = rx_addr - fb_mem;
int checklen = (is_yaffs) ? ((CONFIG_SYS_FASTBOOT_ONFLY_SZ/32)*33)
: CONFIG_SYS_FASTBOOT_ONFLY_SZ;
if (len == checklen || force_burn) {
if (!is_yaffs) {
flash_len = len & ~(parts->align_len - 1);
if (flash_len != len) {
flash_len += parts->align_len;
memset((char *)rx_addr, 0xff, flash_len - len);
}
} else
flash_len = len;
composite_command(1);
flash_start += CONFIG_SYS_FASTBOOT_ONFLY_SZ;
fb_set_buf((void *)fb_mem);
rx_addr = fb_mem;
}
}
static int check_part(char *name)
{
int i;
for (i = 0; i < parts->part_num; i++) {
if (strncmp(name, parts->part[i].name,
strlen(name)) == 0)
break;
}
if (i == parts->part_num) {
printf("There is no such part!\n");
fb_tx_status("FAILno such part");
return -1;
}
return i;
}
static int find_devnum(const char *p)
{
int devnum;
if (*p == '\0') {
printf("no partition number specified\n");
return -1;
}
devnum = simple_strtoul(p, (char **)&p, 0);
if (*p != '\0') {
printf("unexpected trailing character '%c'\n", *p);
return -1;
}
return devnum;
}
static int init_partitions(void)
{
struct mmc *mmc;
int i, pnum = 0;
disk_partition_t info;
const char *p;
mmc_dev = NULL;
p = getenv("fbenv");
if (0 == strncmp(p, "mmc", 3)) {
#ifdef CONFIG_MMC
parts = malloc(sizeof(struct fb_parts));
if (!parts) {
printf("Out of memory %s:%d\n", __func__, __LINE__);
return 1;
}
i = find_devnum(p + 3);
if (i == -1)
return 1;
mmc = find_mmc_device(i);
if (!mmc)
return 1;
mmc_init(mmc);
parts->part_num = 0;
parts->part = 0;
parts->align_len = SECTOR_SIZE;
parts->dev = FB_MMC;
parts->dev_num = i;
mmc_dev = mmc_get_dev(i);
pnum = get_partition_num(mmc_dev);
if (pnum) {
parts->part = malloc(sizeof(struct fb_part)*pnum);
if (!parts->part) {
printf("Out of memory %s:%d\n",
__func__, __LINE__);
return 1;
}
} else
parts->part = NULL;
parts->part_num = pnum;
if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) {
for (i = 1; i <= pnum; i++) {
int len;
if (get_partition_info(mmc_dev, i, &info))
break;
len = strlen((char *)info.name);
len = (len < MAX_NAME_NUM)
? len : MAX_NAME_NUM - 1;
memcpy(parts->part[i - 1].name, info.name, len);
parts->part[i - 1].name[len] = 0;
parts->part[i - 1].start =
(unsigned long long)info.start * info.blksz;
parts->part[i - 1].size =
(unsigned long long)info.size * info.blksz;
parts->part[i - 1].attr = 0;
}
}
#else
printf("CONFIG_MMC not be defined!!\n");
return 1;
#endif
} else {
#ifdef CONFIG_MTD_DEVICE
enum fb_dev type;
struct mtd_device *dev;
struct part_info *part;
int j, align;
if (mtdparts_init()) {
printf("mtd part init fail!\n");
return 1;
}
if (0 == strncmp(p, "nand", 4)) {
p += 4;
type = FB_NAND;
align = CONFIG_SYS_FASTBOOT_ONFLY_SZ;
} else if (0 == strncmp(p, "onenand", 4)) {
p += 7;
type = FB_ONENAND;
align = CONFIG_SYS_FASTBOOT_ONFLY_SZ;
} else if (0 == strncmp(p, "nor", 3)) {
p += 3;
type = FB_NOR;
align = 1;
} else {
printf("fbenv must de defined!!");
return 1;
}
i = find_devnum(p);
if (i == -1)
return 1;
dev = device_find(type, i);
if (dev == NULL) {
printf("There is no device as %s\n", getenv("fbenv"));
return 1;
}
if (!dev->num_parts)
return 1;
parts = malloc(sizeof(struct fb_parts));
if (!parts) {
printf("Out of memory %s:%d\n", __func__, __LINE__);
return 1;
}
parts->part = malloc(sizeof(struct fb_part)*dev->num_parts);
if (!parts->part) {
printf("Out of memory %s:%d\n", __func__, __LINE__);
return 1;
}
parts->dev = type;
parts->dev_num = i;
parts->part_num = dev->num_parts;
parts->align_len = align;
pnum = dev->num_parts;
for (i = 0; i < dev->num_parts; i++) {
part = mtd_part_info(dev, i);
if (part) {
int len;
len = strlen((char *)part->name);
len = (len < MAX_NAME_NUM)
? len : MAX_NAME_NUM - 1;
memcpy(parts->part[i].name, part->name, len);
parts->part[i].name[len] = 0;
parts->part[i].start = part->offset;
parts->part[i].size = part->size;
parts->part[i].attr = 0;
for (j = 0; j < ARRAY_SIZE(part_yaffs); j++)
if (!memcmp(part_yaffs[j], part->name,
strlen(part->name))) {
parts->part[i].attr =
PART_ATTR_YAFFS;
break;
}
} else {
printf("fail to find the part:%s\n",
part->name);
return 1;
}
}
#else
printf("CONFIG_MTD_DEVICE not be defined!!\n");
return 1;
#endif
}
switch (parts->dev) {
case FB_NOR:
printf("nor\n"); break;
case FB_NAND:
printf("nand\n"); break;
case FB_ONENAND:
printf("onenand\n"); break;
case FB_MMC:
printf("mmc\n"); break;
default:
printf("error!!!\n"); break;
}
for (i = 0; i < pnum; i++)
printf("part %3d::attr %x::%12s\t"
"start 0x%08llx, size 0x%08llx\n",
i, parts->part[i].attr, parts->part[i].name,
parts->part[i].start, parts->part[i].size);
return 0;
}
static int modify_part(char *name, unsigned long long x[3])
{
int len = strlen(name);
parts->part = realloc(parts->part,
sizeof(struct fb_part)*(parts->part_num + 1));
if (!parts->part) {
printf("Out of memory %s:%d\n", __func__, __LINE__);
return 1;
}
len = (len < MAX_NAME_NUM) ? len : MAX_NAME_NUM - 1;
memcpy(parts->part[parts->part_num].name, name, len);
parts->part[parts->part_num].name[len] = 0;
parts->part[parts->part_num].attr = (unsigned int)x[0];
parts->part[parts->part_num].start = x[1];
parts->part[parts->part_num].size = x[2];
parts->part_num++;
return 0;
}
#define MAX_RESP_SZ 64
void rcv_cmd(void)
{
char status[MAX_RESP_SZ];
int len;
static char *cmdbuf;
len = fb_get_rcv_len();
cmdbuf = (char *)fb_get_buf();
if (rx_length) {
rx_length -= len;
rx_addr += len;
fb_set_buf(cmdbuf + len);
/* Here we do some check for the downloaded image header */
#ifdef CONFIG_CMD_UNSPARSE
if (first_rcv) {
sparse_header_t *header;
first_rcv = 0;
header = (sparse_header_t *)fb_mem;
if (header->magic == SPARSE_HEADER_MAGIC) {
if (parts->dev == FB_MMC) {
if (onfly)
onfly = 0;
need_unsparse = 1;
} else
printf("Only mmc support sparsed"
" Image now!\n");
}
}
#endif
if (!rx_length) {
if (onfly)
burn_image(1);
fb_tx_status("OKAY");
fb_set_buf (rcv_buf);
onfly = 0;
} else {
if (onfly)
burn_image(0);
}
return;
}
if (len >= UDC_OUT_PACKET_SIZE)
len = UDC_OUT_PACKET_SIZE - 1;
cmdbuf[len] = 0;
printf("\n> %s\n", cmdbuf);
if (memcmp(cmdbuf, "download:", 9) == 0) {
rx_addr = fb_mem;
first_rcv = 1;
fb_set_buf((void *)rx_addr);
rx_length = simple_strtoul(cmdbuf + 9, NULL, 16);
if ((onfly && (rx_length > flash_len)) ||
(!onfly && (rx_length > (CONFIG_FB_RESV*1024*1024)))) {
fb_tx_status("FAILdata too large");
rx_length = 0;
return;
}
kernel_size = rx_length;
printf("recv data addr=%x size=%x\n", rx_addr, rx_length);
strcpy(status, "DATA");
sprintf(status + 4, "%08x", rx_length);
fb_tx_status(status);
return;
}
if (memcmp(cmdbuf, "boot", 4) == 0) {
if (init_boot_linux())
fb_tx_status("FAILinvalid boot image");
else {
printf("booting linux...\n");
fb_tx_status("OKAY");
#if 0
if (!bootsp) {
if (ramdisk_size > 0)
sprintf(status, "bootm 0x%x 0x%x 0x%x",
kernel_addr, ramdisk_addr,
ramdisk_size);
else
sprintf(status, "bootm 0x%x",
kernel_addr);
} else
sprintf(status, "go 0x%x", kernel_addr);
#else
#define MK2_RAMDISK_ADDR 0x02100000
#define MK2_RAMDISK_SIZE 0x200
//sprintf(status, "mmc rescan; mmc dev 0 0; mmc read 0x02100000 0x8c00 0x200;setenv bootm 0x%x 0x01100000 0x%x", kernel_addr, 0x200);
sprintf(status, "setenv ramdisk_addr 0x%x; setenv ramdisk_size 0x%x; \
mmc rescan; mmc dev 0 0; mmc read 0x%x 0x8c00 0x%x; \
bootm 0x%x 0x%X 0x%x",
MK2_RAMDISK_ADDR, MK2_RAMDISK_SIZE,
MK2_RAMDISK_ADDR, MK2_RAMDISK_SIZE,
kernel_addr, MK2_RAMDISK_ADDR, MK2_RAMDISK_SIZE);
#endif
printf("cmd:%s\n", status);
run_command(status, 0);
fb_tx_status("FAILNot zImage, use bootsp oem cmd"
" to boot special app");
}
return;
}
onfly = 0;
bootsp = 0;
if (memcmp(cmdbuf, "flash", 5) == 0) {
int ret = 0;
if (rx_addr > fb_mem) {
if (strncmp(cmdbuf + 6, "trust", strlen("trust")) == 0){
struct mmc *mmc;
int mmc_dev_id = 0;
mmc = find_mmc_device(mmc_dev_id);
if (!mmc)
return 1;
mmc_init(mmc);
mmc_switch_part(mmc_dev_id, 1);
mmc->block_dev.block_write(mmc_dev_id, 0x0, 2048, (volatile unsigned *)fb_mem);
mmc_switch_part(mmc_dev_id, 2);
mmc->block_dev.block_write(mmc_dev_id, 0x0, 2048, (volatile unsigned *)(fb_mem + 0x00100000));
mmc_switch_part(mmc_dev_id, 0);
mmc->block_dev.block_write(mmc_dev_id, 0x0, (rx_addr - fb_mem - 0x00100000 - 0x00100000)/512, (volatile unsigned *)(fb_mem + 0x00200000));
fb_tx_status("OKAY");
return;
}
else if (strncmp(cmdbuf + 6, "secondary_GPT", strlen("secondary_GPT")) == 0) {
struct mmc *mmc;
int mmc_dev_id = 0;
mmc = find_mmc_device(mmc_dev_id);
if (!mmc)
return 1;
mmc_init(mmc);
mmc_switch_part(mmc_dev_id, 0);
mmc->block_dev.block_write(mmc_dev_id, 0x1DA9FDF, 33, (volatile unsigned *)fb_mem);
fb_tx_status("OKAY");
return;
}
else{
part_num = check_part(cmdbuf + 6);
if (part_num < 0)
return;
is_yaffs = parts->part[part_num].attr & PART_ATTR_YAFFS;
flash_start = parts->part[part_num].start;
flash_len = parts->part[part_num].size;
if ((rx_addr - fb_mem) > flash_len) {
fb_tx_status("FAILdata too large");
return;
}
burn_image(1);
}
is_yaffs = 0;
if (!ret)
fb_tx_status("OKAY");
}
return;
}
if (memcmp(cmdbuf, "senddata", 8) == 0) {
int i, ret;
char *p;
ret = sprintf(status, "OKAY");
p = status + ret;
for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
if (!gd->bd->bi_dram[i].size)
continue;
ret = sprintf(p, "%lx:%lx:",
gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size);
p += ret;
}
fb_tx_status(status);
return;
}
if (memcmp(cmdbuf, "upload", 6) == 0) {
char *s = cmdbuf + 7, *p;
unsigned long upload_start, upload_sz;
p = strchr(s, ':');
if (!p) {
fb_tx_status("FAILno valid start pos");
return;
}
*p = 0;
upload_start = simple_strtoul(s, (char **)&s, 16);
s = p + 1;
p = strchr(s, ':');
if (!p) {
fb_tx_status("FAILno valid start pos");
return;
}
*p = 0;
upload_sz = simple_strtoul(s, (char **)&s, 16);
if (!upload_sz) {
fb_tx_status("FAILno valid upload sz");
return;
}
fb_tx_data((void *)upload_start, upload_sz);
return;
}
if (memcmp(cmdbuf, "erase", 5) == 0) {
part_num = check_part(cmdbuf + 6);
if (part_num < 0)
return;
flash_start = parts->part[part_num].start;
flash_len = parts->part[part_num].size;
composite_command(0);
fb_tx_status("OKAY");
return;
}
if (memcmp(cmdbuf, "oem", 3) == 0) {
if (memcmp(cmdbuf + 4, "onfly", 5) == 0) {
onfly = 0;
part_num = check_part(cmdbuf + 10);
if (part_num < 0)
return;
is_yaffs = parts->part[part_num].attr & PART_ATTR_YAFFS;
flash_start = parts->part[part_num].start;
flash_len = parts->part[part_num].size;
onfly = 1;
fb_tx_status("OKAY");
return;
}
if (memcmp(cmdbuf + 4, "bootsp", 6) == 0) {
bootsp = 1;
fb_tx_status("OKAY");
return;
}
if (memcmp(cmdbuf + 4, "setno", 5) == 0) {
char *s = cmdbuf + 4, *p;
p = strchr(s, ':');
s = p + 1;
if (!p || !s) {
fb_tx_status("FAILno valid serial no");
return;
}
setenv("fb_serial", s);
fb_tx_status("OKAY");
fb_init();
return;
}
if (memcmp(cmdbuf + 4, "part", 4) == 0) {
unsigned int i;
unsigned long long x[3];
char *s = cmdbuf + 8, *p, *name;
p = strchr(s, ':');
if (p) {
s = p + 1;
p = strchr(s, ':');
*p = 0;
name = s;
if (!name) {
fb_tx_status("FAILno name specified");
return;
}
s = p + 1;
for (i = 0; i < 3 && s; i++) {
p = strchr(s, ':');
if (p)
*p = 0;
x[i] = simple_strtoull(s,
(char **)&s, 0);
if (p)
s = p + 1;
else
break;
}
if (i == 2) {
if (modify_part(name, x)) {
fb_tx_status("FAILadd part"
" error");
return;
} else {
fb_tx_status("OKAY");
return;
}
}
}
i = 0;
init_partitions();
sprintf(status, "INFO::there %s %d partition",
(parts->part_num == 1) ? "is" : "are",
parts->part_num);
fb_tx_status(status);
do {
strcpy(status, "INFO");
status[4] = '\0';
for (; i < parts->part_num; i++) {
strcat(status, parts->part[i].name);
strcat(status, ",");
if ((i == (parts->part_num - 1))
|| (strlen(status) +
strlen(parts->part[i + 1].name) >=
(MAX_RESP_SZ - 1))) {
i++;
fb_tx_status(status);
break;
}
}
} while (i < parts->part_num);
fb_tx_status("OKAY");
return;
}
fb_tx_status("FAILbad oem command");
return;
}
if (memcmp(cmdbuf, "reboot", 6) == 0) {
fb_tx_status("OKAY");
if (strncmp(cmdbuf + 6, "-bootloader", 11) == 0)
keep_running = 0;
else
reset_cpu(0);
return;
}
fb_tx_status("FAILinvalid command");
}
int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
ALLOC_CACHE_ALIGN_BUFFER(char, cmd_pool, 512);
rx_length = 0;
part_num = -1;
onfly = 0;
bootsp = 0;
first_rcv = 0;
fb_mem = USB_LOADADDR - 2048;
ramdisk_size = 0;
kernel_addr = 0;
kernel_size = 0;
keep_running = 1;
is_yaffs = 0;
need_unsparse = 0;
init_partitions();
rcv_buf = cmd_pool;
fb_init();
fb_set_buf((void *)rcv_buf);
while (keep_running)
fb_run();
keep_running = 1;
fb_halt();
return 0;
}
U_BOOT_CMD(
fb, 1, 1, do_fastboot,
"android fastboot client application",
""
);