This is the faastboot gadget code based on git://git.omapzoom.org/repo/u-boot.git as of commit 601ff71c8 including a few changes. Some of them are: - generic mmc framework - "new" gadget framework - different / easier command parsing - booti command is missing
It was tested before it has been re-written. It is possible that it is broken. It is posted for reference only not for merging.
Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de --- common/Makefile | 2 + common/cmd_fastboot.c | 1334 +++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/fastboot_oem.c | 7 + drivers/usb/dwc3/misc.c | 19 + drivers/usb/dwc3/sparse.c | 8 + drivers/usb/gadget/Makefile | 5 + drivers/usb/gadget/g_fastboot.c | 608 ++++++++++++++++++ drivers/usb/gadget/misc.h | 14 + include/linux/usb/ch9.h | 1 + include/linux/usb/gadget.h | 4 + include/usb/fastboot.h | 340 ++++++++++ include/usb/sparse.h | 33 + include/usb/sparse_format.h | 47 ++ 13 files changed, 2422 insertions(+), 0 deletions(-) create mode 100644 common/cmd_fastboot.c create mode 100644 drivers/usb/dwc3/fastboot_oem.c create mode 100644 drivers/usb/dwc3/misc.c create mode 100644 drivers/usb/dwc3/sparse.c create mode 100644 drivers/usb/gadget/g_fastboot.c create mode 100644 drivers/usb/gadget/misc.h create mode 100644 include/usb/fastboot.h create mode 100644 include/usb/sparse.h create mode 100644 include/usb/sparse_format.h
diff --git a/common/Makefile b/common/Makefile index d662468..da40d64 100644 --- a/common/Makefile +++ b/common/Makefile @@ -157,6 +157,8 @@ COBJS-y += cmd_usb.o COBJS-y += usb.o COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o endif +COBJS-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o + COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 0000000..263dd52 --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,1334 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Part of the rx_handler were copied from the Android project. + * Specifically rx command parsing in the usb_rx_data_complete + * function of the file bootable/bootloader/legacy/usbloader/usbloader.c + * + * The logical naming of flash comes from the Android project + * Thse structures and functions that look like fastboot_flash_* + * They come from bootable/bootloader/legacy/libboot/flash.c + * + * This is their Copyright: + * + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <asm/byteorder.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <nand.h> +#include <usb/fastboot.h> +#include <usb/sparse.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <environment.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Use do_reset for fastboot's 'reboot' command */ +#ifndef CONFIG_GENERIC_MMC +#error Generic MMC support is required +#endif + +/* Forward decl */ +static void reset_handler (void); + +static struct cmd_fastboot_interface interface = +{ + .reset_handler = reset_handler, + .product_name = NULL, + .serial_no = NULL, + .storage_medium = 0, + .nand_block_size = 0, + .transfer_buffer = (unsigned char *)0xffffffff, + .transfer_buffer_size = 0, +}; + +static unsigned int download_size; +static unsigned int download_bytes; +static unsigned int download_bytes_unpadded; +static unsigned int download_error; +static unsigned int current_mmc_controller_no = -1; + +extern cmd_tbl_t __u_boot_cmd_mmc; +int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + +static void set_mmc_controller(unsigned int num) +{ + char str_num[20]; + char *argv[] = { + NULL, + "dev", + str_num, + }; + int ret; + + if (current_mmc_controller_no == num) + return; + sprintf(str_num, "%u\n", num); + ret = do_mmcops(&__u_boot_cmd_mmc, 0, 3, argv); + if (ret) + return; + current_mmc_controller_no = num; +} + +static int do_mmc_read(u32 block, u32 count, void *addr) +{ + char str_block[20]; + char str_count[20]; + char str_addr[20]; + char *argv[] = { + NULL, + "read", + str_block, + str_count, + str_addr, + }; + + sprintf(str_block, "%x", block); + sprintf(str_count, "%x", count); + sprintf(str_addr, "%p", addr); + return do_mmcops(&__u_boot_cmd_mmc, 0, 5, argv); +} + +static int do_mmc_write(u32 block, u32 count, void *addr) +{ + char str_block[20]; + char str_count[20]; + char str_addr[20]; + char *argv[] = { + NULL, + "write", + str_block, + str_count, + str_addr, + }; + + sprintf(str_block, "%x", block); + sprintf(str_count, "%x", count); + sprintf(str_addr, "%p", addr); + return do_mmcops(&__u_boot_cmd_mmc, 0, 5, argv); +} + +static int do_mmc_erase(u32 block, u32 count) +{ + char str_block[20]; + char str_count[20]; + char *argv[] = { + NULL, + "erase", + str_block, + str_count, + }; + + sprintf(str_block, "%x", block); + sprintf(str_count, "%x", count); + return do_mmcops(&__u_boot_cmd_mmc, 0, 4, argv); +} + +static void save_env(struct fastboot_ptentry *ptn, + char *var, char *val) +{ + setenv(var, val); + saveenv(); +} +int fastboot_tx_write(const char *buffer, unsigned int buffer_size); + +static int fastboot_tx_write_str(const char *buffer) +{ + return fastboot_tx_write(buffer, strlen(buffer)); +} + +static void save_block_values(struct fastboot_ptentry *ptn, + unsigned int offset, + unsigned int size) +{ + char var[64], val[32]; + + printf ("saving it..\n"); + if (size == 0) { + /* The error case, where the variables are being unset */ + + sprintf (var, "%s_nand_offset", ptn->name); + val[0] = '\0'; + setenv(var, val); + + sprintf (var, "%s_nand_size", ptn->name); + val[0] = '\0'; + setenv(var, val); + } else { + /* Normal case */ + + sprintf (var, "%s_nand_offset", ptn->name); + sprintf (val, "0x%x", offset); + printf ("setenv %s %s\n", var, val); + setenv(var, val); + + sprintf (var, "%s_nand_size", ptn->name); + sprintf (val, "0x%x", size); + printf ("setenv %s %s\n", var, val); + setenv(var, val); + } + saveenv(); +} + +static void reset_handler () +{ + /* If there was a download going on, bail */ + download_size = 0; + download_bytes = 0; + download_bytes_unpadded = 0; + download_error = 0; +} + +/* 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; + char *var = NULL; + char *var_end = NULL; + char *val = NULL; + char *val_end = NULL; + unsigned int i; + + char *buff = (char *)interface.transfer_buffer; + unsigned int size = download_bytes_unpadded; + + /* The input does not have to be null terminated. + This will cause a problem in the corner case + where the last line does not have a new line. + Put a null after the end of the input. + + WARNING : Input buffer is assumed to be bigger + than the size of the input */ + if (save) + buff[size] = 0; + + for (i = 0; i < size; i++) { + if (NULL == var) { + if (!((buff[i] == ' ') || + (buff[i] == '\t') || + (buff[i] == '\r') || + (buff[i] == '\n'))) + var = &buff[i]; + } else if (((NULL == var_end) || (NULL == val)) && + ((buff[i] == '\r') || (buff[i] == '\n'))) { + + /* This is the case when a variable + is unset. */ + + if (save) { + /* Set the var end to null so the + normal string routines will work + + WARNING : This changes the input */ + buff[i] = '\0'; + + save_env(ptn, var, val); + + if (debug) + printf("Unsetting %s\n", var); + } + + /* Clear the variable so state is parse is back + to initial. */ + var = NULL; + var_end = NULL; + sets++; + } else if (NULL == var_end) { + if ((buff[i] == ' ') || + (buff[i] == '\t')) + var_end = &buff[i]; + } else if (NULL == val) { + if (!((buff[i] == ' ') || + (buff[i] == '\t'))) + val = &buff[i]; + } else if (NULL == val_end) { + if ((buff[i] == '\r') || + (buff[i] == '\n')) { + /* look for escaped cr or ln */ + if ('\' == buff[i - 1]) { + /* check for dos */ + if ((buff[i] == '\r') && + (buff[i+1] == '\n')) + buff[i + 1] = ' '; + buff[i - 1] = buff[i] = ' '; + } else { + val_end = &buff[i]; + } + } + } else { + sprintf(err_string, "Internal Error"); + + if (debug) + printf("Internal error at %s %d\n", + __FILE__, __LINE__); + return 1; + } + /* Check if a var / val pair is ready */ + if (NULL != val_end) { + if (save) { + /* Set the end's with nulls so + normal string routines will + work. + + WARNING : This changes the input */ + *var_end = '\0'; + *val_end = '\0'; + + save_env(ptn, var, val); + + if (debug) + printf("Setting %s %s\n", var, val); + } + + /* Clear the variable so state is parse is back + to initial. */ + var = NULL; + var_end = NULL; + val = NULL; + val_end = NULL; + + sets++; + } + } + + /* Corner case + Check for the case that no newline at end of the input */ + if ((NULL != var) && + (NULL == val_end)) { + if (save) { + /* case of val / val pair */ + if (var_end) + *var_end = '\0'; + /* else case handled by setting 0 past + the end of buffer. + Similar for val_end being null */ + save_env(ptn, var, val); + + if (debug) { + if (var_end) + printf("Trailing Setting %s %s\n", var, val); + else + printf("Trailing Unsetting %s\n", var); + } + } + sets++; + } + /* Did we set anything ? */ + if (0 == sets) + sprintf(err_string, "No variables set"); + else + ret = 0; + + return ret; +} + +static int saveenv_to_ptn(struct fastboot_ptentry *ptn, char *err_string) +{ + int ret = 1; + int save = 0; + int debug = 0; + + /* err_string is only 32 bytes + Initialize with a generic error message. */ + sprintf(err_string, "%s", "Unknown Error"); + + /* Parse the input twice. + Only save to the enviroment if the entire input if correct */ + save = 0; + if (0 == parse_env(ptn, err_string, save, debug)) { + save = 1; + ret = parse_env(ptn, err_string, save, debug); + } + return ret; +} + +static int write_to_ptn(struct fastboot_ptentry *ptn) +{ + int ret = 1; + char start[32], length[32]; + char wstart[32], wlength[32], addr[32]; + char ecc_type[32], write_type[32]; + int repeat, repeat_max; + + char *lock[5] = { "nand", "lock", NULL, NULL, NULL, }; + char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, }; + char *write[6] = { "nand", "write", NULL, NULL, NULL, NULL, }; + char *ecc[4] = { "nand", "ecc", NULL, NULL, }; + char *erase[5] = { "nand", "erase", NULL, NULL, NULL, }; + + lock[2] = unlock[2] = erase[2] = start; + lock[3] = unlock[3] = erase[3] = length; + + write[1] = write_type; + write[2] = addr; + write[3] = wstart; + write[4] = wlength; + + printf("flashing '%s'\n", ptn->name); + + /* Which flavor of write to use */ + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_I) + sprintf(write_type, "write.i"); +#ifdef CFG_NAND_YAFFS_WRITE + else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS) + sprintf(write_type, "write.yaffs"); +#endif + else + sprintf(write_type, "write"); + + + /* Some flashing requires the nand's ecc to be set */ + ecc[2] = ecc_type; + if ((ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) && + (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC)) { + /* Both can not be true */ + printf("Warning can not do hw and sw ecc for partition '%s'\n", + ptn->name); + printf("Ignoring these flags\n"); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC) { + sprintf(ecc_type, "hw"); + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC) { + sprintf(ecc_type, "sw"); + } + + /* Some flashing requires writing the same data in multiple, + consecutive flash partitions */ + repeat_max = 1; + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK) { + if (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) { + printf("Warning can not do both 'contiguous block' and 'repeat' writes for for partition '%s'\n", ptn->name); + printf("Ignoring repeat flag\n"); + } else { + repeat_max = ptn->flags & + FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK; + } + } + + /* Unlock the whole partition instead of trying to + manage special cases */ + sprintf(length, "0x%x", ptn->length * repeat_max); + + for (repeat = 0; repeat < repeat_max; repeat++) { + sprintf(start, "0x%x", ptn->start + (repeat * ptn->length)); + + if ((ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) && + (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK)) { + /* Both can not be true */ + printf("Warning can not do 'next good block' and 'contiguous block' for partition '%s'\n", ptn->name); + printf("Ignoring these flags\n"); + } else if (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_NEXT_GOOD_BLOCK) { + /* Keep writing until you get a good block + transfer_buffer should already be aligned */ + if (interface.nand_block_size) { + unsigned int blocks = download_bytes / + interface.nand_block_size; + unsigned int i = 0; + unsigned int offset = 0; + + sprintf(wlength, "0x%x", + interface.nand_block_size); + while (i < blocks) { + /* Check for overflow */ + if (offset >= ptn->length) + break; + + /* download's address only advance + if last write was successful */ + sprintf(addr, "0x%p", + interface.transfer_buffer + + (i * interface.nand_block_size)); + + /* nand's address always advances */ + sprintf(wstart, "0x%x", + ptn->start + (repeat * ptn->length) + offset); + if (ret) + break; + else + i++; + + /* Go to next nand block */ + offset += interface.nand_block_size; + } + } else { + printf("Warning nand block size can not be 0 when using 'next good block' for partition '%s'\n", ptn->name); + printf("Ignoring write request\n"); + } + } else if (ptn->flags & + FASTBOOT_PTENTRY_FLAGS_WRITE_CONTIGUOUS_BLOCK) { + } else { + /* Normal case */ + sprintf(addr, "0x%p", interface.transfer_buffer); + sprintf(wstart, "0x%x", ptn->start + + (repeat * ptn->length)); + sprintf(wlength, "0x%x", download_bytes); +#ifdef CFG_NAND_YAFFS_WRITE + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS) + sprintf(wlength, "0x%x", + download_bytes_unpadded); +#endif + + + if (0 == repeat) { + if (ret) /* failed */ + save_block_values(ptn, 0, 0); + else /* success */ + save_block_values(ptn, ptn->start, + download_bytes); + } + } + + if (ret) + break; + } + + return ret; +} + +static unsigned int rx_bytes_expected(void) +{ + int rx_remain = download_size - download_bytes; + if (rx_remain < 0) + return 0; + if (rx_remain > EP_OUT_BUFFER_SIZE) + return EP_OUT_BUFFER_SIZE; + return rx_remain; +} + +static char boot_addr_start[32]; +static char *booti_args[4] = { "booti", NULL, "boot", NULL }; + +static inline int do_booti (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_bootm(cmdtp, flag, argc, argv); +} + +static void do_booti_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + req->complete = NULL; + fastboot_shutdown(); + printf("Booting kernel..\n"); + + /* boot the boot.img */ + do_booti(NULL, 0, 3, booti_args); + /* + * the alternative would be to re-init fastboot and start from the + * beginning + */ + do_reset(NULL, 0, 0, NULL); +} + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + char response[65]; + unsigned int transfer_size = download_size - download_bytes; + const unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + + if (!buffer_size) { + /* Ignore empty buffers */ + printf ("Warning empty download buffer\n"); + printf ("Ignoring\n"); + return; + } + /* Handle possible overflow */ + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + /* Save the data to the transfer buffer */ + memcpy(interface.transfer_buffer + download_bytes, + buffer, transfer_size); + + download_bytes += transfer_size; + + /* Check if transfer is done */ + if (download_bytes >= download_size) { + /* Reset global transfer variable, + Keep download_bytes because it will be + used in the next possible flashing command */ + download_size = 0; + req->complete = rx_handler_command; + req->length = EP_OUT_BUFFER_SIZE; + + if (download_error) { + /* There was an earlier error */ + sprintf(response, "ERROR"); + } else { + /* Everything has transferred, + send the OK response */ + sprintf(response, "OKAY"); + } + fastboot_tx_write_str(response); + + printf ("\ndownloading of %d bytes finished\n", + download_bytes); + + /* Padding is required only if storage medium is NAND */ + if (interface.storage_medium == NAND) { + /* Pad to block length + In most cases, padding the download to be + block aligned is correct. The exception is + when the following flash writes to the oob + area. This happens when the image is a + YAFFS image. Since we do not know what + the download is until it is flashed, + go ahead and pad it, but save the true + size in case if should have + been unpadded */ + download_bytes_unpadded = download_bytes; + if (interface.nand_block_size) + { + if (download_bytes % + interface.nand_block_size) + { + unsigned int pad = interface.nand_block_size - (download_bytes % interface.nand_block_size); + unsigned int i; + + for (i = 0; i < pad; i++) + { + if (download_bytes >= interface.transfer_buffer_size) + break; + + interface.transfer_buffer[download_bytes] = 0; + download_bytes++; + } + } + } + } + } else + req->length = rx_bytes_expected(); + + /* Provide some feedback */ + if (download_bytes && + 0 == (download_bytes % + (16 * interface.nand_block_size))) + { + /* Some feeback that the + download is happening */ + if (download_error) + printf("X"); + else + printf("."); + if (0 == (download_bytes % + (80 * 16 * + interface.nand_block_size))) + printf("\n"); + + } + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void compl_reboot_bootloader(struct usb_ep *ep, struct usb_request *req) +{ + /* Clear all reset reasons */ + //__raw_writel(0xfff, PRM_RSTST); + + //strcpy(PUBLIC_SAR_RAM_1_FREE, "reboot-bootloader"); + + /* now warm reset the silicon */ + //__raw_writel(PRM_RSTCTRL_RESET_WARM_BIT, PRM_RSTCTRL); + printf("bootloader reset\n"); + reset_cpu(0); +} + +static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{ + do_reset(NULL, 0, 0, NULL); +} + +static void compl_do_recovery(struct usb_ep *ep, struct usb_request *req) +{ + printf("%s()\n", __func__); + /* Clear all reset reasons */ + //__raw_writel(0xfff, PRM_RSTST); + //strcpy(PUBLIC_SAR_RAM_1_FREE, "recovery"); + /* now warm reset the silicon */ + //__raw_writel(PRM_RSTCTRL_RESET_WARM_BIT, + // PRM_RSTCTRL); + /* Never returns */ + while (1) + ; +} + +static void cb_reboot_bootloader(struct usb_ep *ep, struct usb_request *req) +{ + req_in->complete = compl_reboot_bootloader; + fastboot_tx_write_str("OKAY"); +} + +static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{ + req_in->complete = compl_do_reset; + fastboot_tx_write_str("OKAY"); +} + +static void cb_getvar(struct usb_ep *ep, struct usb_request *req) +{ + /* getvar + Get common fastboot variables + Board has a chance to handle other variables */ + char *cmd = req->buf; + char response[65]; + + strcpy(response,"OKAY"); + strsep(&cmd, ":"); + if (!cmd) { + fastboot_tx_write_str("FAILmissing var"); + return; + } + + if(!strcmp(cmd, "version")) { + strncat(response, FASTBOOT_VERSION, sizeof(response)); + + } else if(!strcmp(cmd, "product")) { + if (interface.product_name) + strncat(response, interface.product_name, sizeof(response)); + else + strcpy(response, "FAILproduct not set"); + + } else if(!strcmp(cmd, "serialno")) { + if (interface.serial_no) + strncat(response, interface.serial_no, sizeof(response)); + else + strcpy(response, "FAILserialno not set"); + + } else if(!strcmp(cmd, "downloadsize")) { + char str_num[12]; + + sprintf(str_num, "%08x", interface.transfer_buffer_size); + strncat(response, str_num, sizeof(response)); + + } else if(!strcmp(cmd, "cpurev")) { + if (interface.proc_rev) + strncat(response, interface.proc_rev, sizeof(response)); + else + strcpy(response, "FAILcpurev not set"); + } else if(!strcmp(cmd, "secure")) { + if (interface.proc_type) + strncat(response, interface.proc_type, sizeof(response)); + else + strcpy(response, "FAILsecure not set"); + } else { + fastboot_getvar(cmd, response); + } + fastboot_tx_write_str(response); +} + +static void cb_oem(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[65]; + + strcpy(response,"OKAY"); + strsep(&cmd, " "); + + /* fastboot oem format */ + if(memcmp(cmd, "format", 6) == 0){ + int ret; + + ret = fastboot_oem(cmd); + if (ret < 0) { + strcpy(response,"FAIL"); + } else { + strcpy(response,"OKAY"); + } + + } else if(memcmp(cmd, "recovery", 8) == 0){ + /* fastboot oem recovery */ + + sprintf(response,"OKAY"); + req_in->complete = compl_do_recovery; + + } else if(memcmp(cmd, "unlock", 6) == 0) { + /* fastboot oem unlock */ + + sprintf(response,"FAILnot implemented"); + printf("\nfastboot: oem unlock not implemented yet!!\n"); + } else { + printf("\nfastboot: do not understand oem %s\n", cmd); + strcpy(response,"FAILunknown command"); + } + fastboot_tx_write_str(response); +} + +static void cb_erase(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[65]; + + /* erase + * Erase a register flash partition + * Board has to set up flash partitions + */ + strsep(&cmd, ":"); + + if (interface.storage_medium == NAND) { + /* storage medium is NAND */ + + struct fastboot_ptentry *ptn; + + ptn = fastboot_flash_find_ptn(cmd); + if (ptn == 0) + { + sprintf(response, "FAILpartition does not exist"); + } + else + { + char start[32], length[32]; + int status = 0, repeat, repeat_max; + + printf("erasing '%s'\n", ptn->name); + + char *lock[5] = { "nand", "lock", NULL, NULL, NULL, }; + char *unlock[5] = { "nand", "unlock", NULL, NULL, NULL, }; + char *erase[5] = { "nand", "erase", NULL, NULL, NULL, }; + + lock[2] = unlock[2] = erase[2] = start; + lock[3] = unlock[3] = erase[3] = length; + + repeat_max = 1; + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK) + repeat_max = ptn->flags & FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK; + + sprintf (length, "0x%x", ptn->length); + for (repeat = 0; repeat < repeat_max; repeat++) + { + sprintf (start, "0x%x", ptn->start + (repeat * ptn->length)); + + if (status) + break; + } + + if (status) + { + sprintf(response, "FAILfailed to erase partition"); + } + else + { + printf("partition '%s' erased\n", ptn->name); + sprintf(response, "OKAY"); + } + } + } else if (interface.storage_medium == EMMC) { + /* storage medium is EMMC */ + + struct fastboot_ptentry *ptn; + + /* Save the MMC controller number */ +#if defined(CONFIG_4430PANDA) + /* panda board does not have eMMC on mmc1 */ + set_mmc_controller(0); +#else + /* blaze has emmc on mmc1 */ + set_mmc_controller(1); +#endif + + /* Find the partition and erase it */ + ptn = fastboot_flash_find_ptn(cmd); + + if (ptn == 0) { + sprintf(response, + "FAIL: partition doesn't exist"); + } else { + printf("Erasing '%s'\n", ptn->name); + if (do_mmc_erase(ptn->start, ptn->length)) { + printf("Erasing '%s' FAILED!\n", ptn->name); + sprintf(response, "FAIL: Erase partition"); + } else { + printf("Erasing '%s' DONE!\n", ptn->name); + sprintf(response, "OKAY"); + } + } + } + fastboot_tx_write_str(response); +} + +static void cb_mmcerase(struct usb_ep *ep, struct usb_request *req) +{ + struct fastboot_ptentry *ptn; + char *cmd = req->buf; + char response[65]; + int mmc_no; + + /* EMMC Erase + * Erase a register flash partition on MMC + * Board has to set up flash partitions + */ + strsep(&cmd, ":"); + + /* Save the MMC controller number */ + mmc_no = simple_strtoul(cmd, NULL, 10); + set_mmc_controller(mmc_no); + /* Find the partition and erase it */ + strsep(&cmd, " "); + ptn = fastboot_flash_find_ptn(cmd); + + if (ptn == 0) { + sprintf(response, + "FAIL: partition doesn't exist"); + } else { + /* Call MMC erase function here */ + /* This is not complete */ + + printf("Erasing '%s'\n", ptn->name); + if (do_mmc_erase(ptn->start, ptn->length)) { + sprintf(response, "FAIL: Erase partition"); + } else { + sprintf(response, "OKAY"); + } + } + fastboot_tx_write_str(response); +} + +static void cb_download(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[65]; + /* + * download something .. + * What happens to it depends on the next command after data + */ + + strsep(&cmd, ":"); + /* save the size */ + download_size = simple_strtoul(cmd, NULL, 16); + /* Reset the bytes count, now it is safe */ + download_bytes = 0; + /* Reset error */ + download_error = 0; + + printf("Starting download of %d bytes\n", + download_size); + + if (0 == download_size) { + /* bad user input */ + sprintf(response, "FAILdata invalid size"); + } else if (download_size > + interface.transfer_buffer_size) + { + /* set download_size to 0 + * because this is an error */ + download_size = 0; + sprintf(response, "FAILdata too large"); + } else { + /* The default case, the transfer fits + completely in the interface buffer */ + sprintf(response, "DATA%08x", download_size); + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(); + } + fastboot_tx_write_str(response); +} + +static void cb_boot(struct usb_ep *ep, struct usb_request *req) +{ + /* boot + boot what was downloaded + ** + ** +-----------------+ + ** | boot header | 1 page + ** +-----------------+ + ** | kernel | n pages + ** +-----------------+ + ** | ramdisk | m pages + ** +-----------------+ + ** | second stage | o pages + ** +-----------------+ + ** + Pagesize has default value of + CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE + */ + + if ((download_bytes) && + (CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE < download_bytes)) + { + + /* Skip the mkbootimage header */ + //boot_img_hdr*hdr = + // (boot_img_hdr *) + // &interface.transfer_buffer[CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE]; + + booti_args[1] = boot_addr_start; + sprintf(boot_addr_start, "0x%p", interface.transfer_buffer); + + req_in->complete = do_booti_on_complete; + /* Execution should jump to kernel so send the response + now and wait a bit. */ + fastboot_tx_write_str("OKAY"); + return; + } + fastboot_tx_write_str("FAILinvalid boot image"); +} + +static void cb_mmcwrite(struct usb_ep *ep, struct usb_request *req) +{ + struct fastboot_ptentry *ptn; + char *cmd = req->buf; + u32 mmc_no; + + /* mmcwrite + write what was downloaded on MMC*/ + /* Write to MMC whatever was downloaded */ + + if (!download_bytes) { + fastboot_tx_write_str("FAILno image downloaded"); + return; + } + + /* Save the MMC controller number */ + strsep(&cmd, ":"); + mmc_no = simple_strtoul(cmd, NULL, 10); + set_mmc_controller(mmc_no); + + /* Next is the partition name */ + strsep(&cmd, " "); + ptn = fastboot_flash_find_ptn(cmd); + + if (ptn == 0) { + fastboot_tx_write_str("FAILpartition does not exist"); + return; + } + + printf("writing to partition '%s'\n", ptn->name); + if (do_mmc_write(ptn->start, download_bytes, interface.transfer_buffer)) + fastboot_tx_write_str("FAIL: Write partition"); + else + fastboot_tx_write_str("OKAY"); +} + +static void cb_flash(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[64]; + + /* flash + Flash what was downloaded */ + strsep(&cmd, ":"); + + if (interface.storage_medium == NAND) { + /* storage medium is NAND */ + + if (download_bytes) + { + struct fastboot_ptentry *ptn; + + ptn = fastboot_flash_find_ptn(cmd); + if (ptn == 0) { + sprintf(response, "FAILpartition does not exist"); + } else if ((download_bytes > ptn->length) && + !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { + sprintf(response, "FAILimage too large for partition"); + /* TODO : Improve check for yaffs write */ + } else { + /* Check if this is not really a flash write + but rather a saveenv */ + if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { + /* Since the response can only be 64 bytes, + there is no point in having a large error message. */ + char err_string[32]; + if (saveenv_to_ptn(ptn, &err_string[0])) { + printf("savenv '%s' failed : %s\n", ptn->name, err_string); + sprintf(response, "FAIL%s", err_string); + } else { + printf("partition '%s' saveenv-ed\n", ptn->name); + sprintf(response, "OKAY"); + } + } else { + /* Normal case */ + if (write_to_ptn(ptn)) { + printf("flashing '%s' failed\n", ptn->name); + sprintf(response, "FAILfailed to flash partition"); + } else { + printf("partition '%s' flashed\n", ptn->name); + sprintf(response, "OKAY"); + } + } + } + } + else + { + sprintf(response, "FAILno image downloaded"); + } + } else if (interface.storage_medium == EMMC) { + /* storage medium is EMMC */ + + if (download_bytes) { + + struct fastboot_ptentry *ptn; + + /* Save the MMC controller number */ +#if defined(CONFIG_4430PANDA) + /* panda board does not have eMMC on mmc1 */ + set_mmc_controller(0); +#else + /* blaze has emmc on mmc1 */ + set_mmc_controller(1); +#endif + + /* Next is the partition name */ + ptn = fastboot_flash_find_ptn(cmd); + + if (ptn == 0) { + printf("Partition:'%s' does not exist\n", ptn->name); + sprintf(response, "FAILpartition does not exist"); + } else if ((download_bytes > ptn->length) && + !(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) { + printf("Image too large for the partition\n"); + sprintf(response, "FAILimage too large for partition"); + + } else if (ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV) { + + if (himport_r(&env_htab, (char *)interface.transfer_buffer, ENV_SIZE, '\0', 0)) { + gd->flags |= GD_FLG_ENV_READY; + saveenv(); + printf("saveenv to '%s' DONE!\n", ptn->name); + sprintf(response, "OKAY"); + } + } else { + /* Normal case */ + + printf("writing to partition '%s'\n", ptn->name); + + /* Check if we have sparse compressed image */ + if ( ((sparse_header_t *)interface.transfer_buffer)->magic + == SPARSE_HEADER_MAGIC) { + printf("fastboot: %s is in sparse format\n", ptn->name); + if (!do_unsparse(interface.transfer_buffer, + ptn->start, + ptn->length, + current_mmc_controller_no)) { + printf("Writing sparsed: '%s' DONE!\n", ptn->name); + } else { + printf("Writing sparsed '%s' FAILED!\n", ptn->name); + sprintf(response, "FAIL: Sparsed Write"); + } + } else { + /* Normal image: no sparse */ + printf("Writing '%s'\n", ptn->name); + if (do_mmc_write(ptn->start, download_bytes, interface.transfer_buffer)) { + printf("Writing '%s' FAILED!\n", ptn->name); + sprintf(response, "FAIL: Write partition"); + } else { + printf("Writing '%s' DONE!\n", ptn->name); + } + } + } /* Normal Case */ + + } else { + sprintf(response, "FAILno image downloaded"); + } + } /* EMMC */ + 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 = "oem ", + .cb = cb_oem, + }, { + .cmd = "erase:", + .cb = cb_erase, + }, { + .cmd = "mmcerase:", + .cb = cb_mmcerase, + }, { + .cmd = "download:", + .cb = cb_download, + }, { + .cmd = "boot", + .cb = cb_boot, + }, { + .cmd = "mmcwrite:", + .cb = cb_mmcwrite, + }, { + .cmd = "flash:", + .cb = cb_flash, + }, +}; + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + /* Use 65 instead of 64 + null gets dropped + strcpy's need the extra byte */ + char response[65]; + /* A command */ + const char *cmdbuf = req->buf; + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + int i; + + /* Generic failed response */ + sprintf(response, "FAIL"); + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (!memcmp(cmdbuf, cmd_dispatch_info[i].cmd, strlen(cmd_dispatch_info[i].cmd))) { + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) { + fastboot_tx_write_str("FAIL: unknown command"); + } else { + func_cb(ep, req); + } + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = 1; + + /* Initialize the board specific support */ + if (!fastboot_init(&interface)) + { + printf ("Fastboot entered...\n"); + + /* If we got this far, we are a success */ + ret = 0; + + /* On disconnect or error, polling returns non zero */ + while (1) + { + if (fastboot_poll()) + break; + } + } + + /* Reset the board specific support */ + fastboot_shutdown(); + + return ret; +} + +U_BOOT_CMD( + fastboot, 1, 1, do_fastboot, + "fastboot- use USB Fastboot protocol\n", + "" +); + +/* To support the Android-style naming of flash */ +#define MAX_PTN 16 + +static fastboot_ptentry ptable[MAX_PTN]; +static unsigned int pcount = 0; + +void fastboot_flash_reset_ptn(void) +{ + 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; + printf("ptn %d name='%s' start=%d len=%d\n", + n, ptn->name, ptn->start, ptn->length); + } +} + + +fastboot_ptentry *fastboot_flash_find_ptn(const char *name) +{ + unsigned int n; + + for(n = 0; n < pcount; n++) { + /* Make sure a substring is not accepted */ + if (strlen(name) == strlen(ptable[n].name)) + { + if(0 == strcmp(ptable[n].name, name)) + return ptable + n; + } + } + return 0; +} + +fastboot_ptentry *fastboot_flash_get_ptn(unsigned int n) +{ + if(n < pcount) { + return ptable + n; + } else { + return 0; + } +} + +unsigned int fastboot_flash_get_ptn_count(void) +{ + return pcount; +} diff --git a/drivers/usb/dwc3/fastboot_oem.c b/drivers/usb/dwc3/fastboot_oem.c new file mode 100644 index 0000000..4974b7d --- /dev/null +++ b/drivers/usb/dwc3/fastboot_oem.c @@ -0,0 +1,7 @@ +#include <common.h> +#include <usb/fastboot.h> + +int fastboot_oem(const char *cmd) +{ + return -1; +} diff --git a/drivers/usb/dwc3/misc.c b/drivers/usb/dwc3/misc.c new file mode 100644 index 0000000..346e42d --- /dev/null +++ b/drivers/usb/dwc3/misc.c @@ -0,0 +1,19 @@ +#include <common.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "misc.h" + +int snprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + if (i > size) + printf("*** Wrote too much bytes into the buffer ***\n"); + return i; +} diff --git a/drivers/usb/dwc3/sparse.c b/drivers/usb/dwc3/sparse.c new file mode 100644 index 0000000..387f8ce --- /dev/null +++ b/drivers/usb/dwc3/sparse.c @@ -0,0 +1,8 @@ +#include <common.h> +#include <usb/fastboot.h> + +u8 do_unsparse(unsigned char *source, u32 sector, u32 section_size, int slot_no) +{ + printf("%s() unsupported\n", __func__); + return 1; +} diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7d5b504..80e5e5b 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -42,6 +42,11 @@ COBJS-$(CONFIG_SPEARUDC) += spr_udc.o endif endif
+ifdef CONFIG_CMD_FASTBOOT +COBJS-$(CONFIG_CMD_FASTBOOT) += g_fastboot.o +COBJS-y += epautoconf.o +endif + COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/usb/gadget/g_fastboot.c b/drivers/usb/gadget/g_fastboot.c new file mode 100644 index 0000000..ea60cc2 --- /dev/null +++ b/drivers/usb/gadget/g_fastboot.c @@ -0,0 +1,608 @@ +#include <common.h> +#include <errno.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "misc.h" + +#ifdef USB_EP_DESC_USE_AUDIO +#error Can not deal with the two extra bytes right now +#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) +/* upgrade to HS? */ +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +#define DEVICE_STRING_PRODUCT_INDEX 1 +#define DEVICE_STRING_SERIAL_NUMBER_INDEX 2 +#define DEVICE_STRING_CONFIG_INDEX 3 +#define DEVICE_STRING_INTERFACE_INDEX 4 +#define DEVICE_STRING_MANUFACTURER_INDEX 5 +#define DEVICE_STRING_PROC_REVISION 6 +#define DEVICE_STRING_PROC_TYPE 7 +#define DEVICE_STRING_MAX_INDEX DEVICE_STRING_PROC_TYPE +#define DEVICE_STRING_LANGUAGE_ID 0x0409 /* English (United States) */ + +unsigned int dwc3_cable_connected = 1; + +static u8 ep0_buffer[4096] __aligned(16); +static u8 ep_out_buffer[EP_OUT_BUFFER_SIZE] __aligned(16); +static u8 ep_in_buffer[4096] __aligned(16); +static char *device_strings[DEVICE_STRING_MAX_INDEX + 1]; +static struct cmd_fastboot_interface *fastboot_interface; +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, +}; + +int fastboot_preboot(void) +{ + printf("\n%s() is always off\n", __func__); + return 0; +} + +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("%s() ep %s status %d\n", __func__, ep->name, status); +} + +static int fastboot_bind(struct usb_gadget *gadget) +{ + + g = gadget; + ep0_req = usb_ep_alloc_request(g->ep0, GFP_KERNEL); + 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 0; +err: + return -1; +} + +static void fastboot_unbind(struct usb_gadget *gadget) +{ + printf("%s\n", __func__); + usb_ep_free_request(g->ep0, ep0_req); + ep_in->driver_data = NULL; + ep_out->driver_data = NULL; +} + +/* This is the TI USB vendor id */ +#define DEVICE_VENDOR_ID 0x0451 +/* This is just made up.. */ +#define DEVICE_PRODUCT_ID 0xd022 +/* This is just made up.. */ +#define DEVICE_BCD 0x0100 + +struct usb_device_descriptor fb_descriptor = { + .bDescriptorType = USB_DT_DEVICE, +#ifdef CONFIG_USB_1_1_DEVICE + .bcdUSB = 0x110, +#else + .bcdUSB = 0x200, +#endif + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 0x40, + .idVendor = DEVICE_VENDOR_ID, + .idProduct = DEVICE_PRODUCT_ID, + .bcdDevice = DEVICE_BCD, + .iManufacturer = DEVICE_STRING_MANUFACTURER_INDEX, + .iProduct = DEVICE_STRING_PRODUCT_INDEX, + .iSerialNumber = DEVICE_STRING_SERIAL_NUMBER_INDEX, + .bNumConfigurations = 1, +}; + +struct fast_boot_config { + struct usb_config_descriptor c; + struct usb_interface_descriptor i; + struct usb_endpoint_descriptor e1; + struct usb_endpoint_descriptor e2; +} __attribute__((packed)); + +static struct fast_boot_config g_config; +static int fastboot_setup_get_descr(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_qualifier_descriptor d; + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 val; + int ret; + unsigned char bytes_remaining; + unsigned char bytes_total; + unsigned char bLength; + unsigned char string_index; + u8 this_inc; + + val = w_value >> 8; + + switch (val) { + case USB_DT_DEVICE: + + fb_descriptor.bLength = sizeof(fb_descriptor); + memcpy(ep0_buffer, &fb_descriptor, fb_descriptor.bLength); + 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 = w_length; + bytes_total = 0; + + g_config.c.bLength = sizeof(struct usb_config_descriptor); + g_config.c.bDescriptorType = USB_DT_CONFIG; + /* Set this to the total we want */ + g_config.c.wTotalLength = cpu_to_le16(sizeof(g_config)); + g_config.c.bNumInterfaces = 1; + g_config.c.bConfigurationValue = CONFIGURATION_NORMAL; + g_config.c.iConfiguration = DEVICE_STRING_CONFIG_INDEX; + g_config.c.bmAttributes = 0xc0; + g_config.c.bMaxPower = 0x32; + + this_inc = min(bytes_remaining, sizeof(struct usb_config_descriptor)); + bytes_remaining -= this_inc; + bytes_total += this_inc; + + g_config.i.bLength = sizeof(struct usb_interface_descriptor); + g_config.i.bDescriptorType = USB_DT_INTERFACE; + g_config.i.bInterfaceNumber = 0x00; + g_config.i.bAlternateSetting = 0x00; + g_config.i.bNumEndpoints = 0x02; + g_config.i.bInterfaceClass = FASTBOOT_INTERFACE_CLASS; + g_config.i.bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS; + g_config.i.bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL; + g_config.i.iInterface = DEVICE_STRING_INTERFACE_INDEX; + + this_inc = min(bytes_remaining, sizeof(struct usb_interface_descriptor)); + bytes_remaining -= this_inc; + bytes_total += this_inc; + + g_config.e1 = fs_ep_in; + + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + bytes_remaining -= this_inc; + bytes_total += this_inc; + + if (gadget->speed == USB_SPEED_HIGH) + g_config.e2 = hs_ep_out; + else + g_config.e2 = fs_ep_out; + + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + bytes_remaining -= this_inc; + bytes_total += this_inc; + + memcpy(ep0_buffer, &g_config, bytes_total); + + ep0_req->length = bytes_total; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + + case USB_DT_STRING: + + string_index = w_value & 0xff; + + if (string_index > DEVICE_STRING_MAX_INDEX) { + + printf("%s(%d)\n", __func__, __LINE__); + + } else if (string_index == 0) { + + /* Language ID */ + bLength = min(4, w_length); + + ep0_buffer[0] = 4; /* length */ + ep0_buffer[1] = USB_DT_STRING; /* descriptor = string */ + ep0_buffer[2] = DEVICE_STRING_LANGUAGE_ID & 0xff; + ep0_buffer[3] = DEVICE_STRING_LANGUAGE_ID >> 8; + + ep0_req->length = bLength; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + } else { + unsigned char s; + unsigned char sl = strlen(&device_strings[string_index][0]); + + /* Size of string in chars */ + /* Size of descriptor + 1 : header + 2 : type + 2*sl : string */ + unsigned char sLength = 2 + (2 * sl); + unsigned char bLength; + bLength = min(sLength, w_length); + + ep0_buffer[0] = sLength; /* length */ + ep0_buffer[1] = USB_DT_STRING; /* descriptor = string */ + + /* Copy device string to fifo, expand to simple unicode */ + for (s = 0; s < sl; s++) + { + ep0_buffer[2+ 2*s + 0] = device_strings[string_index][s]; + ep0_buffer[2+ 2*s + 1] = 0; + } + + ep0_req->length = bLength; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + } + break; + + case USB_DT_DEVICE_QUALIFIER: +#ifdef CONFIG_USB_1_1_DEVICE + /* This is an invalid request for usb 1.1, nak it */ + NAK_REQ(); +#else + d.bLength = min(w_length, sizeof(d)); + d.bDescriptorType = USB_DT_DEVICE_QUALIFIER; + d.bcdUSB = 0x200; + d.bDeviceClass = 0xff; + d.bDeviceSubClass = 0xff; + d.bDeviceProtocol = 0xff; + d.bMaxPacketSize0 = 0x40; + d.bNumConfigurations = 1; + d.bRESERVED = 0; + + memcpy(ep0_buffer, &d, d.bLength); + ep0_req->length = d.bLength; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); +#endif + break; + default: + ret = -EINVAL; + } + return ret; +} + +#define USB_STATUS_SELFPOWERED 0x01 +#define USB_REQ_TYPE_MASK 0x60 +#define USB_REQ_TYPE_STANDARD 0x00 +#define USB_REQ_DIRECTION_MASK 0x80 +#define USB_REQ_RECIPIENT_MASK 0x1f + +static int fastbot_setup_get_status(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (w_length == 0) { + ep0_req->length = 0; + return usb_ep_queue(gadget->ep0, ep0_req, 0); + } + ep0_buffer[0] = USB_STATUS_SELFPOWERED; + ep0_buffer[1] = 0; + ep0_req->length = 2; + return usb_ep_queue(gadget->ep0, ep0_req, 0); +} + +static int fastboot_setup_get_configuration(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; + + if (status) + printf("status: %d ep_in trans: %d\n", + status, + req->actual); + req->actual = 0; + if (fastboot_interface && fastboot_interface->tx_complete) + fastboot_interface->tx_complete(); +} + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req); + +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("%s(%d): %d\n", __func__, __LINE__, ret); + goto err; + } + + req_out = usb_ep_alloc_request(ep_out, 0); + if (!req_out) { + printf("%s(%d): %d\n", __func__, __LINE__, ret); + goto err; + } + + ret = usb_ep_enable(ep_in, &fs_ep_in); + if (ret) { + printf("%s(%d): %d\n", __func__, __LINE__, ret); + goto err; + } + req_in = usb_ep_alloc_request(ep_in, 0); + if (!req_in) { + printf("%s(%d): %d\n", __func__, __LINE__, ret); + goto err; + } + + req_out->complete = rx_handler_command; + req_out->buf = ep_out_buffer; + req_out->length = sizeof(ep_out_buffer); + + req_in->complete = fastboot_complete_in; + 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(struct usb_gadget *gadget, const struct usb_ctrlrequest *req) +{ + if ((req->bRequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD) { + if ((req->bRequestType & USB_REQ_DIRECTION_MASK) == 0) { + /* host-to-device */ + if ((req->bRequestType & USB_REQ_RECIPIENT_MASK) == 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; + }; + } + if (USB_RECIP_INTERFACE == (req->bRequestType & USB_REQ_RECIPIENT_MASK)) { + 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; + } + } + + if ((req->bRequestType & USB_REQ_RECIPIENT_MASK) == USB_RECIP_ENDPOINT) { + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + return usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + return -1; + } + } + } + /* device-to-host */ + if ((req->bRequestType & USB_REQ_RECIPIENT_MASK) == USB_RECIP_DEVICE) { + switch(req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + return fastboot_setup_get_descr(gadget, req); + break; + case USB_REQ_GET_STATUS: + /* should be handled by dwc3 anyway */ + return fastbot_setup_get_status(gadget, req); + + case USB_REQ_GET_CONFIGURATION: + return fastboot_setup_get_configuration(gadget, req); + break; + default: + return -1; + } + } else { + return -1; + } + } else + return -1; +} + +static void fastboot_disconnect(struct usb_gadget *gadget) +{ + fastboot_disable_ep(gadget); +} + +struct usb_gadget_driver fast_gadget = { + .bind = fastboot_bind, + .unbind = fastboot_unbind, + .setup = fastboot_setup, + .disconnect = fastboot_disconnect, +}; + +static int dwc3_is_up; +#define CFG_FASTBOOT_TRANSFER_BUFFER_SIZE (1024) +static unsigned char CFG_FASTBOOT_TRANSFER_BUFFER[CFG_FASTBOOT_TRANSFER_BUFFER_SIZE]; + +int fastboot_init(struct cmd_fastboot_interface *interface) +{ + int ret; + + device_strings[DEVICE_STRING_MANUFACTURER_INDEX] = "Manucacturer"; + device_strings[DEVICE_STRING_PRODUCT_INDEX] = "Product"; + device_strings[DEVICE_STRING_SERIAL_NUMBER_INDEX] = "00123"; + device_strings[DEVICE_STRING_CONFIG_INDEX] = "Android Fastboot"; + device_strings[DEVICE_STRING_INTERFACE_INDEX] = "Android Fastboot"; + device_strings[DEVICE_STRING_PROC_REVISION] = "ES1.0"; + device_strings[DEVICE_STRING_PROC_TYPE] = "Type"; + + fastboot_interface = interface; + fastboot_interface->product_name = device_strings[DEVICE_STRING_PRODUCT_INDEX]; + fastboot_interface->serial_no = device_strings[DEVICE_STRING_SERIAL_NUMBER_INDEX]; + fastboot_interface->proc_rev = device_strings[DEVICE_STRING_PROC_REVISION]; + fastboot_interface->proc_type = device_strings[DEVICE_STRING_PROC_TYPE]; + + fastboot_interface->storage_medium = NAND; + fastboot_interface->nand_block_size = 2048; + fastboot_interface->transfer_buffer = (unsigned char *) CFG_FASTBOOT_TRANSFER_BUFFER; + fastboot_interface->transfer_buffer_size = CFG_FASTBOOT_TRANSFER_BUFFER_SIZE; + + ret = usb_gadget_init_udc(); + if (ret) { + printf("%s() Probe failed.\n", __func__); + return 1; + } + dwc3_is_up = 1; + + ret = usb_gadget_probe_driver(&fast_gadget, fastboot_bind); + if (ret) { + printf("Add gadget failed\n"); + goto err; + } + + usb_gadget_handle_interrupts(); + return 0; + +err: + fastboot_shutdown(); + return 1; +} + +int fastboot_poll(void) +{ + usb_gadget_handle_interrupts(); + + if (dwc3_cable_connected) + return 0; + else + return 1; +} + +void fastboot_shutdown(void) +{ + if (!dwc3_is_up) + return; + dwc3_is_up = 0; + fastboot_interface = NULL; + usb_gadget_exit_udc(); +} + +void (*complete)(struct usb_ep *ep, struct usb_request *req); + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + int ret; + + memcpy(req_in->buf, buffer, buffer_size); + req_in->length = buffer_size; + ret = usb_ep_queue(ep_in, req_in, 0); + if (ret) + printf("%s() error %d on queue\n", __func__, ret); + return 0; +} + +int fastboot_getvar(const char *rx_buffer, char *tx_buffer) +{ + printf("%s() missing\n", __func__); + return 0; +} diff --git a/drivers/usb/gadget/misc.h b/drivers/usb/gadget/misc.h new file mode 100644 index 0000000..acd23c8 --- /dev/null +++ b/drivers/usb/gadget/misc.h @@ -0,0 +1,14 @@ +#ifndef dwc3_misc_h +#define dwc3_misc_h + +#include <linux/compiler.h> +#include <malloc.h> +#include <asm/io.h> + +#define GFP_KERNEL 0 + +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); + +#endif diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 49b7483..70cea71 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -559,6 +559,7 @@ enum usb_device_speed { USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ USB_SPEED_HIGH, /* usb 2.0 */ USB_SPEED_VARIABLE, /* wireless (usb 2.5) */ + USB_SPEED_SUPER, /* usb 3.0 */ };
enum usb_device_state { diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 275cb5f..4de6ca8 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -19,6 +19,7 @@ #define __LINUX_USB_GADGET_H
#include <linux/list.h> +#include <errno.h>
struct usb_ep;
@@ -854,4 +855,7 @@ extern void usb_ep_autoconfig_reset(struct usb_gadget *);
extern int usb_gadget_handle_interrupts(void);
+extern int usb_gadget_init_udc(void); +extern void usb_gadget_exit_udc(void); + #endif /* __LINUX_USB_GADGET_H */ diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h new file mode 100644 index 0000000..1d67d94 --- /dev/null +++ b/include/usb/fastboot.h @@ -0,0 +1,340 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * 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 + * system/core/mkbootimg/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> + +/* From fastboot client.. */ +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +#define FASTBOOT_VERSION "0.5" + +/* The fastboot client uses a value of 2048 for the + page size of it boot.img file format. + Reset this in your board config file as needed. */ +#ifndef CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE +#define CFG_FASTBOOT_MKBOOTIMAGE_PAGE_SIZE 2048 +#endif + +typedef enum { NAND, EMMC } storage_medium_type; + +struct cmd_fastboot_interface +{ + /* This function is called when a buffer has been + recieved from the client app. + The buffer is a supplied by the board layer and must be unmodified. + The buffer_size is how much data is passed in. + Returns 0 on success + Returns 1 on failure + + Set by cmd_fastboot */ + int (*rx_handler)(const unsigned char *buffer, + unsigned int buffer_size); + + /* + * Returns the number of bytes which are expected in the next RX. + * Returns 0 if unknown or the exact amount. This is required during + * the transfer of an image where size may be a multiple of + * MacPacketSize and the host is not sending a ZeroSizePacket to + * terminate the tranfer. Thank you google. + */ + unsigned int (*rx_bytes_expected)(void); + /* + * notify fastboot that a given tx-transfer completed. Used by the + * "boot" command which should be executed after the transfer of + * the "OKAY" message has been delived to the USB host. + */ + void (*tx_complete)(void); + /* This function is called when an exception has + occurred in the device code and the state + off fastboot needs to be reset + + Set by cmd_fastboot */ + void (*reset_handler)(void); + + /* A getvar string for the product name + It can have a maximum of 60 characters + + Set by board */ + char *product_name; + + /* A getvar string for the serial number + It can have a maximum of 60 characters + + Set by board */ + char *serial_no; + + /* A getvar string for the processor revision + It can have a maximum of 60 characters + + Set by board */ + char *proc_rev; + + /* A getvar string for the processor type + this can be GP, EMU or HS + It can have a maximum of 60 characters + + Set by board */ + char *proc_type; + + /* To determine the storage type NAND or EMMC */ + storage_medium_type storage_medium; + + /* Nand block size + Supports the write option WRITE_NEXT_GOOD_BLOCK + + Set by board */ + unsigned int nand_block_size; + + /* Transfer buffer, for handling flash updates + Should be multiple of the nand_block_size + Care should be take so it does not overrun bootloader memory + Controlled by the configure variable CFG_FASTBOOT_TRANSFER_BUFFER + + Set by board */ + unsigned char *transfer_buffer; + + /* How big is the transfer buffer + Controlled by the configure variable + CFG_FASTBOOT_TRANSFER_BUFFER_SIZE + + Set by board */ + unsigned int transfer_buffer_size; + +}; + +/* 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(n) (n & 0x0f) +#define FASTBOOT_PTENTRY_FLAGS_REPEAT_MASK 0x0000000F + +/* 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 hardware before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_HW_ECC 0x00000040 + +/* Sets the ECC to software before writing + HW and SW ECC should not both be set. */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_SW_ECC 0x00000080 + +/* Write the file with write.i */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_I 0x00000100 + +/* Write the file with write.yaffs */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_YAFFS 0x00000200 + +/* Write the file as a series of variable/value pairs + using the setenv and saveenv commands */ +#define FASTBOOT_PTENTRY_FLAGS_WRITE_ENV 0x00000400 + +/* Android bootimage file format */ +#define FASTBOOT_BOOT_MAGIC "ANDROID!" +#define FASTBOOT_BOOT_MAGIC_SIZE 8 +#define FASTBOOT_BOOT_NAME_SIZE 16 +#define FASTBOOT_BOOT_ARGS_SIZE 512 + +struct fastboot_boot_img_hdr { + unsigned char magic[FASTBOOT_BOOT_MAGIC_SIZE]; + + unsigned kernel_size; /* size in bytes */ + unsigned kernel_addr; /* physical load addr */ + + unsigned ramdisk_size; /* size in bytes */ + unsigned ramdisk_addr; /* physical load addr */ + + unsigned second_size; /* size in bytes */ + unsigned second_addr; /* physical load addr */ + + unsigned tags_addr; /* physical addr for kernel tags */ + unsigned page_size; /* flash page size we assume */ + unsigned unused[2]; /* future expansion: should be 0 */ + + unsigned char name[FASTBOOT_BOOT_NAME_SIZE]; /* asciiz product name */ + + unsigned char cmdline[FASTBOOT_BOOT_ARGS_SIZE]; + + unsigned id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +#if (CONFIG_CMD_FASTBOOT) +/* A board specific test if u-boot should go into the fastboot command + ahead of the bootcmd + Returns 0 to continue with normal u-boot flow + Returns 1 to execute fastboot */ +extern int fastboot_preboot(void); + +/* Initizes the board specific fastboot + Returns 0 on success + Returns 1 on failure */ +extern int fastboot_init(struct cmd_fastboot_interface *interface); + +/* Cleans up the board specific fastboot */ +extern void fastboot_shutdown(void); + +/* Handles board specific usb protocol exchanges + Returns 0 on success + Returns 1 on disconnects, break out of loop + Returns -1 on failure, unhandled usb requests and other error conditions */ +extern int fastboot_poll(void); + +/* Is this high speed (2.0) or full speed (1.1) ? + Returns 0 on full speed + Returns 1 on high speed */ +extern int fastboot_is_highspeed(void); + +/* Return the size of the fifo */ +extern int fastboot_fifo_size(void); + +/* A board specific variable handler. + The size of the buffers is governed by the fastboot spec. + rx_buffer is at most 57 bytes + tx_buffer is at most 60 bytes + Returns 0 on success + Returns 1 on failure */ +extern int fastboot_getvar(const char *rx_buffer, char *tx_buffer); +/* board-specific fastboot commands */ +extern int fastboot_oem(const char *command); + +/* 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_flash_init(void); +extern int fastboot_flash_erase(fastboot_ptentry *ptn); +extern int fastboot_flash_read_ext(fastboot_ptentry *ptn, + unsigned extra_per_page, unsigned offset, + void *data, unsigned bytes); +#define fastboot_flash_read(ptn, offset, data, bytes) \ + flash_read_ext(ptn, 0, offset, data, bytes) +extern int fastboot_flash_write(fastboot_ptentry *ptn, unsigned extra_per_page, + const void *data, unsigned bytes); + + +#else + +static inline int fastboot_preboot(void) { return 0; } +static inline int fastboot_init(struct cmd_fastboot_interface *interface) { return 1; } +static inline void fastboot_shutdown(void) { } +static inline int fastboot_poll(void) { return 1; } +static inline int fastboot_is_highspeed(void) { return 0; } +static inline int fastboot_fifo_size(void) { return 0; } +static inline int fastboot_tx_write(const char *buffer, unsigned int buffer_size) { return 1; } +static inline int fastboot_getvar(const char *rx_buffer, char *tx_buffer) { return 1; } +static inline int fastboot_oem(const char *command); +static inline void fastboot_flash_add_ptn(fastboot_ptentry *ptn) { } +static inline fastboot_ptentry *fastboot_flash_find_ptn(const char *name) { return NULL; } +static inline fastboot_ptentry *fastboot_flash_get_ptn(unsigned n) { return NULL; } +static inline unsigned int fastboot_flash_get_ptn_count(void) { return 0; } +static inline void fastboot_flash_dump_ptn(void) { } +static inline int fastboot_flash_init(void) { } +static inline int fastboot_flash_erase(fastboot_ptentry *ptn) { return 1; } +static inline int fastboot_flash_read_ext(fastboot_ptentry *ptn, + unsigned extra_per_page, unsigned offset, + void *data, unsigned bytes) { return 0; } +static inline int fastboot_flash_write(fastboot_ptentry *ptn, unsigned extra_per_page, + const void *data, unsigned bytes) { return 0; } + +#endif /* CONFIG_FASTBOOT */ + +extern struct usb_ep *ep_in; +extern struct usb_request *req_in; +extern struct usb_ep *ep_out; +extern struct usb_request *req_out; + +#define EP_OUT_BUFFER_SIZE 4096 + +#endif /* FASTBOOT_H */ diff --git a/include/usb/sparse.h b/include/usb/sparse.h new file mode 100644 index 0000000..6249e68 --- /dev/null +++ b/include/usb/sparse.h @@ -0,0 +1,33 @@ +/* + * (C) Copyright 2011 + * Texas Instruments, <www.ti.com> + * Author: Vikram Pandita vikram.pandita@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 + */ + +#ifndef __SPARSE_H +#define __SPARSE_H + +/* This file is taken as such from Android repo */ +#include <usb/sparse_format.h> + +u8 +do_unsparse(unsigned char *source, + u32 sector, + u32 section_size, + int slot_no); +#endif /* __SPARSE_H */ diff --git a/include/usb/sparse_format.h b/include/usb/sparse_format.h new file mode 100644 index 0000000..ba13214 --- /dev/null +++ b/include/usb/sparse_format.h @@ -0,0 +1,47 @@ +/* + * 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 + +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 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. + */ +