[U-Boot] [PATCH 2/2] usb/gadget: add the fastboot gadget

Sebastian Andrzej Siewior bigeasy at linutronix.de
Mon Nov 21 15:09:35 CET 2011


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.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy at linutronix.de>
---
 common/Makefile                      |    2 +
 common/cmd_fastboot.c                |   28 ++
 doc/README.android-fastboot          |   86 ++++++
 doc/README.android-fastboot-protocol |  170 ++++++++++
 drivers/usb/gadget/Makefile          |    5 +
 drivers/usb/gadget/f_fastboot.c      |  559 ++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/g_fastboot.h      |   20 ++
 drivers/usb/gadget/u_fastboot.c      |  308 +++++++++++++++++++
 include/usb/fastboot.h               |  100 ++++++
 9 files changed, 1278 insertions(+), 0 deletions(-)
 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 1b672ad..9d6659e 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -162,6 +162,8 @@ COBJS-y += cmd_usb.o
 COBJS-y += usb.o
 COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o
 endif
+COBJS-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o
+
 COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
 COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
 
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c
new file mode 100644
index 0000000..9a656dd
--- /dev/null
+++ b/common/cmd_fastboot.c
@@ -0,0 +1,28 @@
+#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;
+		}
+	}
+
+	fastboot_shutdown();
+	return ret;
+}
+
+U_BOOT_CMD(
+	fastboot,	1,	1,	do_fastboot,
+	"fastboot- use USB Fastboot protocol\n",
+	""
+);
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 7d5b504..75d4a12 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -42,6 +42,11 @@ COBJS-$(CONFIG_SPEARUDC) += spr_udc.o
 endif
 endif
 
