Merge branch 'master' of git://git.denx.de/u-boot-usb
authorTom Rini <trini@ti.com>
Thu, 22 May 2014 16:56:15 +0000 (12:56 -0400)
committerTom Rini <trini@ti.com>
Thu, 22 May 2014 16:56:15 +0000 (12:56 -0400)
24 files changed:
README
board/samsung/common/Makefile
board/samsung/common/ums.c [deleted file]
common/Makefile
common/cmd_bootm.c
common/cmd_fastboot.c [new file with mode: 0644]
common/cmd_usb_mass_storage.c
common/image-android.c [new file with mode: 0644]
common/image.c
doc/README.android-fastboot [new file with mode: 0644]
doc/README.android-fastboot-protocol [new file with mode: 0644]
drivers/dfu/dfu_nand.c
drivers/usb/gadget/Makefile
drivers/usb/gadget/ci_udc.c
drivers/usb/gadget/ci_udc.h
drivers/usb/gadget/f_dfu.c
drivers/usb/gadget/f_fastboot.c [new file with mode: 0644]
drivers/usb/gadget/storage_common.c
drivers/usb/musb-new/musb_gadget_ep0.c
include/android_image.h [new file with mode: 0644]
include/configs/omap3_beagle.h
include/dfu.h
include/image.h
include/usb_mass_storage.h

diff --git a/README b/README
index 5f89552..d8fcd95 100644 (file)
--- a/README
+++ b/README
@@ -1579,6 +1579,28 @@ The following options need to be configured:
                entering dfuMANIFEST state. Host waits this timeout, before
                sending again an USB request to the device.
 
+- USB Device Android Fastboot support:
+               CONFIG_CMD_FASTBOOT
+               This enables the command "fastboot" which enables the Android
+               fastboot mode for the platform's USB device. Fastboot is a USB
+               protocol for downloading images, flashing and device control
+               used on Android devices.
+               See doc/README.android-fastboot for more information.
+
+               CONFIG_ANDROID_BOOT_IMAGE
+               This enables support for booting images which use the Android
+               image format header.
+
+               CONFIG_USB_FASTBOOT_BUF_ADDR
+               The fastboot protocol requires a large memory buffer for
+               downloads. Define this to the starting RAM address to use for
+               downloaded images.
+
+               CONFIG_USB_FASTBOOT_BUF_SIZE
+               The fastboot protocol requires a large memory buffer for
+               downloads. This buffer should be as large as possible for a
+               platform. Define this to the size available RAM for fastboot.
+
 - Journaling Flash filesystem support:
                CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE,
                CONFIG_JFFS2_NAND_DEV
index 7d2bb8c..41d0cc3 100644 (file)
@@ -7,7 +7,6 @@
 
 obj-$(CONFIG_SOFT_I2C_MULTI_BUS) += multi_i2c.o
 obj-$(CONFIG_THOR_FUNCTION) += thor.o
-obj-$(CONFIG_CMD_USB_MASS_STORAGE) += ums.o
 obj-$(CONFIG_MISC_COMMON) += misc.o
 
 ifndef CONFIG_SPL_BUILD
diff --git a/board/samsung/common/ums.c b/board/samsung/common/ums.c
deleted file mode 100644 (file)
index cebabe9..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2013 Samsung Electronics
- * Lukasz Majewski <l.majewski@samsung.com>
- *
- * SPDX-License-Identifier:    GPL-2.0+
- */
-
-#include <common.h>
-#include <usb_mass_storage.h>
-#include <part.h>
-
-static int ums_read_sector(struct ums *ums_dev,
-                          ulong start, lbaint_t blkcnt, void *buf)
-{
-       block_dev_desc_t *block_dev = &ums_dev->mmc->block_dev;
-       lbaint_t blkstart = start + ums_dev->start_sector;
-       int dev_num = block_dev->dev;
-
-       return block_dev->block_read(dev_num, blkstart, blkcnt, buf);
-}
-
-static int ums_write_sector(struct ums *ums_dev,
-                           ulong start, lbaint_t blkcnt, const void *buf)
-{
-       block_dev_desc_t *block_dev = &ums_dev->mmc->block_dev;
-       lbaint_t blkstart = start + ums_dev->start_sector;
-       int dev_num = block_dev->dev;
-
-       return block_dev->block_write(dev_num, blkstart, blkcnt, buf);
-}
-
-static struct ums ums_dev = {
-       .read_sector = ums_read_sector,
-       .write_sector = ums_write_sector,
-       .name = "UMS disk",
-};
-
-static struct ums *ums_disk_init(struct mmc *mmc)
-{
-       uint64_t mmc_end_sector = mmc->capacity / SECTOR_SIZE;
-       uint64_t ums_end_sector = UMS_NUM_SECTORS + UMS_START_SECTOR;
-
-       if (!mmc_end_sector) {
-               error("MMC capacity is not valid");
-               return NULL;
-       }
-
-       ums_dev.mmc = mmc;
-
-       if (ums_end_sector <= mmc_end_sector) {
-               ums_dev.start_sector = UMS_START_SECTOR;
-               if (UMS_NUM_SECTORS)
-                       ums_dev.num_sectors = UMS_NUM_SECTORS;
-               else
-                       ums_dev.num_sectors = mmc_end_sector - UMS_START_SECTOR;
-       } else {
-               ums_dev.num_sectors = mmc_end_sector;
-               puts("UMS: defined bad disk parameters. Using default.\n");
-       }
-
-       printf("UMS: disk start sector: %#x, count: %#x\n",
-              ums_dev.start_sector, ums_dev.num_sectors);
-
-       return &ums_dev;
-}
-
-struct ums *ums_init(unsigned int dev_num)
-{
-       struct mmc *mmc = find_mmc_device(dev_num);
-
-       if (!mmc || mmc_init(mmc))
-               return NULL;
-       return ums_disk_init(mmc);
-}
index 7c853ae..219cb51 100644 (file)
@@ -168,6 +168,8 @@ obj-y += cmd_usb.o
 obj-y += usb.o usb_hub.o
 obj-$(CONFIG_USB_STORAGE) += usb_storage.o
 endif
+obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o
+
 obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o
 obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o
 obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o
@@ -237,6 +239,7 @@ obj-y += console.o
 obj-$(CONFIG_CROS_EC) += cros_ec.o
 obj-y += dlmalloc.o
 obj-y += image.o
+obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
 obj-$(CONFIG_OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_FIT) += image-fit.o
 obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o
index e683af3..34b4b58 100644 (file)
@@ -222,6 +222,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
                         char * const argv[])
 {
        const void *os_hdr;
+       bool ep_found = false;
 
        /* get kernel image header, start address and length */
        os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,
@@ -273,6 +274,18 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
                        return 1;
                }
                break;
+#endif
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+       case IMAGE_FORMAT_ANDROID:
+               images.os.type = IH_TYPE_KERNEL;
+               images.os.comp = IH_COMP_NONE;
+               images.os.os = IH_OS_LINUX;
+               images.ep = images.os.load;
+               ep_found = true;
+
+               images.os.end = android_image_get_end(os_hdr);
+               images.os.load = android_image_get_kload(os_hdr);
+               break;
 #endif
        default:
                puts("ERROR: unknown image format type!\n");
@@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
                        return 1;
                }
 #endif
-       } else {
+       } else if (!ep_found) {
                puts("Could not find kernel entry point!\n");
                return 1;
        }
