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

Lukasz Majewski l.majewski at samsung.com
Wed Apr 23 13:02:13 CEST 2014


Hi Rob,

> From: Sebastian Siewior <bigeasy at linutronix.de>
> 
> This patch contains an implementation of the fastboot protocol on the
> device side and documentation. This is based on USB download gadget
> infrastructure. The fastboot function 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
> v4:
> - Major re-write to use the USB download gadget. Consolidated function
> code to a single file.
> - Moved globals into single struct.
> - Use puts and putc as appropriate.
> - Added CONFIG_USB_FASTBOOT_BUF_ADDR and CONFIG_USB_FASTBOOT_BUF_SIZE
> to set the fastboot transfer buffer.
> 
> 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          |  91 ++++++
>  doc/README.android-fastboot-protocol | 170 ++++++++++++
>  drivers/usb/gadget/Makefile          |   1 +
>  drivers/usb/gadget/f_fastboot.c      | 518
> +++++++++++++++++++++++++++++++++++
> drivers/usb/gadget/g_dnl.c           |   6 +
> include/fastboot.h                   |  22 ++ 8 files changed, 846
> 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 include/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..ce7e198
> --- /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>
> + *
> + * (C) Copyright 2014 Linaro, Ltd.
> + * Rob Herring <robh at kernel.org>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +#include <common.h>
> +#include <command.h>
> +#include <g_dnl.h>
> +
> +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char
> *const argv[]) +{
> +	int ret;
> +
> +	ret = g_dnl_register("fastboot");
> +	if (ret)
> +		return ret;
> +
> +	while (1) {
> +		if (ctrlc())
> +			break;
> +		usb_gadget_handle_interrupts();
> +	}
> +
> +	g_dnl_unregister();
> +	return CMD_RET_SUCCESS;
> +}
> +
> +U_BOOT_CMD(
> +	fastboot,	1,	1,	do_fastboot,
> +	"fastboot - enter USB Fastboot protocol",
> +	""
> +);
> diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot
> new file mode 100644
> index 0000000..f1d128c
> --- /dev/null
> +++ b/doc/README.android-fastboot
> @@ -0,0 +1,91 @@
> +Android Fastboot
> +~~~~~~~~~~~~~~~~
> +
> +Overview
> +========
> +The protocol that is used over USB is described in
> +README.android-fastboot-protocol in same directory.
> +
> +The current implementation does not yet support the flash and erase
> +commands.
> +
> +Client installation
> +===================
> +The counterpart to this gadget is the fastboot client which can
> +be found in Android's platform/system/core repository in the fastboot
> +folder. It runs on Windows, Linux and even OSX. Linux user are lucky
> since +they only need libusb.
> +Windows users need to bring some time until they have Android SDK
> (currently +http://dl.google.com/android/installer_r12-windows.exe)
> installed. You +need to install ADB package which contains the
> required glue libraries for +accessing USB. Also you need "Google USB
> driver package" and "SDK platform +tools". Once installed the usb
> driver is placed in your SDK folder under +extras\google\usb_driver.
> The android_winusb.inf needs a line like +
> +   %SingleBootLoaderInterface% = USB_Install, USB\VID_0451&PID_D022
> +
> +either in the [Google.NTx86] section for 32bit Windows or
> [Google.NTamd64] +for 64bit Windows. VID and PID should match
> whatever the fastboot is +advertising.
> +
> +Board specific
> +==============
> +The fastboot gadget relies on the USB download gadget, so the
> following +options must be configured:
> +
> +CONFIG_USBDOWNLOAD_GADGET
> +CONFIG_G_DNL_VENDOR_NUM
> +CONFIG_G_DNL_PRODUCT_NUM
> +CONFIG_G_DNL_MANUFACTURER
> +
> +The fastboot function is enabled by defining CONFIG_CMD_FASTBOOT and
> +CONFIG_ANDROID_BOOT_IMAGE.
> +
> +The fastboot protocol requires a large memory buffer for downloads.
> This +buffer should be as large as possible for a platform. The
> location of the +buffer and size are set with
> CONFIG_USB_FASTBOOT_BUF_ADDR and +CONFIG_USB_FASTBOOT_BUF_SIZE.
> +
> +In Action
> +=========
> +Enter into fastboot by executing the fastboot command in u-boot and
> you +should see:
> +|GADGET DRIVER: usb_dnl_fastboot
> +
> +On the client side you can fetch the bootloader version for instance:
> +|>fastboot getvar bootloader-version
> +|bootloader-version: U-Boot 2014.04-00005-gd24cabc
> +|finished. total time: 0.000s
> +
> +or initiate a reboot:
> +|>fastboot reboot
> +
> +and once the client comes back, the board should reset.
> +
> +You can also specify a kernel image to boot. You have to either
> specify +the an image in Android format _or_ pass a binary kernel and
> let the +fastboot client wrap the Android suite around it. On OMAP
> for instance you +take zImage kernel and pass it to the fastboot
> client: +
> +|>fastboot -b 0x80000000 -c "console=ttyO2 earlyprintk root=/dev/ram0
> +|	mem=128M" boot zImage
> +|creating boot image...
> +|creating boot image - 1847296 bytes
> +|downloading 'boot.img'...
> +|OKAY [  2.766s]
> +|booting...
> +|OKAY [ -0.000s]
> +|finished. total time: 2.766s
> +
> +and on the gadget side you should see:
> +|Starting download of 1847296 bytes
> +|........................................................
> +|downloading of 1847296 bytes finished
> +|Booting kernel..
> +|## Booting Android Image at 0x81000000 ...
> +|Kernel load addr 0x80008000 size 1801 KiB
> +|Kernel command line: console=ttyO2 earlyprintk root=/dev/ram0
> mem=128M +|   Loading Kernel Image ... OK
> +|OK
> +|
> +|Starting kernel ...
> diff --git a/doc/README.android-fastboot-protocol
> b/doc/README.android-fastboot-protocol new file mode 100644
> 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..3375f68 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
>  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..514279f
> --- /dev/null
> +++ b/drivers/usb/gadget/f_fastboot.c
> @@ -0,0 +1,518 @@
> +/*
> + * (C) Copyright 2008 - 2009
> + * Windriver, <www.windriver.com>
> + * Tom Rix <Tom.Rix at windriver.com>
> + *
> + * Copyright 2011 Sebastian Andrzej Siewior <bigeasy at linutronix.de>
> + *
> + * Copyright 2014 Linaro, Ltd.
> + * Rob Herring <robh at kernel.org>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +#include <common.h>
> +#include <errno.h>
> +#include <malloc.h>
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/gadget.h>
> +#include <linux/usb/composite.h>
> +#include <linux/compiler.h>
> +#include <version.h>
> +#include <g_dnl.h>
> +#include <fastboot.h>
> +
> +#define FASTBOOT_VERSION		"0.4"
> +
> +#define FASTBOOT_INTERFACE_CLASS	0xff
> +#define FASTBOOT_INTERFACE_SUB_CLASS	0x42
> +#define FASTBOOT_INTERFACE_PROTOCOL	0x03
> +
> +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0  (0x0200)
> +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1  (0x0040)
> +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE      (0x0040)
> +
> +/* The 64 defined bytes plus \0 */
> +#define RESPONSE_LEN	(64 + 1)
> +
> +#define EP_BUFFER_SIZE			4096
> +
> +struct f_fastboot {
> +	struct usb_function usb_function;
> +
> +	/* IN/OUT EP's and correspoinding requests */
> +	struct usb_ep *in_ep, *out_ep;
> +	struct usb_request *in_req, *out_req;
> +};
> +
> +static inline struct f_fastboot *func_to_fastboot(struct
> usb_function *f) +{
> +	return container_of(f, struct f_fastboot, usb_function);
> +}
> +
> +static struct f_fastboot *fastboot_func;
> +static unsigned int download_size;
> +static unsigned int download_bytes;
> +
> +static struct usb_endpoint_descriptor fs_ep_in = {
> +	.bLength            = USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType    = USB_DT_ENDPOINT,
> +	.bEndpointAddress   = USB_DIR_IN,
> +	.bmAttributes       = USB_ENDPOINT_XFER_BULK,
> +	.wMaxPacketSize     = TX_ENDPOINT_MAXIMUM_PACKET_SIZE,
> +	.bInterval          = 0x00,
> +};
> +
> +static struct usb_endpoint_descriptor fs_ep_out = {
> +	.bLength		= USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType	= USB_DT_ENDPOINT,
> +	.bEndpointAddress	= USB_DIR_OUT,
> +	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
> +	.wMaxPacketSize		=
> RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1,
> +	.bInterval		= 0x00,
> +};
> +
> +static struct usb_endpoint_descriptor hs_ep_out = {
> +	.bLength		= USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType	= USB_DT_ENDPOINT,
> +	.bEndpointAddress	= USB_DIR_OUT,
> +	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
> +	.wMaxPacketSize		=
> RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0,
> +	.bInterval		= 0x00,
> +};
> +
> +static struct usb_interface_descriptor interface_desc = {
> +	.bLength		= USB_DT_INTERFACE_SIZE,
> +	.bDescriptorType	= USB_DT_INTERFACE,
> +	.bInterfaceNumber	= 0x00,
> +	.bAlternateSetting	= 0x00,
> +	.bNumEndpoints		= 0x02,
> +	.bInterfaceClass	= FASTBOOT_INTERFACE_CLASS,
> +	.bInterfaceSubClass	= FASTBOOT_INTERFACE_SUB_CLASS,
> +	.bInterfaceProtocol	= FASTBOOT_INTERFACE_PROTOCOL,
> +};
> +
> +static struct usb_descriptor_header *fb_runtime_descs[] = {
> +	(struct usb_descriptor_header *)&interface_desc,
> +	(struct usb_descriptor_header *)&fs_ep_in,
> +	(struct usb_descriptor_header *)&hs_ep_out,
> +	NULL,
> +};
> +
> +/*
> + * static strings, in UTF-8
> + */
> +static const char fastboot_name[] = "Android Fastboot";
> +
> +static struct usb_string fastboot_string_defs[] = {
> +	[0].s = fastboot_name,
> +	{  }			/* end of list */
> +};
> +
> +static struct usb_gadget_strings stringtab_fastboot = {
> +	.language	= 0x0409,	/* en-us */
> +	.strings	= fastboot_string_defs,
> +};
> +
> +static struct usb_gadget_strings *fastboot_strings[] = {
> +	&stringtab_fastboot,
> +	NULL,
> +};
> +
> +static void rx_handler_command(struct usb_ep *ep, struct usb_request
> *req); +
> +static void fastboot_complete(struct usb_ep *ep, struct usb_request
> *req) +{
> +	int status = req->status;
> +	if (!status)
> +		return;
> +	printf("status: %d ep '%s' trans: %d\n", status, ep->name,
> req->actual); +}
> +
> +static int fastboot_bind(struct usb_configuration *c, struct
> usb_function *f) +{
> +	int status, id;
> +	struct usb_gadget *gadget = c->cdev->gadget;
> +	struct f_fastboot *f_fb = func_to_fastboot(f);
> +
> +	/* DYNAMIC interface numbers assignments */
> +	status = usb_interface_id(c, f);
> +	if (status < 0)
> +		goto err;
> +
> +	interface_desc.bInterfaceNumber = status;
> +
> +	id = usb_string_id(c->cdev);
> +	if (id < 0)
> +		goto err;
> +	fastboot_string_defs[0].id = id;
> +	interface_desc.iInterface = id;
> +
> +	f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
> +	if (!f_fb->in_ep)
> +		goto err;
> +	f_fb->in_ep->driver_data = c->cdev;
> +
> +	f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
> +	if (!f_fb->out_ep)
> +		goto err;
> +	f_fb->out_ep->driver_data = c->cdev;
> +
> +	hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
> +
> +	return 0;
> +err:
> +	return -1;

		Maybe -ENODEV or -EINVAL?

> +}
> +
> +static void fastboot_unbind(struct usb_configuration *c, struct
> usb_function *f) +{
> +	memset(fastboot_func, 0, sizeof(*fastboot_func));
> +}
> +
> +static void fastboot_disable(struct usb_function *f)
> +{
> +	struct f_fastboot *f_fb = func_to_fastboot(f);
> +
> +	usb_ep_disable(f_fb->out_ep);
> +	usb_ep_disable(f_fb->in_ep);
> +
> +	if (f_fb->out_req) {
> +		free(f_fb->out_req->buf);
> +		usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
> +		f_fb->out_req = NULL;
> +	}
> +	if (f_fb->in_req) {
> +		free(f_fb->in_req->buf);
> +		usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
> +		f_fb->in_req = NULL;
> +	}
> +}
> +
> +static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
> +{
> +	struct usb_request *req;
> +
> +	req = usb_ep_alloc_request(ep, 0);
> +	if (!req)
> +		return NULL;
> +
> +	req->length = EP_BUFFER_SIZE;
> +	req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
> EP_BUFFER_SIZE);
> +	if (!req->buf) {
> +		usb_ep_free_request(ep, req);
> +		return NULL;
> +	}
> +
> +	memset(req->buf, 0, req->length);
> +	return req;
> +}
> +
> +static int fastboot_set_alt(struct usb_function *f,
> +			    unsigned interface, unsigned alt)
> +{
> +	int ret;
> +	struct usb_composite_dev *cdev = f->config->cdev;
> +	struct usb_gadget *gadget = cdev->gadget;
> +	struct f_fastboot *f_fb = func_to_fastboot(f);
> +
> +	debug("%s: func: %s intf: %d alt: %d\n",
> +	      __func__, f->name, interface, alt);
> +
> +	/* make sure we don't enable the ep twice */
> +	if (gadget->speed == USB_SPEED_HIGH)
> +		ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out);
> +	else
> +		ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out);
> +	if (ret) {
> +		puts("failed to enable out ep\n");
> +		goto err;
> +	}
> +
> +	f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
> +	if (!f_fb->out_req) {
> +		puts("failed to alloc out req\n");
> +		goto err;
> +	}
> +
> +	ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in);
> +	if (ret) {
> +		puts("failed to enable in ep\n");
> +		goto err;
> +	}
> +	f_fb->out_req->complete = rx_handler_command;
> +
> +	f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
> +	if (!f_fb->in_req) {
> +		puts("failed alloc req in\n");
> +		goto err;
> +	}
> +	f_fb->in_req->complete = fastboot_complete;
> +
> +	ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
> +	if (ret)
> +		goto err;
> +
> +	return 0;
> +err:
> +	fastboot_disable(f);
> +	return -1;

	Here,I would also encourage to use appropriate error code from
	<errno.h>.

