diff options
Diffstat (limited to 'common')
| -rw-r--r-- | common/Makefile | 2 | ||||
| -rw-r--r-- | common/cmd_fastboot.c | 117 | ||||
| -rw-r--r-- | common/cmd_fastboot_omap.c | 2544 | ||||
| -rw-r--r-- | common/cmd_nand.c | 2 | ||||
| -rw-r--r-- | common/cmd_nvedit.c | 4 | 
5 files changed, 2666 insertions, 3 deletions
| diff --git a/common/Makefile b/common/Makefile index d12cba5bf..de4253401 100644 --- a/common/Makefile +++ b/common/Makefile @@ -163,6 +163,8 @@ obj-y += cmd_usb.o  obj-y += usb.o usb_hub.o  obj-$(CONFIG_USB_STORAGE) += usb_storage.o  endif +obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o +  obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o  obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o  obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 000000000..4ccf9af86 --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de> + * + * Modified by Vishveshwar Bhat <vishveshwar.bhat@ti.com> + *             Pankaj Bharadiya <pankaj.bharadiya@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 + * + */ + +#include <common.h> +#include <command.h> +#include <usb/fastboot.h> +#include <asm/sizes.h> + +static char serial_number[28] = "001234"; + +static struct usb_string def_usb_fb_strings[] = { +	/* This has to match the product name used in the AOSP build. +	   Should not be hard coded here... */ +	/* { FB_STR_PRODUCT_IDX,		"AM335xevm" }, */ +	{ FB_STR_PRODUCT_IDX,		"beagleboneblack" }, +	{ FB_STR_SERIAL_IDX,		serial_number }, +	{ FB_STR_PROC_REV_IDX,		"ES2.x" }, +	{ FB_STR_PROC_TYPE_IDX,		"ARMv7" }, +	{ FB_STR_MANUFACTURER_IDX,	"Texas Instruments" }, +	{  } +}; + +static struct usb_gadget_strings def_fb_strings = { +	.language	= 0x0409, /* en-us */ +	.strings	= def_usb_fb_strings, +}; + +/* + * Hardcoded memory region to stash data which comes over USB before it is + * stored on media + */ +DECLARE_GLOBAL_DATA_PTR; +#define CFG_FASTBOOT_TRANSFER_BUFFER (void *)(gd->bd->bi_dram[0].start + SZ_16M) + +static void set_serial_number(void) +{ +	/* use ethaddr for fastboot serial no. */ +	char *ethaddr = "123456789012345678901234567890";/*getenv("ethaddr");*/ + +	if (ethaddr != NULL) { +		int len; + +		memset(&serial_number[0], 0, 28); +		len = strlen(ethaddr); +		if (len > 28) +			len = 26; + +		strncpy(&serial_number[0], ethaddr, len); +	} +} + +int fastboot_board_init(struct fastboot_config *interface, +			struct usb_gadget_strings **str) +{ +	/* Initialize board serial no.  */ +	set_serial_number(); + +	interface->transfer_buffer = CFG_FASTBOOT_TRANSFER_BUFFER; +	interface->transfer_buffer_size = CONFIG_FASTBOOT_MAX_TRANSFER_SIZE; +	interface->nand_block_size  = FASTBOOT_NAND_BLOCK_SIZE; +	interface->nand_oob_size  = FASTBOOT_NAND_OOB_SIZE; +	interface->download_bytes = 0; +	interface->download_size = 0; + +	*str = &def_fb_strings; +	return 0; +} + +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ +	int ret = 1; + +	printf("Fastboot dbg...\n"); +	if (!fastboot_init()) { +		//board_mmc_fbtptn_init(); +		printf("Fastboot entered...\n"); + +		ret = 0; + +		while (1) { +			if (fastboot_poll()) +			{ +				printf("Fastboot poll break...\n"); +				break; +			} +		} +	} + +	fastboot_shutdown(); +	return ret; +} + +U_BOOT_CMD( +	fastboot,	1,	1,	do_fastboot, +	"fastboot- use USB Fastboot protocol\n", +	"" +); diff --git a/common/cmd_fastboot_omap.c b/common/cmd_fastboot_omap.c new file mode 100644 index 000000000..c0b5c283e --- /dev/null +++ b/common/cmd_fastboot_omap.c @@ -0,0 +1,2544 @@ +/* + * 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 <usb/fastboot.h> +#include <omap_fastboot.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 <timestamp_autogenerated.h> +#define CONFIG_FASTBOOT_VERSION_BOOTLOADER \ +				(FASTBOOT_PRODUCT_NAME FASTBOOT_TIMESTAMP) +#endif +#endif + +#define FASTBOOT_RUN_RECOVERY_ENV_NAME "fastboot_run_recovery" +#define FASTBOOT_UNLOCKED_ENV_NAME "fastboot_unlocked" +#define FASTBOOT_UNLOCK_TIMEOUT_SECS 5 + +#ifndef CONFIG_ENV_BLK_PARTITION +#define CONFIG_ENV_BLK_PARTITION "environment" +#endif +#ifndef CONFIG_INFO_PARTITION +#define CONFIG_INFO_PARTITION "device_info" +#endif + +#define	ERR +#define	WARN +#undef	INFO +#undef	DEBUG + +#ifndef CONFIG_FASTBOOT_LOG_SIZE +#define CONFIG_FASTBOOT_LOG_SIZE 4000 +#endif +static char log_buffer[CONFIG_FASTBOOT_LOG_SIZE]; +static uint32_t log_position; + + +#ifdef DEBUG +#define FBTDBG(fmt, args...)\ +	printf("DEBUG: [%s]: %d:\n"fmt, __func__, __LINE__, ##args) +#else +#define FBTDBG(fmt, args...) do {} while (0) +#endif + +#ifdef INFO +#define FBTINFO(fmt, args...)\ +	printf("INFO: [%s]: "fmt, __func__, ##args) +#else +#define FBTINFO(fmt, args...) do {} while (0) +#endif + +#ifdef WARN +#define FBTWARN(fmt, args...)\ +	printf("WARNING: [%s]: "fmt, __func__, ##args) +#else +#define FBTWARN(fmt, args...) do {} while (0) +#endif + +#ifdef ERR +#define FBTERR(fmt, args...)\ +	printf("ERROR: [%s]: "fmt, __func__, ##args) +#else +#define FBTERR(fmt, args...) do {} while (0) +#endif + +#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> +#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	1 +#define	TX_EP_INDEX	2 + +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 disk_partition_t *fastboot_flash_find_ptn(const char *name); +static void fbt_run_recovery_wipe_data(void); +static void fbt_run_recovery(int do_saveenv); + +/* 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 u8 wstr_lang[4] = {4, USB_DT_STRING, 0x9, 0x4}; +static u8 wstr_manufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; +static u8 wstr_product[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)]; +static u8 wstr_serial[2 + 2*(sizeof(serial_number) - 1)]; +static u8 wstr_configuration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)]; +static 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 const char info_partition_magic[] = {'I', 'n', 'f', 'o'}; + +static struct cmd_fastboot_interface priv = { +	.transfer_buffer       = (u8 *)CONFIG_FASTBOOT_TRANSFER_BUFFER, +	.transfer_buffer_size  = CONFIG_FASTBOOT_TRANSFER_BUFFER_SIZE, +}; + +static void fbt_init_endpoints(void); +static int do_booti(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[]); + +/* To support the Android-style naming of flash */ +#define MAX_PTN (CONFIG_MAX_PARTITION_NUM - CONFIG_MIN_PARTITION_NUM + 1) +static disk_partition_t ptable[MAX_PTN]; +static unsigned int pcount; + +/* 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->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]); + +	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 (device_instance->speed == USB_SPEED_FULL) { +			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; +		} else if (device_instance->speed == USB_SPEED_HIGH) { +			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; +		} + +		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]); +	} +} + +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; +	printf("fastboot serial_number = %s\n", serial_number); +} + +static void create_serial_number(void) +{ +	char *dieid = getenv("fbt_id#"); + +	if (dieid == NULL) +		dieid = getenv("dieid#"); + +	if (dieid == NULL) { +		printf("Setting serial number from constant (no dieid info)\n"); +		set_serial_number("00123"); +	} else { +		printf("Setting serial number from unique id\n"); +		set_serial_number(dieid); +	} +} + +static int is_env_partition(disk_partition_t *ptn) +{ +	return !strcmp((char *)ptn->name, CONFIG_ENV_BLK_PARTITION); +} +static int is_info_partition(disk_partition_t *ptn) +{ +	return !strcmp((char *)ptn->name, CONFIG_INFO_PARTITION); +} + +void fbt_add_ptn(disk_partition_t *ptn) +{ +	if (pcount < MAX_PTN) { +		memcpy(ptable + pcount, ptn, sizeof(*ptn)); +		pcount++; +	} +} + +static int fbt_load_partition_table(void) +{ +	disk_partition_t *info_ptn; +	unsigned int i; + +	if (board_fbt_load_ptbl()) { +		printf("board_fbt_load_ptbl() failed\n"); +		return -1; +	} + +	/* load device info partition if it exists */ +	info_ptn = fastboot_flash_find_ptn(CONFIG_INFO_PARTITION); +	if (info_ptn) { +		struct info_partition_header *info_header; +		char *name, *next_name; +		char *value; + +		lbaint_t num_blks = 1; +		i = partition_read_blks(priv.dev_desc, info_ptn, +					&num_blks, priv.transfer_buffer); +		if (i) { +			printf("failed to read info partition. error=%d\n", i); +			goto no_existing_info; +		} + +		/* parse the info partition read from the device */ +		info_header = +			(struct info_partition_header *)priv.transfer_buffer; +		name = (char *)(info_header + 1); +		value = name; + +		if (memcmp(&info_header->magic, info_partition_magic, +			   sizeof(info_partition_magic)) != 0) { +			printf("info partition magic 0x%x invalid," +			       " assuming none\n", info_header->magic); +			goto no_existing_info; +		} +		if (info_header->num_values > FASTBOOT_MAX_NUM_DEVICE_INFO) { +			printf("info partition num values %d too large " +			       " (max %d)\n", info_header->num_values, +			       FASTBOOT_MAX_NUM_DEVICE_INFO); +			goto no_existing_info; +		} +		priv.num_device_info = info_header->num_values; +		/* the name/value pairs are in the format: +		 *    name1=value1\n +		 *    name2=value2\n +		 * this makes it easier to read if we dump the partition +		 * to a file +		 */ +		printf("%d device info entries read from %s partition:\n", +		       priv.num_device_info, info_ptn->name); +		for (i = 0; i < priv.num_device_info; i++) { +			while (*value != '=') +				value++; +			*value++ = '\0'; +			next_name = value; +			while (*next_name != '\n') +				next_name++; +			*next_name++ = '\0'; +			priv.dev_info[i].name = strdup(name); +			priv.dev_info[i].value = strdup(value); +			printf("\t%s=%s\n", priv.dev_info[i].name, +			       priv.dev_info[i].value); +			/* initialize serial number from device info */ +			if (!strcmp(name, FASTBOOT_SERIALNO_BOOTARG)) +				set_serial_number(value); +			name = next_name; +		} +		priv.dev_info_uninitialized = 0; +	} else { +no_existing_info: +		priv.dev_info_uninitialized = 1; +		printf("No existing device info found.\n"); +	} + +	if (priv.serial_no == NULL) +		create_serial_number(); + +	return 0; +} + +static disk_partition_t *fastboot_flash_find_ptn(const char *name) +{ +	unsigned int n; + +	if (pcount == 0) { +		if (fbt_load_partition_table()) { +			printf("Unable to load partition table, aborting\n"); +			return NULL; +		} +	} + +	for (n = 0; n < pcount; n++) +		if (!strcmp((char *)ptable[n].name, name)) +			return ptable + n; +	return NULL; +} + +void fbt_reset_ptn(void) +{ +	pcount = 0; +	if (fbt_load_partition_table()) +		FBTERR("Unable to load partition table\n"); +} + +static void fbt_set_unlocked(int unlocked) +{ +	char *unlocked_string; + +	printf("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; +	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 { +			printf("bad env setting %s of %s," +			       " initializing to locked\n", +			       fastboot_unlocked_env, +			       FASTBOOT_UNLOCKED_ENV_NAME); +			fbt_set_unlocked(0); +		} +	} else { +		printf("no existing env setting for %s\n", +		       FASTBOOT_UNLOCKED_ENV_NAME); +		printf("creating one set to false\n"); +		fbt_set_unlocked(0); +	} +	if (priv.unlocked) +		printf("Device is unlocked\n"); +	else +		printf("Device is locked\n"); + +	priv.dev_desc = get_dev_by_name(FASTBOOT_BLKDEV); +	if (!priv.dev_desc) { +		FBTERR("%s: fastboot device %s not found\n", +						__func__, FASTBOOT_BLKDEV); +		return; +	} + +	/* +	 * We need to be able to run fastboot even if there isn't a partition +	 * table (so we can use "oem format") and fbt_load_partition_table +	 * already printed an error, so just ignore the error return. +	 */ +	(void)fbt_load_partition_table(); +} + +static void fbt_handle_erase(char *cmdbuf) +{ +	disk_partition_t *ptn; +	int err; +	char *partition_name = cmdbuf + 6; +	char *num_blocks_str; +	lbaint_t num_blocks; +	lbaint_t *num_blocks_p = NULL; + +	/* see if there is an optional num_blocks after the partition name */ +	num_blocks_str = strchr(partition_name, ' '); +	if (num_blocks_str) { +		/* null terminate the partition name */ +		*num_blocks_str = 0; +		num_blocks_str++; +		num_blocks = simple_strtoull(num_blocks_str, NULL, 10); +		num_blocks_p = &num_blocks; +	} + +	ptn = fastboot_flash_find_ptn(partition_name); +	if (ptn == 0) { +		printf("Partition %s does not exist\n", partition_name); +		sprintf(priv.response, "FAILpartition does not exist"); +		return; +	} + +#ifndef CONFIG_MFG +	/* don't allow erasing environment partition or a valid +	 * device info partition in a production u-boot */ +	if (is_env_partition(ptn) || +	    (is_info_partition(ptn) && (!priv.dev_info_uninitialized))) { +		printf("Not allowed to erase %s partition\n", ptn->name); +		strcpy(priv.response, "FAILnot allowed to erase partition"); +		return; +	} +#endif + +	printf("Erasing partition '%s':\n", ptn->name); + +	printf("\tstart blk %lu, blk_cnt %lu of %lu\n", ptn->start, +			num_blocks_p ? num_blocks : ptn->size, ptn->size); + +	err = partition_erase_blks(priv.dev_desc, ptn, num_blocks_p); +	if (err) { +		printf("Erasing '%s' FAILED! error=%d\n", ptn->name, err); +		sprintf(priv.response, +				"FAILfailed to erase partition (%d)", err); +	} else { +		printf("partition '%s' erased\n", ptn->name); +		sprintf(priv.response, "OKAY"); +	} +} + +#define SPARSE_HEADER_MAJOR_VER 1 + +static int _unsparse(unsigned char *source, +					lbaint_t sector, lbaint_t num_blks) +{ +	sparse_header_t *header = (void *) source; +	u32 i; +	unsigned long blksz = priv.dev_desc->blksz; +	u64 section_size = (u64)num_blks * blksz; +	u64 outlen = 0; + +	FBTINFO("sparse_header:\n"); +	FBTINFO("\t         magic=0x%08X\n", header->magic); +	FBTINFO("\t       version=%u.%u\n", header->major_version, +						header->minor_version); +	FBTINFO("\t file_hdr_size=%u\n", header->file_hdr_sz); +	FBTINFO("\tchunk_hdr_size=%u\n", header->chunk_hdr_sz); +	FBTINFO("\t        blk_sz=%u\n", header->blk_sz); +	FBTINFO("\t    total_blks=%u\n", header->total_blks); +	FBTINFO("\t  total_chunks=%u\n", header->total_chunks); +	FBTINFO("\timage_checksum=%u\n", header->image_checksum); + +	if (header->magic != SPARSE_HEADER_MAGIC) { +		printf("sparse: bad magic\n"); +		return 1; +	} + +	if (((u64)header->total_blks * header->blk_sz) > section_size) { +		printf("sparse: section size %llu MB limit: exceeded\n", +				section_size/(1024*1024)); +		return 1; +	} + +	if ((header->major_version != SPARSE_HEADER_MAJOR_VER) || +	    (header->file_hdr_sz != sizeof(sparse_header_t)) || +	    (header->chunk_hdr_sz != sizeof(chunk_header_t))) { +		printf("sparse: incompatible format\n"); +		return 1; +	} + +	/* Skip the header now */ +	source += header->file_hdr_sz; + +	for (i = 0; i < header->total_chunks; i++) { +		u64 clen = 0; +		lbaint_t blkcnt; +		chunk_header_t *chunk = (void *) source; + +		FBTINFO("chunk_header:\n"); +		FBTINFO("\t    chunk_type=%u\n", chunk->chunk_type); +		FBTINFO("\t      chunk_sz=%u\n", chunk->chunk_sz); +		FBTINFO("\t      total_sz=%u\n", chunk->total_sz); +		/* move to next chunk */ +		source += sizeof(chunk_header_t); + +		switch (chunk->chunk_type) { +		case CHUNK_TYPE_RAW: +			clen = (u64)chunk->chunk_sz * header->blk_sz; +			FBTINFO("sparse: RAW blk=%d bsz=%d:" +			       " write(sector=%lu,clen=%llu)\n", +			       chunk->chunk_sz, header->blk_sz, sector, clen); + +			if (chunk->total_sz != (clen + sizeof(chunk_header_t))) { +				printf("sparse: bad chunk size for" +				       " chunk %d, type Raw\n", i); +				return 1; +			} + +			outlen += clen; +			if (outlen > section_size) { +				printf("sparse: section size %llu MB limit:" +				       " exceeded\n", section_size/(1024*1024)); +				return 1; +			} +			blkcnt = clen / blksz; +			FBTDBG("sparse: RAW blk=%d bsz=%d:" +			       " write(sector=%lu,clen=%llu)\n", +			       chunk->chunk_sz, header->blk_sz, sector, clen); +			if (priv.dev_desc->block_write(priv.dev_desc->dev, +						       sector, blkcnt, source) +						!= blkcnt) { +				printf("sparse: block write to sector %lu" +					" of %llu bytes (%ld blkcnt) failed\n", +					sector, clen, blkcnt); +				return 1; +			} + +			sector += (clen / blksz); +			source += clen; +			break; + +		case CHUNK_TYPE_DONT_CARE: +			if (chunk->total_sz != sizeof(chunk_header_t)) { +				printf("sparse: bogus DONT CARE chunk\n"); +				return 1; +			} +			clen = (u64)chunk->chunk_sz * header->blk_sz; +			FBTDBG("sparse: DONT_CARE blk=%d bsz=%d:" +			       " skip(sector=%lu,clen=%llu)\n", +			       chunk->chunk_sz, header->blk_sz, sector, clen); + +			outlen += clen; +			if (outlen > section_size) { +				printf("sparse: section size %llu MB limit:" +				       " exceeded\n", section_size/(1024*1024)); +				return 1; +			} +			sector += (clen / blksz); +			break; + +		default: +			printf("sparse: unknown chunk ID %04x\n", +			       chunk->chunk_type); +			return 1; +		} +	} + +	printf("sparse: out-length %llu MB\n", outlen/(1024*1024)); +	return 0; +} + +static int do_unsparse(disk_partition_t *ptn, unsigned char *source, +					lbaint_t sector, lbaint_t num_blks) +{ +	int rtn; +	if (partition_write_pre(ptn)) +		return 1; + +	rtn = _unsparse(source, sector, num_blks); + +	if (partition_write_post(ptn)) +		return 1; + +	return rtn; +} + +static int fbt_save_info(disk_partition_t *info_ptn) +{ +	struct info_partition_header *info_header; +	char *name; +	char *value; +	int i; + +	if (!priv.dev_info_uninitialized) { +		printf("%s partition already initialized, " +		       " cannot write to it again\n", info_ptn->name); +		return -1; +	} + +	info_header = (struct info_partition_header *)priv.transfer_buffer; +	name = (char *)(info_header + 1); +	memset(info_header, 0, priv.dev_desc->blksz); +	memcpy(&info_header->magic, info_partition_magic, +	       sizeof(info_partition_magic)); +	info_header->num_values = priv.num_device_info; + +	for (i = 0; i < priv.num_device_info; i++) { +		unsigned int len = strlen(priv.dev_info[i].name); +		memcpy(name, priv.dev_info[i].name, len); +		value = name + len; +		*value++ = '='; +		if (priv.dev_info[i].value) { +			len = strlen(priv.dev_info[i].value); +			memcpy(value, priv.dev_info[i].value, len); +			name = value + len; +			*name++ = '\n'; +		} +	} +	lbaint_t num_blks = 1; +	i = partition_write_blks(priv.dev_desc, info_ptn, &num_blks, +								info_header); +	if (i) { +		printf("block write to sector %lu failed, error=%d", +							info_ptn->start, i); +		return -1; +	} +	priv.dev_info_uninitialized = 0; +	return 0; +} + +static void fbt_handle_flash(char *cmdbuf, int check_unlock) +{ +	disk_partition_t *ptn; + +	if (check_unlock && !priv.unlocked) { +		printf("%s: failed, device is locked\n", __func__); +		sprintf(priv.response, "FAILdevice is locked"); +		return; +	} + +	if (!priv.d_bytes) { +		printf("%s: failed, no image downloaded\n", __func__); +		sprintf(priv.response, "FAILno image downloaded"); +		return; +	} + +	ptn = fastboot_flash_find_ptn(cmdbuf + 6); +	if (ptn == 0) { +		printf("%s: failed, partition %s does not exist\n", +		       __func__, cmdbuf + 6); +		sprintf(priv.response, "FAILpartition does not exist"); +		return; +	} + +	/* do board/cpu specific special handling if needed.  this +	 * can include modifying priv.image_start_ptr to flash from +	 * an address other than the start of the transfer buffer. +	 */ +	priv.image_start_ptr = priv.transfer_buffer; +	if (board_fbt_handle_flash(ptn, &priv)) { +		/* error case, return.  expect priv.response to be +		 * set by the board specific handler. +		 */ +		printf("%s: failed, board_fbt_handle_flash() error\n", +		       __func__); +		return; +	} + +	/* Prevent using flash command to write to device_info partition */ +	if (is_info_partition(ptn)) { +		printf("%s: failed, partition not writable" +		       " using flash command\n", +		       __func__); +		sprintf(priv.response, +			"FAILpartition not writable using flash command"); +		return; +	} + +	/* Check if this is not really a flash write but rather a saveenv */ +	if (is_env_partition(ptn)) { +		if (!himport_r(&env_htab, +			       (const char *)priv.image_start_ptr, +			       priv.d_bytes, '\n', H_NOCLEAR)) { +			FBTINFO("Import '%s' FAILED!\n", ptn->name); +			sprintf(priv.response, "FAIL: Import environment"); +			return; +		} + +#if defined(CONFIG_CMD_SAVEENV) +		if (saveenv()) { +			printf("Writing '%s' FAILED!\n", ptn->name); +			sprintf(priv.response, "FAIL: Write partition"); +			return; +		} +		printf("saveenv to '%s' DONE!\n", ptn->name); +#endif +		sprintf(priv.response, "OKAY"); +	} else { +		/* Normal case */ +		printf("writing to partition '%s'\n", ptn->name); + +		/* Check if we have sparse compressed image */ +		if (((sparse_header_t *)priv.image_start_ptr)->magic +		    == SPARSE_HEADER_MAGIC) { +			printf("fastboot: %s is in sparse format\n", ptn->name); +			if (!do_unsparse(ptn, priv.image_start_ptr, +					 ptn->start, ptn->size)) { +				printf("Writing sparsed: '%s' DONE!\n", +				       ptn->name); +				sprintf(priv.response, "OKAY"); +			} else { +				printf("Writing sparsed '%s' FAILED!\n", +				       ptn->name); +				sprintf(priv.response, "FAIL: Sparsed Write"); +			} +		} else { +			/* Normal image: no sparse */ +			int err; +			loff_t num_bytes = priv.d_bytes; + +			printf("Writing %llu bytes to '%s'\n", +						num_bytes, ptn->name); +			err = partition_write_bytes(priv.dev_desc, ptn, +				&num_bytes, priv.image_start_ptr); +			if (err) { +				printf("Writing '%s' FAILED! error=%d\n", +							ptn->name, err); +				sprintf(priv.response, +					"FAILWrite partition, error=%d", err); +			} else { +				printf("Writing '%s' DONE!\n", ptn->name); +				sprintf(priv.response, "OKAY"); +			} +		} +	} /* Normal Case */ +} + +struct getvar_entry { +	const char *variable_name; +	int exact_match; +	const char *(*getvar_func)(const char *arg); +}; + +static const char *getvar_version(const char *unused) +{ +	return version_string; +} + +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"); +} + +#if defined (CONFIG_OMAP) +static const char *getvar_device_type(const char *unused) +{ +	switch(get_device_type()) { +	case TST_DEVICE: +		return "TST"; +	case EMU_DEVICE: +		return "EMU"; +	case HS_DEVICE: +		return "HS"; +	case GP_DEVICE: +		return "GP"; +	default: +		return "unknown"; +	} +} +#endif + +static const char *getvar_product(const char *unused) +{ +	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; +	const char *type; + +	if (!strcmp(args, "all")) { +		int i; +		for (i = 0; i < pcount; i++) { +			partition_name = (char*)ptable[i].name; +			printf("partition \"%s\" has type \"%s\"\n", +			       partition_name, +			       board_fbt_get_partition_type(partition_name)); +		} +		return NULL; +	} + +	partition_name = args + sizeof("partition-type:") - 1; +	type = board_fbt_get_partition_type(partition_name); +	if (type) { +		return type; +	} +	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; +	disk_partition_t *ptn; + +	if (!strcmp(args, "all")) { +		int i; +		for (i = 0; i < pcount; i++) { +			printf("partition \"%s\" has size 0x%016llx bytes\n", +			       ptable[i].name, +			       (uint64_t)ptable[i].size * ptable[i].blksz); +		} +		return NULL; +	} + +	partition_name = args + sizeof("partition-size:") - 1; +	ptn = fastboot_flash_find_ptn(partition_name); +	if (ptn) { +		snprintf(priv.response, sizeof(priv.response), +			 "OKAY0x%016llx", (uint64_t)ptn->size * ptn->blksz); +	} 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}, +#if defined (CONFIG_OMAP) +	{"device_type", 1, getvar_device_type}, +#endif +	{"product", 1, getvar_product}, +	{"serialno", 1, getvar_serialno}, +	{"partition-type:", 0, getvar_partition_type}, +	{"partition-size:", 0, getvar_partition_size} +}; + +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 ) { +				printf("%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 */ + +	board_fbt_end(); + +	do_reset(NULL, 0, 0, NULL); +} + +static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of fastboot cmdbuf */ + +static void fbt_handle_oem_setinfo(const char *cmdbuf) +{ +	char *name, *value; +	struct device_info *di; + +	FBTDBG("oem setinfo\n"); + +	/* this is only allowed if the device info isn't already +	 * initlialized in flash +	 */ +	if (!priv.dev_info_uninitialized) { +		printf("Not allowed to change device info already in flash\n"); +		strcpy(priv.response, "FAILnot allowed to change" +		       " device info already in flash"); +		return; +	} + +	if (priv.num_device_info == FASTBOOT_MAX_NUM_DEVICE_INFO) { +		printf("Already at maximum number of device info (%d)," +		       " no more allowed\n", FASTBOOT_MAX_NUM_DEVICE_INFO); +		strcpy(priv.response, "FAILmax device info reached"); +		return; +	} + +	/* copy to tmp_buf which will be modified by str_tok() */ +	strcpy(tmp_buf, cmdbuf); + +	name = strtok(tmp_buf, "="); +	value = strtok(NULL, "\n"); +	if (!name || !value) { +		printf("Invalid format for setinfo.\n"); +		printf("Syntax is " +		       "'fastboot oem setinfo <info_name>=<info_value>\n"); +		strcpy(priv.response, "FAILinvalid device info"); +		return; +	} + +	/* we enter new value at end so last slot should be free. +	 * we don't currently allow changing a value already set. +	 */ +	di = &priv.dev_info[priv.num_device_info]; +	if (di->name || di->value) { +		printf("Error, device info entry not free as expected\n"); +		strcpy(priv.response, "FAILinternal error"); +		return; +	} + +	di->name = strdup(name); +	di->value = strdup(value); +	if ((di->name == NULL) || (di->value == NULL)) { +		printf("strdup() failed, unable to set info\n"); +		strcpy(priv.response, "FAILstrdup() failure\n"); +		free(di->name); +		free(di->value); +		return; +	} + +	printf("Set device info %s=%s\n", di->name, di->value); +	if (!strcmp(di->name, FASTBOOT_SERIALNO_BOOTARG)) +		set_serial_number(di->value); +	priv.num_device_info++; + +	strcpy(priv.response, "OKAY"); +} + +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; +} + +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) { +		printf("%s: unexpected buf size of 0\n", __func__); +		return; +	} + +	/* guarantee null termination for strchr/strlen */ +	buf[buf_size - 1] = 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; +		} +	} +} + +struct ram_console_buffer { +	uint32_t sig; +	uint32_t start; +	uint32_t size; +	uint8_t  data[0]; +}; +#define RAM_CONSOLE_SIG (0x43474244) /* 'DBGC' */ + +static void fbt_dump_kmsg(void) +{ +	/* the kmsg log is similar to the log we keep in the bootloader +	 * except that it has a header, is at an address fixed for +	 * each board, and starts with a ram_console_buffer structure. +	 */ +	struct ram_console_buffer *buf; +#ifndef CONFIG_FASTBOOT_RAMCONSOLE_START +	printf("No ram console start address defined\n"); +	strcpy(priv.response, "FAILNo ram console start address defined"); +	return; +#else +	buf = (struct ram_console_buffer *)CONFIG_FASTBOOT_RAMCONSOLE_START; +#endif +	if (buf->sig != RAM_CONSOLE_SIG) { +		printf("Ram console signature not found\n"); +		strcpy(priv.response, "FAILRam console signature not found\n"); +		return; +	} +	printf("Ram console found (size %d, start %d):\n", +	       buf->size, buf->start); +	fbt_dump_log((char *)&buf->data[0], buf->start); +	strcpy(priv.response, "OKAY"); +} + +static void fbt_handle_oem(char *cmdbuf) +{ +	cmdbuf += 4; + +	/* %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; +	} + +	/* %fastboot oem kmsg */ +	if (strcmp(cmdbuf, "kmsg") == 0) { +		FBTDBG("oem %s\n", cmdbuf); +		fbt_dump_kmsg(); +		return; +	} + +	/* %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) { +			printf("oem unlock ignored, device already unlocked\n"); +			strcpy(priv.response, "FAILalready unlocked"); +			return; +		} +		printf("oem unlock requested:\n"); +		printf("\tUnlocking forces a factory reset and could\n"); +		printf("\topen your device up to a world of hurt.  If you\n"); +		printf("\tare sure you know what you're doing, then accept\n"); +		printf("\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) { +			printf("oem unlock_accept ignored, not pending\n"); +			strcpy(priv.response, "FAILoem unlock not requested"); +			return; +		} +		priv.unlock_pending_start_time = 0; +		printf("Erasing userdata partition\n"); +		err = partition_erase_blks(priv.dev_desc, +					fastboot_flash_find_ptn("userdata"), +					NULL); +		if (err) { +			printf("Erase failed with error %d\n", err); +			strcpy(priv.response, "FAILErasing userdata failed"); +			return; +		} +		printf("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) { +			printf("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 setinfo <info_name>=<info_value> */ +	if (strncmp(cmdbuf, "setinfo ", 8) == 0) { +		cmdbuf += 8; +		fbt_handle_oem_setinfo(cmdbuf); +		return; +	} + +	/* %fastboot oem saveinfo */ +	if (strcmp(cmdbuf, "saveinfo") == 0) { +		disk_partition_t *info_ptn; +		info_ptn = fastboot_flash_find_ptn(CONFIG_INFO_PARTITION); + +		if (info_ptn == NULL) { +			sprintf(priv.response, "FAILpartition does not exist"); +			return; +		} +		if (fbt_save_info(info_ptn)) { +			printf("Writing '%s' FAILED!\n", info_ptn->name); +			sprintf(priv.response, "FAIL: Write partition"); +		} else { +			printf("Device info saved to partition '%s'\n", +			       info_ptn->name); +			sprintf(priv.response, "OKAY"); +		} +		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; +	} + +	printf("\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; + +		board_fbt_end(); + +		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); + +		printf("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"); +		board_fbt_start(); +	} +	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) { +		if (length < priv.d_size) { +			/* 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; +		} + +		/* transfer complete */ +		priv.d_bytes = priv.d_size; +		priv.d_size = 0; +		strcpy(priv.response, "OKAY"); +		priv.flag |= FASTBOOT_FLAG_RESPONSE; + +		/* restore default buffer in urb */ +		ep = &endpoint_instance[RX_EP_INDEX]; +		ep->rcv_urb->buffer = (u8 *)ep->rcv_urb->buffer_data; +		ep->rcv_urb->buffer_length = sizeof(ep->rcv_urb->buffer_data); + +		FBTINFO("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"); + +	FBTDBG("command\n"); + +	printf("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); +	} + +	/* %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 ? */ +		priv.d_size = simple_strtoul (cmdbuf + 9, NULL, 16); +		priv.d_bytes = 0; + +		FBTINFO("starting download of %llu bytes\n", priv.d_size); +		if (priv.d_size == 0) { +			strcpy(priv.response, "FAILdata invalid size"); +		} else if (priv.d_size > priv.transfer_buffer_size) { +			priv.d_size = 0; +			strcpy(priv.response, "FAILdata too large"); +		} else { +			sprintf(priv.response, "DATA%08llx", priv.d_size); + +			/* 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[RX_EP_INDEX]; +			ep->rcv_urb->buffer = priv.transfer_buffer; +			ep->rcv_urb->buffer_length = priv.d_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[RX_EP_INDEX]; + +	/* 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; +		} +		fbt_handle_response(); +	} +} + +static void fbt_response_process(void) +{ +	struct usb_endpoint_instance *ep = &endpoint_instance[TX_EP_INDEX]; +	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_clear_recovery_flag(void) +{ +	if (getenv(FASTBOOT_RUN_RECOVERY_ENV_NAME)) { +		setenv(FASTBOOT_RUN_RECOVERY_ENV_NAME, NULL); +#if defined(CONFIG_CMD_SAVEENV) +		saveenv(); +#endif +	} +} + +static void fbt_run_recovery(int do_saveenv) +{ +	board_fbt_end(); + +	/* to make recovery (which processes OTAs) more failsafe, +	 * we save the fact that we were asked to boot into +	 * recovery.  if power is pulled and then restored, we +	 * will use that info to rerun recovery again and try +	 * to complete the OTA installation. +	 */ +	if (do_saveenv) { +		setenv(FASTBOOT_RUN_RECOVERY_ENV_NAME, "1"); +#ifdef CONFIG_CMD_SAVEENV +		saveenv(); +#endif +	} + +	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 */ +	board_fbt_start(); +	printf("\nfastboot: Error: Invalid recovery img\n"); + +	/* Always clear so we don't wind up rebooting again into +	 * bad recovery img. +	 */ +	fbt_clear_recovery_flag(); +} + +struct bootloader_message { +	char command[32]; +	char status[32]; +	char recovery[1024]; +}; + +static void fbt_run_recovery_wipe_data(void) +{ +	struct bootloader_message *bmsg; + +	printf("Rebooting into recovery to do wipe_data\n"); + +	bmsg = (struct bootloader_message*)priv.transfer_buffer; +	memset(bmsg, 0, sizeof(*bmsg)); +	bmsg->command[0] = 0; +	bmsg->status[0] = 0; +	strcpy(bmsg->recovery, "recovery\n--wipe_data"); +	priv.d_bytes = sizeof(*bmsg); + +	/* write this structure to the "misc" partition, no unlock check */ +	fbt_handle_flash("flash:misc", 0); + +	/* now reboot to recovery */ +	fbt_run_recovery(1); +} + +/* + * 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 0; +} +static enum fbt_reboot_type __def_fbt_key_command(void) +{ +	return FASTBOOT_REBOOT_NONE; +} +static int __def_fbt_load_ptbl(void) +{ +	u64 length; +	disk_partition_t ptn; +	int n; +	int res = -1; +	block_dev_desc_t *blkdev = priv.dev_desc; +	unsigned long blksz = blkdev->blksz; + +	init_part(blkdev); +	if (blkdev->part_type == PART_TYPE_UNKNOWN) { +		printf("unknown partition table on %s\n", FASTBOOT_BLKDEV); +		return -1; +	} + +	printf("lba size = %lu\n", blksz); +	printf("lba_start      partition_size          name\n"); +	printf("=========  ======================  ==============\n"); +	for (n = CONFIG_MIN_PARTITION_NUM; n <= CONFIG_MAX_PARTITION_NUM; n++) { +		if (get_partition_info(blkdev, n, &ptn)) +			continue;	/* No partition <n> */ +		if (!ptn.size || !ptn.blksz || !ptn.name[0]) +			continue;	/* Partition <n> is empty (or sick) */ +		fbt_add_ptn(&ptn); + +		length = (u64)blksz * ptn.size; +		if (length > (1024 * 1024)) +			printf(" %8lu  %12llu(%7lluM)  %s\n", +						ptn.start, +						length, length/(1024*1024), +						ptn.name); +		else +			printf(" %8lu  %12llu(%7lluK)  %s\n", +						ptn.start, +						length, length/1024, +						ptn.name); +		res = 0; +	} +	printf("=========  ======================  ==============\n"); +	return res; +} +static void __def_fbt_start(void) +{ +} +static void __def_fbt_end(void) +{ +} +static void __def_board_fbt_finalize_bootargs(char* args, size_t buf_sz) +{ +	return; +} +static int __def_board_fbt_handle_flash(disk_partition_t *ptn, +					struct cmd_fastboot_interface *priv) +{ +	return 0; +} +static const char *__def_board_fbt_get_partition_type(const char *p_name) +{ +	return NULL; +} + +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"))); +enum fbt_reboot_type board_fbt_key_command(void) +	__attribute__((weak, alias("__def_fbt_key_command"))); +int board_fbt_load_ptbl(void) +	__attribute__((weak, alias("__def_fbt_load_ptbl"))); +void board_fbt_start(void) +	__attribute__((weak, alias("__def_fbt_start"))); +void board_fbt_end(void) +	__attribute__((weak, alias("__def_fbt_end"))); +void board_fbt_finalize_bootargs(char* args, size_t buf_sz) +	__attribute__((weak, alias("__def_board_fbt_finalize_bootargs"))); +int board_fbt_handle_flash(disk_partition_t *ptn, +			   struct cmd_fastboot_interface *priv) +	__attribute__((weak, alias("__def_board_fbt_handle_flash"))); +const char *board_fbt_get_partition_type(const char *partition_name) +	__attribute__((weak, alias("__def_board_fbt_get_partition_type"))); + +/* command */ +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, +							char * const argv[]) +{ +	int ret; + +	if (!priv.dev_desc) { +		printf("fastboot was not successfully initialized\n"); +		return -1; +	} + +	/* 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) { +		printf("fastboot can't be restarted\n"); +		return -1; +	} +	priv.flag |= FASTBOOT_FLAG_HAS_RUN; + +	printf("Starting fastboot protocol\n"); + +	board_fbt_start(); + +	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"); + +	while (1) { +		udc_irq(); +		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)) { +					printf("unlock pending expired\n"); +					priv.unlock_pending_start_time = 0; +				} +			} +		} +		priv.exit |= ctrlc(); +		if (priv.exit) { +			FBTINFO("fastboot end\n"); +			break; +		} +		switch(board_fbt_key_command()) { +		case FASTBOOT_REBOOT_NORMAL: +			printf("rebooting due to key\n"); +			fbt_handle_reboot("reboot"); +			break; +		case FASTBOOT_REBOOT_BOOTLOADER: +			printf("rebooting to bootloader due to key\n"); +			fbt_handle_reboot("reboot-bootloader"); +			break; +		case FASTBOOT_REBOOT_RECOVERY_WIPE_DATA: +			fbt_run_recovery_wipe_data(); +			break; +		case FASTBOOT_REBOOT_RECOVERY: +			printf("starting recovery due to key\n"); +			fbt_run_recovery(1); +			break; +		case FASTBOOT_REBOOT_UNKNOWN: +		case FASTBOOT_REBOOT_NONE: +		default: +			break; +		} +	} + +out: +	board_fbt_end(); +	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 +} + +/* booti [ <addr> | <partition> ] */ +static int do_booti(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +	char *boot_source = "boot"; +	block_dev_desc_t *blkdev = priv.dev_desc; +	disk_partition_t *ptn; +	struct fastboot_boot_img_hdr *hdr = NULL; +	bootm_headers_t images; +	int need_post_ran = 0; + +	if (argc >= 2) +		boot_source = argv[1]; + +	if (!blkdev) { +		printf("fastboot was not successfully initialized\n"); +		return -1; +	} + +	ptn = fastboot_flash_find_ptn(boot_source); +	if (ptn) { +		unsigned long blksz; +		unsigned sector; +		unsigned blocks; + +		if (partition_read_pre(ptn)) { +			printf("pre-read commands for partition '%s' failed\n", +								ptn->name); +			goto fail; +		} +		need_post_ran = 1; + +		blksz = blkdev->blksz; +		hdr = malloc(blksz); +		if (hdr == NULL) { +			printf("error allocating blksz(%lu) buffer\n", blksz); +			goto fail; +		} +		if (blkdev->block_read(blkdev->dev, ptn->start, +					      1, (void *) hdr) != 1) { +			printf("booti: failed to read bootimg header\n"); +			goto fail; +		} +		if (memcmp(hdr->magic, FASTBOOT_BOOT_MAGIC, +			   FASTBOOT_BOOT_MAGIC_SIZE)) { +			printf("booti: bad boot image magic\n"); +			goto fail; +		} + +		bootimg_print_image_hdr(hdr); + +		sector = ptn->start + (hdr->page_size / blksz); +		blocks = DIV_ROUND_UP(hdr->kernel_size, blksz); +		if (blkdev->block_read(blkdev->dev, sector, blocks, +					      (void *) hdr->kernel_addr) != +		    blocks) { +			printf("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 (blkdev->block_read(blkdev->dev, sector, blocks, +					      (void *) hdr->ramdisk_addr) != +		    blocks) { +			printf("booti: failed to read ramdisk\n"); +			goto fail; +		} +		if (need_post_ran) { +			need_post_ran = 0; +			if (partition_read_post(ptn)) { +				printf("post-read commands for partition '%s' " +							"failed\n", ptn->name); +				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)); + +		if (memcmp(hdr->magic, FASTBOOT_BOOT_MAGIC, +			   FASTBOOT_BOOT_MAGIC_SIZE)) { +			printf("booti: bad boot image magic\n"); +			goto fail; +		} + +		bootimg_print_image_hdr(hdr); + +		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); +	} + +	printf("kernel   @ %08x (%d)\n", hdr->kernel_addr, hdr->kernel_size); +	printf("ramdisk  @ %08x (%d)\n", hdr->ramdisk_addr, hdr->ramdisk_size); + +#ifdef CONFIG_CMDLINE_TAG + +#ifdef CONFIG_FASTBOOT_PRESERVE_BOOTARGS +	setenv("hdr_cmdline", (char *)hdr->cmdline); +#else +	{ +		/* static just to be safe when it comes to the stack */ +		static char command_line[1024]; +		int i, amt; + +		/* Use the command line in the bootimg header 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. +		 */ +		amt = snprintf(command_line, +				sizeof(command_line), +				"%s androidboot.bootloader=%s", +				hdr->cmdline, +				CONFIG_FASTBOOT_VERSION_BOOTLOADER); + +		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); +		} + +		/* 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; +		board_fbt_finalize_bootargs(command_line, sizeof(command_line)); + +		setenv("bootargs", command_line); +	} +#endif /* CONFIG_FASTBOOT_PRESERVE_BOOTARGS */ +#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); +	do_bootm_linux(0, 0, NULL, &images); + +	puts("booti: Control returned to monitor - resetting...\n"); +	do_reset(cmdtp, flag, argc, argv); +	return 1; + +fail: +	if (need_post_ran && partition_read_post(ptn)) +		printf("post-read commands for partition '%s' failed\n", +								ptn->name); +	/* if booti fails, always start fastboot */ +	free(hdr); /* hdr may be NULL, but that's ok. */ +	return do_fastboot(NULL, 0, 0, NULL); +} + +U_BOOT_CMD( +	booti,	2,	1,	do_booti, +	"boot android bootimg", +	"[ <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" +); + +static void fbt_request_start_fastboot(void) +{ +	char buf[512]; +	char *old_preboot = getenv("preboot"); +	printf("old preboot env = %s\n", old_preboot); + +	fbt_clear_recovery_flag(); + +	if (old_preboot) { +		snprintf(buf, sizeof(buf), +			 "setenv preboot %s; fastboot", old_preboot); +		setenv("preboot", buf); +	} else +		setenv("preboot", "setenv preboot; fastboot"); + +	printf("%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, clearing reboot to recovery flag, etc. + */ +void fbt_preboot(void) +{ +	enum fbt_reboot_type frt; + +	/* need to init this ASAP so we know the unlocked state */ +	fbt_fastboot_init(); + +	if (board_fbt_key_pressed()) { +		fbt_request_start_fastboot(); +		return; +	} + +	frt = board_fbt_get_reboot_type(); +	if (frt == FASTBOOT_REBOOT_RECOVERY) { +		printf("\n%s: starting recovery img because of reboot flag\n", +		       __func__); + +		return fbt_run_recovery(1); +	} else if (frt == FASTBOOT_REBOOT_RECOVERY_WIPE_DATA) { +		printf("\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_BOOTLOADER) { + +		/* Case: %fastboot reboot-bootloader +		 * Case: %adb reboot bootloader +		 * Case: %adb reboot-bootloader +		 */ +		printf("\n%s: starting fastboot because of reboot flag\n", +		       __func__); +		fbt_request_start_fastboot(); +	} else if (frt == FASTBOOT_REBOOT_NORMAL) { +		/* explicit request for a regular reboot */ +		printf("\n%s: request for a normal boot\n", +		       __func__); +		fbt_clear_recovery_flag(); +	} else { +		/* unknown reboot cause (typically because of a cold boot). +		 * check if we had flag set to boot recovery and it +		 * was never cleared properly (i.e. recovery didn't finish). +		 * if so, jump to recovery again. +		 */ +		char *run_recovery = getenv(FASTBOOT_RUN_RECOVERY_ENV_NAME); +		if (run_recovery) { +			printf("\n%s: starting recovery because of " +			       "saved reboot flag\n", __func__); +			return fbt_run_recovery(0); +		} +		printf("\n%s: no special reboot flags, doing normal boot\n", +		       __func__); +	} +} + +int fbt_send_info(const char *info) +{ +	int len; +	unsigned long space_in_log = CONFIG_FASTBOOT_LOG_SIZE - log_position; +	unsigned long bytes_to_log; + +	len = strlen(info); + +	/* 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; +		} +	} + +	return fbt_send_raw_info(info, len); +} diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 04ab0f19b..4d0d75b59 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -462,7 +462,7 @@ static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev)  	}  } -static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  {  	int i, ret = 0;  	ulong addr; diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 5bcc32467..b09dea6dc 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -339,7 +339,7 @@ ulong getenv_hex(const char *varname, ulong default_val)  }  #ifndef CONFIG_SPL_BUILD -static int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  {  	if (argc < 2)  		return CMD_RET_USAGE; @@ -684,7 +684,7 @@ ulong getenv_ulong(const char *name, int base, ulong default_val)  #ifndef CONFIG_SPL_BUILD  #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE) -static int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, +int do_env_save(cmd_tbl_t *cmdtp, int flag, int argc,  		       char * const argv[])  {  	printf("Saving Environment to %s...\n", env_name_spec); |