@@ -1001,6 +1014,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
                images->fit_uname_cfg = fit_uname_config;
                images->fit_noffset_os = os_noffset;
                break;
+#endif
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+       case IMAGE_FORMAT_ANDROID:
+               printf("## Booting Android Image at 0x%08lx ...\n", img_addr);
+               if (android_image_get_kernel((void *)img_addr, images->verify,
+                                            os_data, os_len))
+                       return NULL;
+               break;
 #endif
        default:
                printf("Wrong Image Format for %s command\n", cmdtp->name);
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c
new file mode 100644 (file)
index 0000000..83fa7bd
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 - 2009 Windriver, <www.windriver.com>
+ * Author: Tom Rix <Tom.Rix@windriver.com>
+ *
+ * (C) Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <command.h>
+#include <g_dnl.h>
+
+static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       int ret;
+
+       ret = g_dnl_register("usb_dnl_fastboot");
+       if (ret)
+               return ret;
+
+       while (1) {
+               if (ctrlc())
+                       break;
+               usb_gadget_handle_interrupts();
+       }
+
+       g_dnl_unregister();
+       return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+       fastboot,       1,      1,      do_fastboot,
+       "fastboot - enter USB Fastboot protocol",
+       ""
+);
index d8d9efd..2c879ea 100644 (file)
 #include <common.h>
 #include <command.h>
 #include <g_dnl.h>
+#include <part.h>
 #include <usb.h>
 #include <usb_mass_storage.h>
 
+static int ums_read_sector(struct ums *ums_dev,
+                          ulong start, lbaint_t blkcnt, void *buf)
+{
+       block_dev_desc_t *block_dev = ums_dev->block_dev;
+       lbaint_t blkstart = start + ums_dev->start_sector;
+       int dev_num = block_dev->dev;
+
+       return block_dev->block_read(dev_num, blkstart, blkcnt, buf);
+}
+
+static int ums_write_sector(struct ums *ums_dev,
+                           ulong start, lbaint_t blkcnt, const void *buf)
+{
+       block_dev_desc_t *block_dev = ums_dev->block_dev;
+       lbaint_t blkstart = start + ums_dev->start_sector;
+       int dev_num = block_dev->dev;
+
+       return block_dev->block_write(dev_num, blkstart, blkcnt, buf);
+}
+
+static struct ums ums_dev = {
+       .read_sector = ums_read_sector,
+       .write_sector = ums_write_sector,
+       .name = "UMS disk",
+};
+
+struct ums *ums_init(const char *devtype, const char *devnum)
+{
+       block_dev_desc_t *block_dev;
+       int ret;
+
+       ret = get_device(devtype, devnum, &block_dev);
+       if (ret < 0)
+               return NULL;
+
+       /* f_mass_storage.c assumes SECTOR_SIZE sectors */
+       if (block_dev->blksz != SECTOR_SIZE)
+               return NULL;
+
+       ums_dev.block_dev = block_dev;
+       ums_dev.start_sector = 0;
+       ums_dev.num_sectors = block_dev->lba;
+
+       printf("UMS: disk start sector: %#x, count: %#x\n",
+              ums_dev.start_sector, ums_dev.num_sectors);
+
+       return &ums_dev;
+}
+
 int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag,
                               int argc, char * const argv[])
 {
+       const char *usb_controller;
+       const char *devtype;
+       const char *devnum;
+       struct ums *ums;
+       unsigned int controller_index;
+       int rc;
+       int cable_ready_timeout __maybe_unused;
+
        if (argc < 3)
                return CMD_RET_USAGE;
 
-       const char *usb_controller = argv[1];
-       const char *mmc_devstring  = argv[2];
-
-       unsigned int dev_num = simple_strtoul(mmc_devstring, NULL, 0);
+       usb_controller = argv[1];
+       if (argc >= 4) {
+               devtype = argv[2];
+               devnum  = argv[3];
+       } else {
+               devtype = "mmc";
+               devnum  = argv[2];
+       }
 
-       struct ums *ums = ums_init(dev_num);
+       ums = ums_init(devtype, devnum);
        if (!ums)
                return CMD_RET_FAILURE;
 
-       unsigned int controller_index = (unsigned int)(simple_strtoul(
-                                       usb_controller, NULL, 0));
+       controller_index = (unsigned int)(simple_strtoul(
+                               usb_controller, NULL, 0));
        if (board_usb_init(controller_index, USB_INIT_DEVICE)) {
                error("Couldn't init USB controller.");
                return CMD_RET_FAILURE;
        }
 
-       int rc = fsg_init(ums);
+       rc = fsg_init(ums);
        if (rc) {
                error("fsg_init failed");
                return CMD_RET_FAILURE;
        }
 
-       g_dnl_register("usb_dnl_ums");
+       rc = g_dnl_register("usb_dnl_ums");
+       if (rc) {
+               error("g_dnl_register failed");
+               return CMD_RET_FAILURE;
+       }
 
        /* Timeout unit: seconds */
-       int cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
+       cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
 
        if (!g_dnl_board_usb_cable_connected()) {
                /*
@@ -91,7 +157,8 @@ exit:
        return CMD_RET_SUCCESS;
 }
 
-U_BOOT_CMD(ums, CONFIG_SYS_MAXARGS, 1, do_usb_mass_storage,
+U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage,
        "Use the UMS [User Mass Storage]",
-       "ums <USB_controller> <mmc_dev>  e.g. ums 0 0"
+       "ums <USB_controller> [<devtype>] <devnum>  e.g. ums 0 mmc 0\n"
+       "    devtype defaults to mmc"
 );
diff --git a/common/image-android.c b/common/image-android.c
new file mode 100644 (file)
index 0000000..6ded7e2
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <image.h>
+#include <android_image.h>
+
+static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1];
+
+int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
+                            ulong *os_data, ulong *os_len)
+{
+       /*
+        * Not all Android tools use the id field for signing the image with
+        * sha1 (or anything) so we don't check it. It is not obvious that the
+        * string is null terminated so we take care of this.
+        */
+       strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE);
+       andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0';
+       if (strlen(andr_tmp_str))
+               printf("Android's image name: %s\n", andr_tmp_str);
+
+       printf("Kernel load addr 0x%08x size %u KiB\n",
+              hdr->kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024));
+       strncpy(andr_tmp_str, hdr->cmdline, ANDR_BOOT_ARGS_SIZE);
+       andr_tmp_str[ANDR_BOOT_ARGS_SIZE] = '\0';
+       if (strlen(andr_tmp_str)) {
+               printf("Kernel command line: %s\n", andr_tmp_str);
+               setenv("bootargs", andr_tmp_str);
+       }
+       if (hdr->ramdisk_size)
+               printf("RAM disk load addr 0x%08x size %u KiB\n",
+                      hdr->ramdisk_addr,
+                      DIV_ROUND_UP(hdr->ramdisk_size, 1024));
+
+       if (os_data) {
+               *os_data = (ulong)hdr;
+               *os_data += hdr->page_size;
+       }
+       if (os_len)
+               *os_len = hdr->kernel_size;
+       return 0;
+}
+
+int android_image_check_header(const struct andr_img_hdr *hdr)
+{
+       return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE);
+}
+
+ulong android_image_get_end(const struct andr_img_hdr *hdr)
+{
+       u32 size = 0;
+       /*
+        * The header takes a full page, the remaining components are aligned
+        * on page boundary
+        */
+       size += hdr->page_size;
+       size += ALIGN(hdr->kernel_size, hdr->page_size);
+       size += ALIGN(hdr->ramdisk_size, hdr->page_size);
+       size += ALIGN(hdr->second_size, hdr->page_size);
+
+       return size;
+}
+
+ulong android_image_get_kload(const struct andr_img_hdr *hdr)
+{
+       return hdr->kernel_addr;
+}
+
+int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
+                             ulong *rd_data, ulong *rd_len)
+{
+       if (!hdr->ramdisk_size)
+               return -1;
+       *rd_data = (unsigned long)hdr;
+       *rd_data += hdr->page_size;
+       *rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
+
+       *rd_len = hdr->ramdisk_size;
+       return 0;
+}
index fcc5a9c..fa4864d 100644 (file)
@@ -660,10 +660,12 @@ int genimg_get_format(const void *img_addr)
        if (image_check_magic(hdr))
                format = IMAGE_FORMAT_LEGACY;
 #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT)