+ifdef CONFIG_CMD_FASTBOOT
+COBJS-y += f_fastboot.o u_fastboot.o
+COBJS-y += epautoconf.o usbstring.o
+endif
+
 COBJS	:= $(COBJS-y)
 SRCS	:= $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(COBJS))
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
new file mode 100644
index 0000000..7570440
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,559 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix at windriver.com>
+ *
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#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 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;
+
+	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_gadget *g;
+static struct usb_request *ep0_req;
+
+struct usb_ep *ep_in;
+struct usb_request *req_in;
+
+struct usb_ep *ep_out;
+struct usb_request *req_out;
+
+static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+
+	if (!status)
+		return;
+	printf("ep0 status %d\n", status);
+}
+
+static int fastboot_bind(struct usb_gadget *gadget)
+{
+
+	g = gadget;
+	ep0_req = usb_ep_alloc_request(g->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;
+	return 0;
+err:
+	return -1;
+}
+
+static void fastboot_unbind(struct usb_gadget *gadget)
+{
+	usb_ep_free_request(g->ep0, ep0_req);
+	ep_in->driver_data = NULL;
+	ep_out->driver_data = NULL;
+}
+
+/* This is the TI USB vendor id a product ID from TI's internal tree */
+#define DEVICE_VENDOR_ID  0x0451
+#define DEVICE_PRODUCT_ID 0xd022
+#define DEVICE_BCD        0x0100
+
+struct usb_device_descriptor fb_descriptor = {
+	.bLength            = sizeof(fb_descriptor),
+	.bDescriptorType    = USB_DT_DEVICE,
+	.bcdUSB             = 0x200,
+	.bMaxPacketSize0    = 0x40,
+	.idVendor           = DEVICE_VENDOR_ID,
+	.idProduct          = DEVICE_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 = {
+	.bind		= fastboot_bind,
+	.unbind		= fastboot_unbind,
+	.setup		= fastboot_setup,
+	.disconnect	= fastboot_disconnect,
+};
+
+static int udc_is_probbed;
+
+int fastboot_init(void)
+{
+	int ret;
+
+	ret = fastboot_board_init(&fb_cfg, &vendor_fb_strings);
+	if (ret)
+		return ret;
+	if (!vendor_fb_strings)
+		return -EINVAL;
+
+	ret = usb_gadget_init_udc();
+	if (ret) {
+		printf("gadget probe failed\n");
+		return 1;
+	}
+	udc_is_probbed = 1;
+
+	ret = usb_gadget_register_driver(&fast_gadget);
+	if (ret) {
+		printf("Add gadget failed\n");
+		goto err;
+	}
+
+	gadget_is_connected = 1;
+	usb_gadget_handle_interrupts();
+	return 0;
+
+err:
+	fastboot_shutdown();
+	return 1;
+}
+
+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_exit_udc();
+}
+
+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..ff2621c
--- /dev/null
+++ b/drivers/usb/gadget/g_fastboot.h
@@ -0,0 +1,20 @@
+#ifndef _G_FASTBOOT_H_
+#define _G_FASTBOOT_H_
+
+#define EP_BUFFER_SIZE			4096
+#define FASTBOOT_INTERFACE_CLASS	0xff
+#define FASTBOOT_INTERFACE_SUB_CLASS	0x42
+#define FASTBOOT_INTERFACE_PROTOCOL	0x03
+#define FASTBOOT_VERSION		"0.4"
+
+extern struct fastboot_config fb_cfg;
+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..00762aa
--- /dev/null
+++ b/drivers/usb/gadget/u_fastboot.c
@@ -0,0 +1,308 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix at windriver.com>
+ *
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Part of the rx_handler were copied from the Android project.
+ * Specifically rx command parsing in the  usb_rx_data_complete
+ * function of the file bootable/bootloader/legacy/usbloader/usbloader.c
+ *
+ * The logical naming of flash comes from the Android project
+ * Thse structures and functions that look like fastboot_flash_*
+ * They come from bootable/bootloader/legacy/libboot/flash.c
+ *
+ * This is their Copyright:
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <common.h>
+#include <usb/fastboot.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "g_fastboot.h"
+
+/* The 64 defined bytes plus \0 */
+#define RESPONSE_LEN	(64 + 1)
+
+struct fastboot_config fb_cfg;
+
+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, "%08x", fb_cfg.transfer_buffer_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(fb_cfg.transfer_buffer + download_bytes,
+			buffer, transfer_size);
+
+	download_bytes += transfer_size;
+
+	/* Check if transfer is done */
+	if (download_bytes >= download_size) {
+		/*
+		 * Reset global transfer variable, keep download_bytes because
+		 * it will be used in the next possible flashing command
+		 */
+		download_size = 0;
+		req->complete = rx_handler_command;
+		req->length = EP_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 >
+			fb_cfg.transfer_buffer_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%p", fb_cfg.transfer_buffer);
+
+	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..e55149a
--- /dev/null
+++ b/include/usb/fastboot.h
@@ -0,0 +1,100 @@
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix at windriver.com>
+ *
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The logical naming of flash comes from the Android project
+ * Thse structures and functions that look like fastboot_flash_*
+ * They come from bootloader/legacy/include/boot/flash.h
+ *
+ * The boot_img_hdr structure and associated magic numbers also
+ * come from the Android project.  They are from
+ * bootloader/legacy/include/boot/bootimg.h
+ *
+ * Here are their copyrights
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef FASTBOOT_H
+#define FASTBOOT_H
+
+#include <common.h>
+#include <command.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <android_image.h>
+
+struct fastboot_config {
+
+	/*
+	 * Transfer buffer for storing data sent by the client. It should be
+	 * able to hold a kernel image and flash partitions. Care should be
+	 * take so it does not overrun bootloader memory
+	 */
+	unsigned char *transfer_buffer;
+
+	/* Size of the buffer mentioned above */
+	unsigned int transfer_buffer_size;
+};
+
+#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
+
+#if (CONFIG_CMD_FASTBOOT)
+
+int fastboot_init(void);
+void fastboot_shutdown(void);
+int fastboot_poll(void);
+
+int fastboot_board_init(struct fastboot_config *interface,
+		struct usb_gadget_strings **str);
+#endif
+#endif
-- 
1.7.7.1



More information about the U-Boot mailing list