/*
 * Copyright (C) 2011 Google, Inc.
 *
 * Copyright (C) 2010 Texas Instruments
 *
 * Author : Mohammed Afzal M A <afzal@ti.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
 *
 *
 * Fastboot is implemented using gadget stack, many of the ideas are
 * derived from fastboot implemented in OmapZoom by
 * Tom Rix <Tom.Rix@windriver.com>, and portion of the code has been
 * ported from OmapZoom.
 *
 * Part of OmapZoom was copied from Android project, Android source
 * (legacy bootloader) was used indirectly here by using OmapZoom.
 *
 * This is Android's 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 <common.h>
#include <command.h>
#include <malloc.h>
#include <fastboot.h>
#include <usbdevice.h>
#include <asm/sizes.h>

DECLARE_GLOBAL_DATA_PTR;

/* If a BUILD_TAG was passed as an argument to make, use it
 * for the fastboot version.  Otherwise, see if a board file
 * defined a CONFIG_FASTBOOT_VERSION_BOOTLOADER and if so, use
 * that.  Otherwise, use an automatically constructed one of the form:
 *    productnameYMDHB
 * where:
 *    productname is FASTBOOT_PRODUCT_NAME
 *    Y is the year with 'A' being 2011 and incrementing from there
 *    M is the month
 *    D is the day of the month
 *    H is the hour
 *    B is the minute divided by two
 * All of the fields are based upon the build time.  M, D, H and B are all
 * output in base 36 (i.e. each digit is in the set 0-9A-Z).  A quick and
 * dirty Python program to convert one of these versions back to the build
 * time:

#! /usr/bin/env python
import sys
foo = sys.argv[1][-5:]
print "%04d/%02d/%02d %02d:%02d\n" % (int(foo[0], 36) + 2001,
                                      int(foo[1], 36),
                                      int(foo[2], 36),
                                      int(foo[3], 36),
                                      int(foo[4], 36) * 2)
 */
#ifdef BUILD_TAG
#undef CONFIG_FASTBOOT_VERSION_BOOTLOADER
#define CONFIG_FASTBOOT_VERSION_BOOTLOADER BUILD_TAG
#else
#ifndef CONFIG_FASTBOOT_VERSION_BOOTLOADER
#include <generated/timestamp_autogenerated.h>
#define CONFIG_FASTBOOT_VERSION_BOOTLOADER \
				(FASTBOOT_PRODUCT_NAME)
#endif
#endif

#define FASTBOOT_RUN_RECOVERY_ENV_NAME "fastboot_run_recovery"
#define FASTBOOT_UNLOCKED_ENV_NAME "fastboot_unlocked"
#define FASTBOOT_UNLOCK_TIMEOUT_SECS 5

#include <exports.h>
#include <environment.h>

/* USB specific */

#include <usb_defs.h>

#if defined(CONFIG_PPC)
#include <usb/mpc8xx_udc.h>
#elif defined(CONFIG_OMAP1510)
#include <usb/omap1510_udc.h>
#elif defined(CONFIG_MUSB_UDC)
#include <usb/musb_udc.h>
#elif defined(CONFIG_PXA27X)
#include <usb/pxa27x_udc.h>
#elif defined(CONFIG_SPEAR3XX) || defined(CONFIG_SPEAR600)
#include <usb/spr_udc.h>
#elif defined(CONFIG_RK_UDC)
#include <usb/dwc_otg_udc.h>
#endif

#if defined (CONFIG_OMAP)
#include <asm/arch/sys_proto.h>
#endif

#define STR_LANG		0x00
#define STR_MANUFACTURER	0x01
#define STR_PRODUCT		0x02
#define STR_SERIAL		0x03
#define STR_CONFIGURATION	0x04
#define STR_INTERFACE		0x05
#define STR_COUNT		0x06

#define CONFIG_USBD_CONFIGURATION_STR	"Android Fastboot Configuration"
#define CONFIG_USBD_INTERFACE_STR	"Android Fastboot Interface"

#define USBFBT_BCD_DEVICE	0x00
#define	USBFBT_MAXPOWER		0x32

#define USB_FLUSH_DELAY_MICROSECS 1000

#define	NUM_CONFIGS	1
#define	NUM_INTERFACES	1
#define	NUM_ENDPOINTS	2

#define	RX_EP_INDEX	2
#define	TX_EP_INDEX	1

struct _fbt_config_desc {
	struct usb_configuration_descriptor configuration_desc;
	struct usb_interface_descriptor interface_desc;
	struct usb_endpoint_descriptor endpoint_desc[NUM_ENDPOINTS];
};

static void fbt_handle_response(void);
static void fbt_run_recovery_wipe_data(void);
static void fbt_run_recovery();

static u8 fastboot_name[] = "rk_fastboot";
/* defined and used by gadget/ep0.c */
extern struct usb_string_descriptor **usb_strings;

/* USB Descriptor Strings */
static char serial_number[33]; /* what should be the length ?, 33 ? */
static __attribute__ ((aligned(4))) u8 wstr_lang[4] = {4, USB_DT_STRING, 0x9, 0x4};
static __attribute__ ((aligned(4))) u8 wstr_manufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)];
static __attribute__ ((aligned(4))) u8 wstr_product[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)];
static __attribute__ ((aligned(4))) u8 wstr_serial[2 + 2*(sizeof(serial_number) - 1)];
static __attribute__ ((aligned(4))) u8 wstr_configuration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)];
static __attribute__ ((aligned(4))) u8 wstr_interface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)];

/* USB descriptors */
static struct usb_device_descriptor device_descriptor = {
	.bLength = sizeof(struct usb_device_descriptor),
	.bDescriptorType =	USB_DT_DEVICE,
	.bcdUSB =		cpu_to_le16(USB_BCD_VERSION),
	.bDeviceClass =		0x00,
	.bDeviceSubClass =	0x00,
	.bDeviceProtocol =	0x00,
	.bMaxPacketSize0 =	EP0_MAX_PACKET_SIZE,
	.idVendor =		cpu_to_le16(CONFIG_USBD_VENDORID),
	.idProduct =		cpu_to_le16(CONFIG_USBD_PRODUCTID),
	.bcdDevice =		cpu_to_le16(USBFBT_BCD_DEVICE),
	.iManufacturer =	STR_MANUFACTURER,
	.iProduct =		STR_PRODUCT,
	.iSerialNumber =	STR_SERIAL,
	.bNumConfigurations =	NUM_CONFIGS
};

static struct _fbt_config_desc fbt_config_desc = {
	.configuration_desc = {
		.bLength = sizeof(struct usb_configuration_descriptor),
		.bDescriptorType = USB_DT_CONFIG,
		.wTotalLength =	cpu_to_le16(sizeof(struct _fbt_config_desc)),
		.bNumInterfaces = NUM_INTERFACES,
		.bConfigurationValue = 1,
		.iConfiguration = STR_CONFIGURATION,
		.bmAttributes =	BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED,
		.bMaxPower = USBFBT_MAXPOWER,
	},
	.interface_desc = {
		.bLength  = sizeof(struct usb_interface_descriptor),
		.bDescriptorType = USB_DT_INTERFACE,
		.bInterfaceNumber = 0,
		.bAlternateSetting = 0,
		.bNumEndpoints = 0x2,
		.bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
		.bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
		.bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
		.iInterface = STR_INTERFACE,
	},
	.endpoint_desc = {
		{
			.bLength = sizeof(struct usb_endpoint_descriptor),
			.bDescriptorType = USB_DT_ENDPOINT,
			/* XXX: can't the address start from 0x1, currently
				seeing problem with "epinfo" */
			.bEndpointAddress = RX_EP_INDEX | USB_DIR_OUT,
			.bmAttributes =	USB_ENDPOINT_XFER_BULK,
			.bInterval = 0xFF,
		},
		{
			.bLength = sizeof(struct usb_endpoint_descriptor),
			.bDescriptorType = USB_DT_ENDPOINT,
			/* XXX: can't the address start from 0x1, currently
				seeing problem with "epinfo" */
			.bEndpointAddress = TX_EP_INDEX | USB_DIR_IN,
			.bmAttributes = USB_ENDPOINT_XFER_BULK,
			.bInterval = 0xFF,
		},
	},
};

static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES];
static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];

static struct usb_string_descriptor *fbt_string_table[STR_COUNT];
static struct usb_device_instance device_instance[1];
static struct usb_bus_instance bus_instance[1];
static struct usb_configuration_instance config_instance[NUM_CONFIGS];
static struct usb_interface_instance interface_instance[NUM_INTERFACES];
static struct usb_alternate_instance alternate_instance[NUM_INTERFACES];
static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS + 1];

/* FASBOOT specific */

/* U-boot version */
extern char version_string[];

static struct cmd_fastboot_interface priv;

#ifdef CONFIG_FASTBOOT_LOG
static char* log_buffer;
static uint32_t log_position;
#endif

static void fbt_init_endpoints(void);
int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);

extern int do_charge(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
/* Use do_bootm_linux and do_go for fastboot's 'boot' command */
extern int do_go(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);
extern int do_bootm_linux(int flag, int argc, char *argv[],
			  bootm_headers_t *images);
extern int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc,
		       char *const argv[]);