-       else {
-               if (fdt_check_header(img_addr) == 0)
-                       format = IMAGE_FORMAT_FIT;
-       }
+       else if (fdt_check_header(img_addr) == 0)
+               format = IMAGE_FORMAT_FIT;
+#endif
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+       else if (android_image_check_header(img_addr) == 0)
+               format = IMAGE_FORMAT_ANDROID;
 #endif
 
        return format;
@@ -933,7 +935,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images,
                                (ulong)images->legacy_hdr_os);
 
                image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len);
-       } else {
+       }
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+       else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) &&
+                (!android_image_get_ramdisk((void *)images->os.start,
+                &rd_data, &rd_len))) {
+               /* empty */
+       }
+#endif
+       else {
                /*
                 * no initrd image
                 */
diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot
new file mode 100644 (file)
index 0000000..f1d128c
--- /dev/null
@@ -0,0 +1,91 @@
+Android Fastboot
+~~~~~~~~~~~~~~~~
+
+Overview
+========
+The protocol that is used over USB is described in
+README.android-fastboot-protocol in same directory.
+
+The current implementation does not yet support the flash and erase
+commands.
+
+Client installation
+===================
+The counterpart to this gadget is the fastboot client which can
+be found in Android's platform/system/core repository in the fastboot
+folder. It runs on Windows, Linux and even OSX. Linux user are lucky since
+they only need libusb.
+Windows users need to bring some time until they have Android SDK (currently
+http://dl.google.com/android/installer_r12-windows.exe) installed. You
+need to install ADB package which contains the required glue libraries for
+accessing USB. Also you need "Google USB driver package" and "SDK platform
+tools". Once installed the usb driver is placed in your SDK folder under
+extras\google\usb_driver. The android_winusb.inf needs a line like
+
+   %SingleBootLoaderInterface% = USB_Install, USB\VID_0451&PID_D022
+
+either in the [Google.NTx86] section for 32bit Windows or [Google.NTamd64]
+for 64bit Windows. VID and PID should match whatever the fastboot is
+advertising.
+
+Board specific
+==============
+The fastboot gadget relies on the USB download gadget, so the following
+options must be configured:
+
+CONFIG_USBDOWNLOAD_GADGET
+CONFIG_G_DNL_VENDOR_NUM
+CONFIG_G_DNL_PRODUCT_NUM
+CONFIG_G_DNL_MANUFACTURER
+
+The fastboot function is enabled by defining CONFIG_CMD_FASTBOOT and
+CONFIG_ANDROID_BOOT_IMAGE.
+
+The fastboot protocol requires a large memory buffer for downloads. This
+buffer should be as large as possible for a platform. The location of the
+buffer and size are set with CONFIG_USB_FASTBOOT_BUF_ADDR and
+CONFIG_USB_FASTBOOT_BUF_SIZE.
+
+In Action
+=========
+Enter into fastboot by executing the fastboot command in u-boot and you
+should see:
+|GADGET DRIVER: usb_dnl_fastboot
+
+On the client side you can fetch the bootloader version for instance:
+|>fastboot getvar bootloader-version
+|bootloader-version: U-Boot 2014.04-00005-gd24cabc
+|finished. total time: 0.000s
+
+or initiate a reboot:
+|>fastboot reboot
+
+and once the client comes back, the board should reset.
+
+You can also specify a kernel image to boot. You have to either specify
+the an image in Android format _or_ pass a binary kernel and let the
+fastboot client wrap the Android suite around it. On OMAP for instance you
+take zImage kernel and pass it to the fastboot client:
+
+|>fastboot -b 0x80000000 -c "console=ttyO2 earlyprintk root=/dev/ram0
+|      mem=128M" boot zImage
+|creating boot image...
+|creating boot image - 1847296 bytes
+|downloading 'boot.img'...
+|OKAY [  2.766s]
+|booting...
+|OKAY [ -0.000s]
+|finished. total time: 2.766s
+
+and on the gadget side you should see:
+|Starting download of 1847296 bytes
+|........................................................
+|downloading of 1847296 bytes finished
+|Booting kernel..
+|## Booting Android Image at 0x81000000 ...
+|Kernel load addr 0x80008000 size 1801 KiB
+|Kernel command line: console=ttyO2 earlyprintk root=/dev/ram0 mem=128M
+|   Loading Kernel Image ... OK
+|OK
+|
+|Starting kernel ...
diff --git a/doc/README.android-fastboot-protocol b/doc/README.android-fastboot-protocol
new file mode 100644 (file)
index 0000000..e9e7166
--- /dev/null
@@ -0,0 +1,170 @@
+FastBoot  Version  0.4
+----------------------
+
+The fastboot protocol is a mechanism for communicating with bootloaders
+over USB.  It is designed to be very straightforward to implement, to
+allow it to be used across a wide range of devices and from hosts running
+Linux, Windows, or OSX.
+
+
+Basic Requirements
+------------------
+
+* Two bulk endpoints (in, out) are required
+* Max packet size must be 64 bytes for full-speed and 512 bytes for
+  high-speed USB
+* The protocol is entirely host-driven and synchronous (unlike the
+  multi-channel, bi-directional, asynchronous ADB protocol)
+
+
+Transport and Framing
+---------------------
+
+1. Host sends a command, which is an ascii string in a single
+   packet no greater than 64 bytes.
+
+2. Client response with a single packet no greater than 64 bytes.
+   The first four bytes of the response are "OKAY", "FAIL", "DATA",
+   or "INFO".  Additional bytes may contain an (ascii) informative
+   message.
+
+   a. INFO -> the remaining 60 bytes are an informative message
+      (providing progress or diagnostic messages).  They should
+      be displayed and then step #2 repeats
+
+   b. FAIL -> the requested command failed.  The remaining 60 bytes
+      of the response (if present) provide a textual failure message
+      to present to the user.  Stop.
+
+   c. OKAY -> the requested command completed successfully.  Go to #5
+
+   d. DATA -> the requested command is ready for the data phase.
+      A DATA response packet will be 12 bytes long, in the form of
+      DATA00000000 where the 8 digit hexidecimal number represents
+      the total data size to transfer.
+
+3. Data phase.  Depending on the command, the host or client will
+   send the indicated amount of data.  Short packets are always
+   acceptable and zero-length packets are ignored.  This phase continues
+   until the client has sent or received the number of bytes indicated
+   in the "DATA" response above.
+
+4. Client responds with a single packet no greater than 64 bytes.
+   The first four bytes of the response are "OKAY", "FAIL", or "INFO".
+   Similar to #2:
+
+   a. INFO -> display the remaining 60 bytes and return to #4
+
+   b. FAIL -> display the remaining 60 bytes (if present) as a failure
+      reason and consider the command failed.  Stop.
+
+   c. OKAY -> success.  Go to #5
+
+5. Success.  Stop.
+
+
+Example Session
+---------------
+
+Host:    "getvar:version"        request version variable
+
+Client:  "OKAY0.4"               return version "0.4"
+
+Host:    "getvar:nonexistant"    request some undefined variable
+
+Client:  "OKAY"                  return value ""
+
+Host:    "download:00001234"     request to send 0x1234 bytes of data
+
+Client:  "DATA00001234"          ready to accept data
+
+Host:    < 0x1234 bytes >        send data
+
+Client:  "OKAY"                  success
+
+Host:    "flash:bootloader"      request to flash the data to the bootloader
+
+Client:  "INFOerasing flash"     indicate status / progress
+         "INFOwriting flash"
+         "OKAY"                  indicate success
+
+Host:    "powerdown"             send a command
+
+Client:  "FAILunknown command"   indicate failure
+
+
+Command Reference
+-----------------
+
+* Command parameters are indicated by printf-style escape sequences.
+
+* Commands are ascii strings and sent without the quotes (which are
+  for illustration only here) and without a trailing 0 byte.
+
+* Commands that begin with a lowercase letter are reserved for this
+  specification.  OEM-specific commands should not begin with a
+  lowercase letter, to prevent incompatibilities with future specs.
+
+ "getvar:%s"           Read a config/version variable from the bootloader.
+                       The variable contents will be returned after the
+                       OKAY response.
+
+ "download:%08x"       Write data to memory which will be later used
+                       by "boot", "ramdisk", "flash", etc.  The client
+                       will reply with "DATA%08x" if it has enough
+                       space in RAM or "FAIL" if not.  The size of
+                       the download is remembered.
+
+  "verify:%08x"        Send a digital signature to verify the downloaded
+                       data.  Required if the bootloader is "secure"
+                       otherwise "flash" and "boot" will be ignored.
+
+  "flash:%s"           Write the previously downloaded image to the
+                       named partition (if possible).
+
+  "erase:%s"           Erase the indicated partition (clear to 0xFFs)
+
+  "boot"               The previously downloaded data is a boot.img
+                       and should be booted according to the normal
+                       procedure for a boot.img
+
+  "continue"           Continue booting as normal (if possible)
+
+  "reboot"             Reboot the device.
+
+  "reboot-bootloader"  Reboot back into the bootloader.
+                       Useful for upgrade processes that require upgrading
+                       the bootloader and then upgrading other partitions
+                       using the new bootloader.
+
+  "powerdown"          Power off the device.
+
+
+
+Client Variables
+----------------
+
+The "getvar:%s" command is used to read client variables which
+represent various information about the device and the software
+on it.
+
+The various currently defined names are:
+
+  version             Version of FastBoot protocol supported.
+                      It should be "0.3" for this document.
+
+  version-bootloader  Version string for the Bootloader.
+
+  version-baseband    Version string of the Baseband Software
+
+  product             Name of the product
+
+  serialno            Product serial number
+
+  secure              If the value is "yes", this is a secure
+                      bootloader requiring a signature before
+                      it will install or boot images.
+
+Names starting with a lowercase character are reserved by this
+specification.  OEM-specific names should not start with lowercase
+characters.
index 2d07097..ccdbef6 100644 (file)
@@ -163,6 +163,18 @@ static int dfu_flush_medium_nand(struct dfu_entity *dfu)
        return ret;
 }
 
+unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu)
+{
+       /*
+        * Currently, Poll Timeout != 0 is only needed on nand
+        * ubi partition, as the not used sectors need an erase
+        */
+       if (dfu->data.nand.ubi)
+               return DFU_MANIFEST_POLL_TIMEOUT;
+
+       return DFU_DEFAULT_POLL_TIMEOUT;
+}
+
 int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
 {
        char *st;
@@ -211,6 +223,7 @@ int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
        dfu->read_medium = dfu_read_medium_nand;
        dfu->write_medium = dfu_write_medium_nand;
        dfu->flush_medium = dfu_flush_medium_nand;
+       dfu->poll_timeout = dfu_polltimeout_nand;
 
        /* initial state */
        dfu->inited = 0;
index 896c8d4..66becdc 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o
 obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o
 obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o
 obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o
+obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o
 endif
 ifdef CONFIG_USB_ETHER
 obj-y += ether.o
index 02d3fda..290863d 100644 (file)
@@ -205,13 +205,26 @@ static void ci_invalidate_qtd(int ep_num)
 static struct usb_request *
 ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
 {
-       struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
-       return &ci_ep->req;
+       struct ci_req *ci_req;
+
+       ci_req = memalign(ARCH_DMA_MINALIGN, sizeof(*ci_req));
+       if (!ci_req)
+               return NULL;
+
+       INIT_LIST_HEAD(&ci_req->queue);
+       ci_req->b_buf = 0;
+
+       return &ci_req->req;
 }
 
-static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
+static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
 {
-       return;
+       struct ci_req *ci_req;
+
+       ci_req = container_of(req, struct ci_req, req);
+       if (ci_req->b_buf)
+               free(ci_req->b_buf);
+       free(ci_req);
 }
 
 static void ep_enable(int num, int in, int maxpacket)
@@ -267,99 +280,102 @@ static int ci_ep_disable(struct usb_ep *ep)
        return 0;
 }
 