> +}
> +
> +int fastboot_add(struct usb_configuration *c)
> +{
> +	struct f_fastboot *f_fb = fastboot_func;
> +	int status;
> +
> +	debug("%s: cdev: 0x%p\n", __func__, c->cdev);
> +
> +	if (!f_fb) {
> +		f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE,
> sizeof(*f_fb));
> +		if (!f_fb)
> +			return -ENOMEM;
> +
> +		fastboot_func = f_fb;
> +		memset(f_fb, 0, sizeof(*f_fb));
> +	}
> +
> +	f_fb->usb_function.name = "f_fastboot";
> +	f_fb->usb_function.hs_descriptors = fb_runtime_descs;
> +	f_fb->usb_function.bind = fastboot_bind;
> +	f_fb->usb_function.unbind = fastboot_unbind;
> +	f_fb->usb_function.set_alt = fastboot_set_alt;
> +	f_fb->usb_function.disable = fastboot_disable;
> +	f_fb->usb_function.strings = fastboot_strings;
> +
> +	status = usb_add_function(c, &f_fb->usb_function);
> +	if (status) {
> +		free(f_fb);
> +		fastboot_func = f_fb;
> +	}
> +
> +	return status;
> +}
> +
> +int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
> +{
> +	struct usb_request *in_req = fastboot_func->in_req;
> +	int ret;
> +
> +	if (in_req->complete == NULL)
> +		in_req->complete = fastboot_complete;
> +
> +	memcpy(in_req->buf, buffer, buffer_size);
> +	in_req->length = buffer_size;
> +	ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
> +	if (ret)
> +		printf("Error %d on queue\n", ret);
> +	return 0;
> +}
> +
> +static int fastboot_tx_write_str(const char *buffer)
> +{
> +	return fastboot_tx_write(buffer, strlen(buffer));
> +}
> +
> +static void compl_do_reset(struct usb_ep *ep, struct usb_request
> *req) +{
> +	do_reset(NULL, 0, 0, NULL);
> +}
> +
> +static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
> +{
> +	fastboot_func->in_req->complete = compl_do_reset;
> +	fastboot_tx_write_str("OKAY");
> +}
> +
> +static int strcmp_l1(const char *s1, const char *s2)
> +{
> +	if (!s1 || !s2)
> +		return -1;
> +	return strncmp(s1, s2, strlen(s1));
> +}
> +
> +static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
> +{
> +	char *cmd = req->buf;
> +	char response[RESPONSE_LEN];
> +	const char *s;
> +
> +	strcpy(response, "OKAY");
> +	strsep(&cmd, ":");
> +	if (!cmd) {
> +		fastboot_tx_write_str("FAILmissing var");
> +		return;
> +	}
> +
> +	if (!strcmp_l1("version", cmd)) {
> +		strncat(response, FASTBOOT_VERSION,
> sizeof(response));
> +	} else if (!strcmp_l1("bootloader-version", cmd)) {
> +		strncat(response, U_BOOT_VERSION, sizeof(response));
> +	} else if (!strcmp_l1("downloadsize", cmd)) {
> +		char str_num[12];
> +
> +		sprintf(str_num, "%08x",
> CONFIG_USB_FASTBOOT_BUF_SIZE);
> +		strncat(response, str_num, sizeof(response));
> +	} else if (!strcmp_l1("serialno", cmd)) {
> +		s = getenv("serial#");
> +		if (s)
> +			strncat(response, s, sizeof(response));
> +		else
> +			strcpy(response, "FAILValue not set");
> +	} else {
> +		strcpy(response, "FAILVariable not implemented");
> +	}
> +	fastboot_tx_write_str(response);
> +}
> +
> +static unsigned int rx_bytes_expected(void)
> +{
> +	int rx_remain = download_size - download_bytes;
> +	if (rx_remain < 0)
> +		return 0;
> +	if (rx_remain > EP_BUFFER_SIZE)
> +		return EP_BUFFER_SIZE;
> +	return rx_remain;
> +}
> +
> +#define BYTES_PER_DOT	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 *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes,

	Please consider using dfu_get_buf() from dfu.c to provide
	dynamically allocated and controlled buffer instead of the
	CONFIG_USB_FASTBOOT_BUF_ADDR and _SIZE.

	Another advantage of this code is the ability to set
	"dfu_bufsiz" env variable with size of the buffer.

