[U-Boot] [PATCH 2/3] usb/gadget: add the fastboot gadget
Sebastian Andrzej Siewior
bigeasy at linutronix.de
Fri Sep 16 18:51:18 CEST 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 | 549 ++++++++++++++++++++++++++++++++++
drivers/usb/gadget/g_fastboot.h | 20 ++
drivers/usb/gadget/u_fastboot.c | 308 +++++++++++++++++++
include/usb/fastboot.h | 95 ++++++
9 files changed, 1263 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 d662468..da40d64 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -157,6 +157,8 @@ COBJS-y += cmd_usb.o
COBJS-y += usb.o
COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o
endif
+COBJS-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o
+
COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c
new file mode 100644
index 0000000..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..6c3cae5
--- /dev/null
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -0,0 +1,549 @@
+/*
+ * The file is based on content which was
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix at windriver.com>
+ *
+ * but after the chainsaw attack by
+ * Copyright (c) 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
+ *
+ * there isn't much left. The license is in both cases GPLv2 as published
+ * by the FSF
+ */
+#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..bc2fd2d
--- /dev/null
+++ b/include/usb/fastboot.h
@@ -0,0 +1,95 @@
+/*
+ * (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.
+ *
+ * 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.5.4
More information about the U-Boot
mailing list