/* USB specific */

/* utility function for converting char * to wide string used by USB */
static void str2wide(char *str, u16 * wide)
{
	int i;
	for (i = 0; i < strlen(str) && str[i]; i++) {
		#if defined(__LITTLE_ENDIAN)
			wide[i] = (u16) str[i];
		#elif defined(__BIG_ENDIAN)
			wide[i] = ((u16)(str[i])<<8);
		#else
			#error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined"
		#endif
	}
}

/* fastboot_init has to be called before this fn to get correct serial string */
static void fbt_init_strings(void)
{
	struct usb_string_descriptor *string;

	fbt_string_table[STR_LANG] = (struct usb_string_descriptor *)wstr_lang;

	string = (struct usb_string_descriptor *) wstr_manufacturer;
	string->bLength = sizeof(wstr_manufacturer);
	string->bDescriptorType = USB_DT_STRING;
	str2wide(CONFIG_USBD_MANUFACTURER, string->wData);
	fbt_string_table[STR_MANUFACTURER] = string;

	string = (struct usb_string_descriptor *) wstr_product;
	string->bLength = sizeof(wstr_product);
	string->bDescriptorType = USB_DT_STRING;
	str2wide(CONFIG_USBD_PRODUCT_NAME, string->wData);
	fbt_string_table[STR_PRODUCT] = string;

	string = (struct usb_string_descriptor *) wstr_serial;
	string->bLength = sizeof(wstr_serial);
	string->bDescriptorType = USB_DT_STRING;
	str2wide(serial_number, string->wData);
	fbt_string_table[STR_SERIAL] = string;

	string = (struct usb_string_descriptor *) wstr_configuration;
	string->bLength = sizeof(wstr_configuration);
	string->bDescriptorType = USB_DT_STRING;
	str2wide(CONFIG_USBD_CONFIGURATION_STR, string->wData);
	fbt_string_table[STR_CONFIGURATION] = string;

	string = (struct usb_string_descriptor *) wstr_interface;
	string->bLength = sizeof(wstr_interface);
	string->bDescriptorType = USB_DT_STRING;
	str2wide(CONFIG_USBD_INTERFACE_STR, string->wData);
	fbt_string_table[STR_INTERFACE] = string;

	/* Now, initialize the string table for ep0 handling */
	usb_strings = fbt_string_table;
}

static void fbt_event_handler (struct usb_device_instance *device,
				  usb_device_event_t event, int data)
{
	switch (event) {
	case DEVICE_RESET:
	case DEVICE_BUS_INACTIVE:
		priv.configured = 0;
		break;
	case DEVICE_CONFIGURED:
		priv.configured = 1;
		break;

	case DEVICE_ADDRESS_ASSIGNED:
		fbt_init_endpoints();

	default:
		break;
	}
}

/* fastboot_init has to be called before this fn to get correct serial string */
static void fbt_init_instances(void)
{
	int i;

	/* initialize device instance */
	memset(device_instance, 0, sizeof(struct usb_device_instance));
	device_instance->name = fastboot_name;
	device_instance->device_state = STATE_INIT;
	device_instance->device_descriptor = &device_descriptor;
	device_instance->event = fbt_event_handler;
	device_instance->cdc_recv_setup = NULL;
	device_instance->bus = bus_instance;
	device_instance->configurations = NUM_CONFIGS;
	device_instance->configuration_instance_array = config_instance;

	/* XXX: what is this bus instance for ?, can't it be removed by moving
	    endpoint_array and serial_number_str is moved to device instance */
	/* initialize bus instance */
	memset(bus_instance, 0, sizeof(struct usb_bus_instance));
	bus_instance->device = device_instance;
	bus_instance->endpoint_array = endpoint_instance;
	/* XXX: what is the relevance of max_endpoints & maxpacketsize ? */
	bus_instance->max_endpoints = 1;
	bus_instance->maxpacketsize = 64;
	bus_instance->serial_number_str = serial_number;

	/* configuration instance */
	memset(config_instance, 0, sizeof(struct usb_configuration_instance));
	config_instance->interfaces = NUM_INTERFACES;
	config_instance->configuration_descriptor =
		(struct usb_configuration_descriptor *)&fbt_config_desc;
	config_instance->interface_instance_array = interface_instance;

	/* XXX: is alternate instance required in case of no alternate ? */
	/* interface instance */
	memset(interface_instance, 0, sizeof(struct usb_interface_instance));
	interface_instance->alternates = 1;
	interface_instance->alternates_instance_array = alternate_instance;

	/* alternates instance */
	memset(alternate_instance, 0, sizeof(struct usb_alternate_instance));
	alternate_instance->interface_descriptor = interface_descriptors;
	alternate_instance->endpoints = NUM_ENDPOINTS;
	alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs;

	/* endpoint instances */
	memset(endpoint_instance, 0, sizeof(endpoint_instance));
	endpoint_instance[0].endpoint_address = 0;
	endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE;
	endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL;
	endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE;
	endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL;
	/* XXX: following statement to done along with other endpoints
		at another place ? */
	//udc_setup_ep(device_instance, 0, &endpoint_instance[0]);//setup ep0 reg in UdcInit()

	for (i = 1; i <= NUM_ENDPOINTS; i++) {
		endpoint_instance[i].endpoint_address =
			ep_descriptor_ptrs[i - 1]->bEndpointAddress;

		endpoint_instance[i].rcv_attributes =
			ep_descriptor_ptrs[i - 1]->bmAttributes;

		endpoint_instance[i].rcv_packetSize =
			le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);

		endpoint_instance[i].tx_attributes =
			ep_descriptor_ptrs[i - 1]->bmAttributes;

		endpoint_instance[i].tx_packetSize =
			le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);

		endpoint_instance[i].tx_attributes =
			ep_descriptor_ptrs[i - 1]->bmAttributes;

		urb_link_init(&endpoint_instance[i].rcv);
		urb_link_init(&endpoint_instance[i].rdy);
		urb_link_init(&endpoint_instance[i].tx);
		urb_link_init(&endpoint_instance[i].done);

		if (endpoint_instance[i].endpoint_address & USB_DIR_IN)
			endpoint_instance[i].tx_urb =
				usbd_alloc_urb(device_instance,
					       &endpoint_instance[i]);
		else
			endpoint_instance[i].rcv_urb =
				usbd_alloc_urb(device_instance,
					       &endpoint_instance[i]);
	}
}

/* XXX: ep_descriptor_ptrs can be removed by making better use of
	fbt_config_desc.endpoint_desc */
static void fbt_init_endpoint_ptrs(void)
{
	ep_descriptor_ptrs[0] = &fbt_config_desc.endpoint_desc[0];
	ep_descriptor_ptrs[1] = &fbt_config_desc.endpoint_desc[1];
}

static void fbt_init_endpoints(void)
{
	int i;

	/* XXX: should it be moved to some other function ? */
	bus_instance->max_endpoints = NUM_ENDPOINTS + 1;

	/* XXX: is this for loop required ?, yes for MUSB it is */
	for (i = 1; i <= NUM_ENDPOINTS; i++) {

		/* configure packetsize based on HS negotiation status */
		if (is_usbd_high_speed()) {
			FBTINFO("setting up HS USB device ep%x\n",
				endpoint_instance[i].endpoint_address);
			ep_descriptor_ptrs[i - 1]->wMaxPacketSize =
				CONFIG_USBD_FASTBOOT_BULK_PKTSIZE_HS;
			} else {
				FBTINFO("setting up FS USB device ep%x\n",
					endpoint_instance[i].endpoint_address);
				ep_descriptor_ptrs[i - 1]->wMaxPacketSize =
					CONFIG_USBD_FASTBOOT_BULK_PKTSIZE_FS;
			}
			
			 endpoint_instance[i].tx_packetSize =
				 le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
			 endpoint_instance[i].rcv_packetSize =
				 le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
			 //udc_setup_ep(device_instance, i, &endpoint_instance[i]);//setup epi reg in UdcInit()
	}
}

static struct urb *next_urb(struct usb_device_instance *device,
			    struct usb_endpoint_instance *endpoint)
{
	struct urb *current_urb;
	int space;

	/* If there's a queue, then we should add to the last urb */
	if (!endpoint->tx_queue)
		current_urb = endpoint->tx_urb;
	else {
		/* Last urb from tx chain */
		current_urb = p2surround(struct urb, link, endpoint->tx.prev);
	}

	/* Make sure this one has enough room */
	space = current_urb->buffer_length - current_urb->actual_length;
	if (space > 0)
		return current_urb;
	else {		/* No space here */
		/* First look at done list */
		current_urb = first_urb_detached(&endpoint->done);
		if (!current_urb)
			current_urb = usbd_alloc_urb(device, endpoint);

		urb_append(&endpoint->tx, current_urb);
		endpoint->tx_queue++;
	}
	return current_urb;
}

static void fbt_wait_usb_fifo_flush(void)
{
	/* give time to flush FIFO and remote to receive data.
	 * otherwise, USB can get hung.  someday we might actually
	 * try checking USB fifo status directly but for now, just
	 * spin for some time.
	 */
	udelay(USB_FLUSH_DELAY_MICROSECS);
}

