From: Sebastian Siewior <bige...@linutronix.de>

This patch contains an implementation of the fastboot protocol on the
device side and a little of documentation.
The gadget expects the new-style gadget framework.
The gadget implements the getvar, reboot, download and reboot commands.
What is missing is the flash handling i.e. writting the image to media.

v3 (Rob Herring):
This is based on http://patchwork.ozlabs.org/patch/126798/ with the
following changes:
- Rebase to current mainline and updates for current gadget API
- Use SPDX identifiers for licenses
- Traced the history and added missing copyright to cmd_fastboot.c
- Use load_addr/load_size for transfer buffer
- Allow vendor strings to be optional
- Set vendor/product ID from config defines
- Allow Ctrl-C to exit fastboot mode

Signed-off-by: Sebastian Andrzej Siewior <bige...@linutronix.de>
Signed-off-by: Rob Herring <r...@kernel.org>
---
 common/Makefile                      |   2 +
 common/cmd_fastboot.c                |  36 +++
 doc/README.android-fastboot          |  86 ++++++
 doc/README.android-fastboot-protocol | 170 +++++++++++
 drivers/usb/gadget/Makefile          |   1 +
 drivers/usb/gadget/f_fastboot.c      | 535 +++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/g_fastboot.h      |  15 +
 drivers/usb/gadget/u_fastboot.c      | 260 +++++++++++++++++
 include/usb/fastboot.h               |  36 +++
 9 files changed, 1141 insertions(+)
 create mode 100644 common/cmd_fastboot.c
 create mode 100644 doc/README.android-fastboot
 create mode 100644 doc/README.android-fastboot-protocol
 create mode 100644 drivers/usb/gadget/f_fastboot.c
 create mode 100644 drivers/usb/gadget/g_fastboot.h
 create mode 100644 drivers/usb/gadget/u_fastboot.c
 create mode 100644 include/usb/fastboot.h

diff --git a/common/Makefile b/common/Makefile
index da208f3..fe1d8b9 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -167,6 +167,8 @@ obj-y += cmd_usb.o
 obj-y += usb.o usb_hub.o
 obj-$(CONFIG_USB_STORAGE) += usb_storage.o
 endif
+obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o
+
 obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o
 obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o
 obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c
