1772 lines
44 KiB
C
Executable File
1772 lines
44 KiB
C
Executable File
/*
|
|
* 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>
|
|
#include <sparse.h>
|
|
#ifdef CONFIG_ALLWINNER
|
|
#include <boot_type.h>
|
|
#endif
|
|
#ifdef CONFIG_FASTBOOT
|
|
|
|
/* Use do_reset for fastboot's 'reboot' command */
|
|
extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
|
|
/* Use do_nand for fastboot's flash commands */
|
|
|
|
extern int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]);
|
|
extern int do_mmcops (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
|
|
extern env_t *env_ptr;
|
|
|
|
/* Use do_setenv and do_env_save to permenantly save data */
|
|
extern int do_env_save (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
|
|
extern int do_env_set ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
|
|
/* Use do_bootm and do_go for fastboot's 'boot' command */
|
|
#ifdef CONFIG_CMD_BOOTM
|
|
extern int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]);
|
|
#endif
|
|
extern int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char * const 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;
|
|
/* 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_env_set(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_env_save(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_env_set (NULL, 0, 3, setenv);
|
|
|
|
sprintf (var, "%s_nand_size", ptn->name);
|
|
sprintf (val, "");
|
|
do_env_set (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_env_set (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_env_set (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("env");
|
|
|
|
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_env_save (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);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
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");
|
|
#ifdef MY_DEBUG
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
#endif
|
|
set_ptn_ecc(ptn);
|
|
#ifdef MY_DEBUG
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
#endif
|
|
/* 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;
|
|
}
|
|
}
|
|
#ifdef MY_DEBUG
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
/* 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));
|
|
#ifdef MY_DEBUG
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
do_nand(NULL, 0, 4, unlock);
|
|
#ifdef MY_DEBUG
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
do_nand(NULL, 0, 4, erase);
|
|
#ifdef MY_DEBUG
|
|
printf("%s %d\n", __FILE__, __LINE__);
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
#else
|
|
static int write_to_ptn(struct fastboot_ptentry *ptn)
|
|
{
|
|
void *addr;
|
|
u32 block_start, nblock;
|
|
|
|
addr = (void *)interface.transfer_buffer;
|
|
block_start = ptn->start/512;
|
|
nblock = download_bytes/512;
|
|
#if 1
|
|
printf("fastboot download_size = %x\n", download_bytes);
|
|
printf("fastboot block start = %x\n", block_start);
|
|
printf("fastboot nblock = %x\n", nblock);
|
|
printf("fastboot addr = %x\n", (u32)addr);
|
|
#endif
|
|
return sunxi_flash_write(block_start, nblock, addr);
|
|
}
|
|
#endif
|
|
|
|
#if 1
|
|
static int erase_ptn(struct fastboot_ptentry *ptn)
|
|
{
|
|
void *addr = (void *)FASTBOOT_ERASE_BUFFER;
|
|
u32 start = ptn->start/512;
|
|
u32 unerased_size;
|
|
u32 nblock = FASTBOOT_ERASE_BUFFER_SIZE/512;
|
|
|
|
unerased_size = ptn->length;
|
|
memset(addr, 0xff, FASTBOOT_ERASE_BUFFER_SIZE);
|
|
while(unerased_size >= FASTBOOT_ERASE_BUFFER_SIZE)
|
|
{
|
|
if(!sunxi_flash_write(start, nblock, addr))
|
|
{
|
|
return -1;
|
|
}
|
|
start += nblock;
|
|
unerased_size -= FASTBOOT_ERASE_BUFFER_SIZE;
|
|
}
|
|
if(unerased_size)
|
|
{
|
|
if(unerased_size & 0x1ff)
|
|
{
|
|
unerased_size += 0x1ff;
|
|
}
|
|
if(!sunxi_flash_write(start, unerased_size/512, addr))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int erase_ptn(struct fastboot_ptentry *ptn)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
|
|
debug("fast boot rx handle storage_type is %d\n",uboot_spare_head.boot_data.storage_type);
|
|
/* 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;
|
|
/* The download buffer and cmd buffer is the same one
|
|
since the partition name ends without a trailing 0 byte
|
|
we need to clear the buffer after download finished
|
|
for the next possible command. */
|
|
memset((char *)buffer, 0, transfer_size);
|
|
|
|
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 MB finished\n",
|
|
download_bytes >> 20);
|
|
|
|
#if 0 /* We don't need to pad */
|
|
#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
|
|
#endif /* #if 0 */
|
|
}
|
|
|
|
/* 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\n");
|
|
else
|
|
printf("downloading %d MB ...\r", download_bytes >> 20);
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
debug("reboot\n");
|
|
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)
|
|
{
|
|
debug("getvar\n");
|
|
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)
|
|
{
|
|
struct fastboot_ptentry *ptn;
|
|
|
|
ptn = fastboot_flash_find_ptn(cmdbuf + 6);
|
|
if(ptn == 0)
|
|
{
|
|
printf("FAIL: partition does not exist\n");
|
|
sprintf(response, "FAILpartition does not exist");
|
|
}
|
|
else
|
|
{
|
|
int status;
|
|
|
|
status = erase_ptn(ptn);
|
|
if (status)
|
|
{
|
|
printf("FAIL: failed to erase partition\n");
|
|
sprintf(response,"FAILfailed to erase partition");
|
|
}
|
|
else
|
|
{
|
|
printf("partition '%s' erased\n", ptn->name);
|
|
sprintf(response, "OKAY");
|
|
}
|
|
}
|
|
ret = 0;
|
|
}
|
|
/* download
|
|
download something ..
|
|
What happens to it depends on the next command after data */
|
|
|
|
if(memcmp(cmdbuf, "download:", 9) == 0) {
|
|
|
|
debug("download\n");
|
|
/* 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;
|
|
|
|
debug("download_size = %d\n", download_size);
|
|
printf ("Starting download of %d MB\n", download_size >> 20);
|
|
|
|
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, larger than buffer");
|
|
}
|
|
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) {
|
|
|
|
debug("boot\n");
|
|
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", (uint)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])) {
|
|
printf("Image has cmdline:");
|
|
printf("%s\n", &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 (download_bytes)
|
|
{
|
|
struct fastboot_ptentry *ptn;
|
|
|
|
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");
|
|
/* 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
|
|
{
|
|
/* Check if we have sparse compressed image */
|
|
if ( ((sparse_header_t *)interface.transfer_buffer)->magic
|
|
== SPARSE_HEADER_MAGIC) {
|
|
printf("fastboot: %s is in sparse format\n", ptn->name);
|
|
if (!do_unsparse(interface.transfer_buffer,
|
|
ptn->start,
|
|
ptn->length,
|
|
NULL)) {
|
|
printf("Writing sparsed: '%s' DONE!\n", ptn->name);
|
|
sprintf(response, "OKAY");
|
|
} else {
|
|
printf("Writing sparsed '%s' FAILED!\n", ptn->name);
|
|
sprintf(response, "FAIL: Sparsed Write");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Normal image: no sparse */
|
|
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");
|
|
}
|
|
ret = 0;
|
|
}
|
|
|
|
/* continue
|
|
Stop doing fastboot */
|
|
if (memcmp(cmdbuf, "continue", 8) == 0) {
|
|
sprintf(response, "OKAY");
|
|
|
|
debug("continue\n");
|
|
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)*/
|
|
if(!uboot_spare_head.boot_data.storage_type){
|
|
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",
|
|
(uint)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;
|
|
}
|
|
|
|
|
|
extern void sunxi_usb_main_loop(int delaytime);
|
|
|
|
int do_fastboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
|
|
{
|
|
#if 0
|
|
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.
|
|
*/
|
|
|
|
/* add by jerry */
|
|
fastboot_partition_init();
|
|
|
|
fastboot_flash_dump_ptn();
|
|
|
|
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 entered\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;
|
|
#else
|
|
if(sunxi_usb_dev_register(3) < 0)
|
|
{
|
|
printf("usb fastboot fail: not support sunxi fastboot\n");
|
|
|
|
return -1;
|
|
}
|
|
|
|
sunxi_usb_main_loop(0);
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
|
|
printf("find ptn: %s\n", name);
|
|
|
|
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 */
|