/* FASTBOOT specific */

/*
 * Android style flash utilties
 */
static void set_serial_number(const char *serial_no)
{
	strncpy(serial_number, serial_no, sizeof(serial_number));
	serial_number[sizeof(serial_number) - 1] = '\0';
	priv.serial_no = serial_number;
	FBTDBG("fastboot serial_number = %s\n", serial_number);
}

static void create_serial_number(void)
{
	char *sn = getenv("fbt_sn#");

	if (sn == NULL) {
		FBTDBG("Setting serial number from constant (no dieid info)\n");
		set_serial_number("0123456789");
	} else {
		FBTDBG("Setting serial number from unique id\n");
		set_serial_number(sn);
	}
}

fbt_partition_t *fastboot_find_ptn(const char *name)
{
    unsigned int i;

    for (i = 0; i < FBT_PARTITION_MAX_NUM; i++) {
        if (!fbt_partitions[i].name)
            break;
        if (!strcmp((char *)fbt_partitions[i].name, name))
            return fbt_partitions + i;
    }

    FBTERR("partition(%s) not found, aborting\ntable:\n", name);
    for (i = 0; i < FBT_PARTITION_MAX_NUM; i++) {
        if (!fbt_partitions[i].name)
            break;
        FBTDBG("partition(%s)\n", fbt_partitions[i].name);
    }
    return NULL;
}

static void fbt_set_unlocked(int unlocked)
{
	char *unlocked_string;

	FBTDBG("Setting device to %s\n",
	       unlocked ? "unlocked" : "locked");
	priv.unlocked = unlocked;
	if (unlocked)
		unlocked_string = "1";
	else
		unlocked_string = "0";
	setenv(FASTBOOT_UNLOCKED_ENV_NAME, unlocked_string);
#if defined(CONFIG_CMD_SAVEENV)
	saveenv();
#endif
}

static void fbt_fastboot_init(void)
{
	char *fastboot_unlocked_env;
	priv.flag = 0;
	priv.d_size = 0;
	priv.d_bytes = 0;
	priv.u_size = 0;
	priv.u_bytes = 0;
	priv.exit = 0;
	priv.unlock_pending_start_time = 0;

	priv.unlocked = 1;

    priv.transfer_buffer = priv.buffer[0] = (u8 *)gd->arch.fastboot_buf_addr;
    priv.buffer[1] = priv.buffer[0] + CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE_EACH;
    priv.transfer_buffer_size  = CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE_EACH;

#ifdef CONFIG_FASTBOOT_LOG
    log_buffer = (char*)gd->arch.fastboot_log_buf_addr;
#endif

	fastboot_unlocked_env = getenv(FASTBOOT_UNLOCKED_ENV_NAME);
	if (fastboot_unlocked_env) {
		unsigned long unlocked;
		if (!strict_strtoul(fastboot_unlocked_env, 10, &unlocked)) {
			if (unlocked)
				priv.unlocked = 1;
			else
				priv.unlocked = 0;
		} else {
			FBTERR("bad env setting %s of %s,"
			       " initializing to locked\n",
			       fastboot_unlocked_env,
			       FASTBOOT_UNLOCKED_ENV_NAME);
			fbt_set_unlocked(0);
		}
	} else {
		FBTDBG("no existing env setting for %s\n",
		       FASTBOOT_UNLOCKED_ENV_NAME);
		FBTDBG("creating one set to false\n");
		fbt_set_unlocked(0);
	}
	if (priv.unlocked)
		FBTDBG("Device is unlocked\n");
	else
		FBTDBG("Device is locked\n");

    //TODO:check parameter, if no parameter, set flag to let some fbt cmds failed.

    //TODO:load device info, setup serial_number
    if (priv.serial_no == NULL)
        create_serial_number();

}

static int fbt_handle_erase(char *cmdbuf)
{
	fbt_partition_t *ptn;
	int err;
	char *partition_name = cmdbuf + 6;
	ptn = fastboot_find_ptn(partition_name);
	if (ptn == 0) {
	    FBTERR("Partition %s does not exist\n", partition_name);
		sprintf(priv.response, "FAILpartition does not exist");
		return -1;
	}

	FBTDBG("Erasing partition '%s':\n", ptn->name);

	FBTDBG("\tstart blk %lu, blk_cnt %lu\n", ptn->offset,
			ptn->size_kb);

	err = handleErase(ptn);
	if (err) {
		FBTERR("Erasing '%s' FAILED! error=%d\n", ptn->name, err);
		sprintf(priv.response,
				"FAILfailed to erase partition (%d)", err);
	} else {
		FBTDBG("partition '%s' erased\n", ptn->name);
		sprintf(priv.response, "OKAY");
	}
    return err;
}

static int fbt_handle_flash(char *cmdbuf, int check_unlock)
{
	fbt_partition_t *ptn;
    const char *name = cmdbuf + 6;

	if (check_unlock && !priv.unlocked) {
		FBTERR("%s: failed, device is locked\n", __func__);
		sprintf(priv.response, "FAILdevice is locked");
		return -1;
	}

	if (!priv.d_bytes) {
		FBTERR("%s: failed, no image downloaded\n", __func__);
		sprintf(priv.response, "FAILno image downloaded");
		return -1;
	}

    if (board_fbt_handle_flash(name, &priv)) {
        //handled by board side.
        return 0;//return val may not right.
    }

	ptn = fastboot_find_ptn(name);
	if (ptn == 0) {
	    FBTERR("%s: failed, partition %s does not exist\n",
		       __func__, cmdbuf + 6);
		sprintf(priv.response, "FAILpartition does not exist");
		return -1;
	}

    /* Normal case */
    FBTDBG("writing to partition '%s'\n", ptn->name);

    int err;

    FBTDBG("Writing %llu bytes to '%s'\n",
            priv.d_bytes, ptn->name);
    err = handleFlash(ptn, priv.transfer_buffer, priv.d_bytes);
    if (err) {
        FBTERR("Writing '%s' FAILED! error=%d\n",
                ptn->name, err);
        sprintf(priv.response,
                "FAILWrite partition, error=%d", err);
        return -1;
    } else {
        FBTDBG("Writing '%s' DONE!\n", ptn->name);
        sprintf(priv.response, "OKAY");
        return 0;
    }
}

struct getvar_entry {
	const char *variable_name;
	int exact_match;
	const char *(*getvar_func)(const char *arg);
};
extern char bootloader_ver[];

static const char *getvar_version(const char *unused)
{
	//return version_string;
	return bootloader_ver;
}

static const char *getvar_version_baseband(const char *unused)
{
    return "n/a";
}

static const char *getvar_version_bootloader(const char *unused)
{
	return CONFIG_FASTBOOT_VERSION_BOOTLOADER;
}

static const char *getvar_unlocked(const char *unused)
{
	return (priv.unlocked ? "yes" : "no");
}

static const char *getvar_secure(const char *unused)
{
	/* we use the inverse meaning of unlocked */
	return (priv.unlocked ? "no" : "yes");
}

static const char *getvar_product(const char *unused)
{
#ifdef CONFIG_ROCKCHIP
    if (PRODUCT_NAME && PRODUCT_NAME[0])
        return PRODUCT_NAME;
#endif
	return FASTBOOT_PRODUCT_NAME;
}

static const char *getvar_serialno(const char *unused)
{
	return priv.serial_no;
}

static const char *getvar_partition_type(const char *args)
{
    const char *partition_name;
	fbt_partition_t *ptn;
    /**
     * we cannot get partition type right now, so just return raw,
     * and skip format command.
     */
    const char *type = "raw";

    if (!strcmp(args, "all")) {
        int i;
        for (i = 0; i < FBT_PARTITION_MAX_NUM; i++) {
            if (!fbt_partitions[i].name)
                break;
            FBTDBG("partition \"%s\" has type \"%s\"\n",
                   fbt_partitions[i].name, type);
        }
        return NULL;
    }

    priv.pending_ptn = NULL;

    partition_name = args + sizeof("partition-type:") - 1;
    ptn = fastboot_find_ptn(partition_name);
    if (ptn) {
        FBTDBG("fastboot pending_ptn:%s\n", ptn->name);
        priv.pending_ptn = ptn;
        return type;
    }
    snprintf(priv.response, sizeof(priv.response),
         "FAILunknown partition %s\n", partition_name);
    return NULL;
}