new file mode 100644
index 0000000..fc8d9e0
--- /dev/null
+++ b/common/cmd_fastboot.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 - 2009 Windriver, <www.windriver.com>
+ * Author: Tom Rix <tom....@windriver.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <command.h>
+#include <usb/fastboot.h>
+
+static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const 
argv[])
+{
+       int ret = 1;
+
+       if (!fastboot_init()) {
+               printf("Fastboot entered...\n");
+
+               ret = 0;
+
+               while (1) {
+                       if (fastboot_poll())
+                               break;
+                       if (ctrlc())
+                               break;
+               }
+       }
+
+       fastboot_shutdown();
+       return ret;
+}
+
+U_BOOT_CMD(
+       fastboot,       1,      1,      do_fastboot,
+       "fastboot - enter USB Fastboot protocol",
+       ""
+);
diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot
new file mode 100644
index 0000000..4b2a9aa
--- /dev/null
+++ b/doc/README.android-fastboot
@@ -0,0 +1,86 @@
+Android Fastboot
+~~~~~~~~~~~~~~~~
+
+Overview
+========
+The protocol that is used over USB is described in
+README.android-fastboot-protocol in same folder.
+
+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 gadget calls at probe time the function fastboot_board_init() which
+should be provided by the board to setup its specific configuration.
+It is possible here to overwrite specific strings like Vendor or Serial
+number. Strings which are not specified here will return a default value.
+This init function must also provide a memory area for the
+"transfer_buffer" and its size. This buffer should be large enough to hold
+whatever the download commands is willing to send or it will fail. This
+can be a kernel image for booting which could be around two MiB or a flash
+partition which could be slightly larger :)
+
+In Action
+=========
+Enter into fastboot by executing the fastboot command in u-boot and you
+should see:
+|Fastboot entered...
+
+The gadget terminates once the is unplugged. On the client side you can
+fetch the product name for instance:
+|>fastboot getvar product
+|product: Default Product
+|finished. total time: 0.016s
+
+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
index 0000000..e9e7166
--- /dev/null
+++ b/doc/README.android-fastboot-protocol
@@ -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.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 804a2bd..b14f60e 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -17,6 +17,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 u_fastboot.o
 endif
 ifdef CONFIG_USB_ETHER
 obj-y += ether.o
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
new file mode 100644
index 0000000..f74a52e
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,535 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <tom....@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bige...@linutronix.de>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <usb/fastboot.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/compiler.h>
+
+#include "g_fastboot.h"
+
+#define FASTBOOT_INTERFACE_CLASS       0xff
+#define FASTBOOT_INTERFACE_SUB_CLASS   0x42
+#define FASTBOOT_INTERFACE_PROTOCOL    0x03
+
+#define CONFIGURATION_NORMAL      1
+#define BULK_ENDPOINT 1
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0  (0x0200)
+#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1  (0x0040)
+#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE      (0x0040)
+
+static struct usb_string def_usb_fb_strings[] = {
+       { FB_STR_PRODUCT_IDX,           "Default Product" },
+       { FB_STR_SERIAL_IDX,            "1234567890" },
+       { FB_STR_CONFIG_IDX,            "Android Fastboot" },
+       { FB_STR_INTERFACE_IDX,         "Android Fastboot" },
+       { FB_STR_MANUFACTURER_IDX,      "Default Manufacturer" },
+       { FB_STR_PROC_REV_IDX,          "Default 1.0" },
+       { FB_STR_PROC_TYPE_IDX,         "Emulator" },
+       {  }
+};
+
+static struct usb_gadget_strings def_fb_strings = {
+       .language       = 0x0409, /* en-us */
+       .strings        = def_usb_fb_strings,
+};
+
+static struct usb_gadget_strings *vendor_fb_strings;
+
+static unsigned int gadget_is_connected;
+
+static u8 ep0_buffer[512];
+static u8 ep_out_buffer[EP_BUFFER_SIZE];
+static u8 ep_in_buffer[EP_BUFFER_SIZE];
+static int current_config;
+
+/* e1 */
+static struct usb_endpoint_descriptor fs_ep_in = {
+       .bLength            = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType    = USB_DT_ENDPOINT,
+       .bEndpointAddress   = USB_DIR_IN, /* IN */
+       .bmAttributes       = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize     = TX_ENDPOINT_MAXIMUM_PACKET_SIZE,
+       .bInterval          = 0x00,
+};
+
+/* e2 */
+static struct usb_endpoint_descriptor fs_ep_out = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_OUT, /* OUT */
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize         = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1,
+       .bInterval              = 0x00,
+};
+
+static struct usb_endpoint_descriptor hs_ep_out = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_OUT, /* OUT */
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize         = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0,
+       .bInterval              = 0x00,
+};
+
+const char *fb_find_usb_string(unsigned int id)
+{
+       struct usb_string *s = NULL;
+
+       if (vendor_fb_strings) {
+               for (s = vendor_fb_strings->strings; s && s->s; s++) {
+                       if (s->id == id)
+                               break;
+               }
+       }
+       if (!s || !s->s) {
+               for (s = def_fb_strings.strings; s && s->s; s++) {
+                       if (s->id == id)
+                               break;
+               }
+       }
+       if (!s)
+               return NULL;
+       return s->s;
+}
+
+static struct usb_request *ep0_req;
+
+struct usb_ep *ep_in;
+struct usb_request *req_in;
+
+struct usb_ep *ep_out;
+struct usb_request *req_out;
+
+static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       int status = req->status;
+
+       if (!status)
+               return;
+       printf("ep0 status %d\n", status);
+}
+
+static int fastboot_bind(struct usb_gadget *gadget)
+{
+       ep0_req = usb_ep_alloc_request(gadget->ep0, 0);
+       if (!ep0_req)
+               goto err;
+       ep0_req->buf = ep0_buffer;
+       ep0_req->complete = fastboot_ep0_complete;
+
+       ep_in = usb_ep_autoconfig(gadget, &fs_ep_in);
+       if (!ep_in)
+               goto err;
+       ep_in->driver_data = ep_in;
+
+       ep_out = usb_ep_autoconfig(gadget, &fs_ep_out);
+       if (!ep_out)
+               goto err;
+       ep_out->driver_data = ep_out;
+
+       hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
+
+       usb_gadget_connect(gadget);
+       gadget_is_connected = 1;
+
+       return 0;
+err:
+       return -1;
+}
+
+static void fastboot_unbind(struct usb_gadget *gadget)
+{
+       usb_ep_free_request(gadget->ep0, ep0_req);
+       ep_in->driver_data = NULL;
+       ep_out->driver_data = NULL;
+
+       gadget_is_connected = 0;
+       usb_gadget_disconnect(gadget);
+}
+
+#define DEVICE_BCD        0x0100
+
+struct usb_device_descriptor fb_descriptor = {
+       .bLength            = sizeof(fb_descriptor),
+       .bDescriptorType    = USB_DT_DEVICE,
+       .bcdUSB             = 0x200,
+       .bMaxPacketSize0    = 0x40,
+       .idVendor           = cpu_to_le16(CONFIG_USB_FASTBOOT_VENDOR_ID),
+       .idProduct          = cpu_to_le16(CONFIG_USB_FASTBOOT_PRODUCT_ID),
+       .bcdDevice          = DEVICE_BCD,
+       .iManufacturer      = FB_STR_MANUFACTURER_IDX,
+       .iProduct           = FB_STR_PRODUCT_IDX,
+       .iSerialNumber      = FB_STR_SERIAL_IDX,
+       .bNumConfigurations = 1,
+};
+
+#define TOT_CFG_DESC_LEN       (USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + \
+               USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE)
+
+static struct usb_config_descriptor config_desc = {
+       .bLength                = USB_DT_CONFIG_SIZE,
+       .bDescriptorType        = USB_DT_CONFIG,
+       .wTotalLength           = cpu_to_le16(TOT_CFG_DESC_LEN),
+       .bNumInterfaces         = 1,
+       .bConfigurationValue    = CONFIGURATION_NORMAL,
+       .iConfiguration         = FB_STR_CONFIG_IDX,
+       .bmAttributes           = 0xc0,
+       .bMaxPower              = 0x32,
+};
+
+static struct usb_interface_descriptor interface_desc = {
+       .bLength                = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = 0x00,
+       .bAlternateSetting      = 0x00,
+       .bNumEndpoints          = 0x02,
+       .bInterfaceClass        = FASTBOOT_INTERFACE_CLASS,
+       .bInterfaceSubClass     = FASTBOOT_INTERFACE_SUB_CLASS,
+       .bInterfaceProtocol     = FASTBOOT_INTERFACE_PROTOCOL,
+       .iInterface             = FB_STR_INTERFACE_IDX,
+};
+
+static struct usb_qualifier_descriptor qual_desc = {
+       .bLength                = sizeof(qual_desc),
+       .bDescriptorType        = USB_DT_DEVICE_QUALIFIER,
+       .bcdUSB                 = 0x200,
+       .bMaxPacketSize0        = 0x40,
+       .bNumConfigurations     = 1,
+};
+
+static int fastboot_setup_get_descr(struct usb_gadget *gadget,
+               const struct usb_ctrlrequest *ctrl)
+{
+       u16 w_value     = le16_to_cpu(ctrl->wValue);
+       u16 w_length    = le16_to_cpu(ctrl->wLength);
+       u16 val;
+       int ret;
+       u32 bytes_remaining;
+       u32 bytes_total;
+       u32 this_inc;
+
+       val = w_value >> 8;
+
+       switch (val) {
+       case USB_DT_DEVICE:
+               memcpy(ep0_buffer, &fb_descriptor, sizeof(fb_descriptor));
+               ep0_req->length = min(w_length, sizeof(fb_descriptor));
+               ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
+               break;
+       case USB_DT_CONFIG:
+               bytes_remaining = min(w_length, sizeof(ep0_buffer));
+               bytes_total = 0;
+
+               /* config */
+               this_inc = min(bytes_remaining, USB_DT_CONFIG_SIZE);
+               bytes_remaining -= this_inc;
+               memcpy(ep0_buffer + bytes_total, &config_desc, this_inc);
+               bytes_total += this_inc;
+
+               /* interface */
+               this_inc = min(bytes_remaining, USB_DT_INTERFACE_SIZE);
+               bytes_remaining -= this_inc;
+               memcpy(ep0_buffer + bytes_total, &interface_desc, this_inc);
+               bytes_total += this_inc;
+
+               /* ep in */
+               this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE);
+               bytes_remaining -= this_inc;
+               memcpy(ep0_buffer + bytes_total, &fs_ep_in, this_inc);
+               bytes_total += this_inc;
+
+               /* ep out */
+               this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE);
+
+               if (gadget->speed == USB_SPEED_HIGH)
+                       memcpy(ep0_buffer + bytes_total, &hs_ep_out, this_inc);
+               else
+                       memcpy(ep0_buffer + bytes_total, &fs_ep_out, this_inc);
+               bytes_total += this_inc;
+
+               ep0_req->length = bytes_total;
+               ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
+               break;
+       case USB_DT_STRING:
+               ret = usb_gadget_get_string(vendor_fb_strings,
+                                           w_value & 0xff, ep0_buffer);
+               if (ret < 0)
+                       ret = usb_gadget_get_string(&def_fb_strings,
+                                                   w_value & 0xff,
+                                                   ep0_buffer);
+               if (ret < 0)
+                       break;
+
+               ep0_req->length = ret;
+               ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
+               break;
+       case USB_DT_DEVICE_QUALIFIER:
+               memcpy(ep0_buffer, &qual_desc, sizeof(qual_desc));
+               ep0_req->length = min(w_length, sizeof(qual_desc));
+               ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static int fastboot_setup_get_conf(struct usb_gadget *gadget,
+               const struct usb_ctrlrequest *ctrl)
+{
+       u16 w_length    = le16_to_cpu(ctrl->wLength);
+
+       if (w_length == 0)
+               return -1;
+
+       ep0_buffer[0] = current_config;
+       ep0_req->length = 1;
+       return usb_ep_queue(gadget->ep0, ep0_req, 0);
+}
+
+static void fastboot_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+       int status = req->status;
+
+       if (status)
+               printf("status: %d ep_in trans: %d\n", status, req->actual);
+}
+
+static int fastboot_disable_ep(struct usb_gadget *gadget)
+{
+       if (req_out) {
+               usb_ep_free_request(ep_out, req_out);
+               req_out = NULL;
+       }
+       if (req_in) {
+               usb_ep_free_request(ep_in, req_in);
+               req_in = NULL;
+       }
+       usb_ep_disable(ep_out);
+       usb_ep_disable(ep_in);
+
+       return 0;
+}
+
+static int fastboot_enable_ep(struct usb_gadget *gadget)
+{
+       int ret;
+
+       /* make sure we don't enable the ep twice */
+       if (gadget->speed == USB_SPEED_HIGH)
+               ret = usb_ep_enable(ep_out, &hs_ep_out);
+       else
+               ret = usb_ep_enable(ep_out, &fs_ep_out);
+       if (ret) {
+               printf("failed to enable out ep\n");
+               goto err;
+       }
+
+       req_out = usb_ep_alloc_request(ep_out, 0);
+       if (!req_out) {
+               printf("failed to alloc out req\n");
+               goto err;
+       }
+
+       ret = usb_ep_enable(ep_in, &fs_ep_in);
+       if (ret) {
+               printf("failed to enable in ep\n");
+               goto err;
+       }
+       req_in = usb_ep_alloc_request(ep_in, 0);
+       if (!req_in) {
+               printf("failed alloc req in\n");
+               goto err;
+       }
+
+       req_out->complete = rx_handler_command;
+       req_out->buf = ep_out_buffer;
+       req_out->length = sizeof(ep_out_buffer);
+
+       req_in->buf = ep_in_buffer;
+       req_in->length = sizeof(ep_in_buffer);
+
+       ret = usb_ep_queue(ep_out, req_out, 0);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       fastboot_disable_ep(gadget);
+       return -1;
+}
+
+static int fastboot_set_interface(struct usb_gadget *gadget, u32 enable)
+{
+       if (enable && req_out)
+               return 0;
+       if (!enable && !req_out)
+               return 0;
+
+       if (enable)
+               return fastboot_enable_ep(gadget);
+       else
+               return fastboot_disable_ep(gadget);
+}
+
+static int fastboot_setup_out_req(struct usb_gadget *gadget,
+               const struct usb_ctrlrequest *req)
+{
+       switch (req->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               switch (req->bRequest) {
+               case USB_REQ_SET_CONFIGURATION:
+
+                       ep0_req->length = 0;
+                       if (req->wValue == CONFIGURATION_NORMAL) {
+                               current_config = CONFIGURATION_NORMAL;
+                               fastboot_set_interface(gadget, 1);
+                               return usb_ep_queue(gadget->ep0,
+                                               ep0_req, 0);
+                       }
+                       if (req->wValue == 0) {
+                               current_config = 0;
+                               fastboot_set_interface(gadget, 0);
+                               return usb_ep_queue(gadget->ep0,
+                                               ep0_req, 0);
+                       }
+                       return -1;
+                       break;
+               default:
+                       return -1;
+               };
+
+       case USB_RECIP_INTERFACE:
+               switch (req->bRequest) {
+               case USB_REQ_SET_INTERFACE:
+
+                       ep0_req->length = 0;
+                       if (!fastboot_set_interface(gadget, 1))
+                               return usb_ep_queue(gadget->ep0,
+                                               ep0_req, 0);
+                       return -1;
+                       break;
+               default:
+                       return -1;
+               }
+
+       case USB_RECIP_ENDPOINT:
+               switch (req->bRequest) {
+               case USB_REQ_CLEAR_FEATURE:
+
+                       return usb_ep_queue(gadget->ep0, ep0_req, 0);
+                       break;
+               default:
+                       return -1;
+               }
+       }
+       return -1;
+}
+
+static int fastboot_setup(struct usb_gadget *gadget,
+               const struct usb_ctrlrequest *req)
+{
+       if ((req->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+               return -1;
+
+       if ((req->bRequestType & USB_DIR_IN) == 0)
+               /* host-to-device */
+               return fastboot_setup_out_req(gadget, req);
+
+       /* device-to-host */
+       if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+               switch (req->bRequest) {
+               case USB_REQ_GET_DESCRIPTOR:
+                       return fastboot_setup_get_descr(gadget, req);
+                       break;
+
+               case USB_REQ_GET_CONFIGURATION:
+                       return fastboot_setup_get_conf(gadget, req);
+                       break;
+               default:
+                       return -1;
+               }
+       }
+       return -1;
+}
+
+static void fastboot_disconnect(struct usb_gadget *gadget)
+{
+       fastboot_disable_ep(gadget);
+       gadget_is_connected = 0;
+}
+
+struct usb_gadget_driver fast_gadget = {
+       .speed          = USB_SPEED_HIGH,
+       .bind           = fastboot_bind,
+       .unbind         = fastboot_unbind,
+       .setup          = fastboot_setup,
+       .disconnect     = fastboot_disconnect,
+};
+
+static int udc_is_probbed;
+
+int __weak fastboot_board_init(struct usb_gadget_strings **str)
+{
+       return 0;
+}
+
+int fastboot_init(void)
+{
+       int ret;
+
+       ret = fastboot_board_init(&vendor_fb_strings);
+       if (ret)
+               return ret;
+
+       ret = usb_gadget_register_driver(&fast_gadget);
+       if (ret) {
+               printf("Add gadget failed\n");
+               return ret;
+       }
+
+       udc_is_probbed = 1;
+       return 0;
+}
+
+int fastboot_poll(void)
+{
+       usb_gadget_handle_interrupts();
+
+       if (gadget_is_connected)
+               return 0;
+       else
+               return 1;
+}
+
+void fastboot_shutdown(void)
+{
+       if (!udc_is_probbed)
+               return;
+       udc_is_probbed = 0;
+       usb_gadget_unregister_driver(&fast_gadget);
+}
+
+int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
+{
+       int ret;
+
+       if (req_in->complete == NULL)
+               req_in->complete = fastboot_complete_in;
+
+       memcpy(req_in->buf, buffer, buffer_size);
+       req_in->length = buffer_size;
+       ret = usb_ep_queue(ep_in, req_in, 0);
+       if (ret)
+               printf("Error %d on queue\n", ret);
+       return 0;
+}
diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h
new file mode 100644
index 0000000..733eb38
--- /dev/null
+++ b/drivers/usb/gadget/g_fastboot.h
@@ -0,0 +1,15 @@
+#ifndef _G_FASTBOOT_H_
+#define _G_FASTBOOT_H_
+
+#define EP_BUFFER_SIZE                 4096
+
+extern struct usb_ep *ep_in;
+extern struct usb_request *req_in;
+extern struct usb_ep *ep_out;
+extern struct usb_request *req_out;
+
+void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+int fastboot_tx_write(const char *buffer, unsigned int buffer_size);
+const char *fb_find_usb_string(unsigned int id);
+
+#endif
diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c
new file mode 100644
index 0000000..76116c0
--- /dev/null
+++ b/drivers/usb/gadget/u_fastboot.c
@@ -0,0 +1,260 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <tom....@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bige...@linutronix.de>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+/*
+ * Part of the rx_handler were copied from the Android project.
+ * Specifically rx command parsing in the usb_rx_cmd_complete
+ * function of the file bootable/bootloader/legacy/usbloader/usbloader.c
+ *
+ * Repository: 
https://android.googlesource.com/platform/bootable/bootloader/legacy
+ * Files: usbloader/usbloader.c
+ * Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <common.h>
+#include <usb/fastboot.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "g_fastboot.h"
+
+#define FASTBOOT_VERSION               "0.4"
+
+/* The 64 defined bytes plus \0 */
+#define RESPONSE_LEN   (64 + 1)
+
+static unsigned int download_size;
+static unsigned int download_bytes;
+
+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)
+{
+       req_in->complete = compl_do_reset;
+       fastboot_tx_write_str("OKAY");
+}
+
+static int strcmp_l1(const char *s1, const char *s2)
+{
+       return strncmp(s1, s2, strlen(s1));
+}
+
+static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
+{
+       char *cmd = req->buf;
+       char response[RESPONSE_LEN];
+       const char *s;
+
+       strcpy(response, "OKAY");
+       strsep(&cmd, ":");
+       if (!cmd) {
+               fastboot_tx_write_str("FAILmissing var");
+               return;
+       }
+
+       if (!strcmp_l1("version", cmd)) {
+               strncat(response, FASTBOOT_VERSION, sizeof(response));
+       } else if (!strcmp_l1("downloadsize", cmd)) {
+               char str_num[12];
+
+               sprintf(str_num, "%08lx", load_size);
+               strncat(response, str_num, sizeof(response));
+       } else if (!strcmp_l1("product", cmd)) {
+               s = fb_find_usb_string(FB_STR_PRODUCT_IDX);
+               if (s)
+                       strncat(response, s, sizeof(response));
+               else
+                       strcpy(response, "FAILValue not set");
+       } else if (!strcmp_l1("serialno", cmd)) {
+               s = fb_find_usb_string(FB_STR_SERIAL_IDX);
+               if (s)
+                       strncat(response, s, sizeof(response));
+               else
+                       strcpy(response, "FAILValue not set");
+       } else if (!strcmp_l1("cpurev", cmd)) {
+               s = fb_find_usb_string(FB_STR_PROC_REV_IDX);
+               if (s)
+                       strncat(response, s, sizeof(response));
+               else
+                       strcpy(response, "FAILValue not set");
+       } else if (!strcmp_l1("secure", cmd)) {
+               s = fb_find_usb_string(FB_STR_PROC_TYPE_IDX);
+               if (s)
+                       strncat(response, s, sizeof(response));
+               else
+                       strcpy(response, "FAILValue not set");
+       } else {
+               strcpy(response, "FAILVariable not implemented");
+       }
+       fastboot_tx_write_str(response);
+}
+
+static unsigned int rx_bytes_expected(void)
+{
+       int rx_remain = 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  32768
+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 *)load_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 (download_bytes && !(download_bytes % BYTES_PER_DOT)) {
+               printf(".");
+               if (!(download_bytes % (74 * BYTES_PER_DOT)))
+                       printf("\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 > load_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();
+       }
+       fastboot_tx_write_str(response);
+}
+
+static char boot_addr_start[32];
+static char *bootm_args[] = { "bootm", boot_addr_start, NULL };
+
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       req->complete = NULL;
+       fastboot_shutdown();
+       printf("Booting kernel..\n");
+
+       do_bootm(NULL, 0, 2, bootm_args);
+
+       /* This only happens if image is somehow faulty so we start over */
+       do_reset(NULL, 0, 0, NULL);
+}
+
+static void cb_boot(struct usb_ep *ep, struct usb_request *req)
+{
+       sprintf(boot_addr_start, "0x%lx", load_addr);
+
+       req_in->complete = do_bootm_on_complete;
+       fastboot_tx_write_str("OKAY");
+       return;
+}
+
+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",
+               .cb = cb_reboot,
+       }, {
+               .cmd = "getvar:",
+               .cb = cb_getvar,
+       }, {
+               .cmd = "download:",
+               .cb = cb_download,
+       }, {
+               .cmd = "boot",
+               .cb = cb_boot,
+       },
+};
+
+void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
+{
+       char response[RESPONSE_LEN];
+       char *cmdbuf = req->buf;
+       void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
+       int i;
+
+       sprintf(response, "FAIL");
+
+       for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
+               if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
+                       func_cb = cmd_dispatch_info[i].cb;
+                       break;
+               }
+       }
+
+       if (!func_cb)
+               fastboot_tx_write_str("FAILunknown command");
+       else
+               func_cb(ep, req);
+
+       if (req->status == 0) {
+               *cmdbuf = '\0';
+               req->actual = 0;
+               usb_ep_queue(ep, req, 0);
+       }
+}
diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h
new file mode 100644
index 0000000..b348886
--- /dev/null
+++ b/include/usb/fastboot.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <tom....@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bige...@linutronix.de>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef FASTBOOT_H
+#define FASTBOOT_H
+
+#include <common.h>
+#include <command.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#define FB_STR_PRODUCT_IDX      1
+#define FB_STR_SERIAL_IDX       2
+#define FB_STR_CONFIG_IDX       3
+#define FB_STR_INTERFACE_IDX    4
+#define FB_STR_MANUFACTURER_IDX 5
+#define FB_STR_PROC_REV_IDX     6
+#define FB_STR_PROC_TYPE_IDX    7
+
+#ifdef CONFIG_CMD_FASTBOOT
+
+int fastboot_init(void);
+void fastboot_shutdown(void);
+int fastboot_poll(void);
+
+int fastboot_board_init(struct usb_gadget_strings **str);
+
+#endif
+#endif
-- 
1.9.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to