Hi,
The patch which should come as a reply to this email contains a fastboot gadget for u-boot. I don't claim that the code has been tested or anything. I just want to post what I have now and get some feedback on it.
The code uses the "newer" gadget API which is used by the rndis gadget for instance. The only udc implementing it (as far as I know) is the at91 udc sitting in cdc-at91 branch [0]. The other udcs in tree (musb for intance) is using the old interface which is something linux kernel 2.4 time frame. Since nobody plans to develop a udc for both frameworks I went for the newer framework. The gadget uses the same callbacks as the the at91 driver. It additionally requires usb_gadget_init_udc() and usb_gadget_exit_udc() to be exported by the driver. This is the ->probe() and ->exit() function from the kernel. The at91 driver does this in its board-setup code which is something I don't like.
The fastboot protocol is described in [1]. Let me give a short summary based on the code I post (which is more or less clean up of [3]). It implements the protocol with a few extensions: - ep0 communication is only used for initial enumeration - one EP-IN BULK and one EP-OUT BULK is required for the communication between host and device. - the device waits until a command is sent by the host. This command is acknowledged (either as OKAY or FAIL). - one command is "download:%08x". Here the Host specifies that it wants to send binary data. The gadget does _not_ know the purpose of the data, it has to suck it up. Once the transfer is complete the host sends another command like "flash:%s" which specifies where to write the earlier received data. - the "boot" command is used to boot the image. The code right now uses the do_bootm() function. However the expected format is different from the uImage format: - it contains phys addr + size of kernel and ram disk. - it contains phys addr + size of "second". I don't know its purpose. Its user [2] is setting this to zero, the gadget code does not use it. - There is tags field. I assume that this are atags but it is also unused. - it contains a command line and a "name" of the image - after the "Android" header, the image follows. - the "flash" command checks in case of the MMC media for the "sparse" header. It is implemented on per-board. Two types are currently implemented: - CHUNK_TYPE_DONT_CARE: don't not write this part to media - CHUNK_TYPE_RAW: write this part 1:1 to media A third type is defined only: CHUNK_TYPE_FILL. It looks like RLE i.e. avoid sending blocks of 0x00 over the wire but write it to the media. - There is support for "oem" commands. The "format" sub command is passed to the board and creates a partition table on MMC. The second sub command is "recovery" which resets the board and starts linux from a recovery partition. - commands "mmcerase" and "mmcwrite". Those seem to serve same purpose as "erase" and "flash" if the media is pointing to mmc except that the "sparse" case is not considered. - partitions (name, offset) are loaded from board coded. This looks like EFI in [3].
This should list everything fastboot specific unless I forgot something.
One think that I don't like is the fact after "download:" we have have suck up the complete data stream. An advantage would be if we could write the data directly to flash/mmc. So we could have two buffers or so and will USB and MMC/NAND one one buffer is complete. Another thing is the custom sparse format. I would prefer to pipe the data via lzo instead. This not only shrinks the amount of 0x00 blocks but also compresses the data image which in case of MMC is mostly uncompressed. The boot image format that is used by Andorid is different from uImage but I don't see any advantages. AFAIK the uImage format is capable of including kernel + ramdisk into one image.
I've been looking at DFU as an alternative. I think its main problem is the fact that it is ep0 based which limits the USB packet size to 64bytes on HighSpeed which makes it slower than necessary.
Given the amount of features we require and the complexity what about implementing the whole gadget as a user space application with a minimal root file system? We could have graphical output during the update process instead some printf on serial line. Only an idea. Linux boots actually quite fast so it shouldn't be an argument. This would also make it easy to use ubiformat for nand upates in order not to lose the erase counters. The userland approach would use same linux udc driver so we wouldn't have two code basis for same driver which might grow apart.
So, any comments on that? Suggestions? Anything? I would prefer a solution which is accepted by both projects Das U-Boot and Android in terms of the protocol and approach (u-boot implementation vs userland).
[0] git://git.denx.de/u-boot-usb.git [1] http://android.git.kernel.org/?p=platform/bootloader/legacy.git%3Ba=blob_pla... [2] http://android.git.kernel.org/?p=platform/system/core.git%3Ba=tree%3Bf=fastb... [3] git://git.omapzoom.org/repo/u-boot.git
Sebastian
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. + */ +
Hi Sebastian,
2011/8/10 Sebastian Andrzej Siewior bigeasy@linutronix.de:
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
I am going to look at it in detail in a couple of days. I will come back to this soon.
Kind regards,
Remy
On 08/13/2011 12:22 PM, Remy Bohmer wrote:
Hi Sebastian,
Hi Remy,
2011/8/10 Sebastian Andrzej Siewiorbigeasy@linutronix.de:
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 Siewiorbigeasy@linutronix.de
I am going to look at it in detail in a couple of days. I will come back to this soon.
any news so far? :)
Kind regards,
Remy
Sebastian
Hi,
2011/8/10 Sebastian Andrzej Siewior bigeasy@linutronix.de:
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.
Looking at this code, I can already say that the way it is written is not suitable for mainline acceptance at all. If you at least would run the Linux checkpatch over it, it would result in an almost endless list of style warnings and errors ;-) But, since it RFC only, let's look into the design parts and not be too picky about the style (yet)
I may be missing something, but it appears to me that this particular patch for the fastboot framework makes several assumptions about: * if MMC is available (max 2 controllers, namely 0 or 1). Let' s say my hardware doe not support it, and I do not want to enable it due to codesize reasons... * if NAND is available and it is either YAFFS or raw, what if I have UBI? * Or let's drop the MMC and NAND, and assume we only have a harddrive or USB storage on our board ;-) * Hardcoded NAND block sizes which are evil. * storage_medium is hardcoded set to NAND, never to EMMC, so what is the EMMC code doing here?
Furthermore: * Board code is mixed up with generic code. * drivers/usb/dwc3/fastboot_oem.c, drivers/usb/dwc3/misc.c, drivers/usb/dwc3/sparse.c contain code that has _nothing_ to do with USB. * generic files (for example like include/linux/usb/ch9.h) are adapted with changes not even used by the code. * Mix up of different licenses: U-boot is still GPLv2, while this patch contains Apache based licenses (Not sure if it conflicts with GPL, but it seems strange) * it makes a lot of assumptions how the hardware looks like. * It is not properly separated across different subsystems. There need to be a proper separation between drivers, library code, U-boot core code and board files. Everything that is board specific should go in board files. NAND-availability or partitioning is board specific, MMC as well, the only assumption you can make about hardware that should always be available is RAM, you can only not assume how much there is, and which address area is free. NAND and MMC usage should be completely configurable per board. * There need to be proper Documentation in the doc directory. * It would be great to have at least a demo tool in /tools or a commonly used OSS package that provides the tools, such that the mechanism can be tested as well.
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
I have not found a ' generic' mmc framework, only a 'fastboot' dedicated mmc framework.
So, I have no objections to the protocol or the mechanism itself, as long as it is properly implemented.
Kind regards,
Remy
On 08/24/2011 09:34 PM, Remy Bohmer wrote:
Hi,
Hi,
thanks for your time.
2011/8/10 Sebastian Andrzej Siewiorbigeasy@linutronix.de:
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.
Looking at this code, I can already say that the way it is written is not suitable for mainline acceptance at all. If you at least would run the Linux checkpatch over it, it would result in an almost endless list of style warnings and errors ;-)
Sure. I just wanted to avoid spending more time on it there are some high level mistakes.
I may be missing something, but it appears to me that this particular patch for the fastboot framework makes several assumptions about:
- if MMC is available (max 2 controllers, namely 0 or 1). Let' s say
my hardware doe not support it, and I do not want to enable it due to codesize reasons...
Why only two (0 / 1)? "mmcwrite:X Y" where X is the number of the controller and Y the partition name do support multiple controllers. The erase command does not it is "erase:Y" where Y is the partition name. If you want a ifdef around mmc code so you can disable it, this can be arranged :)
- if NAND is available and it is either YAFFS or raw, what if I have UBI?
NAND formats are not part of the fastboot protocol. For UBI you could use the raw format. If you want to rescue the erase counters than a per-partition flag has to be added (like it is the case for write.i and write.yaffs).
- Or let's drop the MMC and NAND, and assume we only have a harddrive
or USB storage on our board ;-)
My understanding is that the MMC / NAND partition table is comming from EFI. So if you your requirement is usb disk media than you have to set interface.storage_medium to USB_STORAGE (or whatever we define it) and use the proper write command then.
- Hardcoded NAND block sizes which are evil.
This should be moved to a per-board configuration.
- storage_medium is hardcoded set to NAND, never to EMMC, so what is
the EMMC code doing here?
most of fastboot_init() should be handled per-board. This includes the name of the product, storage media & type and storage area for the download command.
Furthermore:
- Board code is mixed up with generic code.
Yes, this has to be split up.
- drivers/usb/dwc3/fastboot_oem.c,
is a hook for custom / vendor extensions to the fastboot protocol. It will be most likely moved to board specific code.
drivers/usb/dwc3/misc.c,
Contains currently a hacky snprintf() which is not used by the fastboot code and should be part of the patch. sorry.
drivers/usb/dwc3/sparse.c
this contained custom unsparse code. It seemed to be used as some kind of RLE compression which did not make much sense and I left it out.
contain code that has _nothing_ to do with USB.
Yes, it will be moved to proper places.
- generic files (for example like include/linux/usb/ch9.h) are adapted
with changes not even used by the code.
I have also a USB3 gadget in the pipe. I removed it prior the post but forgot about some changes.
- Mix up of different licenses: U-boot is still GPLv2, while this
patch contains Apache based licenses (Not sure if it conflicts with GPL, but it seems strange)
Fastboot are based on BSD license. The sparse header file is the only part under the Apache v2 license. Since it is not compatible with v2 I leave the sparse code completely out.
- it makes a lot of assumptions how the hardware looks like.
This has to be defined somewhere and will be moved to board specific implementation.
- It is not properly separated across different subsystems. There need
to be a proper separation between drivers, library code, U-boot core code and board files. Everything that is board specific should go in board files.
Okay, will split
NAND-availability or partitioning is board specific, MMC as well, the only assumption you can make about hardware that should always be available is RAM, you can only not assume how much there is, and which address area is free.
The protocol requires that the complete data is loaded into ram before anything is doen with it. So what options do I have to achieve this? The old code hardcoded size to 512/256 - 16 MiB on panda/blaze board. The buffer started physram + 16MiB. Should I continue this approach?
NAND and MMC usage should be completely configurable per board.
This will be done.
- There need to be proper Documentation in the doc directory.
I try to add something.
- It would be great to have at least a demo tool in /tools or a
commonly used OSS package that provides the tools, such that the mechanism can be tested as well.
Okay. There is a so called fastboot tool in the wide android repository. I could copy it as-it or add a link to the git repository. Any preferences? That folder can be compiled on windows, linux and mac osx. The linux part requires libusb and is easy to use. The Windows edition requires a bunch of Android's SDK to get access to the USB device.
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
I have not found a ' generic' mmc framework, only a 'fastboot' dedicated mmc framework.
How so? What I meant is that it depends on CONFIG_GENERIC_MMC and uses do_mmcops commands for its needs. Is this appropriate or is another interface preferred?
So, I have no objections to the protocol or the mechanism itself, as long as it is properly implemented.
I see. So I guess I have to stick with it.
Thanks for the review.
Kind regards,
Remy
Sebastian