summaryrefslogtreecommitdiff
path: root/common/cmd_fastboot.c
diff options
context:
space:
mode:
authorTom Rix <Tom.Rix@windriver.com>2010-11-16 19:53:03 +0800
committerSammy He <r62914@freescale.com>2010-11-16 23:13:12 +0800
commit21f34664b93c53d2f9bfd9d4606e5f422705b192 (patch)
treecdfdd3e238a51107fbdc5f96b8f35ce5be86316c /common/cmd_fastboot.c
parent5992019f3fc6bddf9c2154d6d1a8c99759a24e4f (diff)
downloadu-boot-imx-21f34664b93c53d2f9bfd9d4606e5f422705b192.zip
u-boot-imx-21f34664b93c53d2f9bfd9d4606e5f422705b192.tar.gz
u-boot-imx-21f34664b93c53d2f9bfd9d4606e5f422705b192.tar.bz2
Get common/cmd_fastboot.c and include/fastboot.h from omap uboot git.
The code realized common Android USB fastboot protocol. The snapshot can be found of the link: http://dev.omapzoom.org/?p=bootloader/u-boot.git;a=commit; h=9c7c5d8ea2326bf8d2d0bfbe670455fb497eb053 Signed-off-by: Tom Rix <Tom.Rix@windriver.com> Signed-off-by: Moiz Sonasath <m-sonasath@ti.com>
Diffstat (limited to 'common/cmd_fastboot.c')
-rw-r--r--common/cmd_fastboot.c1775
1 files changed, 1775 insertions, 0 deletions
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c
new file mode 100644
index 0000000..bbea12b
--- /dev/null
+++ b/common/cmd_fastboot.c
@@ -0,0 +1,1775 @@
+/*
+ * Copyright 2008 - 2009 (C) Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Part of the rx_handler were copied from the Android project.
+ * Specifically rx command parsing in the usb_rx_data_complete
+ * function of the file bootable/bootloader/legacy/usbloader/usbloader.c
+ *
+ * The logical naming of flash comes from the Android project
+ * Thse structures and functions that look like fastboot_flash_*
+ * They come from bootable/bootloader/legacy/libboot/flash.c
+ *
+ * This is their Copyright:
+ *
+ * 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.
+ */
+#include <asm/byteorder.h>
+#include <common.h>
+#include <command.h>
+#include <nand.h>
+#include <fastboot.h>
+#include <environment.h>
+
+#if (CONFIG_FASTBOOT)
+
+/* Use do_reset for fastboot's 'reboot' command */
+extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+/* Use do_nand for fastboot's flash commands */
+#if defined(CONFIG_STORAGE_NAND)
+extern int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]);
+#elif defined(CONFIG_STORAGE_EMMC)
+extern int do_mmc(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern env_t *env_ptr;
+#endif
+/* Use do_setenv and do_saveenv to permenantly save data */
+int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+int do_setenv ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+/* Use do_bootm and do_go for fastboot's 'boot' command */
+int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+
+/* Forward decl */
+static int tx_handler(void);
+static int rx_handler (const unsigned char *buffer, unsigned int buffer_size);
+static void reset_handler (void);
+
+static struct cmd_fastboot_interface interface =
+{
+ .rx_handler = rx_handler,
+ .reset_handler = reset_handler,
+ .product_name = NULL,
+ .serial_no = NULL,
+ .nand_block_size = 0,
+ .transfer_buffer = (unsigned char *)0xffffffff,
+ .transfer_buffer_size = 0,
+};
+
+static unsigned int download_size;
+static unsigned int download_bytes;
+static unsigned int download_bytes_unpadded;
+static unsigned int download_error;
+static unsigned int continue_booting;
+static unsigned int upload_size;
+static unsigned int upload_bytes;
+static unsigned int upload_error;
+static unsigned int mmc_controller_no;
+
+/* To support the Android-style naming of flash */
+#define MAX_PTN 16
+static fastboot_ptentry ptable[MAX_PTN];
+static unsigned int pcount;
+static int static_pcount = -1;
+
+static void set_env(char *var, char *val)
+{
+ char *setenv[4] = { "setenv", NULL, NULL, NULL, };
+
+ setenv[1] = var;
+ setenv[2] = val;
+
+ do_setenv(NULL, 0, 3, setenv);
+}
+
+static void save_env(struct fastboot_ptentry *ptn,
+ char *var, char *val)
+{
+ char start[32], length[32];
+ char ecc_type[32];
+
+ char *lock[5] = { "nand", "lock", NULL, NULL, NULL, };
+ char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, };
+ char *ecc[4] = { "nand", "ecc", NULL, NULL, };
+ char *saveenv[2] = { "setenv", NULL, };
+
+ lock[2] = unlock[2] = start;
+ lock[3] = unlock[3] = length;
+
+ set_env (var, val);
+
+ /* Some flashing requires the nand's ecc to be set */
+ ecc[2] = ecc_type;
+ if ((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) &&
+ (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) {
+ /* Both can not be true */
+ printf("Warning can not do hw and sw ecc for partition '%s'\n", ptn->name);
+ printf("Ignoring these flags\n");
+ } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) {
+ sprintf(ecc_type, "hw");
+ do_nand(NULL, 0, 3, ecc);
+ } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) {
+ sprintf(ecc_type, "sw");
+ do_nand(NULL, 0, 3, ecc);
+ }
+ sprintf(start, "0x%x", ptn->start);
+ sprintf(length, "0x%x", ptn->length);
+
+ /* This could be a problem is there is an outstanding lock */
+ do_nand(NULL, 0, 4, unlock);
+ do_saveenv(NULL, 0, 1, saveenv);
+ do_nand(NULL, 0, 4, lock);
+}
+
+static void save_block_values(struct fastboot_ptentry *ptn,
+ unsigned int offset,
+ unsigned int size)
+{
+ struct fastboot_ptentry *env_ptn;
+
+ char var[64], val[32];
+ char start[32], length[32];
+ char ecc_type[32];
+
+ char *lock[5] = { "nand", "lock", NULL, NULL, NULL, };
+ char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, };
+ char *ecc[4] = { "nand", "ecc", NULL, NULL, };
+ char *setenv[4] = { "setenv", NULL, NULL, NULL, };
+ char *saveenv[2] = { "setenv", NULL, };
+
+ setenv[1] = var;
+ setenv[2] = val;
+ lock[2] = unlock[2] = start;
+ lock[3] = unlock[3] = length;
+
+ printf ("saving it..\n");
+
+ if (size == 0)
+ {
+ /* The error case, where the variables are being unset */
+
+ sprintf (var, "%s_nand_offset", ptn->name);
+ sprintf (val, "");
+ do_setenv (NULL, 0, 3, setenv);
+
+ sprintf (var, "%s_nand_size", ptn->name);
+ sprintf (val, "");
+ do_setenv (NULL, 0, 3, setenv);
+ }
+ else
+ {
+ /* Normal case */
+
+ sprintf (var, "%s_nand_offset", ptn->name);
+ sprintf (val, "0x%x", offset);
+
+ printf ("%s %s %s\n", setenv[0], setenv[1], setenv[2]);
+
+ do_setenv (NULL, 0, 3, setenv);
+
+ sprintf (var, "%s_nand_size", ptn->name);
+
+ sprintf (val, "0x%x", size);
+
+ printf ("%s %s %s\n", setenv[0], setenv[1], setenv[2]);
+
+ do_setenv (NULL, 0, 3, setenv);
+ }
+
+
+ /* Warning :
+ The environment is assumed to be in a partition named 'enviroment'.
+ It is very possible that your board stores the enviroment
+ someplace else. */
+ env_ptn = fastboot_flash_find_ptn("environment");
+
+ if (env_ptn)
+ {
+ /* Some flashing requires the nand's ecc to be set */
+ ecc[2] = ecc_type;
+ if ((env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) &&
+ (env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC))
+ {
+ /* Both can not be true */
+ printf ("Warning can not do hw and sw ecc for partition '%s'\n", ptn->name);
+ printf ("Ignoring these flags\n");
+ }
+ else if (env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC)
+ {
+ sprintf (ecc_type, "hw");
+ do_nand (NULL, 0, 3, ecc);
+ }
+ else if (env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)
+ {
+ sprintf (ecc_type, "sw");
+ do_nand (NULL, 0, 3, ecc);
+ }
+
+ sprintf (start, "0x%x", env_ptn->start);
+ sprintf (length, "0x%x", env_ptn->length);
+
+ /* This could be a problem is there is an outstanding lock */
+ do_nand (NULL, 0, 4, unlock);
+ }
+
+ do_saveenv (NULL, 0, 1, saveenv);
+
+ if (env_ptn)
+ {
+ do_nand (NULL, 0, 4, lock);
+ }
+}
+
+static void reset_handler ()
+{
+ /* If there was a download going on, bail */
+ download_size = 0;
+ download_bytes = 0;
+ download_bytes_unpadded = 0;
+ download_error = 0;
+ continue_booting = 0;
+ upload_size = 0;
+ upload_bytes = 0;
+ upload_error = 0;
+}
+
+/* When save = 0, just parse. The input is unchanged
+ When save = 1, parse and do the save. The input is changed */
+static int parse_env(void *ptn, char *err_string, int save, int debug)
+{
+ int ret = 1;
+ unsigned int sets = 0;
+ unsigned int comment_start = 0;
+ char *var = NULL;
+ char *var_end = NULL;
+ char *val = NULL;
+ char *val_end = NULL;
+ unsigned int i;
+
+ char *buff = (char *)interface.transfer_buffer;
+ unsigned int size = download_bytes_unpadded;
+
+ /* The input does not have to be null terminated.
+ This will cause a problem in the corner case
+ where the last line does not have a new line.
+ Put a null after the end of the input.
+
+ WARNING : Input buffer is assumed to be bigger
+ than the size of the input */
+ if (save)
+ buff[size] = 0;
+
+ for (i = 0; i < size; i++) {
+
+ if (NULL == var) {
+
+ /*
+ * Check for comments, comment ok only on
+ * mostly empty lines
+ */
+ if (buff[i] == '#')
+ comment_start = 1;
+
+ if (comment_start) {
+ if ((buff[i] == '\r') ||
+ (buff[i] == '\n')) {
+ comment_start = 0;
+ }
+ } else {
+ if (!((buff[i] == ' ') ||
+ (buff[i] == '\t') ||
+ (buff[i] == '\r') ||
+ (buff[i] == '\n'))) {
+ /*
+ * Normal whitespace before the
+ * variable
+ */
+ var = &buff[i];
+ }
+ }
+
+ } else if (((NULL == var_end) || (NULL == val)) &&
+ ((buff[i] == '\r') || (buff[i] == '\n'))) {
+
+ /* This is the case when a variable
+ is unset. */
+
+ if (save) {
+ /* Set the var end to null so the
+ normal string routines will work
+
+ WARNING : This changes the input */
+ buff[i] = '\0';
+
+ save_env(ptn, var, val);
+
+ if (debug)
+ printf("Unsetting %s\n", var);
+ }
+
+ /* Clear the variable so state is parse is back
+ to initial. */
+ var = NULL;
+ var_end = NULL;
+ sets++;
+ } else if (NULL == var_end) {
+ if ((buff[i] == ' ') ||
+ (buff[i] == '\t'))
+ var_end = &buff[i];
+ } else if (NULL == val) {
+ if (!((buff[i] == ' ') ||
+ (buff[i] == '\t')))
+ val = &buff[i];
+ } else if (NULL == val_end) {
+ if ((buff[i] == '\r') ||
+ (buff[i] == '\n')) {
+ /* look for escaped cr or ln */
+ if ('\\' == buff[i - 1]) {
+ /* check for dos */
+ if ((buff[i] == '\r') &&
+ (buff[i+1] == '\n'))
+ buff[i + 1] = ' ';
+ buff[i - 1] = buff[i] = ' ';
+ } else {
+ val_end = &buff[i];
+ }
+ }
+ } else {
+ sprintf(err_string, "Internal Error");
+
+ if (debug)
+ printf("Internal error at %s %d\n",
+ __FILE__, __LINE__);
+ return 1;
+ }
+ /* Check if a var / val pair is ready */
+ if (NULL != val_end) {
+ if (save) {
+ /* Set the end's with nulls so
+ normal string routines will
+ work.
+
+ WARNING : This changes the input */
+ *var_end = '\0';
+ *val_end = '\0';
+
+ save_env(ptn, var, val);
+
+ if (debug)
+ printf("Setting %s %s\n", var, val);
+ }
+
+ /* Clear the variable so state is parse is back
+ to initial. */
+ var = NULL;
+ var_end = NULL;
+ val = NULL;
+ val_end = NULL;
+
+ sets++;
+ }
+ }
+
+ /* Corner case
+ Check for the case that no newline at end of the input */
+ if ((NULL != var) &&
+ (NULL == val_end)) {
+ if (save) {
+ /* case of val / val pair */
+ if (var_end)
+ *var_end = '\0';
+ /* else case handled by setting 0 past
+ the end of buffer.
+ Similar for val_end being null */
+ save_env(ptn, var, val);
+
+ if (debug) {
+ if (var_end)
+ printf("Trailing Setting %s %s\n", var, val);
+ else
+ printf("Trailing Unsetting %s\n", var);
+ }
+ }
+ sets++;
+ }
+ /* Did we set anything ? */
+ if (0 == sets)
+ sprintf(err_string, "No variables set");
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int saveenv_to_ptn(struct fastboot_ptentry *ptn, char *err_string)
+{
+ int ret = 1;
+ int save = 0;
+ int debug = 0;
+
+ /* err_string is only 32 bytes
+ Initialize with a generic error message. */
+ sprintf(err_string, "%s", "Unknown Error");
+
+ /* Parse the input twice.
+ Only save to the enviroment if the entire input if correct */
+ save = 0;
+ if (0 == parse_env(ptn, err_string, save, debug)) {
+ save = 1;
+ ret = parse_env(ptn, err_string, save, debug);
+ }
+ return ret;
+}
+
+static void set_ptn_ecc(struct fastboot_ptentry *ptn)
+{
+ char ecc_type[32];
+ char *ecc[4] = {"nand", "ecc", NULL, NULL, };
+
+ /* Some flashing requires the nand's ecc to be set */
+ ecc[2] = ecc_type;
+ if ((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) &&
+ (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) {
+ /* Both can not be true */
+ printf("Warning can not do hw and sw ecc for partition '%s'\n",
+ ptn->name);
+ printf("Ignoring these flags\n");
+ } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) {
+ sprintf(ecc_type, "hw");
+ do_nand(NULL, 0, 3, ecc);
+ } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) {
+ sprintf(ecc_type, "sw");
+ do_nand(NULL, 0, 3, ecc);
+ }
+}
+
+static int write_to_ptn(struct fastboot_ptentry *ptn)
+{
+ int ret = 1;
+ char start[32], length[32];
+ char wstart[32], wlength[32], addr[32];
+ char write_type[32];
+ int repeat, repeat_max;
+
+ char *lock[5] = { "nand", "lock", NULL, NULL, NULL, };
+ char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, };
+ char *write[6] = { "nand", "write", NULL, NULL, NULL, NULL, };
+ char *erase[5] = { "nand", "erase", NULL, NULL, NULL, };
+
+ lock[2] = unlock[2] = erase[2] = start;
+ lock[3] = unlock[3] = erase[3] = length;
+
+ write[1] = write_type;
+ write[2] = addr;
+ write[3] = wstart;
+ write[4] = wlength;
+
+ printf("flashing '%s'\n", ptn->name);
+
+ /* Which flavor of write to use */
+ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_I)
+ sprintf(write_type, "write.i");
+#ifdef CFG_NAND_YAFFS_WRITE
+ else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS)
+ sprintf(write_type, "write.yaffs");
+#endif
+ else
+ sprintf(write_type, "write");
+
+ set_ptn_ecc(ptn);
+
+ /* Some flashing requires writing the same data in multiple,
+ consecutive flash partitions */
+ repeat_max = 1;
+ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK) {
+ if (ptn->flags &
+ FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) {
+ printf("Warning can not do both 'contiguous block' and 'repeat' writes for for partition '%s'\n", ptn->name);
+ printf("Ignoring repeat flag\n");
+ } else {
+ repeat_max = ptn->flags &
+ FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK;
+ }
+ }
+
+ /* Unlock the whole partition instead of trying to
+ manage special cases */
+ sprintf(length, "0x%x", ptn->length * repeat_max);
+
+ for (repeat = 0; repeat < repeat_max; repeat++) {
+ sprintf(start, "0x%x", ptn->start + (repeat * ptn->length));
+
+ do_nand(NULL, 0, 4, unlock);
+ do_nand(NULL, 0, 4, erase);
+
+ if ((ptn->flags &
+ FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) &&
+ (ptn->flags &
+ FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK)) {
+ /* Both can not be true */
+ printf("Warning can not do 'next good block' and 'contiguous block' for partition '%s'\n", ptn->name);
+ printf("Ignoring these flags\n");
+ } else if (ptn->flags &
+ FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) {
+ /* Keep writing until you get a good block
+ transfer_buffer should already be aligned */
+ if (interface.nand_block_size) {
+ unsigned int blocks = download_bytes /
+ interface.nand_block_size;
+ unsigned int i = 0;
+ unsigned int offset = 0;
+
+ sprintf(wlength, "0x%x",
+ interface.nand_block_size);
+ while (i < blocks) {
+ /* Check for overflow */
+ if (offset >= ptn->length)
+ break;
+
+ /* download's address only advance
+ if last write was successful */
+ sprintf(addr, "0x%x",
+ interface.transfer_buffer +
+ (i * interface.nand_block_size));
+
+ /* nand's address always advances */
+ sprintf(wstart, "0x%x",
+ ptn->start + (repeat * ptn->length) + offset);
+
+ ret = do_nand(NULL, 0, 5, write);
+ if (ret)
+ break;
+ else
+ i++;
+
+ /* Go to next nand block */
+ offset += interface.nand_block_size;
+ }
+ } else {
+ printf("Warning nand block size can not be 0 when using 'next good block' for partition '%s'\n", ptn->name);
+ printf("Ignoring write request\n");
+ }
+ } else if (ptn->flags &
+ FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) {
+ /* Keep writing until you get a good block
+ transfer_buffer should already be aligned */
+ if (interface.nand_block_size) {
+ if (0 == nand_curr_device) {
+ nand_info_t *nand;
+ unsigned long off;
+ unsigned int ok_start;
+
+ nand = &nand_info[nand_curr_device];
+
+ printf("\nDevice %d bad blocks:\n",
+ nand_curr_device);
+
+ /* Initialize the ok_start to the
+ start of the partition
+ Then try to find a block large
+ enough for the download */
+ ok_start = ptn->start;
+
+ /* It is assumed that the start and
+ length are multiples of block size */
+ for (off = ptn->start;
+ off < ptn->start + ptn->length;
+ off += nand->erasesize) {
+ if (nand_block_isbad(nand, off)) {
+ /* Reset the ok_start
+ to the next block */
+ ok_start = off +
+ nand->erasesize;
+ }
+
+ /* Check if we have enough
+ blocks */
+ if ((ok_start - off) >=
+ download_bytes)
+ break;
+ }
+
+ /* Check if there is enough space */
+ if (ok_start + download_bytes <=
+ ptn->start + ptn->length) {
+ sprintf(addr, "0x%x", interface.transfer_buffer);
+ sprintf(wstart, "0x%x", ok_start);
+ sprintf(wlength, "0x%x", download_bytes);
+
+ ret = do_nand(NULL, 0, 5, write);
+
+ /* Save the results into an
+ environment variable on the
+ format
+ ptn_name + 'offset'
+ ptn_name + 'size' */
+ if (ret) {
+ /* failed */
+ save_block_values(ptn, 0, 0);
+ } else {
+ /* success */
+ save_block_values(ptn, ok_start, download_bytes);
+ }
+ } else {
+ printf("Error could not find enough contiguous space in partition '%s' \n", ptn->name);
+ printf("Ignoring write request\n");
+ }
+ } else {
+ /* TBD : Generalize flash handling */
+ printf("Error only handling 1 NAND per board");
+ printf("Ignoring write request\n");
+ }
+ } else {
+ printf("Warning nand block size can not be 0 when using 'continuous block' for partition '%s'\n", ptn->name);
+ printf("Ignoring write request\n");
+ }
+ } else {
+ /* Normal case */
+ sprintf(addr, "0x%x", interface.transfer_buffer);
+ sprintf(wstart, "0x%x", ptn->start +
+ (repeat * ptn->length));
+ sprintf(wlength, "0x%x", download_bytes);
+#ifdef CFG_NAND_YAFFS_WRITE
+ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS)
+ sprintf(wlength, "0x%x",
+ download_bytes_unpadded);
+#endif
+
+ ret = do_nand(NULL, 0, 5, write);
+
+ if (0 == repeat) {
+ if (ret) /* failed */
+ save_block_values(ptn, 0, 0);
+ else /* success */
+ save_block_values(ptn, ptn->start,
+ download_bytes);
+ }
+ }
+
+ do_nand(NULL, 0, 4, lock);
+
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int tx_handler(void)
+{
+ if (upload_size) {
+
+ int bytes_written;
+ bytes_written = fastboot_tx(interface.transfer_buffer +
+ upload_bytes, upload_size -
+ upload_bytes);
+ if (bytes_written > 0) {
+
+ upload_bytes += bytes_written;
+ /* Check if this is the last */
+ if (upload_bytes == upload_size) {
+
+ /* Reset upload */
+ upload_size = 0;
+ upload_bytes = 0;
+ upload_error = 0;
+ }
+ }
+ }
+ return upload_error;
+}
+
+static int rx_handler (const unsigned char *buffer, unsigned int buffer_size)
+{
+ int ret = 1;
+
+ /* Use 65 instead of 64
+ null gets dropped
+ strcpy's need the extra byte */
+ char response[65];
+
+ if (download_size)
+ {
+ /* Something to download */
+
+ if (buffer_size)
+ {
+ /* Handle possible overflow */
+ unsigned int transfer_size =
+ download_size - download_bytes;
+
+ if (buffer_size < transfer_size)
+ transfer_size = buffer_size;
+
+ /* Save the data to the transfer buffer */
+ memcpy (interface.transfer_buffer + download_bytes,
+ buffer, transfer_size);
+
+ download_bytes += transfer_size;
+
+ /* Check if transfer is done */
+ if (download_bytes >= download_size) {
+ /* Reset global transfer variable,
+ Keep download_bytes because it will be
+ used in the next possible flashing command */
+ download_size = 0;
+
+ if (download_error) {
+ /* There was an earlier error */
+ sprintf(response, "ERROR");
+ } else {
+ /* Everything has transferred,
+ send the OK response */
+ sprintf(response, "OKAY");
+ }
+ fastboot_tx_status(response, strlen(response));
+
+ printf ("\ndownloading of %d bytes finished\n",
+ download_bytes);
+
+#if defined(CONFIG_STORAGE_NAND)
+ /* Pad to block length
+ In most cases, padding the download to be
+ block aligned is correct. The exception is
+ when the following flash writes to the oob
+ area. This happens when the image is a
+ YAFFS image. Since we do not know what
+ the download is until it is flashed,
+ go ahead and pad it, but save the true
+ size in case if should have
+ been unpadded */
+ download_bytes_unpadded = download_bytes;
+ if (interface.nand_block_size)
+ {
+ if (download_bytes %
+ interface.nand_block_size)
+ {
+ unsigned int pad = interface.nand_block_size - (download_bytes % interface.nand_block_size);
+ unsigned int i;
+
+ for (i = 0; i < pad; i++)
+ {
+ if (download_bytes >= interface.transfer_buffer_size)
+ break;
+
+ interface.transfer_buffer[download_bytes] = 0;
+ download_bytes++;
+ }
+ }
+ }
+#endif
+ }
+
+ /* Provide some feedback */
+ if (download_bytes &&
+ 0 == (download_bytes %
+ (16 * interface.nand_block_size)))
+ {
+ /* Some feeback that the
+ download is happening */
+ if (download_error)
+ printf("X");
+ else
+ printf(".");
+ if (0 == (download_bytes %
+ (80 * 16 *
+ interface.nand_block_size)))
+ printf("\n");
+
+ }
+ }
+ else
+ {
+ /* Ignore empty buffers */
+ printf ("Warning empty download buffer\n");
+ printf ("Ignoring\n");
+ }
+ ret = 0;
+ }
+ else
+ {
+ /* A command */
+
+ /* Cast to make compiler happy with string functions */
+ const char *cmdbuf = (char *) buffer;
+
+ /* Generic failed response */
+ sprintf(response, "FAIL");
+
+ /* reboot
+ Reboot the board. */
+
+ if(memcmp(cmdbuf, "reboot", 6) == 0)
+ {
+ sprintf(response,"OKAY");
+ fastboot_tx_status(response, strlen(response));
+ udelay (1000000); /* 1 sec */
+
+ do_reset (NULL, 0, 0, NULL);
+
+ /* This code is unreachable,
+ leave it to make the compiler happy */
+ return 0;
+ }
+
+ /* getvar
+ Get common fastboot variables
+ Board has a chance to handle other variables */
+ if(memcmp(cmdbuf, "getvar:", 7) == 0)
+ {
+ strcpy(response,"OKAY");
+
+ if(!strcmp(cmdbuf + strlen("version"), "version"))
+ {
+ strcpy(response + 4, FASTBOOT_VERSION);
+ }
+ else if(!strcmp(cmdbuf + strlen("product"), "product"))
+ {
+ if (interface.product_name)
+ strcpy(response + 4, interface.product_name);
+
+ } else if(!strcmp(cmdbuf + strlen("serialno"), "serialno")) {
+ if (interface.serial_no)
+ strcpy(response + 4, interface.serial_no);
+
+ } else if(!strcmp(cmdbuf + strlen("downloadsize"), "downloadsize")) {
+ if (interface.transfer_buffer_size)
+ sprintf(response + 4, "08x", interface.transfer_buffer_size);
+ }
+ else
+ {
+ fastboot_getvar(cmdbuf + 7, response + 4);
+ }
+ ret = 0;
+
+ }
+
+ /* erase
+ Erase a register flash partition
+ Board has to set up flash partitions */
+
+ if(memcmp(cmdbuf, "erase:", 6) == 0){
+#if defined(CONFIG_STORAGE_NAND)
+ struct fastboot_ptentry *ptn;
+
+ ptn = fastboot_flash_find_ptn(cmdbuf + 6);
+ if(ptn == 0)
+ {
+ sprintf(response, "FAILpartition does not exist");
+ }
+ else
+ {
+ char start[32], length[32];
+ int status, repeat, repeat_max;
+
+ printf("erasing '%s'\n", ptn->name);
+
+ char *lock[5] = { "nand", "lock", NULL, NULL, NULL, };
+ char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, };
+ char *erase[5] = { "nand", "erase", NULL, NULL, NULL, };
+
+ lock[2] = unlock[2] = erase[2] = start;
+ lock[3] = unlock[3] = erase[3] = length;
+
+ repeat_max = 1;
+ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK)
+ repeat_max = ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK;
+
+ sprintf (length, "0x%x", ptn->length);
+ for (repeat = 0; repeat < repeat_max; repeat++)
+ {
+ sprintf (start, "0x%x", ptn->start + (repeat * ptn->length));
+
+ do_nand (NULL, 0, 4, unlock);
+ status = do_nand (NULL, 0, 4, erase);
+ do_nand (NULL, 0, 4, lock);
+
+ if (status)
+ break;
+ }
+
+ if (status)
+ {
+ sprintf(response,"FAILfailed to erase partition");
+ }
+ else
+ {
+ printf("partition '%s' erased\n", ptn->name);
+ sprintf(response, "OKAY");
+ }
+
+ }
+#elif defined(CONFIG_STORAGE_EMMC)
+ struct fastboot_ptentry *ptn;
+
+ /* Save the MMC controller number */
+ mmc_controller_no = CFG_FASTBOOT_MMC_NO;
+
+ /* Find the partition and erase it */
+ ptn = fastboot_flash_find_ptn(cmdbuf + 6);
+
+ if (ptn == 0) {
+ sprintf(response, "FAIL: partition doesn't exist");
+ } else {
+ /* Call MMC erase function here */
+ char start[32], length[32];
+ char slot_no[32];
+
+ char *erase[5] = { "mmc", NULL, "erase", NULL, NULL, };
+ char *mmc_init[2] = {"mmcinit", NULL,};
+
+ mmc_init[1] = slot_no;
+ erase[1] = slot_no;
+ erase[3] = start;
+ erase[4] = length;
+
+ sprintf(slot_no, "%d", mmc_controller_no);
+ sprintf(length, "0x%x", ptn->length);
+ sprintf(start, "0x%x", ptn->start);
+
+ printf("Initializing '%s'\n", ptn->name);
+ if (do_mmc(NULL, 0, 2, mmc_init))
+ sprintf(response, "FAIL: Init of MMC card");
+ else
+ sprintf(response, "OKAY");
+
+ printf("Erasing '%s'\n", ptn->name);
+ if (do_mmc(NULL, 0, 5, erase)) {
+ printf("Erasing '%s' FAILED!\n", ptn->name);
+ sprintf(response, "FAIL: Erase partition");
+ } else {
+ printf("Erasing '%s' DONE!\n", ptn->name);
+ sprintf(response, "OKAY");
+ }
+ }
+#endif
+ ret = 0;
+ }
+
+ /* download
+ download something ..
+ What happens to it depends on the next command after data */
+
+ if(memcmp(cmdbuf, "download:", 9) == 0) {
+
+ /* save the size */
+ download_size = simple_strtoul (cmdbuf + 9, NULL, 16);
+ /* Reset the bytes count, now it is safe */
+ download_bytes = 0;
+ /* Reset error */
+ download_error = 0;
+
+ printf ("Starting download of %d bytes\n", download_size);
+
+ if (0 == download_size)
+ {
+ /* bad user input */
+ sprintf(response, "FAILdata invalid size");
+ }
+ else if (download_size > interface.transfer_buffer_size)
+ {
+ /* set download_size to 0 because this is an error */
+ download_size = 0;
+ sprintf(response, "FAILdata too large");
+ }
+ else
+ {
+ /* The default case, the transfer fits
+ completely in the interface buffer */
+ sprintf(response, "DATA%08x", download_size);
+ }
+ ret = 0;
+ }
+
+ /* boot
+ boot what was downloaded
+
+ WARNING WARNING WARNING
+
+ This is not what you expect.
+ The fastboot client does its own packaging of the
+ kernel. The layout is defined in the android header
+ file bootimage.h. This layeout is copiedlooks like this,
+
+ **
+ ** +-----------------+
+ ** | boot header | 1 page
+ ** +-----------------+
+ ** | kernel | n pages
+ ** +-----------------+
+ ** | ramdisk | m pages
+ ** +-----------------+
+ ** | second stage | o pages
+ ** +-----------------+
+ **
+
+ We only care about the kernel.
+ So we have to jump past a page.
+
+ What is a page size ?
+ The fastboot client uses 2048
+
+ The is the default value of
+
+ CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE
+
+ */
+
+ if(memcmp(cmdbuf, "boot", 4) == 0) {
+
+ if ((download_bytes) &&
+ (CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < download_bytes))
+ {
+ char start[32];
+ char *bootm[3] = { "bootm", NULL, NULL, };
+ char *go[3] = { "go", NULL, NULL, };
+
+ /*
+ * Use this later to determine if a command line was passed
+ * for the kernel.
+ */
+ struct fastboot_boot_img_hdr *fb_hdr =
+ (struct fastboot_boot_img_hdr *) interface.transfer_buffer;
+
+ /* Skip the mkbootimage header */
+ image_header_t *hdr =
+ (image_header_t *)
+ &interface.transfer_buffer[CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE];
+
+ bootm[1] = go[1] = start;
+ sprintf (start, "0x%x", hdr);
+
+ /* Execution should jump to kernel so send the response
+ now and wait a bit. */
+ sprintf(response, "OKAY");
+ fastboot_tx_status(response, strlen(response));
+ udelay (1000000); /* 1 sec */
+
+ if (ntohl(hdr->ih_magic) == IH_MAGIC) {
+ /* Looks like a kernel.. */
+ printf ("Booting kernel..\n");
+
+ /*
+ * Check if the user sent a bootargs down.
+ * If not, do not override what is already there
+ */
+ if (strlen ((char *) &fb_hdr->cmdline[0]))
+ set_env ("bootargs", (char *) &fb_hdr->cmdline[0]);
+
+ do_bootm (NULL, 0, 2, bootm);
+ } else {
+ /* Raw image, maybe another uboot */
+ printf ("Booting raw image..\n");
+
+ do_go (NULL, 0, 2, go);
+ }
+ printf ("ERROR : bootting failed\n");
+ printf ("You should reset the board\n");
+ }
+ sprintf(response, "FAILinvalid boot image");
+ ret = 0;
+ }
+
+ /* flash
+ Flash what was downloaded */
+
+ if(memcmp(cmdbuf, "flash:", 6) == 0) {
+#if defined(CONFIG_STORAGE_NAND)
+ if (download_bytes)
+ {
+ struct fastboot_ptentry *ptn;
+
+ ptn = fastboot_flash_find_ptn(cmdbuf + 6);
+ if (ptn == 0) {
+ sprintf(response, "FAILpartition does not exist");
+ } else if ((download_bytes > ptn->length) &&
+ !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) {
+ sprintf(response, "FAILimage too large for partition");
+ /* TODO : Improve check for yaffs write */
+ } else {
+ /* Check if this is not really a flash write
+ but rather a saveenv */
+ if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) {
+ /* Since the response can only be 64 bytes,
+ there is no point in having a large error message. */
+ char err_string[32];
+ if (saveenv_to_ptn(ptn, &err_string[0])) {
+ printf("savenv '%s' failed : %s\n", ptn->name, err_string);
+ sprintf(response, "FAIL%s", err_string);
+ } else {
+ printf("partition '%s' saveenv-ed\n", ptn->name);
+ sprintf(response, "OKAY");
+ }
+ } else {
+ /* Normal case */
+ if (write_to_ptn(ptn)) {
+ printf("flashing '%s' failed\n", ptn->name);
+ sprintf(response, "FAILfailed to flash partition");
+ } else {
+ printf("partition '%s' flashed\n", ptn->name);
+ sprintf(response, "OKAY");
+ }
+ }
+ }
+ }
+ else
+ {
+ sprintf(response, "FAILno image downloaded");
+ }
+#elif defined(CONFIG_STORAGE_EMMC)
+ if (download_bytes) {
+
+ struct fastboot_ptentry *ptn;
+
+ /* Save the MMC controller number */
+ mmc_controller_no = CFG_FASTBOOT_MMC_NO;
+
+ /* Next is the partition name */
+ ptn = fastboot_flash_find_ptn(cmdbuf + 6);
+
+ if (ptn == 0) {
+ printf("Partition:'%s' does not exist\n", ptn->name);
+ sprintf(response, "FAILpartition does not exist");
+ } else if ((download_bytes > ptn->length) &&
+ !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) {
+ printf("Image too large for the partition\n");
+ sprintf(response, "FAILimage too large for partition");
+ } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) {
+ /* Check if this is not really a flash write,
+ * but instead a saveenv
+ */
+ unsigned int i = 0;
+ /* Env file is expected with a NULL delimeter between
+ * env variables So replace New line Feeds (0x0a) with
+ * NULL (0x00)
+ */
+ for (i = 0; i < download_bytes; i++) {
+ if (interface.transfer_buffer[i] == 0x0a)
+ interface.transfer_buffer[i] = 0x00;
+ }
+ memset(env_ptr->data, 0, ENV_SIZE);
+ memcpy(env_ptr->data, interface.transfer_buffer, download_bytes);
+ do_saveenv(NULL, 0, 1, NULL);
+ printf("saveenv to '%s' DONE!\n", ptn->name);
+ sprintf(response, "OKAY");
+ } else {
+ /* Normal case */
+
+ char source[32], dest[32], length[32];
+ char slot_no[32];
+
+ printf("writing to partition '%s'\n", ptn->name);
+ char *mmc_write[6] = {"mmc", NULL, "write", NULL, NULL, NULL};
+ char *mmc_init[2] = {"mmcinit", NULL,};
+
+ mmc_init[1] = slot_no;
+ mmc_write[1] = slot_no;
+ mmc_write[3] = source;
+ mmc_write[4] = dest;
+ mmc_write[5] = length;
+
+ sprintf(slot_no, "%d", mmc_controller_no);
+ sprintf(source, "0x%x", interface.transfer_buffer);
+ sprintf(dest, "0x%x", ptn->start);
+ sprintf(length, "0x%x", download_bytes);
+
+ printf("Initializing '%s'\n", ptn->name);
+ if (do_mmc(NULL, 0, 2, mmc_init))
+ sprintf(response, "FAIL:Init of MMC card");
+ else
+ sprintf(response, "OKAY");
+
+
+ printf("Writing '%s'\n", ptn->name);
+ if (do_mmc(NULL, 0, 6, mmc_write)) {
+ printf("Writing '%s' FAILED!\n", ptn->name);
+ sprintf(response, "FAIL: Write partition");
+ } else {
+ printf("Writing '%s' DONE!\n", ptn->name);
+ sprintf(response, "OKAY");
+ }
+ }
+
+ } else {
+ sprintf(response, "FAILno image downloaded");
+ }
+#endif
+ ret = 0;
+ }
+
+ /* continue
+ Stop doing fastboot */
+ if (memcmp(cmdbuf, "continue", 8) == 0) {
+ sprintf(response, "OKAY");
+ continue_booting = 1;
+ ret = 0;
+ }
+
+ /* upload
+ Upload just the data in a partition */
+ if ((memcmp(cmdbuf, "upload:", 7) == 0) ||
+ (memcmp(cmdbuf, "uploadraw:", 10) == 0)) {
+#if defined(CONFIG_STORAGE_NAND)
+ unsigned int adv, delim_index, len;
+ struct fastboot_ptentry *ptn;
+ unsigned int is_raw = 0;
+
+ /* Is this a raw read ? */
+ if (memcmp(cmdbuf, "uploadraw:", 10) == 0) {
+ is_raw = 1;
+ adv = 10;
+ } else {
+ adv = 7;
+ }
+
+ /* Scan to the next ':' to find when the size starts */
+ len = strlen(cmdbuf);
+ for (delim_index = adv;
+ delim_index < len; delim_index++) {
+ if (cmdbuf[delim_index] == ':') {
+ /* WARNING, cmdbuf is being modified. */
+ *((char *) &cmdbuf[delim_index]) = 0;
+ break;
+ }
+ }
+
+ ptn = fastboot_flash_find_ptn(cmdbuf + adv);
+ if (ptn == 0) {
+ sprintf(response,
+ "FAILpartition does not exist");
+ } else {
+ /* This is how much the user is expecting */
+ unsigned int user_size;
+ /*
+ * This is the maximum size needed for
+ * this partition
+ */
+ unsigned int size;
+ /* This is the length of the data */
+ unsigned int length;
+ /*
+ * Used to check previous write of
+ * the parition
+ */
+ char env_ptn_length_var[128];
+ char *env_ptn_length_val;
+
+ user_size = 0;
+ if (delim_index < len)
+ user_size =
+ simple_strtoul(cmdbuf + delim_index +
+ 1, NULL, 16);
+
+ /* Make sure output is padded to block size */
+ length = ptn->length;
+ sprintf(env_ptn_length_var,
+ "%s_nand_size", ptn->name);
+ env_ptn_length_val = getenv(env_ptn_length_var);
+ if (env_ptn_length_val) {
+ length =
+ simple_strtoul(env_ptn_length_val,
+ NULL, 16);
+ /* Catch possible problems */
+ if (!length)
+ length = ptn->length;
+ }
+
+ size = length / interface.nand_block_size;
+ size *= interface.nand_block_size;
+ if (length % interface.nand_block_size)
+ size += interface.nand_block_size;
+
+ if (is_raw)
+ size += (size /
+ interface.nand_block_size) *
+ interface.nand_oob_size;
+
+ if (size > interface.transfer_buffer_size) {
+
+ sprintf(response, "FAILdata too large");
+
+ } else if (user_size == 0) {
+
+ /* Send the data response */
+ sprintf(response, "DATA%08x", size);
+
+ } else if (user_size != size) {
+ /* This is the wrong size */
+ sprintf(response, "FAIL");
+ } else {
+ /*
+ * This is where the transfer
+ * buffer is populated
+ */
+ unsigned char *buf =
+ interface.transfer_buffer;
+ char start[32], length[32], type[32],
+ addr[32];
+ char *read[6] = { "nand", NULL, NULL,
+ NULL, NULL, NULL, };
+
+ /*
+ * Setting upload_size causes
+ * transfer to happen in main loop
+ */
+ upload_size = size;
+ upload_bytes = 0;
+ upload_error = 0;
+
+ /*
+ * Poison the transfer buffer, 0xff
+ * is erase value of nand
+ */
+ memset(buf, 0xff, upload_size);
+
+ /* Which flavor of read to use */
+ if (is_raw)
+ sprintf(type, "read.raw");
+ else
+ sprintf(type, "read.i");
+
+ sprintf(addr, "0x%x",
+ interface.transfer_buffer);
+ sprintf(start, "0x%x", ptn->start);
+ sprintf(length, "0x%x", upload_size);
+
+ read[1] = type;
+ read[2] = addr;
+ read[3] = start;
+ read[4] = length;
+
+ set_ptn_ecc(ptn);
+
+ do_nand(NULL, 0, 5, read);
+
+ /* Send the data response */
+ sprintf(response, "DATA%08x", size);
+ }
+ }
+#endif
+ ret = 0;
+ }
+
+ fastboot_tx_status(response, strlen(response));
+
+ } /* End of command */
+
+ return ret;
+}
+
+static int check_against_static_partition(struct fastboot_ptentry *ptn)
+{
+ int ret = 0;
+ struct fastboot_ptentry *c;
+ int i;
+
+ for (i = 0; i < static_pcount; i++) {
+ c = fastboot_flash_get_ptn((unsigned int) i);
+
+ if (0 == ptn->length)
+ break;
+
+ if ((ptn->start >= c->start) &&
+ (ptn->start < c->start + c->length))
+ break;
+
+ if ((ptn->start + ptn->length > c->start) &&
+ (ptn->start + ptn->length <= c->start + c->length))
+ break;
+
+ if ((0 == strcmp(ptn->name, c->name)) &&
+ (0 == strcmp(c->name, ptn->name)))
+ break;
+ }
+
+ if (i >= static_pcount)
+ ret = 1;
+ return ret;
+}
+
+static unsigned long long memparse(char *ptr, char **retptr)
+{
+ char *endptr; /* local pointer to end of parsed string */
+
+ unsigned long ret = simple_strtoul(ptr, &endptr, 0);
+
+ switch (*endptr) {
+ case 'M':
+ case 'm':
+ ret <<= 10;
+ case 'K':
+ case 'k':
+ ret <<= 10;
+ endptr++;
+ default:
+ break;
+ }
+
+ if (retptr)
+ *retptr = endptr;
+
+ return ret;
+}
+
+static int add_partition_from_environment(char *s, char **retptr)
+{
+ unsigned long size;
+ unsigned long offset = 0;
+ char *name;
+ int name_len;
+ int delim;
+ unsigned int flags;
+ struct fastboot_ptentry part;
+
+ size = memparse(s, &s);
+ if (0 == size) {
+ printf("Error:FASTBOOT size of parition is 0\n");
+ return 1;
+ }
+
+ /* fetch partition name and flags */
+ flags = 0; /* this is going to be a regular partition */
+ delim = 0;
+ /* check for offset */
+ if (*s == '@') {
+ s++;
+ offset = memparse(s, &s);
+ } else {
+ printf("Error:FASTBOOT offset of parition is not given\n");
+ return 1;
+ }
+
+ /* now look for name */
+ if (*s == '(')
+ delim = ')';
+
+ if (delim) {
+ char *p;
+
+ name = ++s;
+ p = strchr((const char *)name, delim);
+ if (!p) {
+ printf("Error:FASTBOOT no closing %c found in partition name\n", delim);
+ return 1;
+ }
+ name_len = p - name;
+ s = p + 1;
+ } else {
+ printf("Error:FASTBOOT no partition name for \'%s\'\n", s);
+ return 1;
+ }
+
+ /* test for options */
+ while (1) {
+ if (strncmp(s, "i", 1) == 0) {
+ flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_I;
+ s += 1;
+ } else if (strncmp(s, "yaffs", 5) == 0) {
+ /* yaffs */
+ flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS;
+ s += 5;
+ } else if (strncmp(s, "swecc", 5) == 0) {
+ /* swecc */
+ flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC;
+ s += 5;
+ } else if (strncmp(s, "hwecc", 5) == 0) {
+ /* hwecc */
+ flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC;
+ s += 5;
+ } else {
+ break;
+ }
+ if (strncmp(s, "|", 1) == 0)
+ s += 1;
+ }
+
+ /* enter this partition (offset will be calculated later if it is zero at this point) */
+ part.length = size;
+ part.start = offset;
+ part.flags = flags;
+
+ if (name) {
+ if (name_len >= sizeof(part.name)) {
+ printf("Error:FASTBOOT partition name is too long\n");
+ return 1;
+ }
+ strncpy(&part.name[0], name, name_len);
+ /* name is not null terminated */
+ part.name[name_len] = '\0';
+ } else {
+ printf("Error:FASTBOOT no name\n");
+ return 1;
+ }
+
+
+ /* Check if this overlaps a static partition */
+ if (check_against_static_partition(&part)) {
+ printf("Adding: %s, offset 0x%8.8x, size 0x%8.8x, flags 0x%8.8x\n",
+ part.name, part.start, part.length, part.flags);
+ fastboot_flash_add_ptn(&part);
+ }
+
+ /* return (updated) pointer command line string */
+ *retptr = s;
+
+ /* return partition table */
+ return 0;
+}
+
+
+
+int do_fastboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ int ret = 1;
+ char fbparts[4096], *env;
+ int check_timeout = 0;
+ uint64_t timeout_endtime = 0;
+ uint64_t timeout_ticks = 0;
+ long timeout_seconds = -1;
+ int continue_from_disconnect = 0;
+
+ /*
+ * Place the runtime partitions at the end of the
+ * static paritions. First save the start off so
+ * it can be saved from run to run.
+ */
+ if (static_pcount >= 0) {
+ /* Reset */
+ pcount = static_pcount;
+ } else {
+ /* Save */
+ static_pcount = pcount;
+ }
+ env = getenv("fbparts");
+ if (env) {
+ unsigned int len;
+ len = strlen(env);
+ if (len && len < 4096) {
+ char *s, *e;
+
+ memcpy(&fbparts[0], env, len + 1);
+ printf("Fastboot: Adding partitions from environment\n");
+ s = &fbparts[0];
+ e = s + len;
+ while (s < e) {
+ if (add_partition_from_environment(s, &s)) {
+ printf("Error:Fastboot: Abort adding partitions\n");
+ /* reset back to static */
+ pcount = static_pcount;
+ break;
+ }
+ /* Skip a bunch of delimiters */
+ while (s < e) {
+ if ((' ' == *s) ||
+ ('\t' == *s) ||
+ ('\n' == *s) ||
+ ('\r' == *s) ||
+ (',' == *s)) {
+ s++;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Time out */
+ if (2 == argc) {
+ long try_seconds;
+ char *try_seconds_end;
+ /* Check for timeout */
+ try_seconds = simple_strtol(argv[1],
+ &try_seconds_end, 10);
+ if ((try_seconds_end != argv[1]) &&
+ (try_seconds >= 0)) {
+ check_timeout = 1;
+ timeout_seconds = try_seconds;
+ printf("Fastboot inactivity timeout %ld seconds\n", timeout_seconds);
+ }
+ }
+
+ if (1 == check_timeout) {
+ timeout_ticks = (uint64_t)
+ (timeout_seconds * get_tbclk());
+ }
+
+
+ do {
+ continue_from_disconnect = 0;
+
+ /* Initialize the board specific support */
+ if (0 == fastboot_init(&interface)) {
+
+ int poll_status;
+
+ /* If we got this far, we are a success */
+ ret = 0;
+ printf("fastboot initialized\n");
+
+ timeout_endtime = get_ticks();
+ timeout_endtime += timeout_ticks;
+
+ while (1) {
+ uint64_t current_time = 0;
+ poll_status = fastboot_poll();
+
+ if (1 == check_timeout)
+ current_time = get_ticks();
+
+ if (FASTBOOT_ERROR == poll_status) {
+ /* Error */
+ break;
+ } else if (FASTBOOT_DISCONNECT == poll_status) {
+ /* beak, cleanup and re-init */
+ printf("Fastboot disconnect detected\n");
+ continue_from_disconnect = 1;
+ break;
+ } else if ((1 == check_timeout) &&
+ (FASTBOOT_INACTIVE == poll_status)) {
+
+ /* No activity */
+ if (current_time >= timeout_endtime) {
+ printf("Fastboot inactivity detected\n");
+ break;
+ }
+ } else {
+ /* Something happened */
+ if (1 == check_timeout) {
+ /* Update the timeout endtime */
+ timeout_endtime = current_time;
+ timeout_endtime += timeout_ticks;
+ }
+ }
+
+ /* Check if the user wanted to terminate with ^C */
+ if ((FASTBOOT_INACTIVE == poll_status) &&
+ (ctrlc())) {
+ printf("Fastboot ended by user\n");
+ break;
+ }
+
+ /*
+ * Check if the fastboot client wanted to
+ * continue booting
+ */
+ if (continue_booting) {
+ printf("Fastboot ended by client\n");
+ break;
+ }
+
+ /* Check if there is something to upload */
+ tx_handler();
+ }
+ }
+
+ /* Reset the board specific support */
+ fastboot_shutdown();
+
+ /* restart the loop if a disconnect was detected */
+ } while (continue_from_disconnect);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ fastboot, 2, 1, do_fastboot,
+ "fastboot- use USB Fastboot protocol\n",
+ "[inactive timeout]\n"
+ " - Run as a fastboot usb device.\n"
+ " - The optional inactive timeout is the decimal seconds before\n"
+ " - the normal console resumes\n"
+);
+
+
+/*
+ * Android style flash utilties */
+void fastboot_flash_add_ptn(fastboot_ptentry *ptn)
+{
+ if(pcount < MAX_PTN){
+ memcpy(ptable + pcount, ptn, sizeof(*ptn));
+ pcount++;
+ }
+}
+
+void fastboot_flash_dump_ptn(void)
+{
+ unsigned int n;
+ for(n = 0; n < pcount; n++) {
+ fastboot_ptentry *ptn = ptable + n;
+ printf("ptn %d name='%s' start=%d len=%d\n",
+ n, ptn->name, ptn->start, ptn->length);
+ }
+}
+
+
+fastboot_ptentry *fastboot_flash_find_ptn(const char *name)
+{
+ unsigned int n;
+
+ for(n = 0; n < pcount; n++) {
+ /* Make sure a substring is not accepted */
+ if (strlen(name) == strlen(ptable[n].name))
+ {
+ if(0 == strcmp(ptable[n].name, name))
+ return ptable + n;
+ }
+ }
+ return 0;
+}
+
+fastboot_ptentry *fastboot_flash_get_ptn(unsigned int n)
+{
+ if(n < pcount) {
+ return ptable + n;
+ } else {
+ return 0;
+ }
+}
+
+unsigned int fastboot_flash_get_ptn_count(void)
+{
+ return pcount;
+}
+
+
+
+#endif /* CONFIG_FASTBOOT */