-static int ci_bounce(struct ci_ep *ep, int in)
+static int ci_bounce(struct ci_req *ci_req, int in)
 {
-       uint32_t addr = (uint32_t)ep->req.buf;
-       uint32_t ba;
+       struct usb_request *req = &ci_req->req;
+       uint32_t addr = (uint32_t)req->buf;
+       uint32_t hwaddr;
+       uint32_t aligned_used_len;
 
        /* Input buffer address is not aligned. */
        if (addr & (ARCH_DMA_MINALIGN - 1))
                goto align;
 
        /* Input buffer length is not aligned. */
-       if (ep->req.length & (ARCH_DMA_MINALIGN - 1))
+       if (req->length & (ARCH_DMA_MINALIGN - 1))
                goto align;
 
        /* The buffer is well aligned, only flush cache. */
-       ep->b_len = ep->req.length;
-       ep->b_buf = ep->req.buf;
+       ci_req->hw_len = req->length;
+       ci_req->hw_buf = req->buf;
        goto flush;
 
 align:
-       /* Use internal buffer for small payloads. */
-       if (ep->req.length <= 64) {
-               ep->b_len = 64;
-               ep->b_buf = ep->b_fast;
-       } else {
-               ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN);
-               ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len);
-               if (!ep->b_buf)
+       if (ci_req->b_buf && req->length > ci_req->b_len) {
+               free(ci_req->b_buf);
+               ci_req->b_buf = 0;
+       }
+       if (!ci_req->b_buf) {
+               ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN);
+               ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len);
+               if (!ci_req->b_buf)
                        return -ENOMEM;
        }
+       ci_req->hw_len = ci_req->b_len;
+       ci_req->hw_buf = ci_req->b_buf;
+
        if (in)
-               memcpy(ep->b_buf, ep->req.buf, ep->req.length);
+               memcpy(ci_req->hw_buf, req->buf, req->length);
 
 flush:
-       ba = (uint32_t)ep->b_buf;
-       flush_dcache_range(ba, ba + ep->b_len);
+       hwaddr = (uint32_t)ci_req->hw_buf;
+       aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN);
+       flush_dcache_range(hwaddr, hwaddr + aligned_used_len);
 
        return 0;
 }
 
-static void ci_debounce(struct ci_ep *ep, int in)
+static void ci_debounce(struct ci_req *ci_req, int in)
 {
-       uint32_t addr = (uint32_t)ep->req.buf;
-       uint32_t ba = (uint32_t)ep->b_buf;
+       struct usb_request *req = &ci_req->req;
+       uint32_t addr = (uint32_t)req->buf;
+       uint32_t hwaddr = (uint32_t)ci_req->hw_buf;
+       uint32_t aligned_used_len;
 
-       if (in) {
-               if (addr == ba)
-                       return;         /* not a bounce */
-               goto free;
-       }
-       invalidate_dcache_range(ba, ba + ep->b_len);
+       if (in)
+               return;
+
+       aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN);
+       invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len);
 
-       if (addr == ba)
-               return;         /* not a bounce */
+       if (addr == hwaddr)
+               return; /* not a bounce */
 
-       memcpy(ep->req.buf, ep->b_buf, ep->req.actual);
-free:
-       /* Large payloads use allocated buffer, free it. */
-       if (ep->b_buf != ep->b_fast)
-               free(ep->b_buf);
+       memcpy(req->buf, ci_req->hw_buf, req->actual);
 }
 
-static int ci_ep_queue(struct usb_ep *ep,
-               struct usb_request *req, gfp_t gfp_flags)
+static void ci_ep_submit_next_request(struct ci_ep *ci_ep)
 {
-       struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
        struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
        struct ept_queue_item *item;
        struct ept_queue_head *head;
-       int bit, num, len, in, ret;
+       int bit, num, len, in;
+       struct ci_req *ci_req;
+
+       ci_ep->req_primed = true;
+
        num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
        in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
        item = ci_get_qtd(num, in);
        head = ci_get_qh(num, in);
-       len = req->length;
 
-       ret = ci_bounce(ci_ep, in);
-       if (ret)
-               return ret;
+       ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
+       len = ci_req->req.length;
 
        item->next = TERMINATE;
        item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
-       item->page0 = (uint32_t)ci_ep->b_buf;
-       item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000;
-       item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000;
-       item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000;
-       item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000;
+       item->page0 = (uint32_t)ci_req->hw_buf;
+       item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000;
+       item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000;
+       item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000;
+       item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000;
        ci_flush_qtd(num);
 
        head->next = (unsigned) item;
        head->info = 0;
 
-       DBG("ept%d %s queue len %x, buffer %p\n",
-           num, in ? "in" : "out", len, ci_ep->b_buf);
+       DBG("ept%d %s queue len %x, req %p, buffer %p\n",
+           num, in ? "in" : "out", len, ci_req, ci_req->hw_buf);
        ci_flush_qh(num);
 
        if (in)
@@ -368,6 +384,29 @@ static int ci_ep_queue(struct usb_ep *ep,
                bit = EPT_RX(num);
 
        writel(bit, &udc->epprime);
+}
+
+static int ci_ep_queue(struct usb_ep *ep,
+               struct usb_request *req, gfp_t gfp_flags)
+{
+       struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+       struct ci_req *ci_req = container_of(req, struct ci_req, req);
+       int in, ret;
+       int __maybe_unused num;
+
+       num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+       in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
+
+       ret = ci_bounce(ci_req, in);
+       if (ret)
+               return ret;
+
+       DBG("ept%d %s pre-queue req %p, buffer %p\n",
+           num, in ? "in" : "out", ci_req, ci_req->hw_buf);
+       list_add_tail(&ci_req->queue, &ci_ep->queue);
+
+       if (!ci_ep->req_primed)
+               ci_ep_submit_next_request(ci_ep);
 
        return 0;
 }
@@ -376,6 +415,8 @@ static void handle_ep_complete(struct ci_ep *ep)
 {
        struct ept_queue_item *item;
        int num, in, len;
+       struct ci_req *ci_req;
+
        num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
        in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
        if (num == 0)
@@ -387,16 +428,23 @@ static void handle_ep_complete(struct ci_ep *ep)
                printf("EP%d/%s FAIL info=%x pg0=%x\n",
                       num, in ? "in" : "out", item->info, item->page0);
 
+       ci_req = list_first_entry(&ep->queue, struct ci_req, queue);
+       list_del_init(&ci_req->queue);
+       ep->req_primed = false;
+
+       if (!list_empty(&ep->queue))
+               ci_ep_submit_next_request(ep);
+
        len = (item->info >> 16) & 0x7fff;
-       ep->req.actual = ep->req.length - len;
-       ci_debounce(ep, in);
+       ci_req->req.actual = ci_req->req.length - len;
+       ci_debounce(ci_req, in);
 
-       DBG("ept%d %s complete %x\n",
-                       num, in ? "in" : "out", len);
-       ep->req.complete(&ep->ep, &ep->req);
+       DBG("ept%d %s req %p, complete %x\n",
+           num, in ? "in" : "out", ci_req, len);
+       ci_req->req.complete(&ep->ep, &ci_req->req);
        if (num == 0) {
-               ep->req.length = 0;
-               usb_ep_queue(&ep->ep, &ep->req, 0);
+               ci_req->req.length = 0;
+               usb_ep_queue(&ep->ep, &ci_req->req, 0);
                ep->desc = &ep0_in_desc;
        }
 }
@@ -405,13 +453,18 @@ static void handle_ep_complete(struct ci_ep *ep)
 
 static void handle_setup(void)
 {
-       struct usb_request *req = &controller.ep[0].req;
+       struct ci_ep *ci_ep = &controller.ep[0];
+       struct ci_req *ci_req;
+       struct usb_request *req;
        struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
        struct ept_queue_head *head;
        struct usb_ctrlrequest r;
        int status = 0;
        int num, in, _num, _in, i;
        char *buf;
+
+       ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
+       req = &ci_req->req;
        head = ci_get_qh(0, 0); /* EP0 OUT */
 
        ci_invalidate_qh(0);
@@ -424,6 +477,9 @@ static void handle_setup(void)
        DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest),
            r.bRequestType, r.bRequest, r.wIndex, r.wValue);
 
+       list_del_init(&ci_req->queue);
+       ci_ep->req_primed = false;
+
        switch (SETUP(r.bRequestType, r.bRequest)) {
        case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE):
                _num = r.wIndex & 15;
@@ -701,6 +757,8 @@ static int ci_udc_probe(void)
        /* Init EP 0 */
        memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init));
        controller.ep[0].desc = &ep0_in_desc;
+       INIT_LIST_HEAD(&controller.ep[0].queue);
+       controller.ep[0].req_primed = false;
        controller.gadget.ep0 = &controller.ep[0].ep;
        INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
 
@@ -708,6 +766,8 @@ static int ci_udc_probe(void)
        for (i = 1; i < NUM_ENDPOINTS; i++) {
                memcpy(&controller.ep[i].ep, &ci_ep_init[1],
                       sizeof(*ci_ep_init));
+               INIT_LIST_HEAD(&controller.ep[i].queue);
+               controller.ep[i].req_primed = false;
                list_add_tail(&controller.ep[i].ep.ep_list,
                              &controller.gadget.ep_list);
        }
