diff options
| author | mattis fjallstrom <mattis@acm.org> | 2015-09-06 11:09:39 -0700 |
|---|---|---|
| committer | mattis fjallstrom <mattis@acm.org> | 2015-09-06 11:09:39 -0700 |
| commit | ee87ec4d2c955c929a4c27ce8a56f918444cf955 (patch) | |
| tree | c18141755ad893eddd5b504e4cf096ad4e7062b9 | |
| parent | 2c25de1ed5c6f8f6bba3b5ec506f430d8a883a83 (diff) | |
| download | olio-uboot-2014.01-ee87ec4d2c955c929a4c27ce8a56f918444cf955.tar.xz olio-uboot-2014.01-ee87ec4d2c955c929a4c27ce8a56f918444cf955.zip | |
First fastboot commit, MLO built here wont work so be careful.
Change-Id: Ic8d65a92da82896282eee71cf0d0515f64c939bc
| -rw-r--r-- | arch/arm/cpu/armv7/omap3/board.c | 2 | ||||
| -rw-r--r-- | board/olio/h1/h1.c | 72 | ||||
| -rw-r--r-- | board/olio/h1/h1.c.bak | 105 | ||||
| -rw-r--r-- | board/olio/h1/pinmux.h | 33 | ||||
| -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 | ||||
| -rw-r--r-- | drivers/mtd/nand/omap_gpmc.c | 2 | ||||
| -rw-r--r-- | drivers/usb/gadget/Makefile | 10 | ||||
| -rw-r--r-- | drivers/usb/gadget/f_fastboot.c | 609 | ||||
| -rw-r--r-- | drivers/usb/gadget/g_fastboot.h | 23 | ||||
| -rw-r--r-- | drivers/usb/gadget/u_fastboot.c | 416 | ||||
| -rw-r--r-- | drivers/usb/gadget/u_fastboot_mmc.c | 609 | ||||
| -rw-r--r-- | drivers/usb/gadget/u_fastboot_nand.c | 838 | ||||
| -rw-r--r-- | drivers/usb/musb-new/musb_core.c | 5 | ||||
| -rw-r--r-- | include/configs/omap3_h1.h | 100 | ||||
| -rw-r--r-- | include/olioh1_reboot.h | 32 | ||||
| -rw-r--r-- | include/sparse_format.h | 50 | ||||
| -rw-r--r-- | include/usb/fastboot.h | 213 |
21 files changed, 5770 insertions, 18 deletions
diff --git a/arch/arm/cpu/armv7/omap3/board.c b/arch/arm/cpu/armv7/omap3/board.c index 2cd2df519..3cee4cb8d 100644 --- a/arch/arm/cpu/armv7/omap3/board.c +++ b/arch/arm/cpu/armv7/omap3/board.c @@ -321,7 +321,7 @@ void abort(void) /****************************************************************************** * OMAP3 specific command to switch between NAND HW and SW ecc *****************************************************************************/ -static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { if (argc < 2 || argc > 3) goto usage; diff --git a/board/olio/h1/h1.c b/board/olio/h1/h1.c index e69797638..43bc998fd 100644 --- a/board/olio/h1/h1.c +++ b/board/olio/h1/h1.c @@ -26,9 +26,43 @@ #include <command.h> #include <power/tps65910.h> #include <i2c.h> +#include <asm/arch/musb.h> +#include <asm/arch/mmc_host_def.h> +#include <asm/arch/am35x_def.h> +#include <i2c.h> +#include <linux/compiler.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/musb.h> +#include <asm/omap_musb.h> + +#ifdef CONFIG_USB_EHCI +#include <usb.h> +#include <asm/ehci-omap.h> +#endif DECLARE_GLOBAL_DATA_PTR; +#define USB_CS 17 + + +static struct omap_usbhs_board_data usbhs_bdata = { + .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY, + .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED, + .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED, +}; + +int ehci_hcd_init(int index, enum usb_init_type init, + struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + return omap_ehci_hcd_init(index, &usbhs_bdata, hccr, hcor); +} + +int ehci_hcd_stop(int index) +{ + return omap_ehci_hcd_stop(); +} + /* * Routine: board_init * Description: Early hardware init. @@ -154,6 +188,38 @@ static int accel_regulator_init(void) return i2c_write(TPS65910_CTRL_I2C_ADDR, TPS65910_VDIG1_REG, 1, &buf, 1); } +static struct musb_hdrc_config musb_config = { + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 16, + .ram_bits = 12, +}; + +static struct omap_musb_board_data musb_board_data = { + .interface_type = MUSB_INTERFACE_ULPI, +}; + +static struct musb_hdrc_platform_data musb_plat = { +#if defined(CONFIG_MUSB_HOST) + .mode = MUSB_HOST, +#elif defined(CONFIG_MUSB_GADGET) + .mode = MUSB_PERIPHERAL, +#else +#error "Please define either CONFIG_MUSB_HOST or CONFIG_MUSB_GADGET" +#endif + .config = &musb_config, + .power = 100, + .platform_ops = &omap2430_ops, + .board_data = &musb_board_data, +}; + + +static void olio_evm_musb_init(void) +{ + printf("musb mode %s \n", musb_plat.mode == MUSB_PERIPHERAL ? "MUSB_PERIPHERAL" : "MUSB_HOST"); + musb_register(&musb_plat, &musb_board_data, (void *)MUSB_BASE); +} + /* * Routine: misc_init_r * Description: Configure board specific parts @@ -174,6 +240,12 @@ int misc_init_r(void) dieid_num_r(); + printf("debug usb_data0 %d mode %d \n", CONTROL_PADCONF_HSUSB0_DATA0 , (IEN | PD | M0)); + gpio_request(USB_CS, "usb_cs"); + gpio_direction_output(USB_CS, 1); + + olio_evm_musb_init(); + return 0; } diff --git a/board/olio/h1/h1.c.bak b/board/olio/h1/h1.c.bak new file mode 100644 index 000000000..325e4ded1 --- /dev/null +++ b/board/olio/h1/h1.c.bak @@ -0,0 +1,105 @@ +/* + * (C) Copyright 2004-2011 + * Texas Instruments, <www.ti.com> + * + * Author : + * Sunil Kumar <sunilsaini05@gmail.com> + * Shashi Ranjan <shashiranjanmca05@gmail.com> + * + * Derived from Beagle Board and 3430 SDP code by + * Richard Woodruff <r-woodruff2@ti.com> + * Syed Mohammed Khasim <khasim@ti.com> + * + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <linux/mtd/nand.h> +#include <asm/io.h> +#include <asm/arch/mux.h> +#include <asm/arch/mem.h> +#include <asm/arch/sys_proto.h> +#include <asm/gpio.h> +#include <asm/mach-types.h> +#include <asm/errno.h> +#include "h1.h" +#include <command.h> +#include <power/tps65910.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Routine: board_init + * Description: Early hardware init. + */ +int board_init(void) +{ + gpmc_init(); /* in SRAM or SDRAM, finish GPMC */ + /* board id for Linux */ + gd->bd->bi_arch_number = MACH_TYPE_OMAP3_H1; + /* boot param addr */ + gd->bd->bi_boot_params = (OMAP34XX_SDRC_CS0 + 0x100); + + return 0; +} + +#ifdef CONFIG_SPL_BUILD +/* + * Routine: get_board_mem_timings + * Description: If we use SPL then there is no x-loader nor config header + * so we have to setup the DDR timings ourself on both banks. + */ +void get_board_mem_timings(struct board_sdrc_timings *timings) +{ + int pop_mfr, pop_id; + + /* + * We need to identify what PoP memory is on the board so that + * we know what timings to use. If we can't identify it then + * we know it's an xM. To map the ID values please see nand_ids.c + */ + identify_nand_chip(&pop_mfr, &pop_id); + + timings->mr = 0x00000032; + timings->mcfg = MICRON_V_MCFG_200(512 << 20); /* OLIO, mfj: not sure what this should be */ + timings->ctrla = 0x7AE1B4C6; + timings->ctrlb = 0x00021217; + timings->rfr_ctrl = 0x00062701; + + /* 128MB DDR - safe settings from Beagle + timings->mr = MICRON_V_MR_165; + timings->mcfg = MICRON_V_MCFG_200(256 << 20); + timings->ctrla = MICRON_V_ACTIMA_200; + timings->ctrlb = MICRON_V_ACTIMB_200; + timings->rfr_ctrl = SDP_3430_SDRC_RFR_CTRL_200MHz; + */ +} +#endif + +/* + * Routine: misc_init_r + * Description: Configure board specific parts + */ +int misc_init_r(void) +{ + struct gpio *gpio5_base = (struct gpio *)OMAP34XX_GPIO5_BASE; + struct gpio *gpio6_base = (struct gpio *)OMAP34XX_GPIO6_BASE; + struct control_prog_io *prog_io_base = (struct control_prog_io *)OMAP34XX_CTRL_BASE; + + //MUX_BEAGLE_XM(); + + dieid_num_r(); + + return 0; +} + +/* + * Routine: set_muxconf_regs + * Description: Setting up the configuration Mux registers specific to the + * hardware. Many pins need to be moved from protect to primary + * mode. + */ +void set_muxconf_regs(void) +{ + MUX_EVM(); +} diff --git a/board/olio/h1/pinmux.h b/board/olio/h1/pinmux.h index 23cfede2e..3861c3bad 100644 --- a/board/olio/h1/pinmux.h +++ b/board/olio/h1/pinmux.h @@ -146,15 +146,15 @@ MUX_VAL(CONTROL_PADCONF_GPMC_WAIT1, (IEN | PU | M7 | SB_PU)) /* safe_mode - edit MUX_VAL(CONTROL_PADCONF_GPMC_WAIT2, (IEN | PU | M7 | SB_PU)) /* safe_mode - edit 2014-05-11 */ \
MUX_VAL(CONTROL_PADCONF_GPMC_WAIT3, (IEN | PU | M7 | SB_PU)) /* safe_mode - edit 2014-05-11 */ \
MUX_VAL(CONTROL_PADCONF_HDQ_SIO, (IEN | PD | M7 | SB_PD)) /* safe_mode - edit 2014-05-11 */ \
-MUX_VAL(CONTROL_PADCONF_HSUSB0_CLK, (IEN | PD | M7 )) /* safe_mode */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA0, (IEN | PD | M4 )) /* gpio_125 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA1, (IEN | PD | M4 )) /* gpio_130 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA2, (IEN | PD | M4 )) /* gpio_131 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA3, (IEN | PD | M4 )) /* gpio_169 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA4, (IEN | PD | M4 )) /* gpio_188 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA5, (IEN | PD | M4 )) /* gpio_189 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA6, (IEN | PD | M4 )) /* gpio_190 */\
-MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA7, (IEN | PD | M4 )) /* gpio_191 */\
+MUX_VAL(CONTROL_PADCONF_HSUSB0_CLK, (IEN | PD | M7 )) /* safe_mode */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA0, (IEN | PD | M4 )) /* gpio_125 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA1, (IEN | PD | M4 )) /* gpio_130 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA2, (IEN | PD | M4 )) /* gpio_131 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA3, (IEN | PD | M4 )) /* gpio_169 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA4, (IEN | PD | M4 )) /* gpio_188 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA5, (IEN | PD | M4 )) /* gpio_189 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA6, (IEN | PD | M4 )) /* gpio_190 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA7, (IEN | PD | M4 )) /* gpio_191 */\ MUX_VAL(CONTROL_PADCONF_HSUSB0_DIR, (IEN | PD | M7 )) /* safe_mode */\
MUX_VAL(CONTROL_PADCONF_HSUSB0_NXT, (IEN | PD | M7 )) /* safe_mode */\
MUX_VAL(CONTROL_PADCONF_HSUSB0_STP, (IEN | PU | M7 )) /* safe_mode */\
@@ -312,5 +312,18 @@ MUX_VAL(CONTROL_PADCONF_UART2_TX, (IEN | PU | M4 )) /* gpio_146 */\ MUX_VAL(CONTROL_PADCONF_UART3_CTS_RCTX, (IEN | PU | M0 )) /* uart3_cts_rctx */\
MUX_VAL(CONTROL_PADCONF_UART3_RTS_SD, (IDIS | PU | M0 )) /* uart3_rts_sd */\
MUX_VAL(CONTROL_PADCONF_UART3_RX_IRRX, (IEN | PU | M0 )) /* uart3_rx_irrx */\
-MUX_VAL(CONTROL_PADCONF_UART3_TX_IRTX, (IDIS | PU | M0 )) /* uart3_tx_irtx */
+MUX_VAL(CONTROL_PADCONF_UART3_TX_IRTX, (IDIS | PU | M0 )) /* uart3_tx_irtx */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_CLK, (IEN | PI | M0 )) /* hsusb0_clk */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA0, (IEN | PI | M0)) /* hsusb0_data0 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA1, (IEN | PI | M0)) /* hsusb0_data1 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA2, (IEN | PI | M0)) /* hsusb0_data2 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA3, (IEN | PI | M0)) /* hsusb0_data3 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA4, (IEN | PI | M0)) /* hsusb0_data4 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA5, (IEN | PI | M0)) /* hsusb0_data5 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA6, (IEN | PI | M0)) /* hsusb0_data6 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DATA7, (IEN | PI | M0)) /* hsusb0_data7 */\ +MUX_VAL(CONTROL_PADCONF_HSUSB0_DIR, (IEN | PI | M0)) /*hsusb0_dir */ \ +MUX_VAL(CONTROL_PADCONF_HSUSB0_STP, (IDIS | PI | M0)) /*hsusb0_stp */ \ +MUX_VAL(CONTROL_PADCONF_HSUSB0_NXT, (IEN | PI | M0)) /*hsusb0_nxt */ \ +MUX_VAL(CONTROL_PADCONF_ETK_D3_ES2, (IDIS | PI | M4)) /* gpio_17 */ #endif
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); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 389c4de59..e3c216891 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -949,7 +949,7 @@ int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) return -EINVAL; } } else { - err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, + err = omap_select_ecc_scheme(nand, OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, mtd->writesize, mtd->oobsize); } diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index f13b172a6..a9df2193e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -34,4 +34,14 @@ obj-$(CONFIG_OMAP1610) += omap1510_udc.o obj-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o obj-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o endif + +ifdef CONFIG_CMD_FASTBOOT +obj-y += f_fastboot.o u_fastboot.o +ifdef CONFIG_FASTBOOT_NAND +obj-y += u_fastboot_nand.o +else +obj-y += u_fastboot_mmc.o +endif +endif + endif diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 000000000..fb341d399 --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,609 @@ +/* + * The file is based on content which was + * + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix at windriver.com> + * + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de> + * + * Modified by Vishveshwar Bhat <vishevshwar.bhat@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 + * + * Some part of this code is based on ideas from Android AOSP project + * bootable/bootloader/legacy/ + * This is their Copyright: + * + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * 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 <errno.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/compiler.h> + +#include "g_fastboot.h" + +#ifdef CONFIG_USB_GADGET_DUALSPEED +#define DEVSPEED USB_SPEED_HIGH +#else +#define DEVSPEED USB_SPEED_FULL +#endif + +#define CONFIGURATION_NORMAL 1 +#define BULK_ENDPOINT 1 +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +static struct usb_string def_usb_fb_strings[] = { + { FB_STR_PRODUCT_IDX, "Default Product" }, + { FB_STR_SERIAL_IDX, "1234567890" }, + { FB_STR_CONFIG_IDX, "Android Fastboot" }, + { FB_STR_INTERFACE_IDX, "Android Fastboot" }, + { FB_STR_MANUFACTURER_IDX, "Default Manufacturer" }, + { FB_STR_PROC_REV_IDX, "Default 1.0" }, + { FB_STR_PROC_TYPE_IDX, "Emulator" }, + { } +}; + +static struct usb_gadget_strings def_fb_strings = { + .language = 0x0409, /* en-us */ + .strings = def_usb_fb_strings, +}; + +static struct usb_gadget_strings *vendor_fb_strings; + +static unsigned int gadget_is_connected; + +static u8 ep0_buffer[512]; +static u8 ep_out_buffer[EP_BUFFER_SIZE]; +static u8 ep_in_buffer[EP_BUFFER_SIZE]; +static int current_config; + +/* e1 */ +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, /* IN */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}; + +/* e2 */ +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, /* OUT */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1, + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, /* OUT */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, + .bInterval = 0x00, +}; + +const char *fb_find_usb_string(unsigned int id) +{ + struct usb_string *s; + + for (s = vendor_fb_strings->strings; s && s->s; s++) { + if (s->id == id) + break; + } + if (!s || !s->s) { + for (s = def_fb_strings.strings; s && s->s; s++) { + if (s->id == id) + break; + } + } + if (!s) + return NULL; + return s->s; +} + +static struct usb_gadget *g; +static struct usb_request *ep0_req; + +struct usb_ep *ep_in; +struct usb_request *req_in; + +struct usb_ep *ep_out; +struct usb_request *req_out; + +static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + if (!status) + return; + printf("ep0 status %d\n", status); +} + +static int fastboot_bind(struct usb_gadget *gadget) +{ + + g = gadget; + ep0_req = usb_ep_alloc_request(g->ep0, 0); + if (!ep0_req) + goto err; + ep0_req->buf = ep0_buffer; + ep0_req->complete = fastboot_ep0_complete; + + ep_in = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!ep_in) + goto err; + ep_in->driver_data = ep_in; + + ep_out = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!ep_out) + goto err; + ep_out->driver_data = ep_out; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + + return usb_gadget_connect(gadget); +err: + return -1; +} + +static void fastboot_unbind(struct usb_gadget *gadget) +{ + usb_gadget_disconnect(gadget); + usb_ep_free_request(g->ep0, ep0_req); + ep_in->driver_data = NULL; + ep_out->driver_data = NULL; +} + +struct usb_device_descriptor fb_descriptor = { + .bLength = sizeof(fb_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x200, + .bMaxPacketSize0 = 0x40, + .idVendor = FASTBOOT_DEVICE_VENDOR_ID, + .idProduct = FASTBOOT_DEVICE_PRODUCT_ID, + .bcdDevice = FASTBOOT_DEVICE_BCD, + .iManufacturer = FB_STR_MANUFACTURER_IDX, + .iProduct = FB_STR_PRODUCT_IDX, + .iSerialNumber = FB_STR_SERIAL_IDX, + .bNumConfigurations = 1, +}; + +#define TOT_CFG_DESC_LEN (USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + \ + USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE) + +static struct usb_config_descriptor config_desc = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(TOT_CFG_DESC_LEN), + .bNumInterfaces = 1, + .bConfigurationValue = CONFIGURATION_NORMAL, + .iConfiguration = FB_STR_CONFIG_IDX, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, + .iInterface = FB_STR_INTERFACE_IDX, +}; + +static struct usb_qualifier_descriptor qual_desc = { + .bLength = sizeof(qual_desc), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = 0x200, + .bMaxPacketSize0 = 0x40, + .bNumConfigurations = 1, +}; + +static int fastboot_setup_get_descr(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 val; + int ret; + u32 bytes_remaining; + u32 bytes_total; + u32 this_inc; + + val = w_value >> 8; + + switch (val) { + case USB_DT_DEVICE: + + memcpy(ep0_buffer, &fb_descriptor, sizeof(fb_descriptor)); + ep0_req->length = min(w_length, sizeof(fb_descriptor)); + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_CONFIG: + + bytes_remaining = min(w_length, sizeof(ep0_buffer)); + bytes_total = 0; + + /* config */ + this_inc = min(bytes_remaining, USB_DT_CONFIG_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &config_desc, this_inc); + bytes_total += this_inc; + + /* interface */ + this_inc = min(bytes_remaining, USB_DT_INTERFACE_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &interface_desc, this_inc); + bytes_total += this_inc; + + /* ep in */ + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &fs_ep_in, this_inc); + bytes_total += this_inc; + + /* ep out */ + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + + if (gadget->speed == USB_SPEED_HIGH) + memcpy(ep0_buffer + bytes_total, &hs_ep_out, + this_inc); + else + memcpy(ep0_buffer + bytes_total, &fs_ep_out, + this_inc); + bytes_total += this_inc; + + ep0_req->length = bytes_total; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_STRING: + + ret = usb_gadget_get_string(vendor_fb_strings, + w_value & 0xff, ep0_buffer); + if (ret < 0) + ret = usb_gadget_get_string(&def_fb_strings, + w_value & 0xff, ep0_buffer); + if (ret < 0) + break; + + ep0_req->length = ret; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_DEVICE_QUALIFIER: + + memcpy(ep0_buffer, &qual_desc, sizeof(qual_desc)); + ep0_req->length = min(w_length, sizeof(qual_desc)); + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int fastboot_setup_get_conf(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (w_length == 0) + return -1; + + ep0_buffer[0] = current_config; + ep0_req->length = 1; + return usb_ep_queue(gadget->ep0, ep0_req, 0); +} + +static void fastboot_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + FBTINFO("status: %d ep_in trans: %d\n", + status, + req->actual); +} + +static int fastboot_disable_ep(struct usb_gadget *gadget) +{ + if (req_out) { + usb_ep_free_request(ep_out, req_out); + req_out = NULL; + } + if (req_in) { + usb_ep_free_request(ep_in, req_in); + req_in = NULL; + } + usb_ep_disable(ep_out); + usb_ep_disable(ep_in); + + return 0; +} + +static int fastboot_enable_ep(struct usb_gadget *gadget) +{ + int ret; + + /* make sure we don't enable the ep twice */ + if (gadget->speed == USB_SPEED_HIGH) + ret = usb_ep_enable(ep_out, &hs_ep_out); + else + ret = usb_ep_enable(ep_out, &fs_ep_out); + if (ret) { + printf("failed to enable out ep\n"); + goto err; + } + + req_out = usb_ep_alloc_request(ep_out, 0); + if (!req_out) { + printf("failed to alloc out req\n"); + goto err; + } + + ret = usb_ep_enable(ep_in, &fs_ep_in); + if (ret) { + printf("failed to enable in ep\n"); + goto err; + } + req_in = usb_ep_alloc_request(ep_in, 0); + if (!req_in) { + printf("failed alloc req in\n"); + goto err; + } + + req_out->complete = rx_handler_command; + req_out->buf = ep_out_buffer; + req_out->length = sizeof(ep_out_buffer); + + req_in->buf = ep_in_buffer; + req_in->length = sizeof(ep_in_buffer); + + ret = usb_ep_queue(ep_out, req_out, 0); + if (ret) + goto err; + + return 0; +err: + fastboot_disable_ep(gadget); + return -1; +} + +static int fastboot_set_interface(struct usb_gadget *gadget, u32 enable) +{ + if (enable && req_out) + return 0; + if (!enable && !req_out) + return 0; + + if (enable) + return fastboot_enable_ep(gadget); + else + return fastboot_disable_ep(gadget); +} + +static int fastboot_setup_out_req(struct usb_gadget *gadget, + const struct usb_ctrlrequest *req) +{ + switch (req->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + + ep0_req->length = 0; + if (req->wValue == CONFIGURATION_NORMAL) { + current_config = CONFIGURATION_NORMAL; + fastboot_set_interface(gadget, 1); + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + } + if (req->wValue == 0) { + current_config = 0; + fastboot_set_interface(gadget, 0); + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + } + return -1; + break; + default: + return -1; + }; + + case USB_RECIP_INTERFACE: + switch (req->bRequest) { + case USB_REQ_SET_INTERFACE: + + ep0_req->length = 0; + if (!fastboot_set_interface(gadget, 1)) + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + return -1; + break; + default: + return -1; + } + + case USB_RECIP_ENDPOINT: + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + + return usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + return -1; + } + } + return -1; +} + +static int fastboot_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *req) +{ + if ((req->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + return -1; + + if ((req->bRequestType & USB_DIR_IN) == 0) + /* host-to-device */ + return fastboot_setup_out_req(gadget, req); + + /* device-to-host */ + if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + switch (req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + return fastboot_setup_get_descr(gadget, req); + break; + + case USB_REQ_GET_CONFIGURATION: + return fastboot_setup_get_conf(gadget, req); + break; + default: + return -1; + } + } + return -1; +} + +static void fastboot_disconnect(struct usb_gadget *gadget) +{ + fastboot_disable_ep(gadget); + gadget_is_connected = 0; +} + +struct usb_gadget_driver fast_gadget = { + .speed = DEVSPEED, + .bind = fastboot_bind, + .unbind = fastboot_unbind, + .setup = fastboot_setup, + .disconnect = fastboot_disconnect, +}; + +/* FIXME: move this board-specific code somewhere else */ +#define BEAGLEBONEBLACK_U0_LED_GPIO 53 +static void turn_fastboot_led_on(void) +{ + printf("olio fastboot enter\n"); + /* + if (!gpio_request(BEAGLEBONEBLACK_U0_LED_GPIO, "fastboot")) { + gpio_direction_output(BEAGLEBONEBLACK_U0_LED_GPIO, 0); + gpio_set_value(BEAGLEBONEBLACK_U0_LED_GPIO, 1); + } else { + printf("gpio_request %d failed\n", BEAGLEBONEBLACK_U0_LED_GPIO); + } + */ +} + +static void turn_fastboot_led_off(void) +{ + printf("olio fastboot exit\n"); + //gpio_set_value(BEAGLEBONEBLACK_U0_LED_GPIO, 0); +} + +int fastboot_init(void) +{ + int ret; + + printf("func: fastboot_init \n"); + + ret = fastboot_board_init(&fastboot_cfg, &vendor_fb_strings); + if (ret) + return ret; + if (!vendor_fb_strings) + return -EINVAL; + + set_fb_config(&fastboot_cfg); + turn_fastboot_led_on(); + ret = usb_gadget_register_driver(&fast_gadget); + if (ret) { + printf("Add gadget failed\n"); + goto err; + } + + gadget_is_connected = 1; + usb_gadget_handle_interrupts(); + + return 0; + +err: + fastboot_shutdown(); + return 1; +} + +int fastboot_poll(void) +{ + usb_gadget_handle_interrupts(); + + if (gadget_is_connected) + return 0; + else + return 1; +} + +void fastboot_shutdown(void) +{ + turn_fastboot_led_off(); +} + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + int ret = 0; + + if (req_in->complete == NULL) + req_in->complete = fastboot_complete_in; + + memcpy(req_in->buf, buffer, buffer_size); + req_in->length = buffer_size; + ret = usb_ep_queue(ep_in, req_in, 0); + if (ret) + printf("Error %d on queue\n", ret); + return ret; +} diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h new file mode 100644 index 000000000..e494edf44 --- /dev/null +++ b/drivers/usb/gadget/g_fastboot.h @@ -0,0 +1,23 @@ +#ifndef _G_FASTBOOT_H_ +#define _G_FASTBOOT_H_ + +#define EP_BUFFER_SIZE 4096 +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 +#define FASTBOOT_VERSION "0.4" + +extern struct fastboot_config fastboot_cfg; +extern struct usb_ep *ep_in; +extern struct usb_request *req_in; +extern struct usb_ep *ep_out; +extern struct usb_request *req_out; + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +int fastboot_tx_write(const char *buffer, unsigned int buffer_size); +const char *fb_find_usb_string(unsigned int id); + +extern int handle_flash(char *part_name, char *response); +extern int do_format(void); + +#endif diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c new file mode 100644 index 000000000..b0b6e0805 --- /dev/null +++ b/drivers/usb/gadget/u_fastboot.c @@ -0,0 +1,416 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix at windriver.com> + * + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Part of the rx_handler were copied from the Android project. + * Specifically rx command parsing in the usb_rx_data_complete + * function of the file bootable/bootloader/legacy/usbloader/usbloader.c + * + * The logical naming of flash comes from the Android project + * Thse structures and functions that look like fastboot_flash_* + * They come from bootable/bootloader/legacy/libboot/flash.c + * + * This is their Copyright: + * + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <common.h> +#include <command.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h" +#include <environment.h> + +#include <olioh1_reboot.h> + +/* The 64 defined bytes plus \0 */ +#define RESPONSE_LEN (64 + 1) + +static struct fastboot_config *fb_cfg; + +/* To support the Android-style naming of flash */ +#define MAX_PTN 16 +static fastboot_ptentry ptable[MAX_PTN]; +static unsigned int pcount; +static int static_pcount = -1; + +void set_fb_config (struct fastboot_config *cfg) +{ + fb_cfg = cfg; +} + + +/* + * Android style flash utilties */ +void fastboot_flash_reset_ptn(void) +{ + FBTINFO("fastboot flash reset partition..!!"); + pcount = 0; +} + +void fastboot_flash_add_ptn(fastboot_ptentry *ptn) +{ + if(pcount < MAX_PTN){ + memcpy(ptable + pcount, ptn, sizeof(*ptn)); + pcount++; + } +} + +void fastboot_flash_dump_ptn(void) +{ + unsigned int n; + for(n = 0; n < pcount; n++) { + fastboot_ptentry *ptn = ptable + n; + FBTINFO("ptn %d name='%s' start=%d len=%d\n", + n, ptn->name, ptn->start, ptn->length); + printf("ptn %d name='%s' start=%d len=%d\n", + n, ptn->name, ptn->start, ptn->length); + } +} + +fastboot_ptentry *fastboot_flash_find_ptn(const char *name) +{ + unsigned int n; + + for(n = 0; n < pcount; n++) { + /* Make sure a substring is not accepted */ + if (strlen(name) == strlen(ptable[n].name)) + { + if(0 == strcmp(ptable[n].name, name)) + return ptable + n; + } + } + return 0; +} + +static int fastboot_tx_write_str(const char *buffer) +{ + return fastboot_tx_write(buffer, strlen(buffer)); +} + +static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{ + *(unsigned int *)REBOOT_REASON_PA = REBOOT_FLAG_NORMAL; + req_in->complete = compl_do_reset; + fastboot_tx_write_str("OKAY"); +} + +static void cb_reboot_bootloader(struct usb_ep *ep, struct usb_request *req) +{ + *(unsigned int *)REBOOT_REASON_PA = REBOOT_FLAG_FASTBOOT; + req_in->complete = compl_do_reset; + fastboot_tx_write_str("OKAY"); +} +static int strcmp_l1(const char *s1, const char *s2) +{ + return strncmp(s1, s2, strlen(s1)); +} + +static void cb_getvar(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + const char *s; + + strcpy(response, "OKAY"); + strsep(&cmd, ":"); + if (!cmd) { + fastboot_tx_write_str("FAILmissing var"); + return; + } + + if (!strcmp_l1("version", cmd)) { + strncat(response, FASTBOOT_VERSION, sizeof(response)); + + } else if (!strcmp_l1("downloadsize", cmd)) { + char str_num[12]; + + sprintf(str_num, "%08x", fb_cfg->transfer_buffer_size); + strncat(response, str_num, sizeof(response)); + + } else if (!strcmp_l1("product", cmd)) { + + s = fb_find_usb_string(FB_STR_PRODUCT_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + + } else if (!strcmp_l1("serialno", cmd)) { + + s = fb_find_usb_string(FB_STR_SERIAL_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + + } else if (!strcmp_l1("cpurev", cmd)) { + + s = fb_find_usb_string(FB_STR_PROC_REV_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else if (!strcmp_l1("secure", cmd)) { + + s = fb_find_usb_string(FB_STR_PROC_TYPE_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else { + strcpy(response, "FAILVariable not implemented"); + } + fastboot_tx_write_str(response); +} + +static unsigned int rx_bytes_expected(void) +{ + int rx_remain = fb_cfg->download_size - fb_cfg->download_bytes; + if (rx_remain < 0) + return 0; + if (rx_remain > EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + return rx_remain; +} + +#define BYTES_PER_DOT 1048576 +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + char response[RESPONSE_LEN]; + unsigned int transfer_size = fb_cfg->download_size - fb_cfg->download_bytes; + const unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + int dnl_complete = 0; + + if (req->status != 0) { + printf("Bad status: %d\n", req->status); + return; + } + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + memcpy(fb_cfg->transfer_buffer + fb_cfg->download_bytes, + buffer, transfer_size); + + fb_cfg->download_bytes += transfer_size; + + /* Check if transfer is done */ + if (fb_cfg->download_bytes >= fb_cfg->download_size) { + /* + * Reset global transfer variable, keep fb_cfg->download_bytes because + * it will be used in the next possible flashing command + */ + fb_cfg->download_size = 0; + req->complete = rx_handler_command; + req->length = EP_BUFFER_SIZE; + dnl_complete = 1; + printf("\ndownloading of %d bytes finished\n", + fb_cfg->download_bytes); + } else + req->length = rx_bytes_expected(); + + if (fb_cfg->download_bytes && !(fb_cfg->download_bytes % BYTES_PER_DOT)) { + printf("."); + if (!(fb_cfg->download_bytes % (74 * BYTES_PER_DOT))) + printf("\n"); + + } + if (dnl_complete) + { + fastboot_tx_write_str("OKAY"); + } + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void cb_download(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + + strsep(&cmd, ":"); + fb_cfg->download_size = simple_strtoul(cmd, NULL, 16); + fb_cfg->download_bytes = 0; + + printf("Starting download of %d bytes\n", + fb_cfg->download_size); + + if (0 == fb_cfg->download_size) { + sprintf(response, "FAILdata invalid size"); + } else if (fb_cfg->download_size > + fb_cfg->transfer_buffer_size) { + fb_cfg->download_size = 0; + sprintf(response, "FAILdata too large"); + } else { + sprintf(response, "DATA%08x", fb_cfg->download_size); + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(); + } + fastboot_tx_write_str(response); +} + +static char boot_addr_start[32]; +static char *bootm_args[] = { "bootm", boot_addr_start, NULL }; + +static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + req->complete = NULL; + fastboot_shutdown(); + printf("Booting kernel..\n"); + + do_bootm(NULL, 0, 2, bootm_args); + + /* This only happens if image is somehow faulty so we start over */ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_boot(struct usb_ep *ep, struct usb_request *req) +{ + sprintf(boot_addr_start, "0x%p", fb_cfg->transfer_buffer); + + req_in->complete = do_bootm_on_complete; + fastboot_tx_write_str("OKAY"); + return; +} + + +int fastboot_oem(const char *cmd) +{ + printf("fastboot_oem:%s", cmd); + if (!strcmp(cmd, "format")) + return do_format(); + return -1; +} + + +static void cb_oem(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + + printf ("calling fastboot oem!! : %s\n", cmd); + int r = fastboot_oem(cmd + 4); + if (r < 0) { + fastboot_tx_write_str("FAIL"); + } else { + fastboot_tx_write_str("OKAY"); + } +} + +static void cb_flash(struct usb_ep *ep, struct usb_request *req) +{ + char *cmdbuf = req->buf; + char response[32]; + char part_name[20]={0,}; + strncpy (part_name, cmdbuf + 6, req->actual - 6); + handle_flash(part_name, response); + fastboot_tx_write_str(response); +} +struct cmd_dispatch_info { + char *cmd; + void (*cb)(struct usb_ep *ep, struct usb_request *req); +}; + +static struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = "reboot-bootloader", + .cb = cb_reboot_bootloader, + }, { + .cmd = "reboot", + .cb = cb_reboot, + }, { + .cmd = "getvar:", + .cb = cb_getvar, + }, { + .cmd = "download:", + .cb = cb_download, + }, { + .cmd = "boot", + .cb = cb_boot, + }, { + .cmd = "oem", + .cb = cb_oem, + },{ + .cmd = "flash:", + .cb = cb_flash, + }, +}; + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + char response[RESPONSE_LEN]; + char *cmdbuf = req->buf; + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + int i; + sprintf(response, "FAIL"); + + *(cmdbuf + req->actual) = '\0'; + FBTINFO ("Recieved command : %s : req len : %d \n", cmdbuf, req->actual); + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) { + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) + fastboot_tx_write_str("FAILunknown command"); + else + func_cb(ep, req); + + if (req->status == 0) { + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); + } +} diff --git a/drivers/usb/gadget/u_fastboot_mmc.c b/drivers/usb/gadget/u_fastboot_mmc.c new file mode 100644 index 000000000..2ed64efcc --- /dev/null +++ b/drivers/usb/gadget/u_fastboot_mmc.c @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2013 Texas Instruments + * + * Author : 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 + * + * + * 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 Sitara 2011 u-boot by + * Mohammed Afzal M A <afzal@ti.com> + * + * 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 <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h" +#include <environment.h> +#include <mmc.h> +#include <sparse_format.h> + +#define EFI_VERSION 0x00010000 +#define EFI_ENTRIES 128 +#define EFI_NAMELEN 36 + +struct partition { + const char *name; + unsigned size_kb; +}; + +/* eMMC partition layout (All sizes are in kB) + * Modify the below partition table to change the GPT configuration. + * The entry for each partition can be modified as per the requirement. + */ +static struct partition partitions[] = { + { "-", 128 }, /* Master Boot Record and GUID Partition Table */ + { "spl", 128 }, /* First stage bootloader */ + { "bootloader", 512 }, /* Second stage bootloader */ + { "misc", 128 }, /* Rserved for internal purpose */ + { "-", 128 }, /* Reserved */ + { "recovery", 8*1024 }, /* Recovery partition */ + { "boot", 8*1024 }, /* Partition contains kernel + ramdisk images */ + { "system", 512*1024 }, /* Android file system */ + { "cache", 256*1024 }, /* Store Application Cache */ + { "userdata", 1047*1024 }, /* User data */ + { "media", 0 }, /* Media files */ + { 0, 0 }, +}; + + +static const u8 partition_type[16] = { + 0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44, + 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7, +}; + +static const u8 random_uuid[16] = { + 0xff, 0x1f, 0xf2, 0xf9, 0xd4, 0xa8, 0x0e, 0x5f, + 0x97, 0x46, 0x59, 0x48, 0x69, 0xae, 0xc3, 0x4e, +}; + +struct efi_entry { + u8 type_uuid[16]; + u8 uniq_uuid[16]; + u64 first_lba; + u64 last_lba; + u64 attr; + u16 name[EFI_NAMELEN]; +}; + +struct efi_header { + u8 magic[8]; + + u32 version; + u32 header_sz; + + u32 crc32; + u32 reserved; + + u64 header_lba; + u64 backup_lba; + u64 first_lba; + u64 last_lba; + + u8 volume_uuid[16]; + + u64 entries_lba; + + u32 entries_count; + u32 entries_size; + u32 entries_crc32; +} __attribute__((packed)); + +struct ptable { + u8 mbr[512]; + union { + struct efi_header header; + u8 block[512]; + }; + struct efi_entry entry[EFI_ENTRIES]; +}; + +static void init_mbr(u8 *mbr, u32 blocks) +{ + mbr[0x1be] = 0x00; /* nonbootable */ + mbr[0x1bf] = 0xFF; /* bogus CHS */ + mbr[0x1c0] = 0xFF; + mbr[0x1c1] = 0xFF; + + mbr[0x1c2] = 0xEE; /* GPT partition */ + mbr[0x1c3] = 0xFF; /* bogus CHS */ + mbr[0x1c4] = 0xFF; + mbr[0x1c5] = 0xFF; + + mbr[0x1c6] = 0x01; /* start */ + mbr[0x1c7] = 0x00; + mbr[0x1c8] = 0x00; + mbr[0x1c9] = 0x00; + + memcpy(mbr + 0x1ca, &blocks, sizeof(u32)); + + mbr[0x1fe] = 0x55; + mbr[0x1ff] = 0xaa; +} + +static void start_ptbl(struct ptable *ptbl, unsigned blocks) +{ + struct efi_header *hdr = &ptbl->header; + + memset(ptbl, 0, sizeof(*ptbl)); + + init_mbr(ptbl->mbr, blocks - 1); + + memcpy(hdr->magic, "EFI PART", 8); + hdr->version = EFI_VERSION; + hdr->header_sz = sizeof(struct efi_header); + hdr->header_lba = 1; + hdr->backup_lba = blocks - 1; + hdr->first_lba = 34; + hdr->last_lba = blocks - 1; + memcpy(hdr->volume_uuid, random_uuid, 16); + hdr->entries_lba = 2; + hdr->entries_count = EFI_ENTRIES; + hdr->entries_size = sizeof(struct efi_entry); +} + +static void end_ptbl(struct ptable *ptbl) +{ + struct efi_header *hdr = &ptbl->header; + u32 n; + + n = crc32(0, 0, 0); + n = crc32(n, (void *) ptbl->entry, sizeof(ptbl->entry)); + hdr->entries_crc32 = n; + + n = crc32(0, 0, 0); + n = crc32(0, (void *) &ptbl->header, sizeof(ptbl->header)); + hdr->crc32 = n; +} + +int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name) +{ + struct efi_header *hdr = &ptbl->header; + struct efi_entry *entry = ptbl->entry; + unsigned n; + + if (first < 34) { + printf("partition '%s' overlaps partition table\n", name); + return -1; + } + + if (last > hdr->last_lba) { + printf("partition '%s' does not fit\n", name); + return -1; + } + for (n = 0; n < EFI_ENTRIES; n++, entry++) { + if (entry->last_lba) + continue; + memcpy(entry->type_uuid, partition_type, 16); + memcpy(entry->uniq_uuid, random_uuid, 16); + entry->uniq_uuid[0] = n; + entry->first_lba = first; + entry->last_lba = last; + for (n = 0; (n < EFI_NAMELEN) && *name; n++) + entry->name[n] = *name++; + return 0; + } + printf("out of partition table entries\n"); + return -1; +} + +void import_efi_partition(struct efi_entry *entry) +{ + struct fastboot_ptentry e; + int n; + if (memcmp(entry->type_uuid, partition_type, sizeof(partition_type))) + return; + for (n = 0; n < (sizeof(e.name)-1); n++) + e.name[n] = entry->name[n]; + e.name[n] = 0; + e.start = entry->first_lba; + e.length = (entry->last_lba - entry->first_lba + 1) * 512; + e.flags = 0; + + if (!strcmp(e.name, "environment")) + e.flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_ENV; + fastboot_flash_add_ptn(&e); + + if (e.length > 0x100000) + printf("%8d %7dM %s\n", e.start, e.length/0x100000, e.name); + else + printf("%8d %7dK %s\n", e.start, e.length/0x400, e.name); +} + +static int load_ptbl(void) +{ + static unsigned char data[512]; + static struct efi_entry entry[4]; + int n, m; + char source[32], dest[32], length[32]; + + char *mmc_read[5] = {"mmc", "read", NULL, NULL, NULL}; + + /* read mbr */ + mmc_read[2] = source; + mmc_read[3] = dest; + mmc_read[4] = length; + + sprintf(source, "0x%x", data); + sprintf(dest, "0x%x", 0x1); + sprintf(length, "0x%x", 1); + + if (do_mmcops(NULL, 0, 5, mmc_read)) { + printf("Reading boot magic FAILED!\n"); + return -1; + } + + if (memcmp(data, "EFI PART", 8)) { + printf("efi partition table not found\n"); + return -1; + } + for (n = 0; n < (128/4); n++) { + + /* read partition */ + source[0] = '\0'; + dest[0] = '\0'; + length[0] = '\0'; + mmc_read[2] = source; + mmc_read[3] = dest; + mmc_read[4] = length; + + sprintf(source, "0x%x", entry); + sprintf(dest, "0x%x", 0x1+n); + sprintf(length, "0x%x", 1); + + if (do_mmcops(NULL, 0, 5, mmc_read)) { + printf("Reading boot magic FAILED!\n"); + return -1; + } + for (m = 0; m < 4; m++) + import_efi_partition(entry + m); + } + return 0; +} + + +static struct ptable the_ptable; + +int do_format(void) +{ + struct ptable *ptbl = &the_ptable; + unsigned sector_sz, blocks; + unsigned next; + int n; + + printf("\ndo_format ..!!"); + /* get mmc info */ + struct mmc *mmc = find_mmc_device(CONFIG_MMC_FASTBOOT_DEV); + if (mmc == 0) { + printf("no mmc device at slot %d", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + mmc->has_init = 0; + if (mmc_init(mmc)) { + + printf("\n mmc init FAILED"); + return -1; + } else{ + printf("\nmmc capacity is:0x%x", mmc->capacity); + printf("\nmmc: number of blocks:0x%x", mmc->block_dev.lba); + printf("\nmmc: block size:0x%x", mmc->block_dev.blksz); + } + + blocks = mmc->block_dev.lba; + sector_sz = mmc->block_dev.blksz; + + start_ptbl(ptbl, blocks); + n = 0; + next = 0; + for (n = 0, next = 0; partitions[n].name; n++) { + /* 10/11 : below line change size from KB to no of blocks */ + unsigned sz = partitions[n].size_kb*2 ; + if (!strcmp(partitions[n].name, "-")) { + next += sz; + continue; + } + if (sz == 0) + sz = blocks - next; + if (add_ptn(ptbl, next, next + sz - 1, partitions[n].name)) + return -1; + next += sz; + } + end_ptbl(ptbl); + + fastboot_flash_reset_ptn(); + + /* 10/11:modified as per PSP release support */ + char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; + char source[32], dest[32], length[32]; + + char dev[2]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + mmc_dev[2] = dev; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + + if (do_mmcops(NULL, 0, 3, mmc_dev)) { + printf("MMC DEV: %d selection FAILED!\n", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + mmc_write[2] = source; + mmc_write[3] = dest; + mmc_write[4] = length; + + sprintf(source, "0x%x", (void *)ptbl); + sprintf(dest, "0x%x", 0x00); + sprintf(length, "0x%x", (sizeof(struct ptable)/512)+1); + + if (do_mmcops(NULL, 0, 5, mmc_write)) { + printf("Writing mbr is FAILED!\n"); + return -1; + } else { + printf("Writing mbr is DONE!\n"); + } + + printf("\nnew partition table:\n"); + load_ptbl(); + + return 0; +} + +struct fastboot_config fastboot_cfg; + +extern env_t *env_ptr; +//extern int do_env_save (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); + +static int write_mmc_chunk(unsigned int source, unsigned int dest_sector, unsigned int num_sectors) +{ + char s_source[32], s_dest[32], s_length[32]; + char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; + char dev[12]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + sprintf(s_source, "0x%x", source); + sprintf(s_dest, "0x%x", dest_sector); + sprintf(s_length, "0x%x", num_sectors); + mmc_write[2] = s_source; + mmc_write[3] = s_dest; + mmc_write[4] = s_length; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + mmc_dev[2] = dev; + +// printf("%s %s %s\n", s_source, s_dest, s_length); + if (do_mmcops(NULL, 0, 5, mmc_write)) { + return -1; + } else { + return 0; + } +} + +static int flash_mmc_sparse_img(unsigned int ptn_start_sector) +{ + void *data; + unsigned int chunk; + unsigned int chunk_data_sz; + sparse_header_t *sparse_header; + chunk_header_t *chunk_header; + uint32_t total_blocks = 0; + uint32_t sectors_per_block; + + data = fastboot_cfg.transfer_buffer; + sparse_header = (sparse_header_t *)data; +#ifdef DEBUG_SPARSE + printf("=== Sparse Image Header ===\n"); + printf("magic: 0x%x\n", sparse_header->magic); + printf("major_version: 0x%x\n", sparse_header->major_version); + printf("minor_version: 0x%x\n", sparse_header->minor_version); + printf("file_hdr_sz: %u\n", sparse_header->file_hdr_sz); + printf("chunk_hdr_sz: %u\n", sparse_header->chunk_hdr_sz); + printf("blk_sz: %u\n", sparse_header->blk_sz); + printf("total_blks: %u\n", sparse_header->total_blks); + printf("total_chunks: %u\n", sparse_header->total_chunks); +#endif + data += sparse_header->file_hdr_sz; + + sectors_per_block = sparse_header->blk_sz/512; + + for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { + /* Read and skip over chunk header */ + chunk_header = (chunk_header_t *)data; + data += sizeof(chunk_header_t); +#ifdef DEBUG_SPARSE + printf("=== Chunk Header ===\n"); + printf("chunk_type: 0x%x\n", chunk_header->chunk_type); + printf("chunk_data_sz: 0x%x\n", chunk_header->chunk_sz); + printf("total_size: 0x%x\n", chunk_header->total_sz); +#endif + chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz; + switch (chunk_header->chunk_type) { + case CHUNK_TYPE_RAW: + if (write_mmc_chunk((unsigned int)data, + ptn_start_sector + (total_blocks * sectors_per_block), + chunk_header->chunk_sz * sectors_per_block) != 0) { + return -1; + } + data += chunk_data_sz; + break; + case CHUNK_TYPE_DONT_CARE: + break; + case CHUNK_TYPE_CRC32: + break; + default: + printf("Unknown chunk type\n"); + return -1; + } + total_blocks += chunk_header->chunk_sz; + } + return 0; +} + +int handle_flash(char *part_name, char *response) +{ + int status = 0; + + if (fastboot_cfg.download_bytes) { + struct fastboot_ptentry *ptn; + + /* Next is the partition name */ + ptn = fastboot_flash_find_ptn(part_name); + + if (ptn == 0) { + printf("Partition:[%s] does not exist\n", part_name); + sprintf(response, "FAILpartition does not exist"); + } else if ((fastboot_cfg.download_bytes > ptn->length) && + !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { + printf("Image too large for the partition\n"); + sprintf(response, "FAILimage too large for partition"); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { + /* Check if this is not really a flash write, + * but instead a saveenv + */ + unsigned int i = 0; + /* Env file is expected with a NULL delimeter between + * env variables So replace New line Feeds (0x0a) with + * NULL (0x00) + */ + for (i = 0; i < fastboot_cfg.download_bytes; i++) { + if (fastboot_cfg.transfer_buffer[i] == 0x0a) + fastboot_cfg.transfer_buffer[i] = 0x00; + } + memset(env_ptr->data, 0, ENV_SIZE); + memcpy(env_ptr->data, fastboot_cfg.transfer_buffer, fastboot_cfg.download_bytes); + //do_env_save(NULL, 0, 1, NULL); + printf("saveenv to '%s' DONE!\n", ptn->name); + sprintf(response, "OKAY"); + } else { + /* Normal case */ + sparse_header_t *sparse_header; + char source[32], dest[32], length[32]; + source[0] = '\0'; + dest[0] = '\0'; + length[0] = '\0'; + + char *mmc_write[5] = {"mmc", "write", NULL, NULL, NULL}; + char *mmc_init[2] = {"mmc", "rescan",}; + char dev[12]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + mmc_dev[2] = dev; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + + if (do_mmcops(NULL, 0, 3, mmc_dev)) { + printf("MMC DEV: %d selection FAILED!\n", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + printf("Initializing '%s'\n", ptn->name); + if (do_mmcops(NULL, 0, 2, mmc_init)) + sprintf(response, "FAIL:Init of MMC card"); + else + sprintf(response, "OKAY"); + + sparse_header = (sparse_header_t *)fastboot_cfg.transfer_buffer; + if (sparse_header->magic == SPARSE_HEADER_MAGIC) { + printf("Image is sparse format\n"); + if (flash_mmc_sparse_img(ptn->start) == 0) { + sprintf(response, "OKAY"); + printf("Writing '%s' DONE!\n", ptn->name); + } else { + sprintf(response, "FAIL: Write partition"); + printf("Writing '%s' FAILED!\n", ptn->name); + } + return 0; + } + + mmc_write[2] = source; + mmc_write[3] = dest; + mmc_write[4] = length; + + sprintf(source, "0x%x", fastboot_cfg.transfer_buffer); + sprintf(dest, "0x%x", ptn->start); + sprintf(length, "0x%x", (fastboot_cfg.download_bytes/512)+1); + + printf("Writing '%s'\n", ptn->name); + if (do_mmcops(NULL, 0, 5, mmc_write)) { + printf("Writing '%s' FAILED!\n", ptn->name); + sprintf(response, "FAIL: Write partition"); + } else { + printf("Writing '%s' DONE!\n", ptn->name); + sprintf(response, "OKAY"); + } + } + } else { + sprintf(response, "FAILno image downloaded"); + } + return 0; +} + +int board_mmc_fbtptn_init(void) +{ + char *mmc_init[2] = {"mmc", "rescan",}; + char dev[2]; + char *mmc_dev[3] = {"mmc", "dev", NULL}; + + mmc_dev[2] = dev; + sprintf(dev,"0x%x", CONFIG_MMC_FASTBOOT_DEV); + + if (do_mmcops(NULL, 0, 3, mmc_dev)) { + printf("MMC DEV: %d selection FAILED!\n", CONFIG_MMC_FASTBOOT_DEV); + return -1; + } + + if (do_mmcops(NULL, 0, 2, mmc_init)) { + printf("FAIL:Init of MMC card\n"); + return 1; + } + + printf("Loading efi partition table:\n"); + return load_ptbl(); +} + diff --git a/drivers/usb/gadget/u_fastboot_nand.c b/drivers/usb/gadget/u_fastboot_nand.c new file mode 100644 index 000000000..29da46dd0 --- /dev/null +++ b/drivers/usb/gadget/u_fastboot_nand.c @@ -0,0 +1,838 @@ +/* + * Copyright (C) 2013 Texas Instruments + * + * Author : 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 + * + * + * 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 Sitara 2011 u-boot by + * Mohammed Afzal M A <afzal@ti.com> + * + * 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 <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h" + +#include <nand.h> +#include <environment.h> + +#define MAX_PTN 11 + +/* Initialize the name of fastboot flash name mappings */ +fastboot_ptentry omap3h1_nand_ptn[MAX_PTN] = { + { + .name = "X-Loader", + .start = 0x0000000, + .length = 0x0020000, /* 256 K */ + /* Written into the first 0x40000 blocks + Use HW ECC */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC + /* FASTBOOT_PTENTRY_FLAGS_REPEAT_4, */ + }, + { + .name = "uboot", + .start = 0x0080000, + .length = 0x01E0000, /* 1.875 M */ + /* Skip bad blocks on write + Use HW ECC */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC, + }, + { + .name = "U-Boot Env", + .start = NAND_ENV_OFFSET, /* set in config file */ + .length = 0x0020000, + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_ENV | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "kernel", + .start = 0x0280000, + .length = 0x0500000, /* 5 M */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "initramfs", + .start = 0x0780000, + .length = 0x0A00000, /* 10 M */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "devicetree", + .start = 0x1180000, + .length = 0x020000, /* 128k */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "ramdisk", + .start = 0x1180000, + .length = 0x0C80000, /* 13 M */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "system", + .start = 0x1E80000, + .length = 0x800 * 0x20000, /* >256 M */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "cache", + .start = 0x11E80000, + .length = 0x400 * 0x20000, /* ~50Mb */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "recovery", + .start = 0x14A80000, + .length = 0x200 * 0x20000, /* ~26Mb */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, + { + .name = "userdata", + .start = 0x2f80000, + .length = 0x28CC0000, /* ~660 Mb */ + .flags = FASTBOOT_PTENTRY_FLAGS_WRITE_I | + FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC, + }, +}; + + +struct fastboot_config fastboot_cfg; + +extern int do_env_save (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); +extern int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]); + +static void set_env(char *var, char *val) +{ + char *setenv[4] = { "setenv", NULL, NULL, NULL, }; + + setenv[1] = var; + setenv[2] = val; + + do_env_set(NULL, 0, 3, setenv); +} + +static void set_ecc(int hw) +{ + char ecc_type[2]; + char ecc_provider[3]; /* make sure there's space */ + char *ecc[4] = { "nandecc", "hw", "0" , NULL }; + + ecc[2] = ecc_type; + ecc[1] = ecc_provider; + + if (hw) + { + /*for hardware ecc : set BCH8*/ + sprintf(ecc_provider, "hw"); + } + else + { + sprintf(ecc_provider, "sw"); + } + +/* OLIO: HW = 1bit ECC, SW = BCH8 */ + + do_switch_ecc(NULL, 0, 2, ecc); +} + +static void save_env(struct fastboot_ptentry *ptn, + char *var, char *val) +{ + char *saveenv[2] = { "setenv", NULL, }; + set_env (var, val); + + if ((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) && + (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) { + /* Both can not be true */ + FBTWARN("can not do hw and sw ecc for partition '%s'\n", ptn->name); + FBTWARN("Ignoring these flags\n"); +#ifdef OLIO_INVALID + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) { + setecc(1); + + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) { + set_ecc(0); +#endif /* OLIO_INVALID */ + } + + /* OLIO: env always saved with SW */ + + set_ecc(0); + do_env_save(NULL, 0, 1, saveenv); +} + +static void save_block_values(struct fastboot_ptentry *ptn, + unsigned int offset, + unsigned int size) +{ + struct fastboot_ptentry *env_ptn; + + char var[64], val[32]; + char start[32], length[32]; + char *setenv[4] = { "setenv", NULL, NULL, NULL, }; + char *saveenv[2] = { "setenv", NULL, }; + + setenv[1] = var; + setenv[2] = val; + + FBTINFO ("saving it..\n"); + + if (size == 0) { + /* The error case, where the variables are being unset */ + + sprintf (var, "%s_nand_offset", ptn->name); + sprintf (val, ""); + do_env_set (NULL, 0, 3, setenv); + + sprintf (var, "%s_nand_size", ptn->name); + sprintf (val, ""); + do_env_set (NULL, 0, 3, setenv); + } else { + /* Normal case */ + + sprintf (var, "%s_nand_offset", ptn->name); + sprintf (val, "0x%x", offset); + + FBTINFO("%s %s %s\n", setenv[0], setenv[1], setenv[2]); + + do_env_set (NULL, 0, 3, setenv); + + sprintf(var, "%s_nand_size", ptn->name); + + sprintf (val, "0x%x", size); + + FBTINFO("%s %s %s\n", setenv[0], setenv[1], setenv[2]); + + do_env_set (NULL, 0, 3, setenv); + } + + + /* Warning : + The environment is assumed to be in a partition named 'enviroment'. + It is very possible that your board stores the enviroment + someplace else. */ + env_ptn = fastboot_flash_find_ptn("environment"); + + if (env_ptn) + { + /* Some flashing requires the nand's ecc to be set */ + if ((env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) && + (env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) + { + /* Both can not be true */ + FBTWARN("can not do hw and sw ecc for partition '%s'\n", ptn->name); + FBTWARN("Ignoring these flags\n"); + } + else if (env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) + { + set_ecc(1); + } + else if (env_ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) + { + set_ecc(0); + } + + sprintf (start, "0x%x", env_ptn->start); + sprintf (length, "0x%x", env_ptn->length); + + } + + do_env_save (NULL, 0, 1, saveenv); +} + +/* When save = 0, just parse. The input is unchanged + When save = 1, parse and do the save. The input is changed */ +static int parse_env(void *ptn, char *err_string, int save, int debug) +{ + int ret = 1; + unsigned int sets = 0; + unsigned int comment_start = 0; + char *var = NULL; + char *var_end = NULL; + char *val = NULL; + char *val_end = NULL; + unsigned int i; + + char *buff = (char *)fastboot_cfg.transfer_buffer; + unsigned int size = fastboot_cfg.download_bytes_unpadded; + + /* The input does not have to be null terminated. + This will cause a problem in the corner case + where the last line does not have a new line. + Put a null after the end of the input. + + WARNING : Input buffer is assumed to be bigger + than the size of the input */ + if (save) + buff[size] = 0; + + for (i = 0; i < size; i++) { + + if (NULL == var) { + + /* + * Check for comments, comment ok only on + * mostly empty lines + */ + if (buff[i] == '#') + comment_start = 1; + + if (comment_start) { + if ((buff[i] == '\r') || + (buff[i] == '\n')) { + comment_start = 0; + } + } else { + if (!((buff[i] == ' ') || + (buff[i] == '\t') || + (buff[i] == '\r') || + (buff[i] == '\n'))) { + /* + * Normal whitespace before the + * variable + */ + var = &buff[i]; + } + } + + } else if (((NULL == var_end) || (NULL == val)) && + ((buff[i] == '\r') || (buff[i] == '\n'))) { + + /* This is the case when a variable + is unset. */ + + if (save) { + /* Set the var end to null so the + normal string routines will work + + WARNING : This changes the input */ + buff[i] = '\0'; + + save_env(ptn, var, val); + + FBTDBG("Unsetting %s\n", var); + } + + /* Clear the variable so state is parse is back + to initial. */ + var = NULL; + var_end = NULL; + sets++; + } else if (NULL == var_end) { + if ((buff[i] == ' ') || + (buff[i] == '\t')) + var_end = &buff[i]; + } else if (NULL == val) { + if (!((buff[i] == ' ') || + (buff[i] == '\t'))) + val = &buff[i]; + } else if (NULL == val_end) { + if ((buff[i] == '\r') || + (buff[i] == '\n')) { + /* look for escaped cr or ln */ + if ('\\' == buff[i - 1]) { + /* check for dos */ + if ((buff[i] == '\r') && + (buff[i+1] == '\n')) + buff[i + 1] = ' '; + buff[i - 1] = buff[i] = ' '; + } else { + val_end = &buff[i]; + } + } + } else { + sprintf(err_string, "Internal Error"); + + FBTDBG("Internal error at %s %d\n", + __FILE__, __LINE__); + return 1; + } + /* Check if a var / val pair is ready */ + if (NULL != val_end) { + if (save) { + /* Set the end's with nulls so + normal string routines will + work. + + WARNING : This changes the input */ + *var_end = '\0'; + *val_end = '\0'; + + save_env(ptn, var, val); + + FBTDBG("Setting %s %s\n", var, val); + } + + /* Clear the variable so state is parse is back + to initial. */ + var = NULL; + var_end = NULL; + val = NULL; + val_end = NULL; + + sets++; + } + } + + /* Corner case + Check for the case that no newline at end of the input */ + if ((NULL != var) && + (NULL == val_end)) { + if (save) { + /* case of val / val pair */ + if (var_end) + *var_end = '\0'; + /* else case handled by setting 0 past + the end of buffer. + Similar for val_end being null */ + save_env(ptn, var, val); + + if (var_end) + FBTDBG("Trailing Setting %s %s\n", var, val); + else + FBTDBG("Trailing Unsetting %s\n", var); + } + sets++; + } + /* Did we set anything ? */ + if (0 == sets) + sprintf(err_string, "No variables set"); + else + ret = 0; + + return ret; +} + +static int saveenv_to_ptn(struct fastboot_ptentry *ptn, char *err_string) +{ + int ret = 1; + int save = 0; + int debug = 0; + + /* err_string is only 32 bytes + Initialize with a generic error message. */ + sprintf(err_string, "%s", "Unknown Error"); + + /* Parse the input twice. + Only save to the enviroment if the entire input if correct */ + save = 0; + if (0 == parse_env(ptn, err_string, save, debug)) { + save = 1; + ret = parse_env(ptn, err_string, save, debug); + } + return ret; +} + +static void set_ptn_ecc(struct fastboot_ptentry *ptn) +{ + if (((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_BCH8_ECC) || + (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC)) && + (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) { + /* Both can not be true */ + FBTERR("can not do hw and sw ecc for partition '%s'\n", + ptn->name); + FBTERR("Ignoring these flags\n"); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_BCH8_ECC) { + set_ecc(1); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) { + set_ecc(1); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) { + set_ecc(0); + } +} + +static int write_to_ptn(struct fastboot_ptentry *ptn) +{ + int ret = 1; + char start[32], length[32]; + char wstart[32], wlength[32], addr[32]; + char write_type[32]; + int repeat, repeat_max; + + char *write[6] = { "nand", "write", NULL, NULL, NULL, NULL, }; + char *erase[5] = { "nand", "erase", NULL, NULL, NULL, }; + + erase[2] = start; + erase[3] = length; + + write[1] = write_type; + write[2] = addr; + write[3] = wstart; + write[4] = wlength; + + FBTINFO("flashing '%s'\n", ptn->name); + + /* Which flavor of write to use */ + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_I) + sprintf(write_type, "write.i"); + else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_JFFS2) + sprintf(write_type, "write.jffs2"); + else + sprintf(write_type, "write"); + + /* Some flashing requires writing the same data in multiple, + consecutive flash partitions */ + repeat_max = 1; + if (FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK(ptn->flags)) { + if (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) { + FBTWARN("can not do both 'contiguous block' and 'repeat' writes for for partition '%s'\n", ptn->name); + FBTWARN("Ignoring repeat flag\n"); + } else { + repeat_max = (FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK(ptn->flags)); + } + } + + sprintf(length, "0x%x", ptn->length); + + for (repeat = 0; repeat < repeat_max; repeat++) { + + set_ptn_ecc(ptn); + sprintf(start, "0x%x", ptn->start + (repeat * ptn->length)); + + do_nand(NULL, 0, 4, erase); + + if ((ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) && + (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK)) { + /* Both can not be true */ + FBTWARN("can not do 'next good block' and 'contiguous block' for partition '%s'\n", ptn->name); + FBTWARN("Ignoring these flags\n"); + } else if (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) { + /* Keep writing until you get a good block + transfer_buffer should already be aligned */ + if (fastboot_cfg.nand_block_size) { + unsigned int blocks = fastboot_cfg.download_bytes / + fastboot_cfg.nand_block_size; + unsigned int i = 0; + unsigned int offset = 0; + + sprintf(wlength, "0x%x", + fastboot_cfg.nand_block_size); + while (i < blocks) { + /* Check for overflow */ + if (offset >= ptn->length) + break; + + /* download's address only advance + if last write was successful */ + sprintf(addr, "0x%x", + fastboot_cfg.transfer_buffer + + (i * fastboot_cfg.nand_block_size)); + + /* nand's address always advances */ + sprintf(wstart, "0x%x", + ptn->start + (repeat * ptn->length) + offset); + + ret = do_nand(NULL, 0, 5, write); + if (ret) + break; + else + i++; + + /* Go to next nand block */ + offset += fastboot_cfg.nand_block_size; + } + } else { + FBTWARN("nand block size can not be 0 when using 'next good block' for partition '%s'\n", ptn->name); + FBTWARN("Ignoring write request\n"); + } + } else if (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) { + /* Keep writing until you get a good block + transfer_buffer should already be aligned */ + if (fastboot_cfg.nand_block_size) { + if (0 == nand_curr_device) { + nand_info_t *nand; + unsigned long off; + unsigned int ok_start; + + nand = &nand_info[nand_curr_device]; + + FBTINFO("\nDevice %d bad blocks:\n", + nand_curr_device); + + /* Initialize the ok_start to the + start of the partition + Then try to find a block large + enough for the download */ + ok_start = ptn->start; + + /* It is assumed that the start and + length are multiples of block size */ + for (off = ptn->start; + off < ptn->start + ptn->length; + off += nand->erasesize) { + if (nand_block_isbad(nand, off)) { + /* Reset the ok_start + to the next block */ + ok_start = off + + nand->erasesize; + } + + /* Check if we have enough + blocks */ + if ((ok_start - off) >= + fastboot_cfg.download_bytes) + break; + } + + /* Check if there is enough space */ + if (ok_start + fastboot_cfg.download_bytes <= + ptn->start + ptn->length) { + sprintf(addr, "0x%x", fastboot_cfg.transfer_buffer); + sprintf(wstart, "0x%x", ok_start); + sprintf(wlength, "0x%x", fastboot_cfg.download_bytes); + + ret = do_nand(NULL, 0, 5, write); + + /* Save the results into an + environment variable on the + format + ptn_name + 'offset' + ptn_name + 'size' */ + if (ret) { + /* failed */ + save_block_values(ptn, 0, 0); + } else { + /* success */ + save_block_values(ptn, ok_start, fastboot_cfg.download_bytes); + } + } else { + FBTERR("could not find enough contiguous space in partition '%s' \n", ptn->name); + FBTERR("Ignoring write request\n"); + } + } else { + /* TBD : Generalize flash handling */ + FBTERR("only handling 1 NAND per board"); + FBTERR("Ignoring write request\n"); + } + } else { + FBTWARN("nand block size can not be 0 when using 'continuous block' for partition '%s'\n", ptn->name); + FBTWARN("Ignoring write request\n"); + } + } else { + /* Normal case */ + sprintf(addr, "0x%x", fastboot_cfg.transfer_buffer); + sprintf(wstart, "0x%x", ptn->start + + (repeat * ptn->length)); + sprintf(wlength, "0x%x", fastboot_cfg.download_bytes); + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_JFFS2) + sprintf(wlength, "0x%x", + fastboot_cfg.download_bytes_unpadded); + + ret = do_nand(NULL, 0, 5, write); + + if (0 == repeat) { + if (ret) /* failed */ + save_block_values(ptn, 0, 0); + else /* success */ + save_block_values(ptn, ptn->start, + fastboot_cfg.download_bytes); + } + } + + + if (ret) + break; + } + + return ret; +} + +int handle_flash(char *part_name , char *response) +{ + int status = 0; + + + fastboot_cfg.download_bytes_unpadded = fastboot_cfg.download_size; + /* XXX: Revisit padding handling */ + if (fastboot_cfg.nand_block_size) { + if (fastboot_cfg.download_bytes % fastboot_cfg.nand_block_size) { + unsigned int pad = fastboot_cfg.nand_block_size - (fastboot_cfg.download_bytes % fastboot_cfg.nand_block_size); + unsigned int i; + + for (i = 0; i < pad; i++) { + if (fastboot_cfg.download_bytes >= fastboot_cfg.transfer_buffer_size) + break; + + fastboot_cfg.transfer_buffer[fastboot_cfg.download_bytes] = 0; + fastboot_cfg.download_bytes++; + } + } + } + + + if (fastboot_cfg.download_bytes) { + struct fastboot_ptentry *ptn; + + ptn = fastboot_flash_find_ptn(part_name); + if (ptn == 0) { + sprintf(response, "FAILpartition does not exist"); + } else if ((fastboot_cfg.download_bytes > ptn->length) && + !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { + sprintf(response, "FAILimage too large for partition"); + } else { + /* Check if this is not really a flash write + but rather a saveenv */ + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { + /* Since the response can only be 64 bytes, + there is no point in having a large error message. */ + char err_string[32]; + + if (saveenv_to_ptn(ptn, &err_string[0])) { + FBTINFO("savenv '%s' failed : %s\n", ptn->name, err_string); + sprintf(response, "FAIL%s", err_string); + } else { + FBTINFO("partition '%s' saveenv-ed\n", ptn->name); + sprintf(response, "OKAY"); + } + } else { + /* Normal case */ + if (write_to_ptn(ptn)) { + FBTINFO("flashing '%s' failed\n", ptn->name); + sprintf(response, "FAILfailed to flash partition"); + } else { + FBTINFO("partition '%s' flashed\n", ptn->name); + sprintf(response, "OKAY"); + } + } + } + } else { + sprintf(response, "FAILno image downloaded"); + } + + return status; +} + +/*This function does nothing with NAND partitioning. +It only adds partition info to fastboot partition table. +*/ +int do_format() +{ + int indx; + + fastboot_flash_reset_ptn(); + + for (indx = 0; indx < MAX_PTN; indx++) + { + fastboot_flash_add_ptn(&omap3h1_nand_ptn[indx]); + } + + fastboot_flash_dump_ptn(); + + return 0; +} + + +static int do_format_nand(void) +{ + struct ptable *ptbl = NULL; //&the_ptable; + unsigned next; + int n; + block_dev_desc_t *dev_desc; + unsigned long blocks_to_write, result; + + dev_desc = get_dev_by_name(FASTBOOT_BLKDEV); + if (!dev_desc) { + printf("error getting device %s\n", FASTBOOT_BLKDEV); + return -1; + } + if (!dev_desc->lba) { + printf("device %s has no space\n", FASTBOOT_BLKDEV); + return -1; + } + + printf("blocks %lu\n", dev_desc->lba); + + #if 0 + start_ptbl(ptbl, dev_desc->lba); + for (n = 0, next = 0; fbt_partitions[n].name; n++) { + u64 sz = fbt_partitions[n].size_kb * 2; + if (fbt_partitions[n].name[0] == '-') { + next += sz; + continue; + } + if (sz == 0) + sz = dev_desc->lba - next; + if (add_ptn(ptbl, next, next + sz - 1, fbt_partitions[n].name)) + return -1; + next += sz; + } + end_ptbl(ptbl); + + blocks_to_write = DIV_ROUND_UP(sizeof(struct ptable), dev_desc->blksz); + result = dev_desc->block_write(dev_desc->dev, 0, blocks_to_write, ptbl); + if (result != blocks_to_write) { + printf("\nFormat failed, block_write() returned %lu instead of %lu\n", result, blocks_to_write); + return -1; + } + + printf("\nnew partition table of %lu %lu-byte blocks\n", + blocks_to_write, dev_desc->blksz); + fbt_reset_ptn(); + #endif + + return 0; +} diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c index 36681b6fc..3900a9e0c 100644 --- a/drivers/usb/musb-new/musb_core.c +++ b/drivers/usb/musb-new/musb_core.c @@ -1532,9 +1532,10 @@ static int __devinit musb_core_init(u16 musb_type, struct musb *musb) /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \ - defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) +//#if defined(CONFIG_SOC_OMAP2430) || defined(CONFIG_SOC_OMAP3430) || \ +// defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_ARCH_U8500) +#if 1 static irqreturn_t generic_interrupt(int irq, void *__hci) { unsigned long flags; diff --git a/include/configs/omap3_h1.h b/include/configs/omap3_h1.h index b406bde99..ebb71ffad 100644 --- a/include/configs/omap3_h1.h +++ b/include/configs/omap3_h1.h @@ -20,6 +20,31 @@ #define CONFIG_OMAP_GPIO #define CONFIG_OMAP_COMMON + +/* USB UHH support options */ +#define CONFIG_CMD_USB +#define CONFIG_USB_HOST +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_OMAP +#define CONFIG_USB_STORAGE +#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 3 +#define CONFIG_EHCI_HCD_INIT_AFTER_RESET + +/* #define CONFIG_USB_MUSB_AM35X */ +#define CONFIG_USB_MUSB_OMAP2PLUS + +/* USB Composite download gadget - g_dnl */ +#define CONFIG_USB_GADGET +#define CONFIG_USBDOWNLOAD_GADGET + + +/* USB TI's IDs */ +#define CONFIG_USBD_HS +#define CONFIG_G_DNL_VENDOR_NUM 0x0403 +#define CONFIG_G_DNL_PRODUCT_NUM 0xBD00 +#define CONFIG_G_DNL_MANUFACTURER "Texas Instruments" + + #define CONFIG_SDRC /* The chip has SDRC controller */ #include <asm/arch/cpu.h> /* get chip and board defs */ @@ -261,6 +286,79 @@ * FLASH and environment organization */ +#define CONFIG_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_OMAP_HSMMC +#define CONFIG_CMD_MMC +#define CONFIG_DOS_PARTITION +#define CONFIG_CMD_FAT +#define CONFIG_FAT_WRITE +#define CONFIG_CMD_EXT2 + +#define CONFIG_SPI +#define CONFIG_OMAP3_SPI +#define CONFIG_MTD_DEVICE +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_WINBOND +#define CONFIG_CMD_SF +#define CONFIG_SF_DEFAULT_SPEED (24000000) + + +/* + * USB configuration + */ +#define CONFIG_USB_MUSB_DSPS +/* #define CONFIG_ARCH_MISC_INIT */ + +#define CONFIG_CMD_SAVEENV +#define CONFIG_MUSB_GADGET +#define CONFIG_MUSB_PIO_ONLY +#define CONFIG_USB_GADGET_DUALSPEED +#define CONFIG_USB_GADGET_VBUS_DRAW 2 + + +#ifdef CONFIG_MUSB_GADGET +/* Android fastboot support over USB */ +#define CONFIG_CMD_FASTBOOT +#define FASTBOOT_DEVICE_VENDOR_ID 0x0451 +#define FASTBOOT_DEVICE_PRODUCT_ID 0xd00e /* TI fastboot PID */ +#define FASTBOOT_DEVICE_BCD 0x0100 + +#define FASTBOOT_BLKDEV "nand0" +/* + * BeagleBone white, AM335x-SK and old AM335xEVMs have only 256MB RAM + * To be compatible with all devices, assume 256MB max RAM. + * + * The current fastboot implementation assumes maximum 16MB of RAM + * will be used by u-boot itself. So the fastboot transfer buffer + * becomes (256-16)=240MB + */ + +/* #define CONFIG_FASTBOOT_MAX_TRANSFER_SIZE (SZ_256M - SZ_16M) */ +/* If using BeagleBone Black you can use the full RAM and so + flash larger images */ +#define CONFIG_FASTBOOT_MAX_TRANSFER_SIZE (SZ_512M - SZ_16M) + +#define FASTBOOT_NAND_BLOCK_SIZE 2048 +#define FASTBOOT_NAND_OOB_SIZE 64 + +#define CONFIG_MMC_FASTBOOT_DEV 1 + +/*Uncomment this to enable NAND fastboot*/ +#define CONFIG_FASTBOOT_NAND + +/*Uncomment this to support eMMC booting*/ +/* #define CONFIG_STORAGE_EMMC */ + +#define NAND_ENV_OFFSET 0x260000 /* environment starts here */ + +/* ethernet gadget conflicts with fastboot, so disabled */ +/* +#define CONFIG_USB_ETHER +#define CONFIG_USB_ETH_RNDIS +#define CONFIG_USBNET_HOST_ADDR "de:ad:be:af:00:00"*/ +#endif /* CONFIG_MUSB_GADGET */ + /* **** PISMO SUPPORT *** */ /* Configure the PISMO */ @@ -350,7 +448,7 @@ #define CONFIG_SYS_NAND_ECCPOS {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} #define CONFIG_SYS_NAND_ECCBYTES 3 -#define CONFIG_NAND_OMAP_ECCSCHEME OMAP_ECC_HAM1_CODE_HW +#define CONFIG_NAND_OMAP_ECCSCHEME OMAP_ECC_HAM1_CODE_SW #endif /* BCH8_ECC */ diff --git a/include/olioh1_reboot.h b/include/olioh1_reboot.h new file mode 100644 index 000000000..1fbea3e74 --- /dev/null +++ b/include/olioh1_reboot.h @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2014 + * Chris Simmonds, 2net Ltd. chris@2net.co.uk + * + * 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 + */ + +/* Here we define an address in SRAM that can contain a flag to indicate + the reason for a reboot. + + I chose an address 16 bytes from the end of SRAM in the hope that + nobody will clobber it between Linux reboot and U-Boot starting */ +#define REBOOT_REASON_PA 0x4030fff0 + +#define REBOOT_FLAG_RECOVERY 0x52564352 +#define REBOOT_FLAG_FASTBOOT 0x54534146 +#define REBOOT_FLAG_NORMAL 0x4D524F4E +#define REBOOT_FLAG_POWER_OFF 0x46464F50 + diff --git a/include/sparse_format.h b/include/sparse_format.h new file mode 100644 index 000000000..6c62c34c7 --- /dev/null +++ b/include/sparse_format.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +typedef struct sparse_header { + __le32 magic; /* 0xed26ff3a */ + __le16 major_version; /* (0x1) - reject images with higher major versions */ + __le16 minor_version; /* (0x0) - allow images with higer minor versions */ + __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + __le32 total_blks; /* total blocks in the non-sparse output image */ + __le32 total_chunks; /* total chunks in the sparse input image */ + __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + /* as 0. Standard 802.3 polynomial, use a Public Domain */ + /* table implementation */ +} sparse_header_t; + +#define SPARSE_HEADER_MAGIC 0xed26ff3a + +#define CHUNK_TYPE_RAW 0xCAC1 +#define CHUNK_TYPE_FILL 0xCAC2 +#define CHUNK_TYPE_DONT_CARE 0xCAC3 +#define CHUNK_TYPE_CRC32 0xCAC4 + +typedef struct chunk_header { + __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + __le16 reserved1; + __le32 chunk_sz; /* in blocks in output image */ + __le32 total_sz; /* in bytes of chunk input file including chunk header and data */ +} chunk_header_t; + +/* Following a Raw or Fill or CRC32 chunk is data. + * For a Raw chunk, it's the data in chunk_sz * blk_sz. + * For a Fill chunk, it's 4 bytes of the fill data. + * For a CRC32 chunk, it's 4 bytes of CRC32 + */ + diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h new file mode 100644 index 000000000..9525454bc --- /dev/null +++ b/include/usb/fastboot.h @@ -0,0 +1,213 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix at windriver.com> + * + * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de> + * + * 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. + * + * The logical naming of flash comes from the Android project + * Thse structures and functions that look like fastboot_flash_* + * They come from bootloader/legacy/include/boot/flash.h + * + * The boot_img_hdr structure and associated magic numbers also + * come from the Android project. They are from + * bootloader/legacy/include/boot/bootimg.h + * + * Here are their copyrights + * + * 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. + * + */ + +#ifndef FASTBOOT_H +#define FASTBOOT_H + +#include <common.h> +#include <command.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#ifdef DEBUG +#define FBTDBG(fmt,args...)\ + printf("DEBUG: [%s]: %d: \n"fmt, __FUNCTION__, __LINE__,##args) +#else +#define FBTDBG(fmt,args...) do{}while(0) +#endif + +#ifdef INFO +#define FBTINFO(fmt,args...)\ + printf("INFO: [%s]: "fmt, __FUNCTION__, ##args) +#else +#define FBTINFO(fmt,args...) do{}while(0) +#endif + +#ifdef WARN +#define FBTWARN(fmt,args...)\ + printf("WARNING: [%s]: "fmt, __FUNCTION__, ##args) +#else +#define FBTWARN(fmt,args...) do{}while(0) +#endif + +#ifdef ERR +#define FBTERR(fmt,args...)\ + printf("ERROR: [%s]: "fmt, __FUNCTION__, ##args) +#else +#define FBTERR(fmt,args...) do{}while(0) +#endif + +struct fastboot_config { + + /* + * Transfer buffer for storing data sent by the client. It should be + * able to hold a kernel image and flash partitions. Care should be + * take so it does not overrun bootloader memory + */ + unsigned char *transfer_buffer; + + /* Size of the buffer mentioned above */ + unsigned int transfer_buffer_size; + + /* Total data to be downloaded */ + unsigned int download_size; + + /* Data downloaded so far */ + unsigned int download_bytes; + + unsigned int nand_block_size; + + unsigned int nand_oob_size; + + unsigned int download_bytes_unpadded; +}; + +#define FB_STR_PRODUCT_IDX 1 +#define FB_STR_SERIAL_IDX 2 +#define FB_STR_CONFIG_IDX 3 +#define FB_STR_INTERFACE_IDX 4 +#define FB_STR_MANUFACTURER_IDX 5 +#define FB_STR_PROC_REV_IDX 6 +#define FB_STR_PROC_TYPE_IDX 7 + +#ifdef CONFIG_CMD_FASTBOOT + +int fastboot_init(void); +void fastboot_shutdown(void); +int fastboot_poll(void); + +int fastboot_board_init(struct fastboot_config *interface, + struct usb_gadget_strings **str); + + +/* Android-style flash naming */ +typedef struct fastboot_ptentry fastboot_ptentry; + +/* flash partitions are defined in terms of blocks + ** (flash erase units) + */ +struct fastboot_ptentry{ + + /* The logical name for this partition, null terminated */ + char name[16]; + /* The start wrt the nand part, must be multiple of nand block size */ + unsigned int start; + /* The length of the partition, must be multiple of nand block size */ + unsigned int length; + /* Controls the details of how operations are done on the partition + See the FASTBOOT_PTENTRY_FLAGS_*'s defined below */ + unsigned int flags; +}; + +/* Lower byte shows if the read/write/erase operation in + repeated. The base address is incremented. + Either 0 or 1 is ok for a default */ + +#define FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK(n) (n & 0x0f) +#define FASTBOOT_PTENTRY_FLAGS_REPEAT_4 0x00000004 + +/* Writes happen a block at a time. + If the write fails, go to next block + NEXT_GOOD_BLOCK and CONTIGOUS_BLOCK can not both be set */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK 0x00000010 + +/* Find a contiguous block big enough for a the whole file + NEXT_GOOD_BLOCK and CONTIGOUS_BLOCK can not both be set */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK 0x00000020 + +/* Sets the ECC to software before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC 0x00000040 + +/* Sets the ECC to hardware before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC 0x00000080 + +/* Sets the ECC to hardware before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_HW_BCH4_ECC 0x00000100 + +/* Sets the ECC to hardware before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_HW_BCH8_ECC 0x00000200 + +/* Sets the ECC to hardware before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_HW_BCH16_ECC 0x00000400 + +/* Write the file with write.i */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_I 0x00000800 + +/* Write the file with write.jffs2 */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_JFFS2 0x00001000 + +/* Write the file as a series of variable/value pairs + using the setenv and saveenv commands */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_ENV 0x00002000 + + +/* The Android-style flash handling */ + +/* tools to populate and query the partition table */ +extern void fastboot_flash_add_ptn(fastboot_ptentry *ptn); +extern fastboot_ptentry *fastboot_flash_find_ptn(const char *name); +extern fastboot_ptentry *fastboot_flash_get_ptn(unsigned n); +extern unsigned int fastboot_flash_get_ptn_count(void); +extern void fastboot_flash_dump_ptn(void); +extern int fastboot_oem(const char *cmd); + + +#endif +#endif |