static const char *getvar_checksum(const char *args)
{
#ifndef CONFIG_ROCKCHIP
    return NULL;
#else
    //1:parse params.
    uint32_t offset = 0;
    uint32_t blocks = 0;
    char* buf = args + sizeof("checksum:") - 1;
    offset = simple_strtoull(buf, &buf, 0);
    //skip a space char.
    buf++;
    blocks = simple_strtoull(buf, &buf, 0);

    if (offset < 0 || blocks <= 0) {
        snprintf(priv.response, sizeof(priv.response),
                "FAILinvalidate params!\n");
        return NULL;
    }
    FBTDBG("try to get checksum, offset:0x%08lx, blocks:0x%08x\n",
            offset, blocks);

    //2:get checksum for each parts
    buf = priv.buffer[0];

    //may overflow?
    uint16_t buf_blocks = priv.transfer_buffer_size / RK_BLK_SIZE;

#ifndef CONFIG_QUICK_CHECKSUM
    uint32_t* crc_array = (uint32_t*) priv.buffer[1];
    uint16_t crc_counts = 0;
    uint32_t checksum = 0;
#else
    uint64_t checksum = 0;
#endif

    while (blocks > 0) {
        uint16_t read_blocks = blocks > buf_blocks? buf_blocks : blocks;
        
        if (StorageReadLba(offset, buf, read_blocks) != 0) {
            FBTERR("read failed, offset:0x%08lx, blocks:0x%08x\n",
                    offset, read_blocks);
            snprintf(priv.response, sizeof(priv.response),
                    "FAILread 0x%08lx failed!\n", offset);
            return NULL;
        }
        offset += read_blocks;
        blocks -= read_blocks;
#ifndef CONFIG_QUICK_CHECKSUM
        crc_array[crc_counts] = crc32(0, buf, read_blocks * RK_BLK_SIZE);
        FBTDBG("offset:0x%08x, blocks:0x%08x, crc:0x%08lx\n",
                offset, read_blocks, crc_array[crc_counts]);
        crc_counts++;
#else
        int i = 0;
        uint32_t* data = (uint32_t*) buf;
        for (i = 0;i < read_blocks * RK_BLK_SIZE >> 2;i++)
            checksum += data[i];
        FBTDBG("offset:0x%08x, blocks:0x%08x, checksum:0x%016llx\n",
                offset, read_blocks, checksum);
#endif
    }

#ifndef CONFIG_QUICK_CHECKSUM
    //3:compute whole checksum
    checksum = (crc_counts == 1)? crc_array[0] :
        crc32(0, (unsigned char*)crc_array, sizeof(uint32_t) * crc_counts);
    FBTDBG("whole checksum:0x%08lx\n", checksum);
#else
    FBTDBG("whole checksum:0x%016llx\n", checksum);
#endif

#ifndef CONFIG_QUICK_CHECKSUM
    snprintf(priv.response, sizeof(priv.response),
            "OKAY0x%08lx\n", checksum);
#else
    snprintf(priv.response, sizeof(priv.response),
            "OKAY0x%016llx\n", checksum);
#endif
    return NULL;
#endif
}

static const char *getvar_partition_offset(const char *args)
{
	const char *partition_name;
	fbt_partition_t *ptn;

	if (!strcmp(args, "all")) {
		int i;
		for (i = 0; i < FBT_PARTITION_MAX_NUM; i++) {
            if (!fbt_partitions[i].name)
                break;
            FBTDBG("partition \"%s\" offset 0x%08llx\n",
			       fbt_partitions[i].name,
			       (uint64_t)fbt_partitions[i].offset);
		}
		return NULL;
	}

	partition_name = args + sizeof("partition-offset:") - 1;
	ptn = fastboot_find_ptn(partition_name);
	if (ptn) {
		snprintf(priv.response, sizeof(priv.response),
			 "OKAY0x%08llx", (uint64_t)ptn->offset);
	} else {
		snprintf(priv.response, sizeof(priv.response),
			 "FAILunknown partition %s\n", partition_name);
	}
	return NULL;
}


static const char *getvar_partition_size(const char *args)
{
	const char *partition_name;
	fbt_partition_t *ptn;

	if (!strcmp(args, "all")) {
		int i;
		for (i = 0; i < FBT_PARTITION_MAX_NUM; i++) {
            if (!fbt_partitions[i].name)
                break;
            FBTDBG("partition \"%s\" has size 0x%016llx kb\n",
			       fbt_partitions[i].name,
			       (uint64_t)fbt_partitions[i].size_kb);
		}
		return NULL;
	}

	partition_name = args + sizeof("partition-size:") - 1;
	ptn = fastboot_find_ptn(partition_name);
	if (ptn) {
		snprintf(priv.response, sizeof(priv.response),
			 "OKAY0x%016llx kb", (uint64_t)ptn->size_kb);
	} else {
		snprintf(priv.response, sizeof(priv.response),
			 "FAILunknown partition %s\n", partition_name);
	}
	return NULL;
}

static const struct getvar_entry getvar_table[] = {
	{"version", 1, getvar_version},
    {"version-baseband", 1, getvar_version_baseband},
	{"version-bootloader", 1, getvar_version_bootloader},
	{"unlocked", 1, getvar_unlocked},
	{"secure", 1, getvar_secure},
	{"product", 1, getvar_product},
	{"serialno", 1, getvar_serialno},
    {"partition-type:", 0, getvar_partition_type},
    {"partition-size:", 0, getvar_partition_size},
    {"partition-offset:", 0, getvar_partition_offset},
    {"checksum:", 0, getvar_checksum}
};

static void fbt_handle_getvar(char *cmdbuf)
{
	char *subcmd = cmdbuf + sizeof("getvar:") - 1;
	const char *value = NULL;
	int do_all;
	int i;
	if (!strcmp(subcmd, "all"))
		do_all = 1;
	else
		do_all = 0;

	if (do_all) {
		for (i = 0; i < ARRAY_SIZE(getvar_table); i++) {
			value = (getvar_table[i].getvar_func)(subcmd);
			if (value ) {
				FBTDBG("%s: %s\n",
				       getvar_table[i].variable_name, value);
			}
		}
		strcpy(priv.response, "OKAY");
	} else {
		for (i = 0; i < ARRAY_SIZE(getvar_table); i++) {
			int match;
			if (getvar_table[i].exact_match) {
				/* look for exact string match */
				match = !strcmp(getvar_table[i].variable_name,
						subcmd);
			} else {
				/* look for the target string at the
				 * beginning of the argument passed
				 */
				match = strstr(subcmd,
					       getvar_table[i].variable_name) ==
					subcmd;
			}
			if (match) {
				value = (getvar_table[i].getvar_func)(subcmd);
				if (value == NULL) {
					/* handler did it all in terms of
					 * creating a response.
					 */
					return;
				}
				/* fall through to let the common code
				 * handle creating a response string.
				 */
				break;
			}
		}
#ifdef CONFIG_FASTBOOT_UBOOT_GETVAR
		if (!value) {
			ENTRY e, *ep;

			e.key = subcmd;
			e.data = NULL;
			ep = NULL;
			if (hsearch_r(e, FIND, &ep, &env_htab) && ep != NULL)
				value = ep->data;
		}
#endif
		if (value) {
			/* At first I was reluctant to use strncpy because it
			 * typically pads the whole buffer with nulls, but
			 * U-Boot's strncpy does not do that.  However, I
			 * do rely on priv.null_term after priv.response
			 * in the struct cmd_fastboot_interface to ensure
			 * the strlen in fbt_response_process doesn't take
			 * a long time.
			 */
			strcpy(priv.response, "OKAY");
			strncpy(priv.response + 4, value,
				(sizeof(priv.response) - 4));
		} else {
			strcpy(priv.response, "FAILunknown variable");
		}
	}
}

static void fbt_handle_reboot(const char *cmdbuf)
{
	if (!strcmp(&cmdbuf[6], "-bootloader")) {
		FBTDBG("%s\n", cmdbuf);
		board_fbt_set_reboot_type(FASTBOOT_REBOOT_BOOTLOADER);
	}
	if (!strcmp(&cmdbuf[6], "-recovery")) {
		FBTDBG("%s\n", cmdbuf);
		board_fbt_set_reboot_type(FASTBOOT_REBOOT_RECOVERY);
	}
	if (!strcmp(&cmdbuf[6], "-recovery:wipe_data")) {
		FBTDBG("%s\n", cmdbuf);
		board_fbt_set_reboot_type(FASTBOOT_REBOOT_RECOVERY_WIPE_DATA);
	}

	strcpy(priv.response, "OKAY");
	priv.flag |= FASTBOOT_FLAG_RESPONSE;
	fbt_handle_response();
	udelay(1000000); /* 1 sec */

	do_reset(NULL, 0, 0, NULL);
}

static int fbt_send_raw_info(const char *info, int bytes_left)
{
	int response_max;

	if (!priv.executing_command)
		return -1;

	/* break up info into response sized chunks */
	/* remove trailing '\n' */
	if (info[bytes_left-1] == '\n')
		bytes_left--;

	/* -4 for the INFO prefix */
	response_max = sizeof(priv.response) - 4;
	strcpy(priv.response, "INFO");
	while (1) {
		if (bytes_left >= response_max) {
			strncpy(priv.response + 4, info,
				response_max);

			/* flush any data set by command */
			priv.flag |= FASTBOOT_FLAG_RESPONSE;
			fbt_handle_response();
			fbt_wait_usb_fifo_flush();

			info += response_max;
			bytes_left -= response_max;
		} else {
			strncpy(priv.response + 4, info,
				bytes_left);

			/* in case we stripped '\n',
			   make sure priv.response is
			   terminated */
			priv.response[4 + bytes_left] = '\0';

			break;
		}
	}

	priv.flag |= FASTBOOT_FLAG_RESPONSE;
	fbt_handle_response();
	fbt_wait_usb_fifo_flush();

	return 0;
}