index 4425fd9..23cff56 100644 (file)
@@ -77,15 +77,22 @@ struct ci_udc {
 #define CTRL_TXT_BULK  (2 << 18)
 #define CTRL_RXT_BULK  (2 << 2)
 
+struct ci_req {
+       struct usb_request      req;
+       struct list_head        queue;
+       /* Bounce buffer allocated if needed to align the transfer */
+       uint8_t *b_buf;
+       uint32_t b_len;
+       /* Buffer for the current transfer. Either req.buf/len or b_buf/len */
+       uint8_t *hw_buf;
+       uint32_t hw_len;
+};
+
 struct ci_ep {
        struct usb_ep ep;
        struct list_head queue;
+       bool req_primed;
        const struct usb_endpoint_descriptor *desc;
-
-       struct usb_request req;
-       uint8_t *b_buf;
-       uint32_t b_len;
-       uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN);
 };
 
 struct ci_drv {
index 1b1e179..859fe82 100644 (file)
@@ -175,10 +175,17 @@ static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
                  req->length, f_dfu->blk_seq_num);
 }
 
+static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu)
+{
+       return dfu->poll_timeout ? dfu->poll_timeout(dfu) :
+               DFU_MANIFEST_POLL_TIMEOUT;
+}
+
 static void handle_getstatus(struct usb_request *req)
 {
        struct dfu_status *dstat = (struct dfu_status *)req->buf;
        struct f_dfu *f_dfu = req->context;
+       struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting);
 
        dfu_set_poll_timeout(dstat, 0);
 
@@ -191,7 +198,8 @@ static void handle_getstatus(struct usb_request *req)
                f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
                break;
        case DFU_STATE_dfuMANIFEST:
