[U-Boot] [PATCH v3 4/5] usb/gadget: add the fastboot gadget

Rob Herring robherring2 at gmail.com
Thu Apr 10 21:18:06 CEST 2014


From: Sebastian Siewior <bigeasy at 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 <bigeasy at linutronix.de>
Signed-off-by: Rob Herring <robh at 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.Rix at 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.Rix at windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy at 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.Rix at windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy at 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.Rix at windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy at 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



More information about the U-Boot mailing list