#ifdef CONFIG_FASTBOOT_LOG
static void fbt_dump_log(char *buf, uint32_t buf_size)
{
	/* the log consists of a bunch of printf output, with
	 * logs of '\n' interspersed. to make it format a
	 * bit better when sending it via the INFO
	 * part of the fastboot protocol, which has a limited
	 * buffer, break the log into bits that end
	 * with '\n', like replaying the printfs.
	 */
	char *line_start = buf;

	if (buf_size == 0) {
		FBTERR("%s: unexpected buf size of 0\n", __func__);
		return;
	}

	/* guarantee null termination for strchr/strlen */
	buf[buf_size] = 0;
	while (buf_size) {
		char *next_line  = strchr(line_start, '\n');
		if (next_line) {
			int len = next_line - line_start + 1;
			fbt_send_raw_info(line_start, len);
			line_start += len;
			buf_size -= len;
		} else {
			fbt_send_raw_info(line_start, strlen(line_start));
			break;
		}
	}
}
#endif //CONFIG_FASTBOOT_LOG

static void fbt_handle_oem(char *cmdbuf)
{
	cmdbuf += 4;

#ifdef CONFIG_FASTBOOT_LOG
	/* %fastboot oem log */
	if (strcmp(cmdbuf, "log") == 0) {
		FBTDBG("oem %s\n", cmdbuf);
		fbt_dump_log(log_buffer, log_position);
		strcpy(priv.response, "OKAY");
		return;
	}
#endif //CONFIG_FASTBOOT_LOG

	/* %fastboot oem recovery */
	if (strcmp(cmdbuf, "recovery") == 0) {
		FBTDBG("oem recovery\n");
		fbt_handle_reboot("reboot-recovery");
		return;
	}

	/* %fastboot oem recovery:wipe_data */
	if (strcmp(cmdbuf, "recovery:wipe_data") == 0) {
		FBTDBG("oem recovery:wipe_data\n");
		fbt_handle_reboot("reboot-recovery:wipe_data");
		return;
	}

	/* %fastboot oem unlock */
	if (strcmp(cmdbuf, "unlock") == 0) {
		FBTDBG("oem unlock\n");
		if (priv.unlocked) {
			FBTDBG("oem unlock ignored, device already unlocked\n");
			strcpy(priv.response, "FAILalready unlocked");
			return;
		}
		FBTDBG("oem unlock requested:\n");
		FBTDBG("\tUnlocking forces a factory reset and could\n");
		FBTDBG("\topen your device up to a world of hurt.  If you\n");
		FBTDBG("\tare sure you know what you're doing, then accept\n");
		FBTDBG("\tin %d seconds via 'fastboot oem unlock_accept'.\n",
		       FASTBOOT_UNLOCK_TIMEOUT_SECS);
		priv.unlock_pending_start_time = get_timer(0);
		strcpy(priv.response, "OKAY");
		return;
	}

	if (strcmp(cmdbuf, "unlock_accept") == 0) {
		int err;
		FBTDBG("oem unlock_accept\n");
		if (!priv.unlock_pending_start_time) {
			FBTERR("oem unlock_accept ignored, not pending\n");
			strcpy(priv.response, "FAILoem unlock not requested");
			return;
		}
		priv.unlock_pending_start_time = 0;
		FBTDBG("Erasing userdata partition\n");
        fbt_partition_t* ptn;
        ptn = fastboot_find_ptn("userdata");
        if (ptn) {
            err = handleErase(ptn);
        }
		if (err) {
			FBTERR("Erase failed with error %d\n", err);
			strcpy(priv.response, "FAILErasing userdata failed");
			return;
		}
		FBTDBG("Erasing succeeded\n");
		fbt_set_unlocked(1);
		strcpy(priv.response, "OKAY");
		priv.flag |= FASTBOOT_FLAG_RESPONSE;
		fbt_handle_response();
		udelay(1000000); /* 1 sec */
		/* now reboot into recovery to do a format of the
		 * userdata partition so it's ready to use on next boot
		 */
		fbt_run_recovery_wipe_data();
		return;
	}

	if (strcmp(cmdbuf, "lock") == 0) {
		FBTDBG("oem lock\n");
		if (!priv.unlocked) {
			FBTERR("oem lock ignored, already locked\n");
			strcpy(priv.response, "FAILalready locked");
			return;
		}
		fbt_set_unlocked(0);
		strcpy(priv.response, "OKAY");
		return;
	}

	/* %fastboot oem erase partition <numblocks>
	 * similar to 'fastboot erase' except an optional number
	 * of blocks can be passed to erase less than the
	 * full partition, for speed
	 */
	if (strncmp(cmdbuf, "erase ", 6) == 0) {
		FBTDBG("oem %s\n", cmdbuf);
		fbt_handle_erase(cmdbuf);
		return;
	}

	/* All other oem commands are not allowed if device is locked */
	if (!priv.unlocked) {
		sprintf(priv.response, "FAILdevice is locked");
		return;
	}

	/* %fastboot oem ucmd ... */
	if (strncmp(cmdbuf, "ucmd ", 5) == 0) {
		FBTDBG("oem %s\n", cmdbuf);
		cmdbuf += 5;

		if (run_command(cmdbuf, 0) < 0)
			strcpy(priv.response, "FAILcommand failed");
		else
			strcpy(priv.response, "OKAY");
		return;
	}

	/* %fastboot oem [xxx] */
	FBTDBG("oem %s\n", cmdbuf);
	if (board_fbt_oem(cmdbuf) >= 0) {
		strcpy(priv.response, "OKAY");
		return;
	}

	FBTDBG("\nfastboot: unsupported oem command %s\n", cmdbuf);
	strcpy(priv.response, "FAILinvalid command");
}

static void fbt_handle_boot(const char *cmdbuf)
{
	if (!priv.unlocked) {
		sprintf(priv.response, "FAILdevice is locked");
		return;
	}

	if ((priv.d_bytes) &&
		(CONFIG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < priv.d_bytes)) {
		char start[32];
		char *booti[] = { "booti", start };
		char *go[]    = { "go",    start };

		/*
		 * 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 *) priv.transfer_buffer;

		sprintf(start, "%p", fb_hdr);

		/* Execution should jump to kernel so send the response
		   now and wait a bit.  */
		sprintf(priv.response, "OKAY");
		priv.flag |= FASTBOOT_FLAG_RESPONSE;
		fbt_handle_response();
		udelay(1000000); /* 1 sec */

		do_booti(NULL, 0, ARRAY_SIZE(booti), booti);

		FBTERR("do_booti() returned, trying go..\n");

		FBTINFO("Booting raw image..\n");
		do_go(NULL, 0, ARRAY_SIZE(go), go);

		FBTERR("booting failed, reset the board\n");
	}
	sprintf(priv.response, "FAILinvalid boot image");
}