-               dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT);
+               dfu_set_poll_timeout(dstat, dfu_get_manifest_timeout(dfu));
+               break;
        default:
                break;
        }
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
new file mode 100644 (file)
index 0000000..9dd85b6
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/compiler.h>
+#include <version.h>
+#include <g_dnl.h>
+
+#define FASTBOOT_VERSION               "0.4"
+
+#define FASTBOOT_INTERFACE_CLASS       0xff
+#define FASTBOOT_INTERFACE_SUB_CLASS   0x42
+#define FASTBOOT_INTERFACE_PROTOCOL    0x03
+
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0  (0x0200)
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1  (0x0040)
+#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE      (0x0040)
+
+/* The 64 defined bytes plus \0 */
+#define RESPONSE_LEN   (64 + 1)
+
+#define EP_BUFFER_SIZE                 4096
+
+struct f_fastboot {
+       struct usb_function usb_function;
+
+       /* IN/OUT EP's and correspoinding requests */
+       struct usb_ep *in_ep, *out_ep;
+       struct usb_request *in_req, *out_req;
+};
+
+static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
+{
+       return container_of(f, struct f_fastboot, usb_function);
+}
+
+static struct f_fastboot *fastboot_func;
+static unsigned int download_size;
+static unsigned int download_bytes;
+
+static struct usb_endpoint_descriptor fs_ep_in = {
+       .bLength            = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType    = USB_DT_ENDPOINT,
+       .bEndpointAddress   = USB_DIR_IN,
+       .bmAttributes       = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize     = TX_ENDPOINT_MAXIMUM_PACKET_SIZE,
+       .bInterval          = 0x00,
+};
+
+static struct usb_endpoint_descriptor fs_ep_out = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_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,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize         = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0,
+       .bInterval              = 0x00,
+};
+
+static struct usb_interface_descriptor interface_desc = {
+       .bLength                = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = 0x00,
+       .bAlternateSetting      = 0x00,
+       .bNumEndpoints          = 0x02,
+       .bInterfaceClass        = FASTBOOT_INTERFACE_CLASS,
+       .bInterfaceSubClass     = FASTBOOT_INTERFACE_SUB_CLASS,
+       .bInterfaceProtocol     = FASTBOOT_INTERFACE_PROTOCOL,
+};
+
+static struct usb_descriptor_header *fb_runtime_descs[] = {
+       (struct usb_descriptor_header *)&interface_desc,
+       (struct usb_descriptor_header *)&fs_ep_in,
+       (struct usb_descriptor_header *)&hs_ep_out,
+       NULL,
+};
+
+/*
+ * static strings, in UTF-8
+ */
+static const char fastboot_name[] = "Android Fastboot";
+
+static struct usb_string fastboot_string_defs[] = {
+       [0].s = fastboot_name,
+       {  }                    /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_fastboot = {
+       .language       = 0x0409,       /* en-us */
+       .strings        = fastboot_string_defs,
+};
+
+static struct usb_gadget_strings *fastboot_strings[] = {
+       &stringtab_fastboot,
+       NULL,
+};
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+
+static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       int status = req->status;
+       if (!status)
+               return;
+       printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
+}
+
+static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       int id;
+       struct usb_gadget *gadget = c->cdev->gadget;
+       struct f_fastboot *f_fb = func_to_fastboot(f);
+
+       /* DYNAMIC interface numbers assignments */
+       id = usb_interface_id(c, f);
+       if (id < 0)
+               return id;
+       interface_desc.bInterfaceNumber = id;
+
+       id = usb_string_id(c->cdev);
+       if (id < 0)
+               return id;
+       fastboot_string_defs[0].id = id;
+       interface_desc.iInterface = id;
+
+       f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
+       if (!f_fb->in_ep)
+               return -ENODEV;
+       f_fb->in_ep->driver_data = c->cdev;
+
+       f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
+       if (!f_fb->out_ep)
+               return -ENODEV;
+       f_fb->out_ep->driver_data = c->cdev;
+
+       hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+
+       return 0;
+}
+
+static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       memset(fastboot_func, 0, sizeof(*fastboot_func));
+}
+
+static void fastboot_disable(struct usb_function *f)
+{
+       struct f_fastboot *f_fb = func_to_fastboot(f);
+
+       usb_ep_disable(f_fb->out_ep);
+       usb_ep_disable(f_fb->in_ep);
+
+       if (f_fb->out_req) {
+               free(f_fb->out_req->buf);
+               usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
+               f_fb->out_req = NULL;
+       }
+       if (f_fb->in_req) {
+               free(f_fb->in_req->buf);
+               usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
+               f_fb->in_req = NULL;
+       }
+}
+
+static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
+{
+       struct usb_request *req;
+
+       req = usb_ep_alloc_request(ep, 0);
+       if (!req)
+               return NULL;
+
+       req->length = EP_BUFFER_SIZE;
+       req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
+       if (!req->buf) {
+               usb_ep_free_request(ep, req);
+               return NULL;
+       }
+
+       memset(req->buf, 0, req->length);
+       return req;
+}
+
+static int fastboot_set_alt(struct usb_function *f,
+                           unsigned interface, unsigned alt)
+{
+       int ret;
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_gadget *gadget = cdev->gadget;
+       struct f_fastboot *f_fb = func_to_fastboot(f);
+
+       debug("%s: func: %s intf: %d alt: %d\n",
+             __func__, f->name, interface, alt);
+
+       /* make sure we don't enable the ep twice */
+       if (gadget->speed == USB_SPEED_HIGH)
+               ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out);
+       else
+               ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out);
+       if (ret) {
+               puts("failed to enable out ep\n");
+               return ret;
+       }
+
+       f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
+       if (!f_fb->out_req) {
+               puts("failed to alloc out req\n");
+               ret = -EINVAL;
+               goto err;
+       }
+       f_fb->out_req->complete = rx_handler_command;
+
+       ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in);
+       if (ret) {
+               puts("failed to enable in ep\n");
+               goto err;
+       }
+
+       f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
+       if (!f_fb->in_req) {
+               puts("failed alloc req in\n");
+               ret = -EINVAL;
+               goto err;
+       }
+       f_fb->in_req->complete = fastboot_complete;
+
+       ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       fastboot_disable(f);
+       return ret;
+}
+
+static int fastboot_add(struct usb_configuration *c)
+{
+       struct f_fastboot *f_fb = fastboot_func;
+       int status;
+
+       debug("%s: cdev: 0x%p\n", __func__, c->cdev);
+
+       if (!f_fb) {
+               f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb));
+               if (!f_fb)
+                       return -ENOMEM;
+
+               fastboot_func = f_fb;
+               memset(f_fb, 0, sizeof(*f_fb));
+       }
+
+       f_fb->usb_function.name = "f_fastboot";
+       f_fb->usb_function.hs_descriptors = fb_runtime_descs;
+       f_fb->usb_function.bind = fastboot_bind;
+       f_fb->usb_function.unbind = fastboot_unbind;
+       f_fb->usb_function.set_alt = fastboot_set_alt;
+       f_fb->usb_function.disable = fastboot_disable;
+       f_fb->usb_function.strings = fastboot_strings;
+
+       status = usb_add_function(c, &f_fb->usb_function);
+       if (status) {
+               free(f_fb);
+               fastboot_func = f_fb;
+       }
+
+       return status;
+}
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add);
+
+int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
+{
+       struct usb_request *in_req = fastboot_func->in_req;
+       int ret;
+
+       memcpy(in_req->buf, buffer, buffer_size);
+       in_req->length = buffer_size;
+       ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
+       if (ret)
+               printf("Error %d on queue\n", ret);
+       return 0;
+}
+
+static int fastboot_tx_write_str(const char *buffer)
+{
+       return fastboot_tx_write(buffer, strlen(buffer));
+}
+
+static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
+{
+       do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
+{
+       fastboot_func->in_req->complete = compl_do_reset;
+       fastboot_tx_write_str("OKAY");
+}
+
+static int strcmp_l1(const char *s1, const char *s2)
+{
+       if (!s1 || !s2)
+               return -1;
+       return strncmp(s1, s2, strlen(s1));
+}
+
+static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
+{
+       char *cmd = req->buf;
+       char response[RESPONSE_LEN];
+       const char *s;
+
+       strcpy(response, "OKAY");
+       strsep(&cmd, ":");
+       if (!cmd) {
+               fastboot_tx_write_str("FAILmissing var");
+               return;
+       }
+
+       if (!strcmp_l1("version", cmd)) {
+               strncat(response, FASTBOOT_VERSION, sizeof(response));
+       } else if (!strcmp_l1("bootloader-version", cmd)) {
+               strncat(response, U_BOOT_VERSION, sizeof(response));
+       } else if (!strcmp_l1("downloadsize", cmd)) {
+               char str_num[12];
+
+               sprintf(str_num, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE);
+               strncat(response, str_num, sizeof(response));
+       } else if (!strcmp_l1("serialno", cmd)) {
+               s = getenv("serial#");
+               if (s)
+                       strncat(response, s, sizeof(response));
+               else
+                       strcpy(response, "FAILValue not set");
+       } else {
+               strcpy(response, "FAILVariable not implemented");
+       }
+       fastboot_tx_write_str(response);
+}
+
+static unsigned int rx_bytes_expected(void)
+{
+       int rx_remain = download_size - download_bytes;
+       if (rx_remain < 0)
+               return 0;
+       if (rx_remain > EP_BUFFER_SIZE)
+               return EP_BUFFER_SIZE;
+       return rx_remain;
+}
+
+#define BYTES_PER_DOT  0x20000
+static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
+{
+       char response[RESPONSE_LEN];
+       unsigned int transfer_size = download_size - download_bytes;
+       const unsigned char *buffer = req->buf;
+       unsigned int buffer_size = req->actual;
+
+       if (req->status != 0) {
+               printf("Bad status: %d\n", req->status);
+               return;
+       }
+
+       if (buffer_size < transfer_size)
+               transfer_size = buffer_size;
+
+       memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + 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_BUFFER_SIZE;
+
+               sprintf(response, "OKAY");
+               fastboot_tx_write_str(response);
+
+               printf("\ndownloading of %d bytes finished\n", download_bytes);
+       } else {
+               req->length = rx_bytes_expected();
+               if (req->length < ep->maxpacket)
+                       req->length = ep->maxpacket;
+       }
+
+       if (download_bytes && !(download_bytes % BYTES_PER_DOT)) {
+               putc('.');
+               if (!(download_bytes % (74 * BYTES_PER_DOT)))
+                       putc('\n');
+       }
+       req->actual = 0;
+       usb_ep_queue(ep, req, 0);
+}
+
+static void cb_download(struct usb_ep *ep, struct usb_request *req)
+{
+       char *cmd = req->buf;
+       char response[RESPONSE_LEN];
+
+       strsep(&cmd, ":");
+       download_size = simple_strtoul(cmd, NULL, 16);
+       download_bytes = 0;
+
+       printf("Starting download of %d bytes\n", download_size);
+
+       if (0 == download_size) {
+               sprintf(response, "FAILdata invalid size");
+       } else if (download_size > CONFIG_USB_FASTBOOT_BUF_SIZE) {
+               download_size = 0;
+               sprintf(response, "FAILdata too large");
+       } else {
+               sprintf(response, "DATA%08x", download_size);
+               req->complete = rx_handler_dl_image;
+               req->length = rx_bytes_expected();
+               if (req->length < ep->maxpacket)
+                       req->length = ep->maxpacket;
+       }
+       fastboot_tx_write_str(response);
+}
+
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       char boot_addr_start[12];
+       char *bootm_args[] = { "bootm", boot_addr_start, NULL };
+
+       puts("Booting kernel..\n");
+
+       sprintf(boot_addr_start, "0x%lx", load_addr);
+       do_bootm(NULL, 0, 2, bootm_args);
+
+       /* This only happens if image is somehow faulty so we start over */
+       do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_boot(struct usb_ep *ep, struct usb_request *req)
+{
+       fastboot_func->in_req->complete = do_bootm_on_complete;
+       fastboot_tx_write_str("OKAY");
+}
+
+struct cmd_dispatch_info {
+       char *cmd;
+       void (*cb)(struct usb_ep *ep, struct usb_request *req);
+};
+
+static const struct cmd_dispatch_info cmd_dispatch_info[] = {
+       {
+               .cmd = "reboot",
+               .cb = cb_reboot,
+       }, {
+               .cmd = "getvar:",
+               .cb = cb_getvar,
+       }, {
+               .cmd = "download:",
+               .cb = cb_download,
+       }, {
+               .cmd = "boot",
+               .cb = cb_boot,
+       },
+};
+
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
+{
+       char *cmdbuf = req->buf;
+       void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
+               if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
+                       func_cb = cmd_dispatch_info[i].cb;
+                       break;
+               }
+       }
+
+       if (!func_cb)
+               fastboot_tx_write_str("FAILunknown command");
+       else
+               func_cb(ep, req);
+
+       if (req->status == 0) {
+               *cmdbuf = '\0';
+               req->actual = 0;
+               usb_ep_queue(ep, req, 0);
+       }
+}
index 7430074..02803df 100644 (file)
@@ -311,11 +311,7 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
 #define DELAYED_STATUS (EP0_BUFSIZE + 999)     /* An impossibly large value */
 
 /* Number of buffers we will use.  2 is enough for double-buffering */
