| /* |
| * 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", |
| "" |
| ); |