/* XXX: Replace magic number & strings with macros */
static int fbt_rx_process(unsigned char *buffer, int length)
{
	struct usb_endpoint_instance *ep;
	char *cmdbuf;
	int clear_cmd_buf;

    if (priv.d_size) {
        ep = &endpoint_instance[1];
        if (length > priv.transfer_buffer_size) {
            FBTERR("buffer overflow when do receive\n");
            length = priv.transfer_buffer_size;
        }
        if (length == priv.transfer_buffer_size) {
            //switch buffer, and resume transfer.

            //compute new buffer_length
            int buffer_length = priv.d_size - priv.d_bytes -//bytes to rcv seens last time.
                (length - priv.transfer_buffer_pos);//bytes we rcved this time.
            
            //keep last 512 to next time(for storage write align)
            buffer_length += RK_BLK_SIZE;

            if (buffer_length > priv.transfer_buffer_size)
                buffer_length = priv.transfer_buffer_size;

            ep->rcv_urb->buffer = (u8 *)priv.buffer[buffer == priv.transfer_buffer];
            
            //keep last 512 to next time(for storage write align)
            ep->rcv_urb->buffer_length = buffer_length;
            ep->rcv_urb->actual_length = RK_BLK_SIZE;
            memcpy(ep->rcv_urb->buffer, buffer + length - RK_BLK_SIZE, RK_BLK_SIZE);

            FBTDBG("switch transfer buffer:%x -> %x len:%ld\n", buffer, ep->rcv_urb->buffer,
                   ep->rcv_urb->buffer_length);
            resume_usb(ep, 0);
        }

        //board handle this
        if (board_fbt_handle_download(buffer, length, &priv)) {
            return 0;
        }

		if (length < priv.d_size && !priv.d_status) {
			/* don't clear cmd buf because we've replaced it
			 * with our transfer buffer.  we'll clear it at
			 * the end of the download.
			 */
			return 0;
		}

        if (priv.d_status < 0 && priv.d_bytes < priv.d_size) {
            FBTDBG("Failed to download, d_bytes:%lld d_size:%lld, length:%d\n",
                    priv.d_bytes, priv.d_size, length);
            //ignore transfer_buffer_pos, consider we ate it up.
            priv.transfer_buffer_pos = 0;
            priv.d_bytes += length;

            //fix 512 bytes we kept for next download.
            if (priv.d_bytes < priv.d_size)
                priv.d_bytes -= RK_BLK_SIZE;
            return 0;
        }
        if (priv.d_status < 0) {
            /* transfer error */
            //TODO: maybe use some error str(convert from d_status)
            strcpy(priv.response, "FAILDownload error");
        } else {
            /* transfer complete */
            strcpy(priv.response, "OKAY");
        }
        priv.d_bytes = priv.d_size;
        priv.d_size = 0;
        priv.transfer_buffer_pos = 0;
        priv.flag |= FASTBOOT_FLAG_RESPONSE;

		/* restore default buffer in urb */
		ep->rcv_urb->buffer = (u8 *)ep->rcv_urb->buffer_data;
		ep->rcv_urb->buffer_length = sizeof(ep->rcv_urb->buffer_data);

        FBTDBG("downloaded %llu bytes\n", priv.d_bytes);

		/* clear the cmd buf from last time */
		return 1;
	}

	/* command */
	cmdbuf = (char *) buffer;
	clear_cmd_buf = 1;

	/* Generic failed response */
	strcpy(priv.response, "FAIL");

    cmdbuf[FASTBOOT_COMMAND_SIZE - 1] = 0;
	FBTDBG("cmdbuf = (%s)\n", cmdbuf);
	priv.executing_command = 1;

	/* %fastboot getvar: <var_name> */
	if (memcmp(cmdbuf, "getvar:", 7) == 0) {
		FBTDBG("getvar\n");
		fbt_handle_getvar(cmdbuf);
	}

	/* %fastboot oem <cmd> */
	else if (memcmp(cmdbuf, "oem ", 4) == 0) {
		FBTDBG("oem\n");
		fbt_handle_oem(cmdbuf);
	}

	/* %fastboot erase <partition_name> */
	else if (memcmp(cmdbuf, "erase:", 6) == 0) {
		FBTDBG("erase\n");
		fbt_handle_erase(cmdbuf);
	}

	/* %fastboot flash:<partition_name> */
	else if (memcmp(cmdbuf, "flash:", 6) == 0) {
		FBTDBG("flash\n");
		fbt_handle_flash(cmdbuf, 1);
        priv.pending_ptn = NULL;
	}

	/* %fastboot reboot
	 * %fastboot reboot-bootloader
	 */
	else if (memcmp(cmdbuf, "reboot", 6) == 0) {
		FBTDBG("reboot or reboot-bootloader\n");
		fbt_handle_reboot(cmdbuf);
	}

	/* %fastboot continue */
	else if (strcmp(cmdbuf, "continue") == 0) {
		FBTDBG("continue\n");
		strcpy(priv.response, "OKAY");
		priv.exit = 1;
	}

	/* %fastboot boot <kernel> [ <ramdisk> ] */
	else if (memcmp(cmdbuf, "boot", 4) == 0) {
		FBTDBG("boot\n");
		fbt_handle_boot(cmdbuf);
	}

	/* Sent as part of a '%fastboot flash <partname>' command
	 * This sends the data over with byte count:
	 * %download:<num_bytes>
	 */
	else if (memcmp(cmdbuf, "download:", 9) == 0) {
		FBTDBG("download\n");

		/* XXX: need any check for size & bytes ? */
        int d_size = simple_strtoul (cmdbuf + 9, NULL, 16);
		priv.d_bytes = 0;
        priv.d_status = 0;

		FBTINFO("starting download of %llu bytes\n", d_size);

        bool needCheck = 
            (!strcmp(priv.pending_ptn->name, RECOVERY_NAME) ||
             !strcmp(priv.pending_ptn->name, BOOT_NAME) ||
             !strcmp(priv.pending_ptn->name, UBOOT_NAME));

        if (!priv.unlocked) {
            FBTERR("download: failed, device is locked\n");
            sprintf(priv.response, "FAILdevice is locked");
        } else if (d_size > CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE_EACH
                && !priv.pending_ptn) {
            //flash loader would not set pending_ptn,
            //because there is not a partition for loader.
            FBTERR("download large image with \"-u\" option\n");
            sprintf(priv.response, "FAILnot support \"-u\" option");
            //what if they use "fastboot getvar partition-type" before flash?
        } else if (d_size >= CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE &&
                needCheck) {
            //image size too large for sign check.
            FBTERR("%s image too large\n", priv.pending_ptn->name);
            sprintf(priv.response, "FAILdata too large");
        } else if (d_size == 0) {
            strcpy(priv.response, "FAILdata invalid size");

        /* maybe board side can handle this.
		} else if (d_size > priv.transfer_buffer_size) {
			d_size = 0;
			strcpy(priv.response, "FAILdata too large");
            */
		} else {
            priv.d_size = d_size;
            sprintf(priv.response, "DATA%08llx", priv.d_size);

            //we will check boot/recovery's sha, so need a big buffer to recv whole image.
            priv.transfer_buffer_size = 
                needCheck? CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE :
                CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE_EACH;

            /* as an optimization, replace the builtin
             * urb->buffer and urb->buffer_length with our
             * own so we don't have to do extra copy.
             */
            ep = &endpoint_instance[1];
            ep->rcv_urb->buffer = priv.transfer_buffer;
            ep->rcv_urb->buffer_length = 
                priv.transfer_buffer_size > priv.d_size? priv.d_size: 
                priv.transfer_buffer_size;
            ep->rcv_urb->actual_length = 0;

            /* don't poison the cmd buffer because
             * we've replaced it with our
             * transfer buffer for the download.
             */
			clear_cmd_buf = 0;
		}
	}
	priv.flag |= FASTBOOT_FLAG_RESPONSE;
	priv.executing_command = 0;
	return clear_cmd_buf;
}

static void fbt_handle_rx(void)
{
	struct usb_endpoint_instance *ep = &endpoint_instance[1];

	/* XXX: Or update status field, if so,
		"usbd_rcv_complete" [gadget/core.c] also need to be modified */
	if (ep->rcv_urb->actual_length) {
		//FBTDBG("rx length: %u\n", ep->rcv_urb->actual_length);
		if (fbt_rx_process(ep->rcv_urb->buffer,
				   ep->rcv_urb->actual_length)) {
			/* Poison the command buffer so there's no confusion
			 * when we receive the next one.  fastboot commands
			 * are sent w/o NULL termination so we don't want
			 * stale data in the buffer.
			 * Also, it is assumed that at the time of creation of
			 * urb it is poisoned.
			*/
			memset(ep->rcv_urb->buffer, 0, FASTBOOT_COMMAND_SIZE);
			ep->rcv_urb->actual_length = 0;
            resume_usb(ep, FASTBOOT_COMMAND_SIZE);
        }
        fbt_handle_response();
    }
}

static void fbt_response_process(void)
{
	struct usb_endpoint_instance *ep = &endpoint_instance[2];
	struct urb *current_urb = NULL;
	unsigned char *dest = NULL;
	int n;

	current_urb = next_urb(device_instance, ep);
	if (!current_urb) {
		FBTERR("%s: current_urb NULL", __func__);
		return;
	}

	dest = current_urb->buffer + current_urb->actual_length;
	n = MIN(64, strlen(priv.response));
	memcpy(dest, priv.response, n);
	current_urb->actual_length += n;
	/*
	 * This FBTDBG appears to break communication when DEBUG
	 * is on, so comment it out.
	FBTDBG("response urb length: %u\n", current_urb->actual_length);
	 */
	if (ep->last == 0)
		udc_endpoint_write(ep);
}

static void fbt_handle_response(void)
{
	if (priv.flag & FASTBOOT_FLAG_RESPONSE) {
		fbt_response_process();
		priv.flag &= ~FASTBOOT_FLAG_RESPONSE;
	}
}

static void fbt_run_charge()
{
    char *const boot_charge_cmd[] = {"booti", "charge"};
    do_booti(NULL, 0, ARRAY_SIZE(boot_charge_cmd), boot_charge_cmd);

    /* returns if boot.img is bad */
    FBTERR("\nfastboot: Error: Invalid boot img\n");
}

static void fbt_run_recovery()
{
	char *const boot_recovery_cmd[] = {"booti", "recovery"};
	do_booti(NULL, 0, ARRAY_SIZE(boot_recovery_cmd), boot_recovery_cmd);

	/* returns if recovery.img is bad */
	FBTERR("\nfastboot: Error: Invalid recovery img\n");
}

struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[1024];
};

static void fbt_run_recovery_wipe_data(void)
{
	struct bootloader_message bmsg;

	FBTDBG("Rebooting into recovery to do wipe_data\n");

    if (!fastboot_find_ptn("misc"))
    {
        FBTERR("not found misc partition, just run recovery.\n");
        fbt_run_recovery();
    }
    strcpy(bmsg.command, "boot-recovery");
    bmsg.status[0] = 0;
    strcpy(bmsg.recovery, "recovery\n--wipe_data");
    if (board_fbt_set_bootloader_msg(&bmsg))
    {
        FBTERR("set bootloader msg failed, retry!\n");
        fbt_handle_reboot("reboot-recovery:wipe_data");
    }
    /* now reboot to recovery */
    fbt_run_recovery();
}

/*
 * default board-specific hooks and defaults
 */