-#ifndef CONFIG_CI_UDC
 #define FSG_NUM_BUFFERS        2
-#else
-#define FSG_NUM_BUFFERS        1 /* ci_udc only allows 1 req per ep at present */
-#endif
 
 /* Default size of buffer length. */
 #define FSG_BUFLEN     ((u32)16384)
index 6599d38..8c3b0a1 100644 (file)
@@ -576,6 +576,10 @@ static void ep0_txstate(struct musb *musb)
        } else
                request = NULL;
 
+       /* send it out, triggering a "txpktrdy cleared" irq */
+       musb_ep_select(musb->mregs, 0);
+       musb_writew(regs, MUSB_CSR0, csr);
+
        /* report completions as soon as the fifo's loaded; there's no
         * win in waiting till this last packet gets acked.  (other than
         * very precise fault reporting, needed by USB TMC; possible with
@@ -588,10 +592,6 @@ static void ep0_txstate(struct musb *musb)
                        return;
                musb->ackpend = 0;
        }
-
-       /* send it out, triggering a "txpktrdy cleared" irq */
-       musb_ep_select(musb->mregs, 0);
-       musb_writew(regs, MUSB_CSR0, csr);
 }
 
 /*
diff --git a/include/android_image.h b/include/android_image.h
new file mode 100644 (file)
index 0000000..094d60a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This is from the Android Project,
+ * Repository: https://android.googlesource.com/platform/bootable/bootloader/legacy
+ * File: include/boot/bootimg.h
+ * Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _ANDROID_IMAGE_H_
+#define _ANDROID_IMAGE_H_
+
+#define ANDR_BOOT_MAGIC "ANDROID!"
+#define ANDR_BOOT_MAGIC_SIZE 8
+#define ANDR_BOOT_NAME_SIZE 16
+#define ANDR_BOOT_ARGS_SIZE 512
+
+struct andr_img_hdr {
+       char magic[ANDR_BOOT_MAGIC_SIZE];
+
+       u32 kernel_size;        /* size in bytes */
+       u32 kernel_addr;        /* physical load addr */
+
+       u32 ramdisk_size;       /* size in bytes */
+       u32 ramdisk_addr;       /* physical load addr */
+
+       u32 second_size;        /* size in bytes */
+       u32 second_addr;        /* physical load addr */
+
+       u32 tags_addr;          /* physical addr for kernel tags */
+       u32 page_size;          /* flash page size we assume */
+       u32 unused[2];          /* future expansion: should be 0 */
+
+       char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */
+
+       char cmdline[ANDR_BOOT_ARGS_SIZE];
+
+       u32 id[8]; /* timestamp / checksum / sha1 / etc */
+};
+
+/*
+ * +-----------------+
+ * | boot header     | 1 page
+ * +-----------------+
+ * | kernel          | n pages
+ * +-----------------+
+ * | ramdisk         | m pages
+ * +-----------------+
+ * | second stage    | o pages
+ * +-----------------+
+ *
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. second is optional (second_size == 0 -> no second)
+ * 3. load each element (kernel, ramdisk, second) at
+ *    the specified physical address (kernel_addr, etc)
+ * 4. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 6. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
+#endif
index fae0e6f..a5b7a40 100644 (file)
 #define CONFIG_TWL4030_USB             1
 #define CONFIG_USB_ETHER
 #define CONFIG_USB_ETHER_RNDIS
+#define CONFIG_USB_GADGET
+#define CONFIG_USB_GADGET_VBUS_DRAW    0
+#define CONFIG_USBDOWNLOAD_GADGET
+#define CONFIG_G_DNL_VENDOR_NUM                0x0451
+#define CONFIG_G_DNL_PRODUCT_NUM       0xd022
+#define CONFIG_G_DNL_MANUFACTURER      "TI"
+#define CONFIG_CMD_FASTBOOT
+#define CONFIG_ANDROID_BOOT_IMAGE
+#define CONFIG_USB_FASTBOOT_BUF_ADDR   CONFIG_SYS_LOAD_ADDR
+#define CONFIG_USB_FASTBOOT_BUF_SIZE   0x07000000
 
 /* USB EHCI */
 #define CONFIG_CMD_USB
index 2409168..986598e 100644 (file)
@@ -100,6 +100,7 @@ struct dfu_entity {
                        u64 offset, void *buf, long *len);
 
        int (*flush_medium)(struct dfu_entity *dfu);
+       unsigned int (*poll_timeout)(struct dfu_entity *dfu);
 
        struct list_head list;
 
index b278778..1886168 100644 (file)
@@ -413,6 +413,7 @@ enum fit_load_op {
 #define IMAGE_FORMAT_INVALID   0x00
 #define IMAGE_FORMAT_LEGACY    0x01    /* legacy image_header based format */
 #define IMAGE_FORMAT_FIT       0x02    /* new, libfdt based format */
+#define IMAGE_FORMAT_ANDROID   0x03    /* Android boot image */
 
 int genimg_get_format(const void *img_addr);
 int genimg_has_config(bootm_headers_t *images);
@@ -1031,4 +1032,16 @@ static inline int fit_image_check_target_arch(const void *fdt, int node)
 #endif /* CONFIG_FIT_VERBOSE */
 #endif /* CONFIG_FIT */
 
+#if defined(CONFIG_ANDROID_BOOT_IMAGE)
+struct andr_img_hdr;
+int android_image_check_header(const struct andr_img_hdr *hdr);
+int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
+                            ulong *os_data, ulong *os_len);
+int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
+                             ulong *rd_data, ulong *rd_len);
+ulong android_image_get_end(const struct andr_img_hdr *hdr);
+ulong android_image_get_kload(const struct andr_img_hdr *hdr);
+
+#endif /* CONFIG_ANDROID_BOOT_IMAGE */
+
 #endif /* __IMAGE_H__ */
index ed46064..69b80cd 100644 (file)
@@ -9,17 +9,9 @@
 #define __USB_MASS_STORAGE_H__
 
 #define SECTOR_SIZE            0x200
-#include <mmc.h>
+#include <part.h>
 #include <linux/usb/composite.h>
 
-#ifndef UMS_START_SECTOR
-#define UMS_START_SECTOR       0
-#endif
-
-#ifndef UMS_NUM_SECTORS
-#define UMS_NUM_SECTORS                0
-#endif
-
 /* Wait at maximum 60 seconds for cable connection */
 #define UMS_CABLE_READY_TIMEOUT        60
 
@@ -31,14 +23,13 @@ struct ums {
        unsigned int start_sector;
        unsigned int num_sectors;
        const char *name;
-       struct mmc *mmc;
+       block_dev_desc_t *block_dev;
 };
 
 extern struct ums *ums;
 
 int fsg_init(struct ums *);
 void fsg_cleanup(void);
-struct ums *ums_init(unsigned int);
 int fsg_main_thread(void *);
 int fsg_add(struct usb_configuration *c);
 #endif /* __USB_MASS_STORAGE_H__ */