> +	       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)) {
> +		putc('.');
> +		if (!(download_bytes % (74 * BYTES_PER_DOT)))
> +			putc('\n');
> +	}
> +	req->actual = 0;
> +	usb_ep_queue(ep, req, 0);
> +}
> +
> +static void cb_download(struct usb_ep *ep, struct usb_request *req)
> +{
> +	char *cmd = req->buf;
> +	char response[RESPONSE_LEN];
> +
> +	strsep(&cmd, ":");
> +	download_size = simple_strtoul(cmd, NULL, 16);
> +	download_bytes = 0;
> +
> +	printf("Starting download of %d bytes\n", download_size);
> +
> +	if (0 == download_size) {
> +		sprintf(response, "FAILdata invalid size");
> +	} else if (download_size > CONFIG_USB_FASTBOOT_BUF_SIZE) {
> +		download_size = 0;
> +		sprintf(response, "FAILdata too large");
> +	} else {
> +		sprintf(response, "DATA%08x", download_size);
> +		req->complete = rx_handler_dl_image;
> +		req->length = rx_bytes_expected();
> +	}
> +	fastboot_tx_write_str(response);
> +}
> +
> +static void do_bootm_on_complete(struct usb_ep *ep, struct
> usb_request *req) +{
> +	char boot_addr_start[12];
> +	char *bootm_args[] = { "bootm", boot_addr_start, NULL };
> +
> +	req->complete = NULL;
> +	g_dnl_unregister();
> +	puts("Booting kernel..\n");
> +
> +	sprintf(boot_addr_start, "0x%lx", load_addr);
> +	do_bootm(NULL, 0, 2, bootm_args);
> +
> +	/* This only happens if image is somehow faulty so we start
> over */
> +	do_reset(NULL, 0, 0, NULL);
> +}
> +
> +static void cb_boot(struct usb_ep *ep, struct usb_request *req)
> +{
> +	fastboot_func->in_req->complete = do_bootm_on_complete;
> +	fastboot_tx_write_str("OKAY");
> +}
> +
> +struct cmd_dispatch_info {
> +	char *cmd;
> +	void (*cb)(struct usb_ep *ep, struct usb_request *req);
> +};
> +
> +static const struct cmd_dispatch_info cmd_dispatch_info[] = {
> +	{
> +		.cmd = "reboot",
> +		.cb = cb_reboot,
> +	}, {
> +		.cmd = "getvar:",
> +		.cb = cb_getvar,
> +	}, {
> +		.cmd = "download:",
> +		.cb = cb_download,
> +	}, {
> +		.cmd = "boot",
> +		.cb = cb_boot,
> +	},
> +};
> +
> +static void rx_handler_command(struct usb_ep *ep, struct usb_request
> *req) +{
> +	char 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/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c
> index dd95afe..dbea322 100644
> --- a/drivers/usb/gadget/g_dnl.c
> +++ b/drivers/usb/gadget/g_dnl.c
> @@ -17,6 +17,7 @@
>  #include <usb_mass_storage.h>
>  #include <dfu.h>
>  #include <thor.h>
> +#include <fastboot.h>
>  
>  #include "gadget_chips.h"
>  #include "composite.c"
> @@ -117,6 +118,8 @@ static int g_dnl_do_config(struct
> usb_configuration *c) ret = fsg_add(c);
>  	else if (!strcmp(s, "usb_dnl_thor"))
>  		ret = thor_add(c);
> +	else if (!strcmp(s, "usb_dnl_fastboot"))
> +		ret = fastboot_add(c);
>  
>  	return ret;
>  }
> @@ -242,6 +245,9 @@ int g_dnl_register(const char *type)
>  	} else if (!strcmp(type, "thor")) {
>  		strcpy(name, shortname);
>  		strcat(name, type);
> +	} else if (!strcmp(type, "fastboot")) {
> +		strcpy(name, shortname);
> +		strcat(name, type);
>  	} else {
>  		printf("%s: unknown command: %s\n", __func__, type);
>  		return -EINVAL;
> diff --git a/include/fastboot.h b/include/fastboot.h
> new file mode 100644
> index 0000000..3c26bd6
> --- /dev/null
> +++ b/include/fastboot.h
> @@ -0,0 +1,22 @@
> +/*
> + * (C) Copyright 2014 Linaro, Ltd.
> + * Rob Herring <robh at kernel.org>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef FASTBOOT_H
> +#define FASTBOOT_H
> +
> +#include <linux/usb/composite.h>
> +
> +#ifdef CONFIG_CMD_FASTBOOT
> +int fastboot_add(struct usb_configuration *c);
> +#else
> +static int fastboot_add(struct usb_configuration *c)
> +{
> +	return 0;
> +}
> +#endif
> +
> +#endif

One more remark regarding your patches.

There is an ongoing work done by Mateusz Zalega (CC'ed). 

Patch series
"[PATCH v4 00/13] DFU, MMC, Gadget, Goni, misc."
embracing below patch:
http://patchwork.ozlabs.org/patch/339263/

is changing the g_dnl interface.

I'm not the USB maintainer :-) (Marek Vasut is), but for me it seems
that above patches are more likely to be earlier accepted to u-boot-usb
tree. 

Marek, is this a correct assumption? 

If Marek don't mind, I'd like to ask you to rebase yours patches on top
of Mateusz work.


-- 
Best regards,

Lukasz Majewski

Samsung R&D Institute Poland (SRPOL) | Linux Platform Group


More information about the U-Boot mailing list