static int __def_fbt_oem(const char *cmdbuf)
{
	return -1;
}
static void __def_fbt_set_reboot_type(enum fbt_reboot_type fre)
{
}
static enum fbt_reboot_type __def_fbt_get_reboot_type(void)
{
	return FASTBOOT_REBOOT_NORMAL;
}
static int __def_fbt_key_pressed(void)
{
	return FASTBOOT_REBOOT_NONE;
}
static void __def_board_fbt_finalize_bootargs(char* args, size_t buf_sz,
       size_t ramdisk_sz, int recovery)
{
	return;
}
static int __def_board_fbt_handle_flash(char *name,
                            struct cmd_fastboot_interface *priv)
{
        return 0;
}
static int __def_board_fbt_handle_download(unsigned char *buffer,
        int length, struct cmd_fastboot_interface *priv)
{
    if (priv->d_size > priv->transfer_buffer_size)
    {
        priv->d_status = -1;
    }
    return 0;
}
static int __def_board_fbt_check_misc()
{
    return 0;
}
static int __def_board_fbt_set_bootloader_msg(struct bootloader_message* bmsg)
{
    memcpy(priv.transfer_buffer, bmsg, sizeof(struct bootloader_message));
    priv.d_bytes = sizeof(struct bootloader_message);

    /* write this structure to the "misc" partition, no unlock check */
    return fbt_handle_flash("flash:misc", 0);
}
static int __def_board_fbt_boot_check(struct fastboot_boot_img_hdr *hdr, int unlocked)
{
    return 0;
}
static int __def_board_fbt_boot_failed(const char* boot)
{
}

int board_fbt_oem(const char *cmdbuf)
	__attribute__((weak, alias("__def_fbt_oem")));
void board_fbt_set_reboot_type(enum fbt_reboot_type fre)
	__attribute__((weak, alias("__def_fbt_set_reboot_type")));
enum fbt_reboot_type board_fbt_get_reboot_type(void)
	__attribute__((weak, alias("__def_fbt_get_reboot_type")));
int board_fbt_key_pressed(void)
	__attribute__((weak, alias("__def_fbt_key_pressed")));
void board_fbt_finalize_bootargs(char* args, size_t buf_sz,
        size_t ramdisk_sz, int recovery)
	__attribute__((weak, alias("__def_board_fbt_finalize_bootargs")));
int board_fbt_handle_flash(char *name,
               struct cmd_fastboot_interface *priv)
    __attribute__((weak, alias("__def_board_fbt_handle_flash")));
int board_fbt_handle_download(unsigned char *buffer,
        int length, struct cmd_fastboot_interface *priv)
    __attribute__((weak, alias("__def_board_fbt_handle_download")));
int board_fbt_check_misc()
    __attribute__((weak, alias("__def_board_fbt_check_misc")));
int board_fbt_set_bootloader_msg(struct bootloader_message* bmsg)
    __attribute__((weak, alias("__def_board_fbt_set_bootloader_msg")));
int board_fbt_boot_check(struct fastboot_boot_img_hdr *hdr, int unlocked)
    __attribute__((weak, alias("__def_board_fbt_boot_check")));
int board_fbt_boot_failed(const char* boot)
    __attribute__((weak, alias("__def_board_fbt_boot_failed")));

/* command */
static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc,
							char * const argv[])
{
	int ret;
	/* currently we don't allow restarting fastboot if you've run
	 * it before and exited to u-boot prompt.  it's possible to
	 * support, but there's are edge cases that we're not
	 * sure the answer to (e.g. do we reload the partition table
	 * or not) and it's just not a common situation so we're
	 * just saying no for now.
	 */
	if (priv.flag & FASTBOOT_FLAG_HAS_RUN) {
		FBTERR("fastboot can't be restarted\n");
		return -1;
	}
	priv.flag |= FASTBOOT_FLAG_HAS_RUN;

	FBTDBG("Starting fastboot protocol\n");

	fbt_init_endpoint_ptrs();

	ret = udc_init();
	if (ret < 0) {
		FBTERR("%s: MUSB UDC init failure\n", __func__);
		goto out;
	}

	fbt_init_strings();
	fbt_init_instances();

	udc_startup_events(device_instance);
	udc_connect();

	FBTINFO("fastboot initialized\n");

    priv.pending_ptn = NULL;
    //load key here.
    SecureBootCheck();

	while (1) {
		if (priv.configured) {
			fbt_handle_rx();
			if (priv.unlock_pending_start_time) {
				/* check if unlock pending should expire */
				if (get_timer(priv.unlock_pending_start_time) >
				    (FASTBOOT_UNLOCK_TIMEOUT_SECS * 1000)) {
					FBTDBG("unlock pending expired\n");
					priv.unlock_pending_start_time = 0;
				}
			}
		}
#ifdef CONFIG_CTRLC
		priv.exit |= ctrlc();
#endif //CONFIG_CTRLC
		if (priv.exit) {
			FBTINFO("fastboot end\n");
			break;
		}
	}
	do_reset(NULL, 0, 0, NULL);
out:
	return ret;
}

U_BOOT_CMD(fastboot, 1,	1, do_fastboot,
	"use USB Fastboot protocol", NULL);

/* Section for Android bootimage format support
 * Refer:
 * http://android.git.kernel.org/?p=platform/system/core.git;a=blob;f=mkbootimg/bootimg.h
 */
static void bootimg_print_image_hdr(struct fastboot_boot_img_hdr *hdr)
{
#ifdef DEBUG
	int i;
	printf("   Image magic:   %s\n", hdr->magic);

	printf("   kernel_size:   0x%x\n", hdr->kernel_size);
	printf("   kernel_addr:   0x%x\n", hdr->kernel_addr);

	printf("   rdisk_size:   0x%x\n", hdr->ramdisk_size);
	printf("   rdisk_addr:   0x%x\n", hdr->ramdisk_addr);

	printf("   second_size:   0x%x\n", hdr->second_size);
	printf("   second_addr:   0x%x\n", hdr->second_addr);

	printf("   tags_addr:   0x%x\n", hdr->tags_addr);
	printf("   page_size:   0x%x\n", hdr->page_size);

	printf("   name:      %s\n", hdr->name);
	printf("   cmdline:   %s\n", hdr->cmdline);

	for (i = 0; i < 8; i++)
		printf("   id[%d]:   0x%x\n", i, hdr->id[i]);
#endif
}

#ifdef CONFIG_ROCKCHIP
extern int loadRkImage(struct fastboot_boot_img_hdr *hdr, fbt_partition_t *boot_ptn, fbt_partition_t *kernel_ptn);
#endif

/* booti [ <addr> | <partition> ] */
int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	char *boot_source = "boot";
	struct fastboot_boot_img_hdr *hdr = NULL;
    fbt_partition_t* ptn;
    bootm_headers_t images;

    bool charge = false;
	if (argc >= 2) {
        if (!strcmp(argv[1], "charge")) {
            charge = true;
        } else {
            boot_source = argv[1];
        }
    }

	ptn = fastboot_find_ptn(boot_source);
	if (ptn) {
        unsigned long blksz = RK_BLK_SIZE;
        unsigned sector;
        unsigned blocks;
        hdr = malloc(blksz << 2);
		if (hdr == NULL) {
		    FBTERR("error allocating blksz(%lu) buffer\n", blksz);
			goto fail;
		}
        if (StorageReadLba(ptn->offset, (void *) hdr, 1 << 2) != 0) {
			FBTERR("booti: failed to read bootimg header\n");
			goto fail;
		}
		if (memcmp(hdr->magic, FASTBOOT_BOOT_MAGIC,
			   FASTBOOT_BOOT_MAGIC_SIZE)) {
#ifdef CONFIG_ROCKCHIP
            memset(hdr, 0, blksz);
            if (loadRkImage(hdr, ptn, fastboot_find_ptn(KERNEL_NAME)) != 0) {
                FBTERR("booti: bad boot or kernel image\n");
                goto fail;
            }
#else
            FBTERR("booti: bad boot image magic\n");
            goto fail;
#endif
        } else {
#ifdef CONFIG_ROCKCHIP
            if (fixHdr(hdr) < 0) {
                goto fail;
            }
#endif
            sector = ptn->offset + (hdr->page_size / blksz);
            blocks = DIV_ROUND_UP(hdr->kernel_size, blksz);
            if (StorageReadLba(sector, (void *) hdr->kernel_addr, \
                        blocks) != 0) {
                FBTERR("booti: failed to read kernel\n");
                goto fail;
            }

            sector += ALIGN(hdr->kernel_size, hdr->page_size) / blksz;
            blocks = DIV_ROUND_UP(hdr->ramdisk_size, blksz);
            if (StorageReadLba(sector, (void *) hdr->ramdisk_addr, \
                        blocks) != 0) {
                FBTERR("booti: failed to read ramdisk\n");
                goto fail;
            }
        }
    } else {
        unsigned addr;
        void *kaddr, *raddr;
        char *ep;

        addr = simple_strtoul(boot_source, &ep, 16);
        if (ep == boot_source || *ep != '\0') {
            printf("'%s' does not seem to be a partition nor "
                        "an address\n", boot_source);
            /* this is most likely due to having no
             * partition table in factory case, or could
             * be argument is wrong.  in either case, start
             * fastboot mode.
             */
            goto fail;
        }

        hdr = malloc(sizeof(*hdr));
        if (hdr == NULL) {
            printf("error allocating buffer\n");
            goto fail;
        }

        /* set this aside somewhere safe */
        memcpy(hdr, (void *) addr, sizeof(*hdr));

#ifdef CONFIG_ROCKCHIP
        if (fixHdr(hdr) < 0) {
            goto fail;
        }
#endif

        if (memcmp(hdr->magic, FASTBOOT_BOOT_MAGIC,
               FASTBOOT_BOOT_MAGIC_SIZE)) {
            printf("booti: bad boot image magic\n");
            goto fail;
        }

        kaddr = (void *)(addr + hdr->page_size);
        raddr = (void *)(kaddr + ALIGN(hdr->kernel_size,
                           hdr->page_size));
        memmove((void *)hdr->kernel_addr, kaddr, hdr->kernel_size);
        memmove((void *)hdr->ramdisk_addr, raddr, hdr->ramdisk_size);
    }

    if (board_fbt_boot_check(hdr, priv.unlocked)) {
        FBTERR("booti: board check boot image error\n");
        goto fail;
    }

    bootimg_print_image_hdr(hdr);

    FBTDBG("kernel   @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size);
    FBTDBG("ramdisk  @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size);

#ifdef CONFIG_CMDLINE_TAG
    {
		/* static just to be safe when it comes to the stack */
		static char command_line[1024];
		int i, amt;
		/* Use the cmdline from board_fbt_finalize_bootargs instead of
		 * any hardcoded into u-boot.  Also, Android wants the
		 * serial number on the command line instead of via
		 * tags so append the serial number to the bootimg header
		 * value and set the bootargs environment variable.
		 * do_bootm_linux() will use the bootargs environment variable
		 * to pass it to the kernel.  Add the bootloader
		 * version too.
		 */
        board_fbt_finalize_bootargs(command_line, sizeof(command_line),
                hdr->ramdisk_size, !strcmp(boot_source, RECOVERY_NAME));
        //printf("board cmdline:\n%s\n", command_line);
		amt = snprintf(command_line,
				sizeof(command_line),
				"%s androidboot.bootloader=%s fb.addr=0x%08lx",
				command_line,
				CONFIG_FASTBOOT_VERSION_BOOTLOADER, gd->fb_base);
        
#if 0
		for (i = 0; i < priv.num_device_info; i++) {
			/* Append device specific information like
			 * MAC addresses and serialno
			 */
			amt += snprintf(command_line + amt,
					sizeof(command_line) - amt,
					" %s=%s",
					priv.dev_info[i].name,
					priv.dev_info[i].value);
		}
#endif
//TODO:add some dev info to cmdline?
//
        if (charge)
            snprintf(command_line, sizeof(command_line),
                "%s %s",command_line," androidboot.mode=charger");

		/* append serial number if it wasn't in device_info already */
		if (!strstr(command_line, FASTBOOT_SERIALNO_BOOTARG)) {
			snprintf(command_line + amt, sizeof(command_line) - amt,
				 " %s=%s", FASTBOOT_SERIALNO_BOOTARG,
				 priv.serial_no);
		}

		command_line[sizeof(command_line) - 1] = 0;

		setenv("bootargs", command_line);
	}
#endif /* CONFIG_CMDLINE_TAG */

	memset(&images, 0, sizeof(images));
	images.ep = hdr->kernel_addr;
	images.rd_start = hdr->ramdisk_addr;
	images.rd_end = hdr->ramdisk_addr + hdr->ramdisk_size;
	free(hdr);
	puts("booti: do_bootm_linux...\n");
	do_bootm_linux(0, 0, NULL, &images);

    //Should not reach here.
    goto fail;

	puts("booti: Control returned to monitor - resetting...\n");
	do_reset(cmdtp, flag, argc, argv);
	return 1;

fail:
	/* if booti fails, always start fastboot */
	free(hdr); /* hdr may be NULL, but that's ok. */

    board_fbt_boot_failed(boot_source);

	return do_fastboot(NULL, 0, 0, NULL);
}

U_BOOT_CMD(
	booti,	2,	1,	do_booti,
	"boot android bootimg",
#ifdef DEBUG
	"[ <addr> | <partition> ]\n    - boot application image\n"
	"\t'addr' should be the address of the boot image which is\n"
	"\tzImage+ramdisk.img if in memory.  'partition' is the name\n"
	"\tof the partition to boot from.  The default is to boot\n"
	"\tfrom the 'boot' partition.\n"
#else
    "\n"
#endif
);

static void fbt_request_start_fastboot(void)
{
	char buf[512];
	char *old_preboot = getenv("preboot");
	FBTDBG("old preboot env = %s\n", old_preboot);

	if (old_preboot) {
		snprintf(buf, sizeof(buf),
			 "setenv preboot %s; fastboot", old_preboot);
		setenv("preboot", buf);
	} else
		setenv("preboot", "setenv preboot; fastboot");

	FBTDBG("%s: setting preboot env to %s\n", __func__, getenv("preboot"));
}

/*
 * Determine if we should enter fastboot mode based on board specific
 * key press or parameter left in memory from previous boot.
 *
 * This is also where we initialize fbt private data.  Even if we
 * don't enter fastboot mode, we need our environment setup for
 * things like unlock state, etc.
 */
void fbt_preboot(void)
{
	enum fbt_reboot_type frt;

	/* need to init this ASAP so we know the unlocked state */
	fbt_fastboot_init();

    frt = board_fbt_key_pressed();

    if (frt == FASTBOOT_REBOOT_NONE) {
        FBTDBG("\n%s: no spec key pressed, get requested reboot type.\n",
                __func__);
	    frt = board_fbt_get_reboot_type();
    } else {
        //clear reboot type when key pressed.
        board_fbt_set_reboot_type(FASTBOOT_REBOOT_NONE);
    }

#ifdef CONFIG_ROCKCHIP
#ifdef CONFIG_LCD
    drv_lcd_init();   //move backlight enable to board_init_r, for don't show logo in rockusb                                         
#endif
#ifdef CONFIG_RK616
	rk616_init(CONFIG_RK616_LCD_CHN);
#endif

#endif// CONFIG_ROCKCHIP
    //check charge mode when no key pressed.
    if(check_charge() || frt == FASTBOOT_REBOOT_CHARGE) {
#ifdef CONFIG_CMD_CHARGE_ANIM
        char *charge[] = { "charge" };
        if (do_charge(NULL, 0, ARRAY_SIZE(charge), charge)) {
            //boot from charge animation.
            frt = FASTBOOT_REBOOT_NONE;
        }
#else
        return fbt_run_charge();
#endif
    }
#ifdef CONFIG_ROCKCHIP
PowerHoldPinInit();
#ifdef CONFIG_LCD
    lcd_enable_logo(true);
    rk_backlight_ctrl(48);
#endif
#endif// CONFIG_ROCKCHIP

	if (frt == FASTBOOT_REBOOT_RECOVERY) {
		FBTDBG("\n%s: starting recovery img because of reboot flag\n",
		       __func__);

		return fbt_run_recovery();
	} else if (frt == FASTBOOT_REBOOT_RECOVERY_WIPE_DATA) {
		FBTDBG("\n%s: starting recovery img to wipe data "
		       "because of reboot flag\n",
		       __func__);
		/* we've not initialized most of our state so don't
		 * save env in this case
		 */
		return fbt_run_recovery_wipe_data();
	} else if (frt == FASTBOOT_REBOOT_FASTBOOT) {
		FBTDBG("\n%s: starting fastboot because of reboot flag\n",
		       __func__);
		fbt_request_start_fastboot();
	} else {
        FBTDBG("\n%s: check misc command.\n", __func__);
		/* unknown reboot cause (typically because of a cold boot).
		 * check if we had misc command to boot recovery.
		 */
		int run_recovery = board_fbt_check_misc();
		if (run_recovery) {
			FBTDBG("\n%s: starting recovery because of misc command\n", 
                    __func__);
			return fbt_run_recovery();
		}
		FBTDBG("\n%s: no special reboot flags, doing normal boot\n",
		       __func__);
	}
}

#ifdef CONFIG_FASTBOOT_LOG
int fbt_log(const char *info, const int len, bool send)
{
    unsigned long space_in_log = CONFIG_FASTBOOT_LOG_SIZE - log_position - 1;
    unsigned long bytes_to_log;

    /* check if relocation is done before we can use globals */
    if (gd->flags & GD_FLG_RELOC) {
        if (len > space_in_log)
            bytes_to_log = space_in_log;
        else
            bytes_to_log = len;

        if (bytes_to_log) {
            strncpy(&log_buffer[log_position], info, bytes_to_log);
            log_position += bytes_to_log;
        }
    }

    if (!send)
        return 0;
    return fbt_send_raw_info(info, len);
}

int fbt_send_info(const char *info)
{
    return fbt_log(info, strlen(info), true);
}
#endif //CONFIG_FASTBOOT_LOG
