[U-Boot] [PATCH v3 0/5] Android Fastboot support

From: Rob Herring robh@kernel.org
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
The Android code used or referenced is BSD 2-clause license. This was originally raised by Wolfgang that it was not compatible with GPLv2+. I believe that has since been demonstrated and agreed that the BSD 2-clause license is compatible with u-boot.
As far as the history of the code, I have traced that back. The u-boot code started in 2008/2009 by Tom Rix @ Windriver. This initial support was then adopted and extended by TI (eMMC support primarily, not included here) in their OMAP u-boot tree[2]. In 2011, the TI code was used as a basis for upstream patches by Sebastian Siewior @ Linutronix. The code has been rearranged quite a bit since the original, but the content is pretty much the same. Some of the re-arranging left stale or missing copyrights in the v2 version which I have corrected.
I've reworked the previous version to make enabling board support more simple including re-using the existing settings for image loading address.
I've tested this series on a BeagleBoard.
Rob
[1] http://lists.denx.de/pipermail/u-boot/2011-November/110557.html [2] http://git.omapzoom.org/?p=repo/u-boot.git;a=commit;h=601ff71c8d46b5e90e1361...
Rob Herring (3): common: introduce maximum load size usb: handle NULL table in usb_gadget_get_string arm: beagle: enable Android fastboot support
Sebastian Siewior (2): image: add support for Android's boot image format usb/gadget: add the fastboot gadget
README | 3 + common/Makefile | 3 + common/board_r.c | 1 + common/cmd_bootm.c | 23 +- common/cmd_fastboot.c | 36 +++ common/image-android.c | 84 ++++++ common/image.c | 37 ++- doc/README.android-fastboot | 86 ++++++ doc/README.android-fastboot-protocol | 170 +++++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_fastboot.c | 535 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/g_fastboot.h | 15 + drivers/usb/gadget/u_fastboot.c | 260 +++++++++++++++++ drivers/usb/gadget/usbstring.c | 3 + include/android_image.h | 69 +++++ include/common.h | 1 + include/config_fallbacks.h | 4 + include/configs/omap3_beagle.h | 5 + include/image.h | 13 + include/usb/fastboot.h | 36 +++ 20 files changed, 1379 insertions(+), 6 deletions(-) create mode 100644 common/cmd_fastboot.c create mode 100644 common/image-android.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/android_image.h create mode 100644 include/usb/fastboot.h

From: Rob Herring robh@kernel.org
Various commands that load images have no checks that a loaded image does not exceed the available RAM space and will happily continue overwriting u-boot or other RAM that should not be touched. Also, some commands such as USB DFU or fastboot need to know the maximum buffer size, but there is no common way to define this.
Introduce a global load_size and environment variable loadsize to specify the size. The default is ~0UL which is effectively unlimited.
Signed-off-by: Rob Herring robh@kernel.org --- README | 3 +++ common/board_r.c | 1 + common/image.c | 17 +++++++++++++++++ include/common.h | 1 + include/config_fallbacks.h | 4 ++++ 5 files changed, 26 insertions(+)
diff --git a/README b/README index 39e05d3..45c0438 100644 --- a/README +++ b/README @@ -4871,6 +4871,9 @@ List of environment variables (most likely not complete): loadaddr - Default load address for commands like "bootp", "rarpboot", "tftpboot", "loadb" or "diskboot"
+ loadsize - Maximum load size for commands like "bootp", + "rarpboot", "tftpboot", "loadb" or "diskboot" + loads_echo - see CONFIG_LOADS_ECHO
serverip - TFTP server IP address; needed for tftpboot command diff --git a/common/board_r.c b/common/board_r.c index 8629a65..b420f43 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -451,6 +451,7 @@ static int initr_env(void)
/* Initialize from environment */ load_addr = getenv_ulong("loadaddr", 16, load_addr); + load_size = getenv_ulong("loadsize", 16, load_size); #if defined(CONFIG_SYS_EXTBDINFO) #if defined(CONFIG_405GP) || defined(CONFIG_405EP) #if defined(CONFIG_I2CFAST) diff --git a/common/image.c b/common/image.c index 9c6bec5..afbf806 100644 --- a/common/image.c +++ b/common/image.c @@ -396,6 +396,7 @@ static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, /*****************************************************************************/ #ifndef USE_HOSTCC ulong load_addr = CONFIG_SYS_LOAD_ADDR; /* Default Load Address */ +ulong load_size = CONFIG_SYS_LOAD_SIZE; /* Default Load Size */ ulong save_addr; /* Default Save Address */ ulong save_size; /* Default Save Size (in bytes) */
@@ -415,6 +416,22 @@ static int on_loadaddr(const char *name, const char *value, enum env_op op, } U_BOOT_ENV_CALLBACK(loadaddr, on_loadaddr);
+static int on_loadsize(const char *name, const char *value, enum env_op op, + int flags) +{ + switch (op) { + case env_op_create: + case env_op_overwrite: + load_size = simple_strtoul(value, NULL, 16); + break; + default: + break; + } + + return 0; +} +U_BOOT_ENV_CALLBACK(loadsize, on_loadsize); + ulong getenv_bootm_low(void) { char *s = getenv("bootm_low"); diff --git a/include/common.h b/include/common.h index cbd3c9e..80f366e 100644 --- a/include/common.h +++ b/include/common.h @@ -342,6 +342,7 @@ void flash_perror (int); int source (ulong addr, const char *fit_uname);
extern ulong load_addr; /* Default Load Address */ +extern ulong load_size; /* Default Load Size (maximum) */ extern ulong save_addr; /* Default Save Address */ extern ulong save_size; /* Default Save Size */
diff --git a/include/config_fallbacks.h b/include/config_fallbacks.h index e6fb47b..92a36f5 100644 --- a/include/config_fallbacks.h +++ b/include/config_fallbacks.h @@ -79,4 +79,8 @@ #define CONFIG_SYS_HZ 1000 #endif
+#ifndef CONFIG_SYS_LOAD_SIZE +#define CONFIG_SYS_LOAD_SIZE (~0UL) +#endif + #endif /* __CONFIG_FALLBACKS_H */

On Thu, Apr 10, 2014 at 02:18:03PM -0500, Rob Herring wrote:
From: Rob Herring robh@kernel.org
Various commands that load images have no checks that a loaded image does not exceed the available RAM space and will happily continue overwriting u-boot or other RAM that should not be touched. Also, some commands such as USB DFU or fastboot need to know the maximum buffer size, but there is no common way to define this.
Introduce a global load_size and environment variable loadsize to specify the size. The default is ~0UL which is effectively unlimited.
Signed-off-by: Rob Herring robh@kernel.org
Reviewed-by: Tom Rini trini@ti.com

Hi Rob,
From: Rob Herring robh@kernel.org
Various commands that load images have no checks that a loaded image does not exceed the available RAM space and will happily continue overwriting u-boot or other RAM that should not be touched. Also, some commands such as USB DFU or fastboot need to know the maximum buffer size, but there is no common way to define this.
Introduce a global load_size and environment variable loadsize to specify the size. The default is ~0UL which is effectively unlimited.
Signed-off-by: Rob Herring robh@kernel.org
README | 3 +++ common/board_r.c | 1 + common/image.c | 17 +++++++++++++++++ include/common.h | 1 + include/config_fallbacks.h | 4 ++++ 5 files changed, 26 insertions(+)
diff --git a/README b/README index 39e05d3..45c0438 100644 --- a/README +++ b/README @@ -4871,6 +4871,9 @@ List of environment variables (most likely not complete): loadaddr - Default load address for commands like "bootp", "rarpboot", "tftpboot", "loadb" or "diskboot"
loadsize - Maximum load size for commands like "bootp",
"rarpboot", "tftpboot", "loadb" or "diskboot"
loads_echo - see CONFIG_LOADS_ECHO
serverip - TFTP server IP address; needed for tftpboot
command diff --git a/common/board_r.c b/common/board_r.c index 8629a65..b420f43 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -451,6 +451,7 @@ static int initr_env(void)
/* Initialize from environment */ load_addr = getenv_ulong("loadaddr", 16, load_addr);
- load_size = getenv_ulong("loadsize", 16, load_size);
#if defined(CONFIG_SYS_EXTBDINFO) #if defined(CONFIG_405GP) || defined(CONFIG_405EP) #if defined(CONFIG_I2CFAST) diff --git a/common/image.c b/common/image.c index 9c6bec5..afbf806 100644 --- a/common/image.c +++ b/common/image.c @@ -396,6 +396,7 @@ static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, /*****************************************************************************/ #ifndef USE_HOSTCC ulong load_addr = CONFIG_SYS_LOAD_ADDR; /* Default Load Address */ +ulong load_size = CONFIG_SYS_LOAD_SIZE; /* Default Load Size */ ulong save_addr; /* Default Save Address */ ulong save_size; /* Default Save Size (in bytes) */ @@ -415,6 +416,22 @@ static int on_loadaddr(const char *name, const char *value, enum env_op op, } U_BOOT_ENV_CALLBACK(loadaddr, on_loadaddr);
+static int on_loadsize(const char *name, const char *value, enum env_op op,
- int flags)
+{
- switch (op) {
- case env_op_create:
- case env_op_overwrite:
load_size = simple_strtoul(value, NULL, 16);
break;
- default:
break;
- }
- return 0;
+} +U_BOOT_ENV_CALLBACK(loadsize, on_loadsize);
ulong getenv_bootm_low(void) { char *s = getenv("bootm_low"); diff --git a/include/common.h b/include/common.h index cbd3c9e..80f366e 100644 --- a/include/common.h +++ b/include/common.h @@ -342,6 +342,7 @@ void flash_perror (int); int source (ulong addr, const char *fit_uname);
extern ulong load_addr; /* Default Load Address */ +extern ulong load_size; /* Default Load Size (maximum) */ extern ulong save_addr; /* Default Save Address */ extern ulong save_size; /* Default Save Size */ diff --git a/include/config_fallbacks.h b/include/config_fallbacks.h index e6fb47b..92a36f5 100644 --- a/include/config_fallbacks.h +++ b/include/config_fallbacks.h @@ -79,4 +79,8 @@ #define CONFIG_SYS_HZ 1000 #endif
+#ifndef CONFIG_SYS_LOAD_SIZE +#define CONFIG_SYS_LOAD_SIZE (~0UL) +#endif
#endif /* __CONFIG_FALLBACKS_H */
Reviewed-by: Lukasz Majewski l.majewski@samsung.com

Dear Rob,
In message 1397157488-8695-2-git-send-email-robherring2@gmail.com you wrote:
Various commands that load images have no checks that a loaded image does not exceed the available RAM space and will happily continue overwriting u-boot or other RAM that should not be touched. Also, some commands such as USB DFU or fastboot need to know the maximum buffer size, but there is no common way to define this.
You are mixing some pretty much unrelated things here, which is IMO not a good idea. Limiting the size for load operations is one thing, but buffer size is something totally different. These must not be mixed up. Please keep in mind that there is (or at least should be) no need to load the whole file into a buffer. [Limitations of the implementation should be fixed rather than supported.]
Introduce a global load_size and environment variable loadsize to specify the size. The default is ~0UL which is effectively unlimited.
I think this feature should be made optional. I don;t think I want to have this on all systems.
- loadsize - Maximum load size for commands like "bootp",
"rarpboot", "tftpboot", "loadb" or "diskboot"
Um... what makes these operations different from any other operations that read images or other data to memory? Like loading from NOR or NAND flash, from USB, IDE or SATA storage devices, from any of the supported file system types?
I feel we should either support this consequently everywhere, or not at all. My personal preference is the second option, as otherwise you will just add a lot of code in too many places for too little benefit.
Best regards,
Wolfgang Denk

On Tue, Apr 15, 2014 at 4:59 PM, Wolfgang Denk wd@denx.de wrote:
Dear Rob,
In message 1397157488-8695-2-git-send-email-robherring2@gmail.com you wrote:
Various commands that load images have no checks that a loaded image does not exceed the available RAM space and will happily continue overwriting u-boot or other RAM that should not be touched. Also, some commands such as USB DFU or fastboot need to know the maximum buffer size, but there is no common way to define this.
You are mixing some pretty much unrelated things here, which is IMO not a good idea. Limiting the size for load operations is one thing, but buffer size is something totally different. These must not be mixed up. Please keep in mind that there is (or at least should be) no need to load the whole file into a buffer. [Limitations of the implementation should be fixed rather than supported.]
That is true if you are loading files to write to storage, but if you are loading them to boot like tftp, diskboot, fsload, fastboot, etc. all do, then you should have some checks in place. Otherwise you can just load things over RAM u-boot is using.
fastboot is loading images to boot just like other commands that use loadaddr, but has the additional need to know the max size. Rather than add yet another custom define, I was looking at how to solve this generically. I considered perhaps this should be based on LMB/bootmap? But LMB is more about what the OS can use rather than what memory is available.
Introduce a global load_size and environment variable loadsize to specify the size. The default is ~0UL which is effectively unlimited.
I think this feature should be made optional. I don;t think I want to have this on all systems.
- loadsize - Maximum load size for commands like "bootp",
"rarpboot", "tftpboot", "loadb" or "diskboot"
Um... what makes these operations different from any other operations that read images or other data to memory? Like loading from NOR or NAND flash, from USB, IDE or SATA storage devices, from any of the supported file system types?
They are not. I just copied the text from loadaddr, but any command that uses load_addr is missing any upper bounds checking.
I feel we should either support this consequently everywhere, or not at all. My personal preference is the second option, as otherwise you will just add a lot of code in too many places for too little benefit.
I believe the former will make things more robust and I would like to see this for all commands that load something to memory, but we should agree on the approach first.
Rob
Best regards,
Wolfgang Denk
-- DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de No man knows what true happiness is until he gets married. By then, of course, its too late.

Dear Rob Herring,
In message CAL_JsqJ3mDsUoqqDL93Aa6OiLVpscVs3M_ixjgF0O+yfkftgEA@mail.gmail.com you wrote:
You are mixing some pretty much unrelated things here, which is IMO not a good idea. Limiting the size for load operations is one thing, but buffer size is something totally different. These must not be mixed up. Please keep in mind that there is (or at least should be) no need to load the whole file into a buffer. [Limitations of the implementation should be fixed rather than supported.]
That is true if you are loading files to write to storage, but if you are loading them to boot like tftp, diskboot, fsload, fastboot, etc. all do, then you should have some checks in place. Otherwise you can just load things over RAM u-boot is using.
This can always happen - like when the user sets the maximum load size too big. Actually the concept of a maximum load size is not working at all, as it takes not into account _where_ you load the image to; when loading it low in RAM the allowable size is different from when loading to a higher address. You cannot tell in advance how much RAM you can actually use - the stack is growing downward, and we don't have any hard limits on the stack size. And even if the loading does not collide with the stack, you don't know if the stack will not grow urther down when you process the image, in which case it would get overwritten _after_ loading, so your test would not catch it either.
fastboot is loading images to boot just like other commands that use loadaddr, but has the additional need to know the max size. Rather
Don't you see that the max actually available size depends on loadaddr, so you cannot set one without considering the other?
Um... what makes these operations different from any other operations that read images or other data to memory? Like loading from NOR or NAND flash, from USB, IDE or SATA storage devices, from any of the supported file system types?
They are not. I just copied the text from loadaddr, but any command that uses load_addr is missing any upper bounds checking.
Yes, and this is intentionally. There is no clean, reliable way to implement this features, so instead of providing some deceptive security we rely on the user knowing what he is doing.
I feel we should either support this consequently everywhere, or not at all. My personal preference is the second option, as otherwise you will just add a lot of code in too many places for too little benefit.
I believe the former will make things more robust and I would like to see this for all commands that load something to memory, but we should agree on the approach first.
Well, we need one that actually works first.
Best regards,
Wolfgang Denk

From: Rob Herring robh@kernel.org
Allow a NULL table to be passed to usb_gadget_get_string for cases when a string table may not be populated.
Signed-off-by: Rob Herring robh@kernel.org --- drivers/usb/gadget/usbstring.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index de5fa3f..8c3ff64 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -108,6 +108,9 @@ usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) struct usb_string *s; int len;
+ if (!table) + return -EINVAL; + /* descriptor 0 has the language id */ if (id == 0) { buf[0] = 4;

On Thu, Apr 10, 2014 at 02:18:04PM -0500, Rob Herring wrote:
From: Rob Herring robh@kernel.org
Allow a NULL table to be passed to usb_gadget_get_string for cases when a string table may not be populated.
Signed-off-by: Rob Herring robh@kernel.org
Reviewed-by: Tom Rini trini@ti.com

On Thursday, April 10, 2014 at 09:18:04 PM, Rob Herring wrote:
From: Rob Herring robh@kernel.org
Allow a NULL table to be passed to usb_gadget_get_string for cases when a string table may not be populated.
Signed-off-by: Rob Herring robh@kernel.org
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

Hi Rob,
From: Rob Herring robh@kernel.org
Allow a NULL table to be passed to usb_gadget_get_string for cases when a string table may not be populated.
I might be wrong, since I'm not the native speaker, but this description is a bit misleading.
For me this patch is supposed to prevent from using uninitialized string table in this function.
Signed-off-by: Rob Herring robh@kernel.org
drivers/usb/gadget/usbstring.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index de5fa3f..8c3ff64 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -108,6 +108,9 @@ usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) struct usb_string *s; int len;
- if (!table)
return -EINVAL;
- /* descriptor 0 has the language id */ if (id == 0) { buf[0] = 4;
Despite the problem with parsing commit message :-)
Acked-by: Lukasz Majewski l.majewski@samsung.com

From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" is currently ignored. I haven't found anything that is creating this.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes: - Rebased to current mainline - Moved android image handling to separate functions in common/image-android.c - s/u8/char/ in header to fix string function warnings - Use SPDX identifiers for licenses - Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code.
Cc: Wolfgang Denk wd@denx.de Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org --- common/Makefile | 1 + common/cmd_bootm.c | 23 +++++++++++++- common/image-android.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ common/image.c | 20 +++++++++--- include/android_image.h | 69 ++++++++++++++++++++++++++++++++++++++++ include/image.h | 13 ++++++++ 6 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 common/image-android.c create mode 100644 include/android_image.h
diff --git a/common/Makefile b/common/Makefile index cecd81a..da208f3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -236,6 +236,7 @@ obj-y += console.o obj-$(CONFIG_CROS_EC) += cros_ec.o obj-y += dlmalloc.o obj-y += image.o +obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_OF_LIBFDT) += image-fdt.o obj-$(CONFIG_FIT) += image-fit.o obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 9751edc..b6c8288 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -223,6 +223,8 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, { const void *os_hdr;
+ images.ep = ~0UL; + /* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start, &images.os.image_len); @@ -274,6 +276,17 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, } break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + images.os.type = IH_TYPE_KERNEL; + images.os.comp = IH_COMP_NONE; + images.os.os = IH_OS_LINUX; + images.ep = images.os.load; + + images.os.end = android_image_get_end(os_hdr); + images.os.load = android_image_get_kload(os_hdr); + break; +#endif default: puts("ERROR: unknown image format type!\n"); return 1; @@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1; } #endif - } else { + } else if (images.ep == ~0UL) { puts("Could not find kernel entry point!\n"); return 1; } @@ -1002,6 +1015,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, images->fit_noffset_os = os_noffset; break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + printf("## Booting Android Image at 0x%08lx ...\n", img_addr); + if (android_image_get_kernel((void *)img_addr, images->verify, + os_data, os_len)) + return NULL; + break; +#endif default: printf("Wrong Image Format for %s command\n", cmdtp->name); bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); diff --git a/common/image-android.c b/common/image-android.c new file mode 100644 index 0000000..ec6fb3d --- /dev/null +++ b/common/image-android.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <image.h> +#include <android_image.h> + +static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; + +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len) +{ + /* + * Not all Android tools use the id field for signing the image with + * sha1 (or anything) so we don't check it. It is not obvious that the + * string is null terminated so we take care of this. + */ + strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE); + andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0'; + if (strlen(andr_tmp_str)) + printf("Android's image name: %s\n", andr_tmp_str); + + printf("Kernel load addr 0x%08x size %u KiB\n", + hdr->kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024)); + strncpy(andr_tmp_str, hdr->cmdline, ANDR_BOOT_ARGS_SIZE); + andr_tmp_str[ANDR_BOOT_ARGS_SIZE] = '\0'; + if (strlen(andr_tmp_str)) { + printf("Kernel command line: %s\n", andr_tmp_str); + setenv("bootargs", andr_tmp_str); + } + if (hdr->ramdisk_size) + printf("RAM disk load addr 0x%08x size %u KiB\n", + hdr->ramdisk_addr, + DIV_ROUND_UP(hdr->ramdisk_size, 1024)); + + if (os_data) { + *os_data = (ulong)hdr; + *os_data += hdr->page_size; + } + if (os_len) + *os_len = hdr->kernel_size; + return 0; +} + +int android_image_check_header(const struct andr_img_hdr *hdr) +{ + return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); +} + +ulong android_image_get_end(const struct andr_img_hdr *hdr) +{ + u32 size = 0; + /* + * The header takes a full page, the remaining components are aligned + * on page boundary + */ + size += hdr->page_size; + size += ALIGN(hdr->kernel_size, hdr->page_size); + size += ALIGN(hdr->ramdisk_size, hdr->page_size); + size += ALIGN(hdr->second_size, hdr->page_size); + + return size; +} + +ulong android_image_get_kload(const struct andr_img_hdr *hdr) +{ + return hdr->kernel_addr; +} + +int andriod_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len) +{ + if (!hdr->ramdisk_size) + return -1; + *rd_data = (unsigned long)hdr; + *rd_data += hdr->page_size; + *rd_data += ALIGN(hdr->kernel_size, hdr->page_size); + + *rd_len = hdr->ramdisk_size; + return 0; +} diff --git a/common/image.c b/common/image.c index afbf806..b6063f6 100644 --- a/common/image.c +++ b/common/image.c @@ -676,10 +676,12 @@ int genimg_get_format(const void *img_addr) if (image_check_magic(hdr)) format = IMAGE_FORMAT_LEGACY; #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) - else { - if (fdt_check_header(img_addr) == 0) - format = IMAGE_FORMAT_FIT; - } + else if (fdt_check_header(img_addr) == 0) + format = IMAGE_FORMAT_FIT; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + else if (android_image_check_header(img_addr) == 0) + format = IMAGE_FORMAT_ANDROID; #endif
return format; @@ -949,7 +951,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); - } else { + } +#ifdef CONFIG_ANDROID_BOOT_IMAGE + else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) && + (!andriod_image_get_ramdisk((void *)images->os.start, + &rd_data, &rd_len))) { + /* empty */ + } +#endif + else { /* * no initrd image */ diff --git a/include/android_image.h b/include/android_image.h new file mode 100644 index 0000000..094d60a --- /dev/null +++ b/include/android_image.h @@ -0,0 +1,69 @@ +/* + * This is from the Android Project, + * Repository: https://android.googlesource.com/platform/bootable/bootloader/legacy + * File: include/boot/bootimg.h + * Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a + * + * Copyright (C) 2008 The Android Open Source Project + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _ANDROID_IMAGE_H_ +#define _ANDROID_IMAGE_H_ + +#define ANDR_BOOT_MAGIC "ANDROID!" +#define ANDR_BOOT_MAGIC_SIZE 8 +#define ANDR_BOOT_NAME_SIZE 16 +#define ANDR_BOOT_ARGS_SIZE 512 + +struct andr_img_hdr { + char magic[ANDR_BOOT_MAGIC_SIZE]; + + u32 kernel_size; /* size in bytes */ + u32 kernel_addr; /* physical load addr */ + + u32 ramdisk_size; /* size in bytes */ + u32 ramdisk_addr; /* physical load addr */ + + u32 second_size; /* size in bytes */ + u32 second_addr; /* physical load addr */ + + u32 tags_addr; /* physical addr for kernel tags */ + u32 page_size; /* flash page size we assume */ + u32 unused[2]; /* future expansion: should be 0 */ + + char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */ + + char cmdline[ANDR_BOOT_ARGS_SIZE]; + + u32 id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +/* + * +-----------------+ + * | boot header | 1 page + * +-----------------+ + * | kernel | n pages + * +-----------------+ + * | ramdisk | m pages + * +-----------------+ + * | second stage | o pages + * +-----------------+ + * + * n = (kernel_size + page_size - 1) / page_size + * m = (ramdisk_size + page_size - 1) / page_size + * o = (second_size + page_size - 1) / page_size + * + * 0. all entities are page_size aligned in flash + * 1. kernel and ramdisk are required (size != 0) + * 2. second is optional (second_size == 0 -> no second) + * 3. load each element (kernel, ramdisk, second) at + * the specified physical address (kernel_addr, etc) + * 4. prepare tags at tag_addr. kernel_args[] is + * appended to the kernel commandline in the tags. + * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr + * 6. if second_size != 0: jump to second_addr + * else: jump to kernel_addr + */ +#endif diff --git a/include/image.h b/include/image.h index 6afd57b..b123860 100644 --- a/include/image.h +++ b/include/image.h @@ -403,6 +403,7 @@ enum fit_load_op { #define IMAGE_FORMAT_INVALID 0x00 #define IMAGE_FORMAT_LEGACY 0x01 /* legacy image_header based format */ #define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format */ +#define IMAGE_FORMAT_ANDROID 0x03 /* Android boot image */
int genimg_get_format(const void *img_addr); int genimg_has_config(bootm_headers_t *images); @@ -996,4 +997,16 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) #endif /* CONFIG_FIT_VERBOSE */ #endif /* CONFIG_FIT */
+#if defined(CONFIG_ANDROID_BOOT_IMAGE) +struct andr_img_hdr; +int android_image_check_header(const struct andr_img_hdr *hdr); +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len); +int andriod_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len); +ulong android_image_get_end(const struct andr_img_hdr *hdr); +ulong android_image_get_kload(const struct andr_img_hdr *hdr); + +#endif /* CONFIG_ANDROID_BOOT_IMAGE */ + #endif /* __IMAGE_H__ */

On Thu, Apr 10, 2014 at 02:18:05PM -0500, Rob Herring wrote:
From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" is currently ignored. I haven't found anything that is creating this.
Reviewed-by: Tom Rini trini@ti.com

On Thursday, April 10, 2014 at 09:18:05 PM, Rob Herring wrote:
From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" is currently ignored. I haven't found anything that is creating this.
Can you please elaborate on those last two sentences ?
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes:
- Rebased to current mainline
- Moved android image handling to separate functions in common/image-android.c
- s/u8/char/ in header to fix string function warnings
- Use SPDX identifiers for licenses
- Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code.
[...]
@@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1; } #endif
- } else {
- } else if (images.ep == ~0UL) {
I don't find this really portable. While it's unlikely the kernel will have the EP here, don't we have a better solution than using special value?
[...]
@@ -949,7 +951,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len);
- } else {
- }
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
- else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) &&
(!andriod_image_get_ramdisk((void *)images->os.start,
andriod_image_get_ramdisk() ? There is a typo in the function name, did you actually ever even compile this patchset please ? [...]

On Fri, Apr 11, 2014 at 4:44 PM, Marek Vasut marex@denx.de wrote:
On Thursday, April 10, 2014 at 09:18:05 PM, Rob Herring wrote:
From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" is currently ignored. I haven't found anything that is creating this.
Can you please elaborate on those last two sentences ?
The android header has 3 images: kernel, ramdisk and "second". I think this is for secondary bootloader, but I'll have to investigate.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes:
- Rebased to current mainline
- Moved android image handling to separate functions in common/image-android.c
- s/u8/char/ in header to fix string function warnings
- Use SPDX identifiers for licenses
- Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code.
[...]
@@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1; } #endif
} else {
} else if (images.ep == ~0UL) {
I don't find this really portable. While it's unlikely the kernel will have the EP here, don't we have a better solution than using special value?
This is to address Wolfgang's prior comments about moving setting of images.ep into the switch statement above. Do you like the previous version better?
[...]
@@ -949,7 +951,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len);
} else {
}
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) &&
(!andriod_image_get_ramdisk((void *)images->os.start,
andriod_image_get_ramdisk() ? There is a typo in the function name, did you actually ever even compile this patchset please ? [...]
Of course it compiles as the same typo is everywhere for this function... :) I've compiled and run this (although I have not tested with a ramdisk).
Rob

On Saturday, April 12, 2014 at 11:54:10 PM, Rob Herring wrote:
On Fri, Apr 11, 2014 at 4:44 PM, Marek Vasut marex@denx.de wrote:
On Thursday, April 10, 2014 at 09:18:05 PM, Rob Herring wrote:
From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct
- its defines are not generic but have something like a namespace. The
header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" is currently ignored. I haven't found anything that is creating this.
Can you please elaborate on those last two sentences ?
The android header has 3 images: kernel, ramdisk and "second". I think this is for secondary bootloader, but I'll have to investigate.
Viva hardcoded b/s :'-(
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes:
Rebased to current mainline
Moved android image handling to separate functions in
common/image-android.c
s/u8/char/ in header to fix string function warnings
Use SPDX identifiers for licenses
Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code.
[...]
@@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1;
}
#endif
} else {
} else if (images.ep == ~0UL) {
I don't find this really portable. While it's unlikely the kernel will have the EP here, don't we have a better solution than using special value?
This is to address Wolfgang's prior comments about moving setting of images.ep into the switch statement above. Do you like the previous version better?
I think I might be missing that part of the discussion. My point is just that you might rather have a separate variable to detect error instead of having a special value like this.
Does my rant make sense to you please ?
[...]
@@ -949,7 +951,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len);
} else {
}
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) &&
(!andriod_image_get_ramdisk((void *)images->os.start,
andriod_image_get_ramdisk() ? There is a typo in the function name, did you actually ever even compile this patchset please ? [...]
Of course it compiles as the same typo is everywhere for this function... :) I've compiled and run this (although I have not tested with a ramdisk).
Okay, thanks for clearing this up (and fixing this in v4)!

Hi Rob,
From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" is currently ignored. I haven't found anything that is creating this.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes:
- Rebased to current mainline
- Moved android image handling to separate functions in common/image-android.c
- s/u8/char/ in header to fix string function warnings
- Use SPDX identifiers for licenses
- Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code.
Cc: Wolfgang Denk wd@denx.de Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org
common/Makefile | 1 + common/cmd_bootm.c | 23 +++++++++++++- common/image-android.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ common/image.c | 20 +++++++++--- include/android_image.h | 69 ++++++++++++++++++++++++++++++++++++++++ include/image.h | 13 ++++++++ 6 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 common/image-android.c create mode 100644 include/android_image.h
diff --git a/common/Makefile b/common/Makefile index cecd81a..da208f3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -236,6 +236,7 @@ obj-y += console.o obj-$(CONFIG_CROS_EC) += cros_ec.o obj-y += dlmalloc.o obj-y += image.o +obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_OF_LIBFDT) += image-fdt.o obj-$(CONFIG_FIT) += image-fit.o obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 9751edc..b6c8288 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -223,6 +223,8 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, { const void *os_hdr;
- images.ep = ~0UL;
- /* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start,
&images.os.image_len); @@ -274,6 +276,17 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, } break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE
- case IMAGE_FORMAT_ANDROID:
images.os.type = IH_TYPE_KERNEL;
images.os.comp = IH_COMP_NONE;
images.os.os = IH_OS_LINUX;
images.ep = images.os.load;
images.os.end = android_image_get_end(os_hdr);
images.os.load = android_image_get_kload(os_hdr);
break;
+#endif default: puts("ERROR: unknown image format type!\n"); return 1; @@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1; } #endif
- } else {
- } else if (images.ep == ~0UL) { puts("Could not find kernel entry point!\n"); return 1; }
@@ -1002,6 +1015,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, images->fit_noffset_os = os_noffset; break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE
- case IMAGE_FORMAT_ANDROID:
printf("## Booting Android Image at 0x%08lx ...\n",
img_addr);
if (android_image_get_kernel((void *)img_addr,
images->verify,
os_data, os_len))
return NULL;
break;
+#endif default: printf("Wrong Image Format for %s command\n", cmdtp->name); bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); diff --git a/common/image-android.c b/common/image-android.c new file mode 100644 index 0000000..ec6fb3d --- /dev/null +++ b/common/image-android.c @@ -0,0 +1,84 @@ +/*
- Copyright (c) 2011 Sebastian Andrzej Siewior
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <image.h> +#include <android_image.h>
+static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1];
+int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
ulong *os_data, ulong *os_len)
+{
- /*
* Not all Android tools use the id field for signing the
image with
* sha1 (or anything) so we don't check it. It is not
obvious that the
* string is null terminated so we take care of this.
*/
- strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE);
- andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0';
- if (strlen(andr_tmp_str))
printf("Android's image name: %s\n", andr_tmp_str);
- printf("Kernel load addr 0x%08x size %u KiB\n",
hdr->kernel_addr, DIV_ROUND_UP(hdr->kernel_size,
1024));
- strncpy(andr_tmp_str, hdr->cmdline, ANDR_BOOT_ARGS_SIZE);
- andr_tmp_str[ANDR_BOOT_ARGS_SIZE] = '\0';
- if (strlen(andr_tmp_str)) {
printf("Kernel command line: %s\n", andr_tmp_str);
setenv("bootargs", andr_tmp_str);
- }
- if (hdr->ramdisk_size)
printf("RAM disk load addr 0x%08x size %u KiB\n",
hdr->ramdisk_addr,
DIV_ROUND_UP(hdr->ramdisk_size, 1024));
- if (os_data) {
*os_data = (ulong)hdr;
*os_data += hdr->page_size;
- }
- if (os_len)
*os_len = hdr->kernel_size;
- return 0;
+}
+int android_image_check_header(const struct andr_img_hdr *hdr) +{
- return memcmp(ANDR_BOOT_MAGIC, hdr->magic,
ANDR_BOOT_MAGIC_SIZE); +}
+ulong android_image_get_end(const struct andr_img_hdr *hdr) +{
- u32 size = 0;
- /*
* The header takes a full page, the remaining components
are aligned
* on page boundary
*/
- size += hdr->page_size;
- size += ALIGN(hdr->kernel_size, hdr->page_size);
- size += ALIGN(hdr->ramdisk_size, hdr->page_size);
- size += ALIGN(hdr->second_size, hdr->page_size);
- return size;
+}
+ulong android_image_get_kload(const struct andr_img_hdr *hdr) +{
- return hdr->kernel_addr;
+}
+int andriod_image_get_ramdisk(const struct andr_img_hdr *hdr,
ulong *rd_data, ulong *rd_len)
+{
- if (!hdr->ramdisk_size)
return -1;
- *rd_data = (unsigned long)hdr;
- *rd_data += hdr->page_size;
- *rd_data += ALIGN(hdr->kernel_size, hdr->page_size);
- *rd_len = hdr->ramdisk_size;
- return 0;
+} diff --git a/common/image.c b/common/image.c index afbf806..b6063f6 100644 --- a/common/image.c +++ b/common/image.c @@ -676,10 +676,12 @@ int genimg_get_format(const void *img_addr) if (image_check_magic(hdr)) format = IMAGE_FORMAT_LEGACY; #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT)
- else {
if (fdt_check_header(img_addr) == 0)
format = IMAGE_FORMAT_FIT;
- }
- else if (fdt_check_header(img_addr) == 0)
format = IMAGE_FORMAT_FIT;
+#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE
- else if (android_image_check_header(img_addr) == 0)
format = IMAGE_FORMAT_ANDROID;
#endif
return format; @@ -949,7 +951,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1,
&rd_data, &rd_len);
- } else {
- }
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
- else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID)
&&
(!andriod_image_get_ramdisk((void
*)images->os.start,
&rd_data, &rd_len))) {
/* empty */
- }
+#endif
- else { /*
*/
- no initrd image
diff --git a/include/android_image.h b/include/android_image.h new file mode 100644 index 0000000..094d60a --- /dev/null +++ b/include/android_image.h @@ -0,0 +1,69 @@ +/*
- This is from the Android Project,
- Repository:
https://android.googlesource.com/platform/bootable/bootloader/legacy
- File: include/boot/bootimg.h
- Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a
- Copyright (C) 2008 The Android Open Source Project
- SPDX-License-Identifier: BSD-2-Clause
- */
+#ifndef _ANDROID_IMAGE_H_ +#define _ANDROID_IMAGE_H_
+#define ANDR_BOOT_MAGIC "ANDROID!" +#define ANDR_BOOT_MAGIC_SIZE 8 +#define ANDR_BOOT_NAME_SIZE 16 +#define ANDR_BOOT_ARGS_SIZE 512
+struct andr_img_hdr {
- char magic[ANDR_BOOT_MAGIC_SIZE];
- u32 kernel_size; /* size in bytes */
- u32 kernel_addr; /* physical load addr */
- u32 ramdisk_size; /* size in bytes */
- u32 ramdisk_addr; /* physical load addr */
- u32 second_size; /* size in bytes */
- u32 second_addr; /* physical load addr */
- u32 tags_addr; /* physical addr for kernel
tags */
- u32 page_size; /* flash page size we assume */
- u32 unused[2]; /* future expansion: should be
0 */ +
- char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */
- char cmdline[ANDR_BOOT_ARGS_SIZE];
- u32 id[8]; /* timestamp / checksum / sha1 / etc */
+};
+/*
- +-----------------+
- | boot header | 1 page
- +-----------------+
- | kernel | n pages
- +-----------------+
- | ramdisk | m pages
- +-----------------+
- | second stage | o pages
- +-----------------+
- n = (kernel_size + page_size - 1) / page_size
- m = (ramdisk_size + page_size - 1) / page_size
- o = (second_size + page_size - 1) / page_size
- all entities are page_size aligned in flash
- kernel and ramdisk are required (size != 0)
- second is optional (second_size == 0 -> no second)
- load each element (kernel, ramdisk, second) at
- the specified physical address (kernel_addr, etc)
- prepare tags at tag_addr. kernel_args[] is
- appended to the kernel commandline in the tags.
- r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
- if second_size != 0: jump to second_addr
- else: jump to kernel_addr
- */
+#endif diff --git a/include/image.h b/include/image.h index 6afd57b..b123860 100644 --- a/include/image.h +++ b/include/image.h @@ -403,6 +403,7 @@ enum fit_load_op { #define IMAGE_FORMAT_INVALID 0x00 #define IMAGE_FORMAT_LEGACY 0x01 /* legacy image_header based format */ #define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format */ +#define IMAGE_FORMAT_ANDROID 0x03 /* Android boot image */ int genimg_get_format(const void *img_addr); int genimg_has_config(bootm_headers_t *images); @@ -996,4 +997,16 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) #endif /* CONFIG_FIT_VERBOSE */ #endif /* CONFIG_FIT */
+#if defined(CONFIG_ANDROID_BOOT_IMAGE) +struct andr_img_hdr; +int android_image_check_header(const struct andr_img_hdr *hdr); +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify,
ulong *os_data, ulong *os_len);
+int andriod_image_get_ramdisk(const struct andr_img_hdr *hdr,
ulong *rd_data, ulong *rd_len);
+ulong android_image_get_end(const struct andr_img_hdr *hdr); +ulong android_image_get_kload(const struct andr_img_hdr *hdr);
+#endif /* CONFIG_ANDROID_BOOT_IMAGE */
#endif /* __IMAGE_H__ */
Reviewed-by: Lukasz Majewski l.majewski@samsung.com

From: Sebastian Siewior bigeasy@linutronix.de
This patch contains an implementation of the fastboot protocol on the device side and a little of documentation. The gadget expects the new-style gadget framework. The gadget implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126798/ with the following changes: - Rebase to current mainline and updates for current gadget API - Use SPDX identifiers for licenses - Traced the history and added missing copyright to cmd_fastboot.c - Use load_addr/load_size for transfer buffer - Allow vendor strings to be optional - Set vendor/product ID from config defines - Allow Ctrl-C to exit fastboot mode
Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org --- common/Makefile | 2 + common/cmd_fastboot.c | 36 +++ doc/README.android-fastboot | 86 ++++++ doc/README.android-fastboot-protocol | 170 +++++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_fastboot.c | 535 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/g_fastboot.h | 15 + drivers/usb/gadget/u_fastboot.c | 260 +++++++++++++++++ include/usb/fastboot.h | 36 +++ 9 files changed, 1141 insertions(+) create mode 100644 common/cmd_fastboot.c create mode 100644 doc/README.android-fastboot create mode 100644 doc/README.android-fastboot-protocol create mode 100644 drivers/usb/gadget/f_fastboot.c create mode 100644 drivers/usb/gadget/g_fastboot.h create mode 100644 drivers/usb/gadget/u_fastboot.c create mode 100644 include/usb/fastboot.h
diff --git a/common/Makefile b/common/Makefile index da208f3..fe1d8b9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -167,6 +167,8 @@ obj-y += cmd_usb.o obj-y += usb.o usb_hub.o obj-$(CONFIG_USB_STORAGE) += usb_storage.o endif +obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o + obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 0000000..fc8d9e0 --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,36 @@ +/* + * Copyright 2008 - 2009 Windriver, <www.windriver.com> + * Author: Tom Rix Tom.Rix@windriver.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <command.h> +#include <usb/fastboot.h> + +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = 1; + + if (!fastboot_init()) { + printf("Fastboot entered...\n"); + + ret = 0; + + while (1) { + if (fastboot_poll()) + break; + if (ctrlc()) + break; + } + } + + fastboot_shutdown(); + return ret; +} + +U_BOOT_CMD( + fastboot, 1, 1, do_fastboot, + "fastboot - enter USB Fastboot protocol", + "" +); diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot new file mode 100644 index 0000000..4b2a9aa --- /dev/null +++ b/doc/README.android-fastboot @@ -0,0 +1,86 @@ +Android Fastboot +~~~~~~~~~~~~~~~~ + +Overview +======== +The protocol that is used over USB is described in +README.android-fastboot-protocol in same folder. + +The current implementation does not yet support the flash and erase +commands. + +Client installation +=================== +The counterpart to this gadget is the fastboot client which can +be found in Android's platform/system/core repository in the fastboot +folder. It runs on Windows, Linux and even OSx. Linux user are lucky since +they only need libusb. +Windows users need to bring some time until they have Android SDK (currently +http://dl.google.com/android/installer_r12-windows.exe) installed. You +need to install ADB package which contains the required glue libraries for +accessing USB. Also you need "Google USB driver package" and "SDK platform +tools". Once installed the usb driver is placed in your SDK folder under +extras\google\usb_driver. The android_winusb.inf needs a line like + + %SingleBootLoaderInterface% = USB_Install, USB\VID_0451&PID_D022 + +either in the [Google.NTx86] section for 32bit Windows or [Google.NTamd64] +for 64bit Windows. VID and PID should match whatever the fastboot is +advertising. + +Board specific +============== +The gadget calls at probe time the function fastboot_board_init() which +should be provided by the board to setup its specific configuration. +It is possible here to overwrite specific strings like Vendor or Serial +number. Strings which are not specified here will return a default value. +This init function must also provide a memory area for the +"transfer_buffer" and its size. This buffer should be large enough to hold +whatever the download commands is willing to send or it will fail. This +can be a kernel image for booting which could be around two MiB or a flash +partition which could be slightly larger :) + +In Action +========= +Enter into fastboot by executing the fastboot command in u-boot and you +should see: +|Fastboot entered... + +The gadget terminates once the is unplugged. On the client side you can +fetch the product name for instance: +|>fastboot getvar product +|product: Default Product +|finished. total time: 0.016s + +or initiate a reboot: +|>fastboot reboot + +and once the client comes back, the board should reset. + +You can also specify a kernel image to boot. You have to either specify +the an image in Android format _or_ pass a binary kernel and let the +fastboot client wrap the Android suite around it. On OMAP for instance you +take zImage kernel and pass it to the fastboot client: + +|>fastboot -b 0x80000000 -c "console=ttyO2 earlyprintk root=/dev/ram0 +| mem=128M" boot zImage +|creating boot image... +|creating boot image - 1847296 bytes +|downloading 'boot.img'... +|OKAY [ 2.766s] +|booting... +|OKAY [ -0.000s] +|finished. total time: 2.766s + +and on the gadget side you should see: +|Starting download of 1847296 bytes +|........................................................ +|downloading of 1847296 bytes finished +|Booting kernel.. +|## Booting Android Image at 0x81000000 ... +|Kernel load addr 0x80008000 size 1801 KiB +|Kernel command line: console=ttyO2 earlyprintk root=/dev/ram0 mem=128M +| Loading Kernel Image ... OK +|OK +| +|Starting kernel ... diff --git a/doc/README.android-fastboot-protocol b/doc/README.android-fastboot-protocol new file mode 100644 index 0000000..e9e7166 --- /dev/null +++ b/doc/README.android-fastboot-protocol @@ -0,0 +1,170 @@ +FastBoot Version 0.4 +---------------------- + +The fastboot protocol is a mechanism for communicating with bootloaders +over USB. It is designed to be very straightforward to implement, to +allow it to be used across a wide range of devices and from hosts running +Linux, Windows, or OSX. + + +Basic Requirements +------------------ + +* Two bulk endpoints (in, out) are required +* Max packet size must be 64 bytes for full-speed and 512 bytes for + high-speed USB +* The protocol is entirely host-driven and synchronous (unlike the + multi-channel, bi-directional, asynchronous ADB protocol) + + +Transport and Framing +--------------------- + +1. Host sends a command, which is an ascii string in a single + packet no greater than 64 bytes. + +2. Client response with a single packet no greater than 64 bytes. + The first four bytes of the response are "OKAY", "FAIL", "DATA", + or "INFO". Additional bytes may contain an (ascii) informative + message. + + a. INFO -> the remaining 60 bytes are an informative message + (providing progress or diagnostic messages). They should + be displayed and then step #2 repeats + + b. FAIL -> the requested command failed. The remaining 60 bytes + of the response (if present) provide a textual failure message + to present to the user. Stop. + + c. OKAY -> the requested command completed successfully. Go to #5 + + d. DATA -> the requested command is ready for the data phase. + A DATA response packet will be 12 bytes long, in the form of + DATA00000000 where the 8 digit hexidecimal number represents + the total data size to transfer. + +3. Data phase. Depending on the command, the host or client will + send the indicated amount of data. Short packets are always + acceptable and zero-length packets are ignored. This phase continues + until the client has sent or received the number of bytes indicated + in the "DATA" response above. + +4. Client responds with a single packet no greater than 64 bytes. + The first four bytes of the response are "OKAY", "FAIL", or "INFO". + Similar to #2: + + a. INFO -> display the remaining 60 bytes and return to #4 + + b. FAIL -> display the remaining 60 bytes (if present) as a failure + reason and consider the command failed. Stop. + + c. OKAY -> success. Go to #5 + +5. Success. Stop. + + +Example Session +--------------- + +Host: "getvar:version" request version variable + +Client: "OKAY0.4" return version "0.4" + +Host: "getvar:nonexistant" request some undefined variable + +Client: "OKAY" return value "" + +Host: "download:00001234" request to send 0x1234 bytes of data + +Client: "DATA00001234" ready to accept data + +Host: < 0x1234 bytes > send data + +Client: "OKAY" success + +Host: "flash:bootloader" request to flash the data to the bootloader + +Client: "INFOerasing flash" indicate status / progress + "INFOwriting flash" + "OKAY" indicate success + +Host: "powerdown" send a command + +Client: "FAILunknown command" indicate failure + + +Command Reference +----------------- + +* Command parameters are indicated by printf-style escape sequences. + +* Commands are ascii strings and sent without the quotes (which are + for illustration only here) and without a trailing 0 byte. + +* Commands that begin with a lowercase letter are reserved for this + specification. OEM-specific commands should not begin with a + lowercase letter, to prevent incompatibilities with future specs. + + "getvar:%s" Read a config/version variable from the bootloader. + The variable contents will be returned after the + OKAY response. + + "download:%08x" Write data to memory which will be later used + by "boot", "ramdisk", "flash", etc. The client + will reply with "DATA%08x" if it has enough + space in RAM or "FAIL" if not. The size of + the download is remembered. + + "verify:%08x" Send a digital signature to verify the downloaded + data. Required if the bootloader is "secure" + otherwise "flash" and "boot" will be ignored. + + "flash:%s" Write the previously downloaded image to the + named partition (if possible). + + "erase:%s" Erase the indicated partition (clear to 0xFFs) + + "boot" The previously downloaded data is a boot.img + and should be booted according to the normal + procedure for a boot.img + + "continue" Continue booting as normal (if possible) + + "reboot" Reboot the device. + + "reboot-bootloader" Reboot back into the bootloader. + Useful for upgrade processes that require upgrading + the bootloader and then upgrading other partitions + using the new bootloader. + + "powerdown" Power off the device. + + + +Client Variables +---------------- + +The "getvar:%s" command is used to read client variables which +represent various information about the device and the software +on it. + +The various currently defined names are: + + version Version of FastBoot protocol supported. + It should be "0.3" for this document. + + version-bootloader Version string for the Bootloader. + + version-baseband Version string of the Baseband Software + + product Name of the product + + serialno Product serial number + + secure If the value is "yes", this is a secure + bootloader requiring a signature before + it will install or boot images. + +Names starting with a lowercase character are reserved by this +specification. OEM-specific names should not start with lowercase +characters. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 804a2bd..b14f60e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o +obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o u_fastboot.o endif ifdef CONFIG_USB_ETHER obj-y += ether.o diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 0000000..f74a52e --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,535 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <errno.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/compiler.h> + +#include "g_fastboot.h" + +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +#define CONFIGURATION_NORMAL 1 +#define BULK_ENDPOINT 1 +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +static struct usb_string def_usb_fb_strings[] = { + { FB_STR_PRODUCT_IDX, "Default Product" }, + { FB_STR_SERIAL_IDX, "1234567890" }, + { FB_STR_CONFIG_IDX, "Android Fastboot" }, + { FB_STR_INTERFACE_IDX, "Android Fastboot" }, + { FB_STR_MANUFACTURER_IDX, "Default Manufacturer" }, + { FB_STR_PROC_REV_IDX, "Default 1.0" }, + { FB_STR_PROC_TYPE_IDX, "Emulator" }, + { } +}; + +static struct usb_gadget_strings def_fb_strings = { + .language = 0x0409, /* en-us */ + .strings = def_usb_fb_strings, +}; + +static struct usb_gadget_strings *vendor_fb_strings; + +static unsigned int gadget_is_connected; + +static u8 ep0_buffer[512]; +static u8 ep_out_buffer[EP_BUFFER_SIZE]; +static u8 ep_in_buffer[EP_BUFFER_SIZE]; +static int current_config; + +/* e1 */ +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, /* IN */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}; + +/* e2 */ +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, /* OUT */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1, + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, /* OUT */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, + .bInterval = 0x00, +}; + +const char *fb_find_usb_string(unsigned int id) +{ + struct usb_string *s = NULL; + + if (vendor_fb_strings) { + for (s = vendor_fb_strings->strings; s && s->s; s++) { + if (s->id == id) + break; + } + } + if (!s || !s->s) { + for (s = def_fb_strings.strings; s && s->s; s++) { + if (s->id == id) + break; + } + } + if (!s) + return NULL; + return s->s; +} + +static struct usb_request *ep0_req; + +struct usb_ep *ep_in; +struct usb_request *req_in; + +struct usb_ep *ep_out; +struct usb_request *req_out; + +static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + if (!status) + return; + printf("ep0 status %d\n", status); +} + +static int fastboot_bind(struct usb_gadget *gadget) +{ + ep0_req = usb_ep_alloc_request(gadget->ep0, 0); + if (!ep0_req) + goto err; + ep0_req->buf = ep0_buffer; + ep0_req->complete = fastboot_ep0_complete; + + ep_in = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!ep_in) + goto err; + ep_in->driver_data = ep_in; + + ep_out = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!ep_out) + goto err; + ep_out->driver_data = ep_out; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + + usb_gadget_connect(gadget); + gadget_is_connected = 1; + + return 0; +err: + return -1; +} + +static void fastboot_unbind(struct usb_gadget *gadget) +{ + usb_ep_free_request(gadget->ep0, ep0_req); + ep_in->driver_data = NULL; + ep_out->driver_data = NULL; + + gadget_is_connected = 0; + usb_gadget_disconnect(gadget); +} + +#define DEVICE_BCD 0x0100 + +struct usb_device_descriptor fb_descriptor = { + .bLength = sizeof(fb_descriptor), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x200, + .bMaxPacketSize0 = 0x40, + .idVendor = cpu_to_le16(CONFIG_USB_FASTBOOT_VENDOR_ID), + .idProduct = cpu_to_le16(CONFIG_USB_FASTBOOT_PRODUCT_ID), + .bcdDevice = DEVICE_BCD, + .iManufacturer = FB_STR_MANUFACTURER_IDX, + .iProduct = FB_STR_PRODUCT_IDX, + .iSerialNumber = FB_STR_SERIAL_IDX, + .bNumConfigurations = 1, +}; + +#define TOT_CFG_DESC_LEN (USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + \ + USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE) + +static struct usb_config_descriptor config_desc = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(TOT_CFG_DESC_LEN), + .bNumInterfaces = 1, + .bConfigurationValue = CONFIGURATION_NORMAL, + .iConfiguration = FB_STR_CONFIG_IDX, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, + .iInterface = FB_STR_INTERFACE_IDX, +}; + +static struct usb_qualifier_descriptor qual_desc = { + .bLength = sizeof(qual_desc), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = 0x200, + .bMaxPacketSize0 = 0x40, + .bNumConfigurations = 1, +}; + +static int fastboot_setup_get_descr(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 val; + int ret; + u32 bytes_remaining; + u32 bytes_total; + u32 this_inc; + + val = w_value >> 8; + + switch (val) { + case USB_DT_DEVICE: + memcpy(ep0_buffer, &fb_descriptor, sizeof(fb_descriptor)); + ep0_req->length = min(w_length, sizeof(fb_descriptor)); + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + case USB_DT_CONFIG: + bytes_remaining = min(w_length, sizeof(ep0_buffer)); + bytes_total = 0; + + /* config */ + this_inc = min(bytes_remaining, USB_DT_CONFIG_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &config_desc, this_inc); + bytes_total += this_inc; + + /* interface */ + this_inc = min(bytes_remaining, USB_DT_INTERFACE_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &interface_desc, this_inc); + bytes_total += this_inc; + + /* ep in */ + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + bytes_remaining -= this_inc; + memcpy(ep0_buffer + bytes_total, &fs_ep_in, this_inc); + bytes_total += this_inc; + + /* ep out */ + this_inc = min(bytes_remaining, USB_DT_ENDPOINT_SIZE); + + if (gadget->speed == USB_SPEED_HIGH) + memcpy(ep0_buffer + bytes_total, &hs_ep_out, this_inc); + else + memcpy(ep0_buffer + bytes_total, &fs_ep_out, this_inc); + bytes_total += this_inc; + + ep0_req->length = bytes_total; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + case USB_DT_STRING: + ret = usb_gadget_get_string(vendor_fb_strings, + w_value & 0xff, ep0_buffer); + if (ret < 0) + ret = usb_gadget_get_string(&def_fb_strings, + w_value & 0xff, + ep0_buffer); + if (ret < 0) + break; + + ep0_req->length = ret; + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + case USB_DT_DEVICE_QUALIFIER: + memcpy(ep0_buffer, &qual_desc, sizeof(qual_desc)); + ep0_req->length = min(w_length, sizeof(qual_desc)); + ret = usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int fastboot_setup_get_conf(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (w_length == 0) + return -1; + + ep0_buffer[0] = current_config; + ep0_req->length = 1; + return usb_ep_queue(gadget->ep0, ep0_req, 0); +} + +static void fastboot_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + if (status) + printf("status: %d ep_in trans: %d\n", status, req->actual); +} + +static int fastboot_disable_ep(struct usb_gadget *gadget) +{ + if (req_out) { + usb_ep_free_request(ep_out, req_out); + req_out = NULL; + } + if (req_in) { + usb_ep_free_request(ep_in, req_in); + req_in = NULL; + } + usb_ep_disable(ep_out); + usb_ep_disable(ep_in); + + return 0; +} + +static int fastboot_enable_ep(struct usb_gadget *gadget) +{ + int ret; + + /* make sure we don't enable the ep twice */ + if (gadget->speed == USB_SPEED_HIGH) + ret = usb_ep_enable(ep_out, &hs_ep_out); + else + ret = usb_ep_enable(ep_out, &fs_ep_out); + if (ret) { + printf("failed to enable out ep\n"); + goto err; + } + + req_out = usb_ep_alloc_request(ep_out, 0); + if (!req_out) { + printf("failed to alloc out req\n"); + goto err; + } + + ret = usb_ep_enable(ep_in, &fs_ep_in); + if (ret) { + printf("failed to enable in ep\n"); + goto err; + } + req_in = usb_ep_alloc_request(ep_in, 0); + if (!req_in) { + printf("failed alloc req in\n"); + goto err; + } + + req_out->complete = rx_handler_command; + req_out->buf = ep_out_buffer; + req_out->length = sizeof(ep_out_buffer); + + req_in->buf = ep_in_buffer; + req_in->length = sizeof(ep_in_buffer); + + ret = usb_ep_queue(ep_out, req_out, 0); + if (ret) + goto err; + + return 0; +err: + fastboot_disable_ep(gadget); + return -1; +} + +static int fastboot_set_interface(struct usb_gadget *gadget, u32 enable) +{ + if (enable && req_out) + return 0; + if (!enable && !req_out) + return 0; + + if (enable) + return fastboot_enable_ep(gadget); + else + return fastboot_disable_ep(gadget); +} + +static int fastboot_setup_out_req(struct usb_gadget *gadget, + const struct usb_ctrlrequest *req) +{ + switch (req->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + + ep0_req->length = 0; + if (req->wValue == CONFIGURATION_NORMAL) { + current_config = CONFIGURATION_NORMAL; + fastboot_set_interface(gadget, 1); + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + } + if (req->wValue == 0) { + current_config = 0; + fastboot_set_interface(gadget, 0); + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + } + return -1; + break; + default: + return -1; + }; + + case USB_RECIP_INTERFACE: + switch (req->bRequest) { + case USB_REQ_SET_INTERFACE: + + ep0_req->length = 0; + if (!fastboot_set_interface(gadget, 1)) + return usb_ep_queue(gadget->ep0, + ep0_req, 0); + return -1; + break; + default: + return -1; + } + + case USB_RECIP_ENDPOINT: + switch (req->bRequest) { + case USB_REQ_CLEAR_FEATURE: + + return usb_ep_queue(gadget->ep0, ep0_req, 0); + break; + default: + return -1; + } + } + return -1; +} + +static int fastboot_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *req) +{ + if ((req->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + return -1; + + if ((req->bRequestType & USB_DIR_IN) == 0) + /* host-to-device */ + return fastboot_setup_out_req(gadget, req); + + /* device-to-host */ + if ((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + switch (req->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + return fastboot_setup_get_descr(gadget, req); + break; + + case USB_REQ_GET_CONFIGURATION: + return fastboot_setup_get_conf(gadget, req); + break; + default: + return -1; + } + } + return -1; +} + +static void fastboot_disconnect(struct usb_gadget *gadget) +{ + fastboot_disable_ep(gadget); + gadget_is_connected = 0; +} + +struct usb_gadget_driver fast_gadget = { + .speed = USB_SPEED_HIGH, + .bind = fastboot_bind, + .unbind = fastboot_unbind, + .setup = fastboot_setup, + .disconnect = fastboot_disconnect, +}; + +static int udc_is_probbed; + +int __weak fastboot_board_init(struct usb_gadget_strings **str) +{ + return 0; +} + +int fastboot_init(void) +{ + int ret; + + ret = fastboot_board_init(&vendor_fb_strings); + if (ret) + return ret; + + ret = usb_gadget_register_driver(&fast_gadget); + if (ret) { + printf("Add gadget failed\n"); + return ret; + } + + udc_is_probbed = 1; + return 0; +} + +int fastboot_poll(void) +{ + usb_gadget_handle_interrupts(); + + if (gadget_is_connected) + return 0; + else + return 1; +} + +void fastboot_shutdown(void) +{ + if (!udc_is_probbed) + return; + udc_is_probbed = 0; + usb_gadget_unregister_driver(&fast_gadget); +} + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + int ret; + + if (req_in->complete == NULL) + req_in->complete = fastboot_complete_in; + + memcpy(req_in->buf, buffer, buffer_size); + req_in->length = buffer_size; + ret = usb_ep_queue(ep_in, req_in, 0); + if (ret) + printf("Error %d on queue\n", ret); + return 0; +} diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h new file mode 100644 index 0000000..733eb38 --- /dev/null +++ b/drivers/usb/gadget/g_fastboot.h @@ -0,0 +1,15 @@ +#ifndef _G_FASTBOOT_H_ +#define _G_FASTBOOT_H_ + +#define EP_BUFFER_SIZE 4096 + +extern struct usb_ep *ep_in; +extern struct usb_request *req_in; +extern struct usb_ep *ep_out; +extern struct usb_request *req_out; + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +int fastboot_tx_write(const char *buffer, unsigned int buffer_size); +const char *fb_find_usb_string(unsigned int id); + +#endif diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c new file mode 100644 index 0000000..76116c0 --- /dev/null +++ b/drivers/usb/gadget/u_fastboot.c @@ -0,0 +1,260 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ +/* + * Part of the rx_handler were copied from the Android project. + * Specifically rx command parsing in the usb_rx_cmd_complete + * function of the file bootable/bootloader/legacy/usbloader/usbloader.c + * + * Repository: https://android.googlesource.com/platform/bootable/bootloader/legacy + * Files: usbloader/usbloader.c + * Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a + * + * Copyright (C) 2008 The Android Open Source Project + * + * SPDX-License-Identifier: BSD-2-Clause + */ +#include <common.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h" + +#define FASTBOOT_VERSION "0.4" + +/* The 64 defined bytes plus \0 */ +#define RESPONSE_LEN (64 + 1) + +static unsigned int download_size; +static unsigned int download_bytes; + +static int fastboot_tx_write_str(const char *buffer) +{ + return fastboot_tx_write(buffer, strlen(buffer)); +} + +static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{ + req_in->complete = compl_do_reset; + fastboot_tx_write_str("OKAY"); +} + +static int strcmp_l1(const char *s1, const char *s2) +{ + return strncmp(s1, s2, strlen(s1)); +} + +static void cb_getvar(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + const char *s; + + strcpy(response, "OKAY"); + strsep(&cmd, ":"); + if (!cmd) { + fastboot_tx_write_str("FAILmissing var"); + return; + } + + if (!strcmp_l1("version", cmd)) { + strncat(response, FASTBOOT_VERSION, sizeof(response)); + } else if (!strcmp_l1("downloadsize", cmd)) { + char str_num[12]; + + sprintf(str_num, "%08lx", load_size); + strncat(response, str_num, sizeof(response)); + } else if (!strcmp_l1("product", cmd)) { + s = fb_find_usb_string(FB_STR_PRODUCT_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else if (!strcmp_l1("serialno", cmd)) { + s = fb_find_usb_string(FB_STR_SERIAL_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else if (!strcmp_l1("cpurev", cmd)) { + s = fb_find_usb_string(FB_STR_PROC_REV_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else if (!strcmp_l1("secure", cmd)) { + s = fb_find_usb_string(FB_STR_PROC_TYPE_IDX); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else { + strcpy(response, "FAILVariable not implemented"); + } + fastboot_tx_write_str(response); +} + +static unsigned int rx_bytes_expected(void) +{ + int rx_remain = download_size - download_bytes; + if (rx_remain < 0) + return 0; + if (rx_remain > EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + return rx_remain; +} + +#define BYTES_PER_DOT 32768 +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + char response[RESPONSE_LEN]; + unsigned int transfer_size = download_size - download_bytes; + const unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + + if (req->status != 0) { + printf("Bad status: %d\n", req->status); + return; + } + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + memcpy((void *)load_addr + download_bytes, buffer, transfer_size); + + download_bytes += transfer_size; + + /* Check if transfer is done */ + if (download_bytes >= download_size) { + /* + * Reset global transfer variable, keep download_bytes because + * it will be used in the next possible flashing command + */ + download_size = 0; + req->complete = rx_handler_command; + req->length = EP_BUFFER_SIZE; + + sprintf(response, "OKAY"); + fastboot_tx_write_str(response); + + printf("\ndownloading of %d bytes finished\n", download_bytes); + } else { + req->length = rx_bytes_expected(); + } + + if (download_bytes && !(download_bytes % BYTES_PER_DOT)) { + printf("."); + if (!(download_bytes % (74 * BYTES_PER_DOT))) + printf("\n"); + } + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void cb_download(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + + strsep(&cmd, ":"); + download_size = simple_strtoul(cmd, NULL, 16); + download_bytes = 0; + + printf("Starting download of %d bytes\n", download_size); + + if (0 == download_size) { + sprintf(response, "FAILdata invalid size"); + } else if (download_size > load_size) { + download_size = 0; + sprintf(response, "FAILdata too large"); + } else { + sprintf(response, "DATA%08x", download_size); + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(); + } + fastboot_tx_write_str(response); +} + +static char boot_addr_start[32]; +static char *bootm_args[] = { "bootm", boot_addr_start, NULL }; + +static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + req->complete = NULL; + fastboot_shutdown(); + printf("Booting kernel..\n"); + + do_bootm(NULL, 0, 2, bootm_args); + + /* This only happens if image is somehow faulty so we start over */ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_boot(struct usb_ep *ep, struct usb_request *req) +{ + sprintf(boot_addr_start, "0x%lx", load_addr); + + req_in->complete = do_bootm_on_complete; + fastboot_tx_write_str("OKAY"); + return; +} + +struct cmd_dispatch_info { + char *cmd; + void (*cb)(struct usb_ep *ep, struct usb_request *req); +}; + +static struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = "reboot", + .cb = cb_reboot, + }, { + .cmd = "getvar:", + .cb = cb_getvar, + }, { + .cmd = "download:", + .cb = cb_download, + }, { + .cmd = "boot", + .cb = cb_boot, + }, +}; + +void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + char response[RESPONSE_LEN]; + char *cmdbuf = req->buf; + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + int i; + + sprintf(response, "FAIL"); + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) { + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) + fastboot_tx_write_str("FAILunknown command"); + else + func_cb(ep, req); + + if (req->status == 0) { + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); + } +} diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h new file mode 100644 index 0000000..b348886 --- /dev/null +++ b/include/usb/fastboot.h @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef FASTBOOT_H +#define FASTBOOT_H + +#include <common.h> +#include <command.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#define FB_STR_PRODUCT_IDX 1 +#define FB_STR_SERIAL_IDX 2 +#define FB_STR_CONFIG_IDX 3 +#define FB_STR_INTERFACE_IDX 4 +#define FB_STR_MANUFACTURER_IDX 5 +#define FB_STR_PROC_REV_IDX 6 +#define FB_STR_PROC_TYPE_IDX 7 + +#ifdef CONFIG_CMD_FASTBOOT + +int fastboot_init(void); +void fastboot_shutdown(void); +int fastboot_poll(void); + +int fastboot_board_init(struct usb_gadget_strings **str); + +#endif +#endif

Hi Rob Herring, I am just do a function testing on Atmel sama5d3xek board. And a small comment as following.
Btw, do you test to transfer big size file. I try a file bigger than 100MiB, it will hang. However use DFU or RNDIS don't meet this issue.
On 04/11/2014 03:18 AM, Rob Herring wrote:
+static void fastboot_unbind(struct usb_gadget *gadget) +{
- usb_ep_free_request(gadget->ep0, ep0_req);
- ep_in->driver_data = NULL;
- ep_out->driver_data = NULL;
- gadget_is_connected = 0;
- usb_gadget_disconnect(gadget);
+}
[snip]
+static void fastboot_disconnect(struct usb_gadget *gadget) +{
- fastboot_disable_ep(gadget);
- gadget_is_connected = 0;
Do this only in unbind, as unbind is really disconnect.
+}
+struct usb_gadget_driver fast_gadget = {
- .speed = USB_SPEED_HIGH,
- .bind = fastboot_bind,
- .unbind = fastboot_unbind,
- .setup = fastboot_setup,
- .disconnect = fastboot_disconnect,
+};
Best Regards, Bo Shen

On Fri, Apr 11, 2014 at 2:14 AM, Bo Shen voice.shen@atmel.com wrote:
Hi Rob Herring, I am just do a function testing on Atmel sama5d3xek board. And a small comment as following.
Btw, do you test to transfer big size file. I try a file bigger than 100MiB, it will hang. However use DFU or RNDIS don't meet this issue.
How much RAM do you have? It will happily download into the end of RAM overwriting u-boot if loadsize is not set. DFU at least has its own buffer size configuration.
On 04/11/2014 03:18 AM, Rob Herring wrote:
+static void fastboot_unbind(struct usb_gadget *gadget) +{
usb_ep_free_request(gadget->ep0, ep0_req);
ep_in->driver_data = NULL;
ep_out->driver_data = NULL;
gadget_is_connected = 0;
usb_gadget_disconnect(gadget);
+}
[snip]
+static void fastboot_disconnect(struct usb_gadget *gadget) +{
fastboot_disable_ep(gadget);
gadget_is_connected = 0;
Do this only in unbind, as unbind is really disconnect.
So, what should disconnect function do?
Rob

On Friday, April 11, 2014 at 02:55:31 PM, Rob Herring wrote:
On Fri, Apr 11, 2014 at 2:14 AM, Bo Shen voice.shen@atmel.com wrote:
Hi Rob Herring,
I am just do a function testing on Atmel sama5d3xek board. And a small
comment as following.
Btw, do you test to transfer big size file. I try a file bigger than
100MiB, it will hang. However use DFU or RNDIS don't meet this issue.
How much RAM do you have? It will happily download into the end of RAM overwriting u-boot if loadsize is not set. DFU at least has its own buffer size configuration.
I really have to wonder ... why does android not use DFU instead and instead forces us to take in yet another (fourth ? ... CDC ethernet, DFU, Thor, Fastboot) usb image download protocol someone invented without cooperating with the rest of the community on a common and functional solution? Sigh :'-(
Best regards, Marek Vasut

Hi Marek,
On Friday, April 11, 2014 at 02:55:31 PM, Rob Herring wrote:
On Fri, Apr 11, 2014 at 2:14 AM, Bo Shen voice.shen@atmel.com wrote:
Hi Rob Herring,
I am just do a function testing on Atmel sama5d3xek board. And a small
comment as following.
Btw, do you test to transfer big size file. I try a file bigger than
100MiB, it will hang. However use DFU or RNDIS don't meet this issue.
How much RAM do you have? It will happily download into the end of RAM overwriting u-boot if loadsize is not set. DFU at least has its own buffer size configuration.
I really have to wonder ... why does android not use DFU
Unfortunately the DFU has its own limitations. The most notable one is using only EP0 for transmission. As a result the transmission speed is slow when compared with other solutions, which are using other EPs.
instead and instead forces us to take in yet another (fourth ? ... CDC ethernet, DFU, Thor, Fastboot)
A little explanation: 1. UMS (CDC Ethernet) - export the content of the whole flash. It should be regarded as a convenient debug option. It is somewhat clumsy to use dd if=/u-boot.bin of=/dev/sda1 .... to store u-boot.
2. DFU it is the standard - devised at 2004 - rather outdated now. It is slow but reliable - targeted to uC flashing.
3. Thor, fastboot - protocols devised to provide faster transmission rates (they use other EPs for transmission).
usb image download protocol someone invented without cooperating with the rest of the community on a common and functional solution? Sigh :'-(
I think, that this problem emerged because of the lack of DFU standard update - adding better checksumming and possibility for using other EPs would solve the problem.
In u-boot it is not a big problem, since we are using one backend (dfu_write()) to store data on the medium. Moreover common gadget code - g_dnl.c is available to reduce code duplication.
To add support for other gadget one needs to implement proper function (like f_thor.c, f_dfu.c, f_fastboot.c).
Best regards, Marek Vasut

Hi Rob,
On 04/11/2014 08:55 PM, Rob Herring wrote:
On Fri, Apr 11, 2014 at 2:14 AM, Bo Shen voice.shen@atmel.com wrote:
Hi Rob Herring, I am just do a function testing on Atmel sama5d3xek board. And a small comment as following.
Btw, do you test to transfer big size file. I try a file bigger than 100MiB, it will hang. However use DFU or RNDIS don't meet this issue.
How much RAM do you have? It will happily download into the end of RAM overwriting u-boot if loadsize is not set. DFU at least has its own buffer size configuration.
There are 512MiB RAM on board. So, it won't be a problem to load this file from the begin of the RAM (As the u-boot is relocated to top RAM).
On 04/11/2014 03:18 AM, Rob Herring wrote:
+static void fastboot_unbind(struct usb_gadget *gadget) +{
usb_ep_free_request(gadget->ep0, ep0_req);
ep_in->driver_data = NULL;
ep_out->driver_data = NULL;
gadget_is_connected = 0;
usb_gadget_disconnect(gadget);
+}
[snip]
+static void fastboot_disconnect(struct usb_gadget *gadget) +{
fastboot_disable_ep(gadget);
gadget_is_connected = 0;
Do this only in unbind, as unbind is really disconnect.
So, what should disconnect function do?
Maybe some misunderstanding. I mean the "gadget_is_connected" should be cleared in unbind function while not in disconnect function.
Rob
Best Regards, Bo Shen

Hi Rob,
Thanks for the patch. I've just glimpsed to them. I will provide a thorough review in a near future (next week probably).
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.
What do you mean by that? To which version of gadget framework from linux does it correspond? The u-boot's gadget infrastructure, used by UMS, DFU and THOR gadgets [*], is based on linux 2.6.36.
The gadget implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media.
For the three above gadgets [*] one "flashing" backend is used. It is done by dfu_write() function defined at ./drivers/dfu/dfu.c.
Additionally some common gadget handling code is already in the u-boot tree. It is called g_dnl.c at drivers/usb/gadget, and is used by [*] gadgets. Maybe fastboot gadget could reuse its code?

On Thu, Apr 10, 2014 at 02:18:06PM -0500, Rob Herring wrote:
From: Sebastian Siewior bigeasy@linutronix.de
This patch contains an implementation of the fastboot protocol on the device side and a little of documentation. The gadget expects the new-style gadget framework. The gadget implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126798/ with the following changes:
- Rebase to current mainline and updates for current gadget API
- Use SPDX identifiers for licenses
- Traced the history and added missing copyright to cmd_fastboot.c
- Use load_addr/load_size for transfer buffer
- Allow vendor strings to be optional
- Set vendor/product ID from config defines
- Allow Ctrl-C to exit fastboot mode
Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org
I suspect Lukasz or Marek will have more in depth comments next week, but please use puts rather than printf when we don't have format chars in the string. Things look generally speaking reasonable however.

On Thursday, April 10, 2014 at 09:18:06 PM, Rob Herring wrote:
From: Sebastian Siewior bigeasy@linutronix.de
[...]
+++ 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.
"directory", this is not windows, so no folders here either ...
+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.
OSX is written like so, the X is also capital.
[...]
+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.
The above sentence makes zero sense to me.
[...]
+/* e1 */
This is a fine comment, but please make it sensible.
[...]
+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;
Please define all global variables at the top of the file.
[...]
diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h new file mode 100644 index 0000000..733eb38 --- /dev/null +++ b/drivers/usb/gadget/g_fastboot.h @@ -0,0 +1,15 @@ +#ifndef _G_FASTBOOT_H_ +#define _G_FASTBOOT_H_
+#define EP_BUFFER_SIZE 4096
+extern struct usb_ep *ep_in; +extern struct usb_request *req_in; +extern struct usb_ep *ep_out; +extern struct usb_request *req_out;
Ick, I really dislike how you're distributing the variables across the code. Is this really necessary ?
+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);
Please produce a consistent naming for those function names, something that can also be identified that it's coming from the fastboot gadget. Otherwise, these function names look rather random and are hard to pair with particular part of U-Boot at first glance.
[...]
+static int strcmp_l1(const char *s1, const char *s2) +{
- return strncmp(s1, s2, strlen(s1));
I suspect someone will sooner or later call this function with non-null- terminated string as $s1 . Can't you do some boundary checking here?
I'd hate to have some fastbleed bug here (sorry, bad pun). [...]
+static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{
- char response[RESPONSE_LEN];
- unsigned int transfer_size = download_size - download_bytes;
- const unsigned char *buffer = req->buf;
- unsigned int buffer_size = req->actual;
- if (req->status != 0) {
printf("Bad status: %d\n", req->status);
return;
- }
- if (buffer_size < transfer_size)
transfer_size = buffer_size;
- memcpy((void *)load_addr + download_bytes, buffer, transfer_size);
- download_bytes += transfer_size;
- /* Check if transfer is done */
- if (download_bytes >= download_size) {
/*
* Reset global transfer variable, keep download_bytes because
* it will be used in the next possible flashing command
*/
download_size = 0;
req->complete = rx_handler_command;
req->length = EP_BUFFER_SIZE;
sprintf(response, "OKAY");
fastboot_tx_write_str(response);
printf("\ndownloading of %d bytes finished\n", download_bytes);
- } else {
req->length = rx_bytes_expected();
- }
- if (download_bytes && !(download_bytes % BYTES_PER_DOT)) {
printf(".");
putc()
if (!(download_bytes % (74 * BYTES_PER_DOT)))
printf("\n");
putc()
- }
- req->actual = 0;
- usb_ep_queue(ep, req, 0);
+}
[...]
+#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
Use enum {} for those for the sake of typechecking ? [..]

Hi Rob,
From: Sebastian Siewior bigeasy@linutronix.de
This patch contains an implementation of the fastboot protocol on the device side and a little of documentation. The gadget expects the new-style gadget framework. The gadget implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126798/ with the following changes:
- Rebase to current mainline and updates for current gadget API
- Use SPDX identifiers for licenses
- Traced the history and added missing copyright to cmd_fastboot.c
- Use load_addr/load_size for transfer buffer
- Allow vendor strings to be optional
I could only propose to use common g_dnl.c code with CONFIG_G_DNL_* defines.
- Set vendor/product ID from config defines
- Allow Ctrl-C to exit fastboot mode
Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org
common/Makefile | 2 + common/cmd_fastboot.c | 36 +++ doc/README.android-fastboot | 86 ++++++ doc/README.android-fastboot-protocol | 170 +++++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_fastboot.c | 535 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/g_fastboot.h | 15 + drivers/usb/gadget/u_fastboot.c | 260 +++++++++++++++++ include/usb/fastboot.h | 36 +++ 9 files changed, 1141 insertions(+) create mode 100644 common/cmd_fastboot.c create mode 100644 doc/README.android-fastboot create mode 100644 doc/README.android-fastboot-protocol create mode 100644 drivers/usb/gadget/f_fastboot.c create mode 100644 drivers/usb/gadget/g_fastboot.h create mode 100644 drivers/usb/gadget/u_fastboot.c create mode 100644 include/usb/fastboot.h
diff --git a/common/Makefile b/common/Makefile index da208f3..fe1d8b9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -167,6 +167,8 @@ obj-y += cmd_usb.o obj-y += usb.o usb_hub.o obj-$(CONFIG_USB_STORAGE) += usb_storage.o endif +obj-$(CONFIG_CMD_FASTBOOT) += cmd_fastboot.o
obj-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o obj-$(CONFIG_CMD_THOR_DOWNLOAD) += cmd_thordown.o obj-$(CONFIG_CMD_XIMG) += cmd_ximg.o diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c new file mode 100644 index 0000000..fc8d9e0 --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,36 @@ +/*
- Copyright 2008 - 2009 Windriver, <www.windriver.com>
- Author: Tom Rix Tom.Rix@windriver.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <command.h> +#include <usb/fastboot.h>
+static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{
- int ret = 1;
- if (!fastboot_init()) {
printf("Fastboot entered...\n");
Please use puts();
ret = 0;
while (1) {
if (fastboot_poll())
break;
if (ctrlc())
break;
}
- }
- fastboot_shutdown();
- return ret;
+}
For the fastboot command you could use the common gadget download code (g_dnl.c). For reference please look into common/cmd_dfu.c
+U_BOOT_CMD(
- fastboot, 1, 1, do_fastboot,
- "fastboot - enter USB Fastboot protocol",
- ""
+); diff --git a/doc/README.android-fastboot b/doc/README.android-fastboot new file mode 100644 index 0000000..4b2a9aa --- /dev/null +++ b/doc/README.android-fastboot @@ -0,0 +1,86 @@ +Android Fastboot +~~~~~~~~~~~~~~~~
+Overview +======== +The protocol that is used over USB is described in +README.android-fastboot-protocol in same folder.
+The current implementation does not yet support the flash and erase +commands.
Flashing of received data can be performed with common dfu_write() method.
+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 :) +
With the write backend (dfu.c) you can specify the buffer size with dfu_bufsiz env variable.
+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.
It is a very nice mix of commands and downloading data. Frankly, I'm quite interested if there is support for "upload" command available (or easily implementable)? It seems, like it is also not possible to execute an arbitrary command from a set supported by the u-boot bootloader.
I'm asking because protocol with such features, would greatly facilitate automated testing for u-boot running on a particular HW.
+Client Variables +----------------
+The "getvar:%s" command is used to read client variables which +represent various information about the device and the software +on it.
+The various currently defined names are:
- version Version of FastBoot protocol supported.
It should be "0.3" for this document.
- version-bootloader Version string for the Bootloader.
- version-baseband Version string of the Baseband Software
- product Name of the product
- serialno Product serial number
- secure If the value is "yes", this is a secure
bootloader requiring a signature before
it will install or boot images.
+Names starting with a lowercase character are reserved by this +specification. OEM-specific names should not start with lowercase +characters. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 804a2bd..b14f60e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o +obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o u_fastboot.o endif ifdef CONFIG_USB_ETHER obj-y += ether.o diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 0000000..f74a52e --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,535 @@ +/*
- (C) Copyright 2008 - 2009
- Windriver, <www.windriver.com>
- Tom Rix Tom.Rix@windriver.com
- Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <errno.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/compiler.h>
+#include "g_fastboot.h"
+#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03
+#define CONFIGURATION_NORMAL 1 +#define BULK_ENDPOINT 1 +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040)
+static struct usb_string def_usb_fb_strings[] = {
- { FB_STR_PRODUCT_IDX, "Default Product" },
- { FB_STR_SERIAL_IDX, "1234567890" },
- { FB_STR_CONFIG_IDX, "Android Fastboot" },
- { FB_STR_INTERFACE_IDX, "Android Fastboot" },
- { FB_STR_MANUFACTURER_IDX, "Default Manufacturer" },
- { FB_STR_PROC_REV_IDX, "Default 1.0" },
- { FB_STR_PROC_TYPE_IDX, "Emulator" },
- { }
+};
Such structure in a more generic way is defined at g_dnl.c file.
+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;
You would probably need DEFINE_CACHE_ALIGN_BUFFER() here to avoid problems with cache coherency.
+static u8 ep0_buffer[512]; +static u8 ep_out_buffer[EP_BUFFER_SIZE]; +static u8 ep_in_buffer[EP_BUFFER_SIZE]; +static int current_config;
+/* e1 */ +static struct usb_endpoint_descriptor fs_ep_in = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN, /* IN */
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE,
- .bInterval = 0x00,
+};
+/* e2 */ +static struct usb_endpoint_descriptor fs_ep_out = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT, /* OUT */
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize =
RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1,
- .bInterval = 0x00,
+};
+static struct usb_endpoint_descriptor hs_ep_out = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT, /* OUT */
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize =
RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0,
- .bInterval = 0x00,
+};
+const char *fb_find_usb_string(unsigned int id) +{
- struct usb_string *s = NULL;
- if (vendor_fb_strings) {
for (s = vendor_fb_strings->strings; s && s->s; s++)
{
if (s->id == id)
break;
}
- }
- if (!s || !s->s) {
for (s = def_fb_strings.strings; s && s->s; s++) {
if (s->id == id)
break;
}
- }
- if (!s)
return NULL;
- return s->s;
+}
+static struct usb_request *ep0_req;
+struct usb_ep *ep_in; +struct usb_request *req_in;
+struct usb_ep *ep_out; +struct usb_request *req_out;
+static void fastboot_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{
- int status = req->status;
- if (!status)
return;
- printf("ep0 status %d\n", status);
+}
+static int fastboot_bind(struct usb_gadget *gadget) +{
- ep0_req = usb_ep_alloc_request(gadget->ep0, 0);
- if (!ep0_req)
goto err;
- ep0_req->buf = ep0_buffer;
- ep0_req->complete = fastboot_ep0_complete;
- ep_in = usb_ep_autoconfig(gadget, &fs_ep_in);
- if (!ep_in)
goto err;
- ep_in->driver_data = ep_in;
- ep_out = usb_ep_autoconfig(gadget, &fs_ep_out);
- if (!ep_out)
goto err;
- ep_out->driver_data = ep_out;
- hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
- usb_gadget_connect(gadget);
- gadget_is_connected = 1;
- return 0;
+err:
- return -1;
+}
+static void fastboot_unbind(struct usb_gadget *gadget) +{
- usb_ep_free_request(gadget->ep0, ep0_req);
- ep_in->driver_data = NULL;
- ep_out->driver_data = NULL;
- gadget_is_connected = 0;
- usb_gadget_disconnect(gadget);
+}
+#define DEVICE_BCD 0x0100
+struct usb_device_descriptor fb_descriptor = {
- .bLength = sizeof(fb_descriptor),
- .bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = 0x200,
- .bMaxPacketSize0 = 0x40,
- .idVendor =
cpu_to_le16(CONFIG_USB_FASTBOOT_VENDOR_ID),
- .idProduct =
cpu_to_le16(CONFIG_USB_FASTBOOT_PRODUCT_ID),
- .bcdDevice = DEVICE_BCD,
- .iManufacturer = FB_STR_MANUFACTURER_IDX,
- .iProduct = FB_STR_PRODUCT_IDX,
- .iSerialNumber = FB_STR_SERIAL_IDX,
- .bNumConfigurations = 1,
+};
+#define TOT_CFG_DESC_LEN (USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE + \
USB_DT_ENDPOINT_SIZE + USB_DT_ENDPOINT_SIZE)
+static struct usb_config_descriptor config_desc = {
- .bLength = USB_DT_CONFIG_SIZE,
- .bDescriptorType = USB_DT_CONFIG,
- .wTotalLength = cpu_to_le16(TOT_CFG_DESC_LEN),
- .bNumInterfaces = 1,
- .bConfigurationValue = CONFIGURATION_NORMAL,
- .iConfiguration = FB_STR_CONFIG_IDX,
- .bmAttributes = 0xc0,
- .bMaxPower = 0x32,
+};
+static struct usb_interface_descriptor interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = 0x00,
- .bAlternateSetting = 0x00,
- .bNumEndpoints = 0x02,
- .bInterfaceClass = FASTBOOT_INTERFACE_CLASS,
- .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS,
- .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL,
- .iInterface = FB_STR_INTERFACE_IDX,
+};
+static struct usb_qualifier_descriptor qual_desc = {
- .bLength = sizeof(qual_desc),
- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
- .bcdUSB = 0x200,
- .bMaxPacketSize0 = 0x40,
- .bNumConfigurations = 1,
+};
+static int fastboot_setup_get_descr(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
+{
- u16 w_value = le16_to_cpu(ctrl->wValue);
- u16 w_length = le16_to_cpu(ctrl->wLength);
- u16 val;
- int ret;
- u32 bytes_remaining;
- u32 bytes_total;
- u32 this_inc;
- val = w_value >> 8;
- switch (val) {
- case USB_DT_DEVICE:
memcpy(ep0_buffer, &fb_descriptor,
sizeof(fb_descriptor));
ep0_req->length = min(w_length,
sizeof(fb_descriptor));
ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
break;
- case USB_DT_CONFIG:
bytes_remaining = min(w_length, sizeof(ep0_buffer));
bytes_total = 0;
/* config */
this_inc = min(bytes_remaining, USB_DT_CONFIG_SIZE);
bytes_remaining -= this_inc;
memcpy(ep0_buffer + bytes_total, &config_desc,
this_inc);
bytes_total += this_inc;
/* interface */
this_inc = min(bytes_remaining,
USB_DT_INTERFACE_SIZE);
bytes_remaining -= this_inc;
memcpy(ep0_buffer + bytes_total, &interface_desc,
this_inc);
bytes_total += this_inc;
/* ep in */
this_inc = min(bytes_remaining,
USB_DT_ENDPOINT_SIZE);
bytes_remaining -= this_inc;
memcpy(ep0_buffer + bytes_total, &fs_ep_in,
this_inc);
bytes_total += this_inc;
/* ep out */
this_inc = min(bytes_remaining,
USB_DT_ENDPOINT_SIZE); +
if (gadget->speed == USB_SPEED_HIGH)
memcpy(ep0_buffer + bytes_total, &hs_ep_out,
this_inc);
else
memcpy(ep0_buffer + bytes_total, &fs_ep_out,
this_inc);
bytes_total += this_inc;
ep0_req->length = bytes_total;
ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
break;
- case USB_DT_STRING:
ret = usb_gadget_get_string(vendor_fb_strings,
w_value & 0xff,
ep0_buffer);
if (ret < 0)
ret = usb_gadget_get_string(&def_fb_strings,
w_value & 0xff,
ep0_buffer);
if (ret < 0)
break;
ep0_req->length = ret;
ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
break;
- case USB_DT_DEVICE_QUALIFIER:
memcpy(ep0_buffer, &qual_desc, sizeof(qual_desc));
ep0_req->length = min(w_length, sizeof(qual_desc));
ret = usb_ep_queue(gadget->ep0, ep0_req, 0);
break;
- default:
ret = -EINVAL;
- }
- return ret;
+}
Here you use only the gadget API. In the THOR, UMS and DFU functions such code is handled by the composite layer (composite.c) for all of them.
+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;
^^^^^^^ maybe one if?
- 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;
I always encourage people to return values from <errno.h>
break;
default:
return -1;
};
- case USB_RECIP_INTERFACE:
switch (req->bRequest) {
case USB_REQ_SET_INTERFACE:
ep0_req->length = 0;
if (!fastboot_set_interface(gadget, 1))
return usb_ep_queue(gadget->ep0,
ep0_req, 0);
return -1;
break;
default:
return -1;
}
- case USB_RECIP_ENDPOINT:
switch (req->bRequest) {
case USB_REQ_CLEAR_FEATURE:
return usb_ep_queue(gadget->ep0, ep0_req, 0);
break;
default:
return -1;
}
- }
- return -1;
+}
+static int fastboot_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *req)
+{
- if ((req->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
return -1;
- if ((req->bRequestType & USB_DIR_IN) == 0)
/* host-to-device */
return fastboot_setup_out_req(gadget, req);
- /* device-to-host */
- if ((req->bRequestType & USB_RECIP_MASK) ==
USB_RECIP_DEVICE) {
switch (req->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
return fastboot_setup_get_descr(gadget, req);
break;
case USB_REQ_GET_CONFIGURATION:
return fastboot_setup_get_conf(gadget, req);
break;
default:
return -1;
}
- }
- return -1;
+}
+static void fastboot_disconnect(struct usb_gadget *gadget) +{
- fastboot_disable_ep(gadget);
- gadget_is_connected = 0;
+}
+struct usb_gadget_driver fast_gadget = {
- .speed = USB_SPEED_HIGH,
- .bind = fastboot_bind,
- .unbind = fastboot_unbind,
- .setup = fastboot_setup,
- .disconnect = fastboot_disconnect,
+};
+static int udc_is_probbed;
+int __weak fastboot_board_init(struct usb_gadget_strings **str) +{
- return 0;
+}
+int fastboot_init(void) +{
- int ret;
- ret = fastboot_board_init(&vendor_fb_strings);
- if (ret)
return ret;
- ret = usb_gadget_register_driver(&fast_gadget);
- if (ret) {
printf("Add gadget failed\n");
return ret;
- }
- udc_is_probbed = 1;
- return 0;
+}
+int fastboot_poll(void) +{
- usb_gadget_handle_interrupts();
- if (gadget_is_connected)
return 0;
- else
return 1;
+}
+void fastboot_shutdown(void) +{
- if (!udc_is_probbed)
return;
- udc_is_probbed = 0;
- usb_gadget_unregister_driver(&fast_gadget);
+}
+int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{
- int ret;
- if (req_in->complete == NULL)
req_in->complete = fastboot_complete_in;
- memcpy(req_in->buf, buffer, buffer_size);
- req_in->length = buffer_size;
- ret = usb_ep_queue(ep_in, req_in, 0);
- if (ret)
printf("Error %d on queue\n", ret);
- return 0;
+} diff --git a/drivers/usb/gadget/g_fastboot.h b/drivers/usb/gadget/g_fastboot.h new file mode 100644 index 0000000..733eb38 --- /dev/null +++ b/drivers/usb/gadget/g_fastboot.h @@ -0,0 +1,15 @@ +#ifndef _G_FASTBOOT_H_ +#define _G_FASTBOOT_H_
+#define EP_BUFFER_SIZE 4096
+extern struct usb_ep *ep_in; +extern struct usb_request *req_in; +extern struct usb_ep *ep_out; +extern struct usb_request *req_out;
+void rx_handler_command(struct usb_ep *ep, struct usb_request *req); +int fastboot_tx_write(const char *buffer, unsigned int buffer_size); +const char *fb_find_usb_string(unsigned int id);
+#endif diff --git a/drivers/usb/gadget/u_fastboot.c b/drivers/usb/gadget/u_fastboot.c new file mode 100644 index 0000000..76116c0 --- /dev/null +++ b/drivers/usb/gadget/u_fastboot.c @@ -0,0 +1,260 @@ +/*
- (C) Copyright 2008 - 2009
- Windriver, <www.windriver.com>
- Tom Rix Tom.Rix@windriver.com
- Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de
- SPDX-License-Identifier: GPL-2.0+
- */
+/*
- Part of the rx_handler were copied from the Android project.
- Specifically rx command parsing in the usb_rx_cmd_complete
- function of the file
bootable/bootloader/legacy/usbloader/usbloader.c
- Repository:
https://android.googlesource.com/platform/bootable/bootloader/legacy
- Files: usbloader/usbloader.c
- Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a
- Copyright (C) 2008 The Android Open Source Project
- SPDX-License-Identifier: BSD-2-Clause
- */
+#include <common.h> +#include <usb/fastboot.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include "g_fastboot.h"
+#define FASTBOOT_VERSION "0.4"
+/* The 64 defined bytes plus \0 */ +#define RESPONSE_LEN (64 + 1)
+static unsigned int download_size; +static unsigned int download_bytes;
+static int fastboot_tx_write_str(const char *buffer) +{
- return fastboot_tx_write(buffer, strlen(buffer));
+}
+static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{
- do_reset(NULL, 0, 0, NULL);
+}
+static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{
- req_in->complete = compl_do_reset;
- fastboot_tx_write_str("OKAY");
+}
+static int strcmp_l1(const char *s1, const char *s2) +{
- return strncmp(s1, s2, strlen(s1));
+}
+static void cb_getvar(struct usb_ep *ep, struct usb_request *req) +{
- char *cmd = req->buf;
- char response[RESPONSE_LEN];
- const char *s;
- strcpy(response, "OKAY");
- strsep(&cmd, ":");
- if (!cmd) {
fastboot_tx_write_str("FAILmissing var");
return;
- }
- if (!strcmp_l1("version", cmd)) {
strncat(response, FASTBOOT_VERSION,
sizeof(response));
- } else if (!strcmp_l1("downloadsize", cmd)) {
char str_num[12];
sprintf(str_num, "%08lx", load_size);
strncat(response, str_num, sizeof(response));
- } else if (!strcmp_l1("product", cmd)) {
s = fb_find_usb_string(FB_STR_PRODUCT_IDX);
if (s)
strncat(response, s, sizeof(response));
else
strcpy(response, "FAILValue not set");
- } else if (!strcmp_l1("serialno", cmd)) {
s = fb_find_usb_string(FB_STR_SERIAL_IDX);
if (s)
strncat(response, s, sizeof(response));
else
strcpy(response, "FAILValue not set");
- } else if (!strcmp_l1("cpurev", cmd)) {
s = fb_find_usb_string(FB_STR_PROC_REV_IDX);
if (s)
strncat(response, s, sizeof(response));
else
strcpy(response, "FAILValue not set");
- } else if (!strcmp_l1("secure", cmd)) {
s = fb_find_usb_string(FB_STR_PROC_TYPE_IDX);
if (s)
strncat(response, s, sizeof(response));
else
strcpy(response, "FAILValue not set");
- } else {
strcpy(response, "FAILVariable not implemented");
- }
- fastboot_tx_write_str(response);
+}
+static unsigned int rx_bytes_expected(void) +{
- int rx_remain = download_size - download_bytes;
- if (rx_remain < 0)
return 0;
- if (rx_remain > EP_BUFFER_SIZE)
return EP_BUFFER_SIZE;
- return rx_remain;
+}
+#define BYTES_PER_DOT 32768 +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{
- char response[RESPONSE_LEN];
- unsigned int transfer_size = download_size - download_bytes;
- const unsigned char *buffer = req->buf;
- unsigned int buffer_size = req->actual;
- if (req->status != 0) {
printf("Bad status: %d\n", req->status);
return;
- }
- if (buffer_size < transfer_size)
transfer_size = buffer_size;
- memcpy((void *)load_addr + download_bytes, buffer,
transfer_size); +
- download_bytes += transfer_size;
The dfu backend uses zero copy approach.
- /* Check if transfer is done */
- if (download_bytes >= download_size) {
/*
* Reset global transfer variable, keep
download_bytes because
* it will be used in the next possible flashing
command
*/
download_size = 0;
req->complete = rx_handler_command;
req->length = EP_BUFFER_SIZE;
sprintf(response, "OKAY");
fastboot_tx_write_str(response);
printf("\ndownloading of %d bytes finished\n",
download_bytes);
- } else {
req->length = rx_bytes_expected();
- }
- if (download_bytes && !(download_bytes % BYTES_PER_DOT)) {
printf(".");
if (!(download_bytes % (74 * BYTES_PER_DOT)))
printf("\n");
- }
- req->actual = 0;
- usb_ep_queue(ep, req, 0);
+}
+static void cb_download(struct usb_ep *ep, struct usb_request *req) +{
- char *cmd = req->buf;
- char response[RESPONSE_LEN];
- strsep(&cmd, ":");
- download_size = simple_strtoul(cmd, NULL, 16);
- download_bytes = 0;
- printf("Starting download of %d bytes\n", download_size);
- if (0 == download_size) {
sprintf(response, "FAILdata invalid size");
- } else if (download_size > load_size) {
download_size = 0;
sprintf(response, "FAILdata too large");
- } else {
sprintf(response, "DATA%08x", download_size);
req->complete = rx_handler_dl_image;
req->length = rx_bytes_expected();
- }
- fastboot_tx_write_str(response);
+}
+static char boot_addr_start[32]; +static char *bootm_args[] = { "bootm", boot_addr_start, NULL };
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) +{
- req->complete = NULL;
- fastboot_shutdown();
- printf("Booting kernel..\n");
puts();
- do_bootm(NULL, 0, 2, bootm_args);
- /* This only happens if image is somehow faulty so we start
over */
- do_reset(NULL, 0, 0, NULL);
+}
+static void cb_boot(struct usb_ep *ep, struct usb_request *req) +{
- sprintf(boot_addr_start, "0x%lx", load_addr);
- req_in->complete = do_bootm_on_complete;
- fastboot_tx_write_str("OKAY");
- return;
+}
+struct cmd_dispatch_info {
- char *cmd;
- void (*cb)(struct usb_ep *ep, struct usb_request *req);
+};
+static struct cmd_dispatch_info cmd_dispatch_info[] = {
- {
.cmd = "reboot",
.cb = cb_reboot,
- }, {
.cmd = "getvar:",
.cb = cb_getvar,
- }, {
.cmd = "download:",
.cb = cb_download,
- }, {
.cmd = "boot",
.cb = cb_boot,
- },
+};
+void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{
- char response[RESPONSE_LEN];
- char *cmdbuf = req->buf;
- void (*func_cb)(struct usb_ep *ep, struct usb_request *req)
= NULL;
- int i;
- sprintf(response, "FAIL");
- for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
func_cb = cmd_dispatch_info[i].cb;
break;
}
- }
- if (!func_cb)
fastboot_tx_write_str("FAILunknown command");
- else
func_cb(ep, req);
- if (req->status == 0) {
*cmdbuf = '\0';
req->actual = 0;
usb_ep_queue(ep, req, 0);
- }
+} diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h new file mode 100644 index 0000000..b348886 --- /dev/null +++ b/include/usb/fastboot.h @@ -0,0 +1,36 @@ +/*
- (C) Copyright 2008 - 2009
- Windriver, <www.windriver.com>
- Tom Rix Tom.Rix@windriver.com
- Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef FASTBOOT_H +#define FASTBOOT_H
+#include <common.h> +#include <command.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h>
+#define FB_STR_PRODUCT_IDX 1 +#define FB_STR_SERIAL_IDX 2 +#define FB_STR_CONFIG_IDX 3 +#define FB_STR_INTERFACE_IDX 4 +#define FB_STR_MANUFACTURER_IDX 5 +#define FB_STR_PROC_REV_IDX 6 +#define FB_STR_PROC_TYPE_IDX 7
+#ifdef CONFIG_CMD_FASTBOOT
+int fastboot_init(void); +void fastboot_shutdown(void); +int fastboot_poll(void);
+int fastboot_board_init(struct usb_gadget_strings **str);
+#endif +#endif
To sum up:
It seems that you are using UDC driver + gadget approach for fastboot.
However, current gadgets for downloading data (DFU, UMS, THOR) use UDC + composite gadget + function.
Also they use common download code - g_dnl.c to implement their commands.
I would be more than happy if you were willing to port the fastboot protocol to the existing framework. For overall picture of the current design please look into the following keynote: http://www.denx.de/wiki/pub/U-Boot/MiniSummitELCE2013/dfu_elce_u-boot.pdf
Please let me know if you have any questions.

On Tue, Apr 15, 2014 at 10:41 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
From: Sebastian Siewior bigeasy@linutronix.de
This patch contains an implementation of the fastboot protocol on the device side and a little of documentation. The gadget expects the new-style gadget framework. The gadget implements the getvar, reboot, download and reboot commands. What is missing is the flash handling i.e. writting the image to media.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126798/ with the following changes:
- Rebase to current mainline and updates for current gadget API
- Use SPDX identifiers for licenses
- Traced the history and added missing copyright to cmd_fastboot.c
- Use load_addr/load_size for transfer buffer
- Allow vendor strings to be optional
[snip]
To sum up:
It seems that you are using UDC driver + gadget approach for fastboot.
However, current gadgets for downloading data (DFU, UMS, THOR) use UDC + composite gadget + function.
Also they use common download code - g_dnl.c to implement their commands.
I would be more than happy if you were willing to port the fastboot protocol to the existing framework. For overall picture of the current design please look into the following keynote: http://www.denx.de/wiki/pub/U-Boot/MiniSummitELCE2013/dfu_elce_u-boot.pdf
Please let me know if you have any questions.
Thanks for your review. I have this working using the common g_dnl.c code now. I have a bit more clean-up to do and I will post it. My current branch is here:
git://git.linaro.org/people/rob.herring/u-boot.git fastboot-v2
Rob

From: Rob Herring robh@kernel.org
Enable Android Fastboot support on omap3_beagle board.
Signed-off-by: Rob Herring robh@kernel.org --- include/configs/omap3_beagle.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 0b57421..e070760 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -110,6 +110,11 @@ #define CONFIG_TWL4030_USB 1 #define CONFIG_USB_ETHER #define CONFIG_USB_ETHER_RNDIS +#define CONFIG_USB_GADGET +#define CONFIG_CMD_FASTBOOT +#define CONFIG_ANDROID_BOOT_IMAGE +#define CONFIG_USB_FASTBOOT_VENDOR_ID 0x0451 +#define CONFIG_USB_FASTBOOT_PRODUCT_ID 0xd022
/* USB EHCI */ #define CONFIG_CMD_USB

On Thu, Apr 10, 2014 at 02:18:07PM -0500, Rob Herring wrote:
From: Rob Herring robh@kernel.org
Enable Android Fastboot support on omap3_beagle board.
Signed-off-by: Rob Herring robh@kernel.org
Reviewed-by: Tom Rini trini@ti.com

From: Rob Herring robh@kernel.org
Signed-off-by: Rob Herring robh@kernel.org --- include/configs/omap3_beagle.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 0b57421..e070760 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -110,6 +110,11 @@ #define CONFIG_TWL4030_USB 1 #define CONFIG_USB_ETHER #define CONFIG_USB_ETHER_RNDIS +#define CONFIG_USB_GADGET +#define CONFIG_CMD_FASTBOOT +#define CONFIG_ANDROID_BOOT_IMAGE +#define CONFIG_USB_FASTBOOT_VENDOR_ID 0x0451 +#define CONFIG_USB_FASTBOOT_PRODUCT_ID 0xd022
/* USB EHCI */ #define CONFIG_CMD_USB

On Thu, Apr 10, 2014 at 2:18 PM, Rob Herring robherring2@gmail.com wrote:
From: Rob Herring robh@kernel.org
Signed-off-by: Rob Herring robh@kernel.org
Oops. Ignore this one...
Rob

On 04/10/2014 09:18 PM, Rob Herring wrote:
From: Rob Herring robh@kernel.org
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
Thank you for picking this up. I believe in the end Wolfgang wasn't concerned about the copyright issue anymore but I simply didn't have the time to re-post the thing again.
Sebastian

On Fri, Apr 11, 2014 at 09:04:36AM +0200, Sebastian Andrzej Siewior wrote:
On 04/10/2014 09:18 PM, Rob Herring wrote:
From: Rob Herring robh@kernel.org
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
Thank you for picking this up. I believe in the end Wolfgang wasn't concerned about the copyright issue anymore but I simply didn't have the time to re-post the thing again.
Yes, for the record, Copyright issues are not a concern, so long as nothing there changed from last time.

On Thu, Apr 10, 2014 at 02:18:02PM -0500, Rob Herring wrote:
From: Rob Herring robh@kernel.org
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
First, thanks for picking this up. I've been hoping someone would get around to this for a while now as it's an often requested thing.
I've tested this series on a BeagleBoard.
Any other boards you can test this on as well? Thanks!

On Fri, Apr 11, 2014 at 9:45 AM, Tom Rini trini@ti.com wrote:
On Thu, Apr 10, 2014 at 02:18:02PM -0500, Rob Herring wrote:
From: Rob Herring robh@kernel.org
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
First, thanks for picking this up. I've been hoping someone would get around to this for a while now as it's an often requested thing.
I've tested this series on a BeagleBoard.
Any other boards you can test this on as well? Thanks!
No, that is the only one I have with USB device. Bo Shen tested on at91, but had some issue with large files I have to look into.
Rob

From: Rob Herring robh@kernel.org
This is the 2nd version since I revived the fastboot patches Sebastian submitted.
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
The Android code used or referenced is BSD 2-clause license. This was originally raised by Wolfgang that it was not compatible with GPLv2+. I believe that has since been demonstrated and agreed that the BSD 2-clause license is compatible with u-boot.
As far as the history of the code, I have traced that back. The u-boot code started in 2008/2009 by Tom Rix @ Windriver. This initial support was then adopted and extended by TI (eMMC support primarily, not included here) in their OMAP u-boot tree[2]. In 2011, the TI code was used as a basis for upstream patches by Sebastian Siewior @ Linutronix. The code has been rearranged quite a bit since the original, but the content is pretty much the same. Some of the re-arranging left stale or missing copyrights in the v2 version which I have corrected.
I've redone the fastboot code significantly to use the USB download gadget composite driver. With this, I've consolidated the fastboot function into a single file. I believe I've addressed all the review comments, but many don't apply with the re-write.
I dropped the patch adding a loadsize env variable and added config uptions instead to set the fastboot buffer size.
I've tested this series on a BeagleBoard.
Rob
[1] http://lists.denx.de/pipermail/u-boot/2011-November/110557.html [2] http://git.omapzoom.org/?p=repo/u-boot.git;a=commit;h=601ff71c8d46b5e90e1361...
Rob Herring (3): usb: handle NULL table in usb_gadget_get_string usb: musb: fill in usb_gadget_unregister_driver arm: beagle: enable Android fastboot support
Sebastian Siewior (2): image: add support for Android's boot image format usb/gadget: add the fastboot gadget
common/Makefile | 3 + common/cmd_bootm.c | 23 +- common/cmd_fastboot.c | 36 +++ common/image-android.c | 84 ++++++ common/image.c | 20 +- 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 + drivers/usb/gadget/usbstring.c | 3 + drivers/usb/musb-new/musb_uboot.c | 5 +- include/android_image.h | 69 +++++ include/configs/omap3_beagle.h | 10 + include/fastboot.h | 22 ++ include/image.h | 13 + 16 files changed, 1067 insertions(+), 7 deletions(-) create mode 100644 common/cmd_fastboot.c create mode 100644 common/image-android.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/android_image.h create mode 100644 include/fastboot.h

From: Rob Herring robh@kernel.org
Allow a NULL table to be passed to usb_gadget_get_string for cases when a string table may not be populated.
Signed-off-by: Rob Herring robh@kernel.org Reviewed-by: Tom Rini trini@ti.com Acked-by: Marek Vasut marex@denx.de Acked-by: Lukasz Majewski l.majewski@samsung.com --- drivers/usb/gadget/usbstring.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index de5fa3f..8c3ff64 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -108,6 +108,9 @@ usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf) struct usb_string *s; int len;
+ if (!table) + return -EINVAL; + /* descriptor 0 has the language id */ if (id == 0) { buf[0] = 4;

From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" defined in the image header is currently not supported. I haven't found anything that is creating this.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes: - Rebased to current mainline - Moved android image handling to separate functions in common/image-android.c - s/u8/char/ in header to fix string function warnings - Use SPDX identifiers for licenses - Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code. v4: - s/andriod/android/ - Use a separate flag ep_found to track if the entry point has been set rather than using a magic value.
Cc: Wolfgang Denk wd@denx.de Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org Reviewed-by: Tom Rini trini@ti.com Reviewed-by: Lukasz Majewski l.majewski@samsung.com --- common/Makefile | 1 + common/cmd_bootm.c | 23 +++++++++++++- common/image-android.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ common/image.c | 20 +++++++++--- include/android_image.h | 69 ++++++++++++++++++++++++++++++++++++++++ include/image.h | 13 ++++++++ 6 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 common/image-android.c create mode 100644 include/android_image.h
diff --git a/common/Makefile b/common/Makefile index cecd81a..da208f3 100644 --- a/common/Makefile +++ b/common/Makefile @@ -236,6 +236,7 @@ obj-y += console.o obj-$(CONFIG_CROS_EC) += cros_ec.o obj-y += dlmalloc.o obj-y += image.o +obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_OF_LIBFDT) += image-fdt.o obj-$(CONFIG_FIT) += image-fit.o obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 9751edc..3de876d 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -222,6 +222,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { const void *os_hdr; + bool ep_found = false;
/* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, @@ -274,6 +275,18 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, } break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + images.os.type = IH_TYPE_KERNEL; + images.os.comp = IH_COMP_NONE; + images.os.os = IH_OS_LINUX; + images.ep = images.os.load; + ep_found = true; + + images.os.end = android_image_get_end(os_hdr); + images.os.load = android_image_get_kload(os_hdr); + break; +#endif default: puts("ERROR: unknown image format type!\n"); return 1; @@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1; } #endif - } else { + } else if (!ep_found) { puts("Could not find kernel entry point!\n"); return 1; } @@ -1002,6 +1015,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, images->fit_noffset_os = os_noffset; break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + printf("## Booting Android Image at 0x%08lx ...\n", img_addr); + if (android_image_get_kernel((void *)img_addr, images->verify, + os_data, os_len)) + return NULL; + break; +#endif default: printf("Wrong Image Format for %s command\n", cmdtp->name); bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); diff --git a/common/image-android.c b/common/image-android.c new file mode 100644 index 0000000..6ded7e2 --- /dev/null +++ b/common/image-android.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <image.h> +#include <android_image.h> + +static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; + +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len) +{ + /* + * Not all Android tools use the id field for signing the image with + * sha1 (or anything) so we don't check it. It is not obvious that the + * string is null terminated so we take care of this. + */ + strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE); + andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0'; + if (strlen(andr_tmp_str)) + printf("Android's image name: %s\n", andr_tmp_str); + + printf("Kernel load addr 0x%08x size %u KiB\n", + hdr->kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024)); + strncpy(andr_tmp_str, hdr->cmdline, ANDR_BOOT_ARGS_SIZE); + andr_tmp_str[ANDR_BOOT_ARGS_SIZE] = '\0'; + if (strlen(andr_tmp_str)) { + printf("Kernel command line: %s\n", andr_tmp_str); + setenv("bootargs", andr_tmp_str); + } + if (hdr->ramdisk_size) + printf("RAM disk load addr 0x%08x size %u KiB\n", + hdr->ramdisk_addr, + DIV_ROUND_UP(hdr->ramdisk_size, 1024)); + + if (os_data) { + *os_data = (ulong)hdr; + *os_data += hdr->page_size; + } + if (os_len) + *os_len = hdr->kernel_size; + return 0; +} + +int android_image_check_header(const struct andr_img_hdr *hdr) +{ + return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); +} + +ulong android_image_get_end(const struct andr_img_hdr *hdr) +{ + u32 size = 0; + /* + * The header takes a full page, the remaining components are aligned + * on page boundary + */ + size += hdr->page_size; + size += ALIGN(hdr->kernel_size, hdr->page_size); + size += ALIGN(hdr->ramdisk_size, hdr->page_size); + size += ALIGN(hdr->second_size, hdr->page_size); + + return size; +} + +ulong android_image_get_kload(const struct andr_img_hdr *hdr) +{ + return hdr->kernel_addr; +} + +int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len) +{ + if (!hdr->ramdisk_size) + return -1; + *rd_data = (unsigned long)hdr; + *rd_data += hdr->page_size; + *rd_data += ALIGN(hdr->kernel_size, hdr->page_size); + + *rd_len = hdr->ramdisk_size; + return 0; +} diff --git a/common/image.c b/common/image.c index 9c6bec5..7ff27d7 100644 --- a/common/image.c +++ b/common/image.c @@ -659,10 +659,12 @@ int genimg_get_format(const void *img_addr) if (image_check_magic(hdr)) format = IMAGE_FORMAT_LEGACY; #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) - else { - if (fdt_check_header(img_addr) == 0) - format = IMAGE_FORMAT_FIT; - } + else if (fdt_check_header(img_addr) == 0) + format = IMAGE_FORMAT_FIT; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + else if (android_image_check_header(img_addr) == 0) + format = IMAGE_FORMAT_ANDROID; #endif
return format; @@ -932,7 +934,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); - } else { + } +#ifdef CONFIG_ANDROID_BOOT_IMAGE + else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) && + (!android_image_get_ramdisk((void *)images->os.start, + &rd_data, &rd_len))) { + /* empty */ + } +#endif + else { /* * no initrd image */ diff --git a/include/android_image.h b/include/android_image.h new file mode 100644 index 0000000..094d60a --- /dev/null +++ b/include/android_image.h @@ -0,0 +1,69 @@ +/* + * This is from the Android Project, + * Repository: https://android.googlesource.com/platform/bootable/bootloader/legacy + * File: include/boot/bootimg.h + * Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a + * + * Copyright (C) 2008 The Android Open Source Project + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _ANDROID_IMAGE_H_ +#define _ANDROID_IMAGE_H_ + +#define ANDR_BOOT_MAGIC "ANDROID!" +#define ANDR_BOOT_MAGIC_SIZE 8 +#define ANDR_BOOT_NAME_SIZE 16 +#define ANDR_BOOT_ARGS_SIZE 512 + +struct andr_img_hdr { + char magic[ANDR_BOOT_MAGIC_SIZE]; + + u32 kernel_size; /* size in bytes */ + u32 kernel_addr; /* physical load addr */ + + u32 ramdisk_size; /* size in bytes */ + u32 ramdisk_addr; /* physical load addr */ + + u32 second_size; /* size in bytes */ + u32 second_addr; /* physical load addr */ + + u32 tags_addr; /* physical addr for kernel tags */ + u32 page_size; /* flash page size we assume */ + u32 unused[2]; /* future expansion: should be 0 */ + + char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */ + + char cmdline[ANDR_BOOT_ARGS_SIZE]; + + u32 id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +/* + * +-----------------+ + * | boot header | 1 page + * +-----------------+ + * | kernel | n pages + * +-----------------+ + * | ramdisk | m pages + * +-----------------+ + * | second stage | o pages + * +-----------------+ + * + * n = (kernel_size + page_size - 1) / page_size + * m = (ramdisk_size + page_size - 1) / page_size + * o = (second_size + page_size - 1) / page_size + * + * 0. all entities are page_size aligned in flash + * 1. kernel and ramdisk are required (size != 0) + * 2. second is optional (second_size == 0 -> no second) + * 3. load each element (kernel, ramdisk, second) at + * the specified physical address (kernel_addr, etc) + * 4. prepare tags at tag_addr. kernel_args[] is + * appended to the kernel commandline in the tags. + * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr + * 6. if second_size != 0: jump to second_addr + * else: jump to kernel_addr + */ +#endif diff --git a/include/image.h b/include/image.h index 6afd57b..5f85b96 100644 --- a/include/image.h +++ b/include/image.h @@ -403,6 +403,7 @@ enum fit_load_op { #define IMAGE_FORMAT_INVALID 0x00 #define IMAGE_FORMAT_LEGACY 0x01 /* legacy image_header based format */ #define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format */ +#define IMAGE_FORMAT_ANDROID 0x03 /* Android boot image */
int genimg_get_format(const void *img_addr); int genimg_has_config(bootm_headers_t *images); @@ -996,4 +997,16 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) #endif /* CONFIG_FIT_VERBOSE */ #endif /* CONFIG_FIT */
+#if defined(CONFIG_ANDROID_BOOT_IMAGE) +struct andr_img_hdr; +int android_image_check_header(const struct andr_img_hdr *hdr); +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len); +int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len); +ulong android_image_get_end(const struct andr_img_hdr *hdr); +ulong android_image_get_kload(const struct andr_img_hdr *hdr); + +#endif /* CONFIG_ANDROID_BOOT_IMAGE */ + #endif /* __IMAGE_H__ */

From: Rob Herring robh@kernel.org
Add missing missing disconnect and unbind calls to the musb gadget driver's usb_gadget_unregister_driver function. Otherwise, any gadget drivers fail to uninitialize and run a 2nd time.
Signed-off-by: Rob Herring robh@kernel.org --- drivers/usb/musb-new/musb_uboot.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 0512680..0d7b89f 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -204,7 +204,10 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { - /* TODO: implement me */ + if (driver->disconnect) + driver->disconnect(&gadget->g); + if (driver->unbind) + driver->unbind(&gadget->g); return 0; } #endif /* CONFIG_MUSB_GADGET */

Hi Rob,
From: Rob Herring robh@kernel.org
Add missing missing disconnect and unbind calls to the musb gadget
^^^^^^^ I suppose that one missing is redundant.
driver's usb_gadget_unregister_driver function. Otherwise, any gadget drivers fail to uninitialize and run a 2nd time.
Signed-off-by: Rob Herring robh@kernel.org
drivers/usb/musb-new/musb_uboot.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 0512680..0d7b89f 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -204,7 +204,10 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) {
- /* TODO: implement me */
- if (driver->disconnect)
driver->disconnect(&gadget->g);
- if (driver->unbind)
return 0;driver->unbind(&gadget->g);
} #endif /* CONFIG_MUSB_GADGET */
Despite the minor problem with commit message, the rest seems correct.
Reviewed-by: Lukasz Majewski l.majewski@samsung.com

From: Sebastian Siewior bigeasy@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@linutronix.de Signed-off-by: Rob Herring robh@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@windriver.com + * + * (C) Copyright 2014 Linaro, Ltd. + * Rob Herring robh@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@windriver.com + * + * Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring robh@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; +} + +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; +} + +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, + 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@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

Hi Rob,
From: Sebastian Siewior bigeasy@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@linutronix.de Signed-off-by: Rob Herring robh@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@windriver.com
- (C) Copyright 2014 Linaro, Ltd.
- Rob Herring robh@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@windriver.com
- Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de
- Copyright 2014 Linaro, Ltd.
- Rob Herring robh@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);
} else { printf("%s: unknown command: %s\n", __func__, type); return -EINVAL;strcat(name, type);
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@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.

On Wednesday, April 23, 2014 at 01:02:13 PM, Lukasz Majewski wrote: [...]
+#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?
Depends on Mateusz, surely we will see V5 of his set.
If Marek don't mind, I'd like to ask you to rebase yours patches on top of Mateusz work.
btw Please learn to trim the message to only the relevant bits.
Best regards, Marek Vasut

On Wed, Apr 23, 2014 at 6:02 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
From: Sebastian Siewior bigeasy@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.
[...]
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?
Okay. Actually, we can remove the goto and just return the original error codes.
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.
I considered this already. I certainly don't like reinventing things which was why I originally used loadaddr and added loadsize to provide a defined load buffer size.
The problem is fastboot needs enough RAM to download an entire sparse filesystem. I have no idea what size exactly is typical or required, but it seems that we want to be able to use nearly all free RAM. We can talk all we want about how this is a crappy design, but it is what it is. This is how the protocol works.
The problem with the DFU buffer is it is allocated from the malloc region. If we just increase the malloc region to be close to total RAM size then we will start to break other commands like tftp and fsload which typically just use the RAM u-boot is not using (i.e. all but the end of memory). The only platforms which have more than a few MB for malloc are the ones that enable DFU.
Rob

Hi Rob,
On Wed, Apr 23, 2014 at 6:02 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
From: Sebastian Siewior bigeasy@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.
[...]
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?
Okay. Actually, we can remove the goto and just return the original error codes.
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.
I considered this already. I certainly don't like reinventing things which was why I originally used loadaddr and added loadsize to provide a defined load buffer size.
The problem is fastboot needs enough RAM to download an entire sparse filesystem. I have no idea what size exactly is typical or required, but it seems that we want to be able to use nearly all free RAM. We can talk all we want about how this is a crappy design, but it is what it is. This is how the protocol works.
I understand you :-). The same situation was with DFU on the beginning. Large buffer with starting address defined per board.
Then, after some discussion, we come to conclusion that it would be better to increase malloc pool and dynamically allocate buffer.
Am I correct, that you don't know beforehand what would be the size of downloaded file - maybe 5 MiB or maybe 512 MiB? Also from your descriptor it seems like fastboot protocol don't want to impose any restrictions about the size. Is it user's responsibility to send data smaller than RAM size?
In the DFU/THOR we store data in buffer size packets (32 MiB). It also has some drawbacks - with large raw data images we cannot download the whole (e.g. rootfs) image and beforehand flashing check integrity.
One question - when your board has e.g. 768 MiB of "available" RAM, then is the size of large rootfs restricted to this size?
The problem with the DFU buffer is it is allocated from the malloc region. If we just increase the malloc region to be close to total RAM size then we will start to break other commands like tftp and fsload which typically just use the RAM u-boot is not using (i.e. all but the end of memory). The only platforms which have more than a few MB for malloc are the ones that enable DFU.
Correct. On the other hand when we want to allocate too large buffer we receive error from malloc and flashing is aborted. No harm is done.
Rob

On Fri, Apr 25, 2014 at 12:26 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
On Wed, Apr 23, 2014 at 6:02 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
From: Sebastian Siewior bigeasy@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.
[...]
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.
I considered this already. I certainly don't like reinventing things which was why I originally used loadaddr and added loadsize to provide a defined load buffer size.
The problem is fastboot needs enough RAM to download an entire sparse filesystem. I have no idea what size exactly is typical or required, but it seems that we want to be able to use nearly all free RAM. We can talk all we want about how this is a crappy design, but it is what it is. This is how the protocol works.
I understand you :-). The same situation was with DFU on the beginning. Large buffer with starting address defined per board.
Then, after some discussion, we come to conclusion that it would be better to increase malloc pool and dynamically allocate buffer.
Am I correct, that you don't know beforehand what would be the size of downloaded file - maybe 5 MiB or maybe 512 MiB? Also from your descriptor it seems like fastboot protocol don't want to impose any restrictions about the size. Is it user's responsibility to send data smaller than RAM size?
Correct. The client side will check the size which is one of the variables. I searched around some to try to get an idea of what the typical buffer size is without much luck.
In the DFU/THOR we store data in buffer size packets (32 MiB). It also has some drawbacks - with large raw data images we cannot download the whole (e.g. rootfs) image and beforehand flashing check integrity.
One question - when your board has e.g. 768 MiB of "available" RAM, then is the size of large rootfs restricted to this size?
Yes, but that is not the size of the rootfs partition. The downloaded files are sparse. I would guess only the minimal filesystem is laid down this way and most optional pieces are installed later.
The problem with the DFU buffer is it is allocated from the malloc region. If we just increase the malloc region to be close to total RAM size then we will start to break other commands like tftp and fsload which typically just use the RAM u-boot is not using (i.e. all but the end of memory). The only platforms which have more than a few MB for malloc are the ones that enable DFU.
Correct. On the other hand when we want to allocate too large buffer we receive error from malloc and flashing is aborted. No harm is done.
If increasing your malloc region breaks various load commands, then harm is done.
Rob

Hi Rob,
On Fri, Apr 25, 2014 at 12:26 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
On Wed, Apr 23, 2014 at 6:02 AM, Lukasz Majewski l.majewski@samsung.com wrote:
Hi Rob,
From: Sebastian Siewior bigeasy@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.
[...]
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.
I considered this already. I certainly don't like reinventing things which was why I originally used loadaddr and added loadsize to provide a defined load buffer size.
The problem is fastboot needs enough RAM to download an entire sparse filesystem. I have no idea what size exactly is typical or required, but it seems that we want to be able to use nearly all free RAM. We can talk all we want about how this is a crappy design, but it is what it is. This is how the protocol works.
I understand you :-). The same situation was with DFU on the beginning. Large buffer with starting address defined per board.
Then, after some discussion, we come to conclusion that it would be better to increase malloc pool and dynamically allocate buffer.
Am I correct, that you don't know beforehand what would be the size of downloaded file - maybe 5 MiB or maybe 512 MiB? Also from your descriptor it seems like fastboot protocol don't want to impose any restrictions about the size. Is it user's responsibility to send data smaller than RAM size?
Correct. The client side will check the size which is one of the variables. I searched around some to try to get an idea of what the typical buffer size is without much luck.
Ok, I see.
In the DFU/THOR we store data in buffer size packets (32 MiB). It also has some drawbacks - with large raw data images we cannot download the whole (e.g. rootfs) image and beforehand flashing check integrity.
One question - when your board has e.g. 768 MiB of "available" RAM, then is the size of large rootfs restricted to this size?
Yes, but that is not the size of the rootfs partition. The downloaded files are sparse. I would guess only the minimal filesystem is laid down this way and most optional pieces are installed later.
Or it is resized when needed.
The problem with the DFU buffer is it is allocated from the malloc region. If we just increase the malloc region to be close to total RAM size then we will start to break other commands like tftp and fsload which typically just use the RAM u-boot is not using (i.e. all but the end of memory). The only platforms which have more than a few MB for malloc are the ones that enable DFU.
Correct. On the other hand when we want to allocate too large buffer we receive error from malloc and flashing is aborted. No harm is done.
If increasing your malloc region breaks various load commands, then harm is done.
To be more precise - in our boards we have at least 1 GiB of RAM. The "large" malloc'ed buffer for DFU has 32 MiB at our boards. The total pool size is 80 MiB, which is less than 10% of total RAM. Hence I don't have problems similar to yours.
My little request - please make those defines to be easily reusable at other boards.
Rob

From: Rob Herring robh@kernel.org
Enable Android Fastboot support on omap3_beagle board.
Signed-off-by: Rob Herring robh@kernel.org --- include/configs/omap3_beagle.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 0b57421..be39b7c 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -110,6 +110,16 @@ #define CONFIG_TWL4030_USB 1 #define CONFIG_USB_ETHER #define CONFIG_USB_ETHER_RNDIS +#define CONFIG_USB_GADGET +#define CONFIG_USB_GADGET_VBUS_DRAW 0 +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_G_DNL_VENDOR_NUM 0x0451 +#define CONFIG_G_DNL_PRODUCT_NUM 0xd022 +#define CONFIG_G_DNL_MANUFACTURER "TI" +#define CONFIG_CMD_FASTBOOT +#define CONFIG_ANDROID_BOOT_IMAGE +#define CONFIG_USB_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR +#define CONFIG_USB_FASTBOOT_BUF_SIZE 0x07000000
/* USB EHCI */ #define CONFIG_CMD_USB

Dear Rob,
In message 1397829272-22266-1-git-send-email-robherring2@gmail.com you wrote:
I dropped the patch adding a loadsize env variable and added config uptions instead to set the fastboot buffer size.
Thanks, but...
common/Makefile | 3 + common/cmd_bootm.c | 23 +- common/cmd_fastboot.c | 36 +++ common/image-android.c | 84 ++++++ common/image.c | 20 +- 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 + drivers/usb/gadget/usbstring.c | 3 + drivers/usb/musb-new/musb_uboot.c | 5 +- include/android_image.h | 69 +++++ include/configs/omap3_beagle.h | 10 + include/fastboot.h | 22 ++ include/image.h | 13 + 16 files changed, 1067 insertions(+), 7 deletions(-)
...I don't see the README in the list of modified files - but new CONFIG options _must_ be documented there. So please add the missing documentation.
Best regards,
Wolfgang Denk

On Friday, April 18, 2014 at 06:14:26 PM, Wolfgang Denk wrote:
Dear Rob,
In message 1397829272-22266-1-git-send-email-robherring2@gmail.com you
wrote:
I dropped the patch adding a loadsize env variable and added config uptions instead to set the fastboot buffer size.
Thanks, but...
common/Makefile | 3 + common/cmd_bootm.c | 23 +- common/cmd_fastboot.c | 36 +++ common/image-android.c | 84 ++++++ common/image.c | 20 +- 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 + drivers/usb/gadget/usbstring.c | 3 + drivers/usb/musb-new/musb_uboot.c | 5 +- include/android_image.h | 69 +++++ include/configs/omap3_beagle.h | 10 + include/fastboot.h | 22 ++ include/image.h | 13 + 16 files changed, 1067 insertions(+), 7 deletions(-)
...I don't see the README in the list of modified files - but new CONFIG options _must_ be documented there. So please add the missing documentation.
I reviewed the patches again. I am with WD on this, please add the changes to the README file.
Other than that, I applied 1/5 and 3/5 now as they are clearly fixes and can go in. Please rebase on top of u-boot-usb/master and re-submit the rest.
Thanks!
Best regards, Marek Vasut

On Mon, Apr 21, 2014 at 10:13 AM, Marek Vasut marex@denx.de wrote:
On Friday, April 18, 2014 at 06:14:26 PM, Wolfgang Denk wrote:
Dear Rob,
In message 1397829272-22266-1-git-send-email-robherring2@gmail.com you
wrote:
[...]
...I don't see the README in the list of modified files - but new CONFIG options _must_ be documented there. So please add the missing documentation.
I reviewed the patches again. I am with WD on this, please add the changes to the README file.
Okay, will do.
Other than that, I applied 1/5 and 3/5 now as they are clearly fixes and can go in. Please rebase on top of u-boot-usb/master and re-submit the rest.
It does not appear you have pushed out those 2 patches to your tree.
Rob

On Wednesday, April 23, 2014 at 04:36:04 PM, Rob Herring wrote:
On Mon, Apr 21, 2014 at 10:13 AM, Marek Vasut marex@denx.de wrote:
On Friday, April 18, 2014 at 06:14:26 PM, Wolfgang Denk wrote:
Dear Rob,
In message 1397829272-22266-1-git-send-email-robherring2@gmail.com you
wrote:
[...]
...I don't see the README in the list of modified files - but new CONFIG options _must_ be documented there. So please add the missing documentation.
I reviewed the patches again. I am with WD on this, please add the changes to the README file.
Okay, will do.
Other than that, I applied 1/5 and 3/5 now as they are clearly fixes and can go in. Please rebase on top of u-boot-usb/master and re-submit the rest.
It does not appear you have pushed out those 2 patches to your tree.
Fixed, thanks for bringing this to my attention.
Best regards, Marek Vasut

From: Rob Herring robh@kernel.org
This is the 3nd version since I revived the fastboot patches Sebastian submitted.
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
The Android code used or referenced is BSD 2-clause license. This was originally raised by Wolfgang that it was not compatible with GPLv2+. I believe that has since been demonstrated and agreed that the BSD 2-clause license is compatible with u-boot.
As far as the history of the code, I have traced that back. The u-boot code started in 2008/2009 by Tom Rix @ Windriver. This initial support was then adopted and extended by TI (eMMC support primarily, not included here) in their OMAP u-boot tree[2]. In 2011, the TI code was used as a basis for upstream patches by Sebastian Siewior @ Linutronix. The code has been rearranged quite a bit since the original, but the content is pretty much the same. Some of the re-arranging left stale or missing copyrights in the v2 version which I have corrected.
This version is rebased on u-boot usb tree with the recent USB downloader gadget registration changes. The primary change is documentation added to README for new config options.
I've tested this series on a BeagleBoard.
Rob
[1] http://lists.denx.de/pipermail/u-boot/2011-November/110557.html [2] http://git.omapzoom.org/?p=repo/u-boot.git;a=commit;h=601ff71c8d46b5e90e1361...
Rob Herring (1): arm: beagle: enable Android fastboot support
Sebastian Siewior (2): image: add support for Android's boot image format usb/gadget: add the fastboot gadget
README | 22 ++ common/Makefile | 3 + common/cmd_bootm.c | 23 +- common/cmd_fastboot.c | 36 +++ common/image-android.c | 84 ++++++ common/image.c | 20 +- doc/README.android-fastboot | 91 +++++++ doc/README.android-fastboot-protocol | 170 ++++++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_fastboot.c | 513 +++++++++++++++++++++++++++++++++++ include/android_image.h | 69 +++++ include/configs/omap3_beagle.h | 10 + include/image.h | 13 + 13 files changed, 1049 insertions(+), 6 deletions(-) create mode 100644 common/cmd_fastboot.c create mode 100644 common/image-android.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/android_image.h

From: Sebastian Siewior bigeasy@linutronix.de
This patch adds support for the Android boot-image format. The header file is from the Android project and got slightly alterted so the struct + its defines are not generic but have something like a namespace. The header file is from bootloader/legacy/include/boot/bootimg.h. The header parsing has been written from scratch and I looked at bootloader/legacy/usbloader/usbloader.c for some details. The image contains the physical address (load address) of the kernel and ramdisk. This address is considered only for the kernel image. The "second image" defined in the image header is currently not supported. I haven't found anything that is creating this.
v3 (Rob Herring): This is based on http://patchwork.ozlabs.org/patch/126797/ with the following changes: - Rebased to current mainline - Moved android image handling to separate functions in common/image-android.c - s/u8/char/ in header to fix string function warnings - Use SPDX identifiers for licenses - Cleaned-up file source information: android_image.h is from file include/boot/bootimg.h in repository: https://android.googlesource.com/platform/bootable/bootloader/legacy The git commit hash is 4205b865141ff2e255fe1d3bd16de18e217ef06a usbloader.c would be from the same commit, but it does not appear to have been used for any actual code. v4: - s/andriod/android/ - Use a separate flag ep_found to track if the entry point has been set rather than using a magic value.
Cc: Wolfgang Denk wd@denx.de Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org Reviewed-by: Tom Rini trini@ti.com Reviewed-by: Lukasz Majewski l.majewski@samsung.com --- common/Makefile | 1 + common/cmd_bootm.c | 23 +++++++++++++- common/image-android.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ common/image.c | 20 +++++++++--- include/android_image.h | 69 ++++++++++++++++++++++++++++++++++++++++ include/image.h | 13 ++++++++ 6 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 common/image-android.c create mode 100644 include/android_image.h
diff --git a/common/Makefile b/common/Makefile index 7c853ae..bfcb466 100644 --- a/common/Makefile +++ b/common/Makefile @@ -237,6 +237,7 @@ obj-y += console.o obj-$(CONFIG_CROS_EC) += cros_ec.o obj-y += dlmalloc.o obj-y += image.o +obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_OF_LIBFDT) += image-fdt.o obj-$(CONFIG_FIT) += image-fit.o obj-$(CONFIG_FIT_SIGNATURE) += image-sig.o diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index c243a5b..993b906 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -222,6 +222,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { const void *os_hdr; + bool ep_found = false;
/* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, @@ -274,6 +275,18 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, } break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + images.os.type = IH_TYPE_KERNEL; + images.os.comp = IH_COMP_NONE; + images.os.os = IH_OS_LINUX; + images.ep = images.os.load; + ep_found = true; + + images.os.end = android_image_get_end(os_hdr); + images.os.load = android_image_get_kload(os_hdr); + break; +#endif default: puts("ERROR: unknown image format type!\n"); return 1; @@ -293,7 +306,7 @@ static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, return 1; } #endif - } else { + } else if (!ep_found) { puts("Could not find kernel entry point!\n"); return 1; } @@ -1002,6 +1015,14 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, images->fit_noffset_os = os_noffset; break; #endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + case IMAGE_FORMAT_ANDROID: + printf("## Booting Android Image at 0x%08lx ...\n", img_addr); + if (android_image_get_kernel((void *)img_addr, images->verify, + os_data, os_len)) + return NULL; + break; +#endif default: printf("Wrong Image Format for %s command\n", cmdtp->name); bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO); diff --git a/common/image-android.c b/common/image-android.c new file mode 100644 index 0000000..6ded7e2 --- /dev/null +++ b/common/image-android.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <image.h> +#include <android_image.h> + +static char andr_tmp_str[ANDR_BOOT_ARGS_SIZE + 1]; + +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len) +{ + /* + * Not all Android tools use the id field for signing the image with + * sha1 (or anything) so we don't check it. It is not obvious that the + * string is null terminated so we take care of this. + */ + strncpy(andr_tmp_str, hdr->name, ANDR_BOOT_NAME_SIZE); + andr_tmp_str[ANDR_BOOT_NAME_SIZE] = '\0'; + if (strlen(andr_tmp_str)) + printf("Android's image name: %s\n", andr_tmp_str); + + printf("Kernel load addr 0x%08x size %u KiB\n", + hdr->kernel_addr, DIV_ROUND_UP(hdr->kernel_size, 1024)); + strncpy(andr_tmp_str, hdr->cmdline, ANDR_BOOT_ARGS_SIZE); + andr_tmp_str[ANDR_BOOT_ARGS_SIZE] = '\0'; + if (strlen(andr_tmp_str)) { + printf("Kernel command line: %s\n", andr_tmp_str); + setenv("bootargs", andr_tmp_str); + } + if (hdr->ramdisk_size) + printf("RAM disk load addr 0x%08x size %u KiB\n", + hdr->ramdisk_addr, + DIV_ROUND_UP(hdr->ramdisk_size, 1024)); + + if (os_data) { + *os_data = (ulong)hdr; + *os_data += hdr->page_size; + } + if (os_len) + *os_len = hdr->kernel_size; + return 0; +} + +int android_image_check_header(const struct andr_img_hdr *hdr) +{ + return memcmp(ANDR_BOOT_MAGIC, hdr->magic, ANDR_BOOT_MAGIC_SIZE); +} + +ulong android_image_get_end(const struct andr_img_hdr *hdr) +{ + u32 size = 0; + /* + * The header takes a full page, the remaining components are aligned + * on page boundary + */ + size += hdr->page_size; + size += ALIGN(hdr->kernel_size, hdr->page_size); + size += ALIGN(hdr->ramdisk_size, hdr->page_size); + size += ALIGN(hdr->second_size, hdr->page_size); + + return size; +} + +ulong android_image_get_kload(const struct andr_img_hdr *hdr) +{ + return hdr->kernel_addr; +} + +int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len) +{ + if (!hdr->ramdisk_size) + return -1; + *rd_data = (unsigned long)hdr; + *rd_data += hdr->page_size; + *rd_data += ALIGN(hdr->kernel_size, hdr->page_size); + + *rd_len = hdr->ramdisk_size; + return 0; +} diff --git a/common/image.c b/common/image.c index 9c6bec5..7ff27d7 100644 --- a/common/image.c +++ b/common/image.c @@ -659,10 +659,12 @@ int genimg_get_format(const void *img_addr) if (image_check_magic(hdr)) format = IMAGE_FORMAT_LEGACY; #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) - else { - if (fdt_check_header(img_addr) == 0) - format = IMAGE_FORMAT_FIT; - } + else if (fdt_check_header(img_addr) == 0) + format = IMAGE_FORMAT_FIT; +#endif +#ifdef CONFIG_ANDROID_BOOT_IMAGE + else if (android_image_check_header(img_addr) == 0) + format = IMAGE_FORMAT_ANDROID; #endif
return format; @@ -932,7 +934,15 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, (ulong)images->legacy_hdr_os);
image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len); - } else { + } +#ifdef CONFIG_ANDROID_BOOT_IMAGE + else if ((genimg_get_format(images) == IMAGE_FORMAT_ANDROID) && + (!android_image_get_ramdisk((void *)images->os.start, + &rd_data, &rd_len))) { + /* empty */ + } +#endif + else { /* * no initrd image */ diff --git a/include/android_image.h b/include/android_image.h new file mode 100644 index 0000000..094d60a --- /dev/null +++ b/include/android_image.h @@ -0,0 +1,69 @@ +/* + * This is from the Android Project, + * Repository: https://android.googlesource.com/platform/bootable/bootloader/legacy + * File: include/boot/bootimg.h + * Commit: 4205b865141ff2e255fe1d3bd16de18e217ef06a + * + * Copyright (C) 2008 The Android Open Source Project + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef _ANDROID_IMAGE_H_ +#define _ANDROID_IMAGE_H_ + +#define ANDR_BOOT_MAGIC "ANDROID!" +#define ANDR_BOOT_MAGIC_SIZE 8 +#define ANDR_BOOT_NAME_SIZE 16 +#define ANDR_BOOT_ARGS_SIZE 512 + +struct andr_img_hdr { + char magic[ANDR_BOOT_MAGIC_SIZE]; + + u32 kernel_size; /* size in bytes */ + u32 kernel_addr; /* physical load addr */ + + u32 ramdisk_size; /* size in bytes */ + u32 ramdisk_addr; /* physical load addr */ + + u32 second_size; /* size in bytes */ + u32 second_addr; /* physical load addr */ + + u32 tags_addr; /* physical addr for kernel tags */ + u32 page_size; /* flash page size we assume */ + u32 unused[2]; /* future expansion: should be 0 */ + + char name[ANDR_BOOT_NAME_SIZE]; /* asciiz product name */ + + char cmdline[ANDR_BOOT_ARGS_SIZE]; + + u32 id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +/* + * +-----------------+ + * | boot header | 1 page + * +-----------------+ + * | kernel | n pages + * +-----------------+ + * | ramdisk | m pages + * +-----------------+ + * | second stage | o pages + * +-----------------+ + * + * n = (kernel_size + page_size - 1) / page_size + * m = (ramdisk_size + page_size - 1) / page_size + * o = (second_size + page_size - 1) / page_size + * + * 0. all entities are page_size aligned in flash + * 1. kernel and ramdisk are required (size != 0) + * 2. second is optional (second_size == 0 -> no second) + * 3. load each element (kernel, ramdisk, second) at + * the specified physical address (kernel_addr, etc) + * 4. prepare tags at tag_addr. kernel_args[] is + * appended to the kernel commandline in the tags. + * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr + * 6. if second_size != 0: jump to second_addr + * else: jump to kernel_addr + */ +#endif diff --git a/include/image.h b/include/image.h index 2508d7d..e1f9297 100644 --- a/include/image.h +++ b/include/image.h @@ -412,6 +412,7 @@ enum fit_load_op { #define IMAGE_FORMAT_INVALID 0x00 #define IMAGE_FORMAT_LEGACY 0x01 /* legacy image_header based format */ #define IMAGE_FORMAT_FIT 0x02 /* new, libfdt based format */ +#define IMAGE_FORMAT_ANDROID 0x03 /* Android boot image */
int genimg_get_format(const void *img_addr); int genimg_has_config(bootm_headers_t *images); @@ -1030,4 +1031,16 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) #endif /* CONFIG_FIT_VERBOSE */ #endif /* CONFIG_FIT */
+#if defined(CONFIG_ANDROID_BOOT_IMAGE) +struct andr_img_hdr; +int android_image_check_header(const struct andr_img_hdr *hdr); +int android_image_get_kernel(const struct andr_img_hdr *hdr, int verify, + ulong *os_data, ulong *os_len); +int android_image_get_ramdisk(const struct andr_img_hdr *hdr, + ulong *rd_data, ulong *rd_len); +ulong android_image_get_end(const struct andr_img_hdr *hdr); +ulong android_image_get_kload(const struct andr_img_hdr *hdr); + +#endif /* CONFIG_ANDROID_BOOT_IMAGE */ + #endif /* __IMAGE_H__ */

From: Sebastian Siewior bigeasy@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. v5: - Add CONFIG option documentation to README - Rebase using new downloader registration
Signed-off-by: Sebastian Andrzej Siewior bigeasy@linutronix.de Signed-off-by: Rob Herring robh@kernel.org --- README | 22 ++ 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 | 513 +++++++++++++++++++++++++++++++++++ 7 files changed, 835 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
diff --git a/README b/README index b973344..8114f79 100644 --- a/README +++ b/README @@ -1558,6 +1558,28 @@ The following options need to be configured: entering dfuMANIFEST state. Host waits this timeout, before sending again an USB request to the device.
+- USB Device Android Fastboot support: + CONFIG_CMD_FASTBOOT + This enables the command "fastboot" which enables the Android + fastboot mode for the platform's USB device. Fastboot is a USB + protocol for downloading images, flashing and device control + used on Android devices. + See doc/README.android-fastboot for more information. + + CONFIG_ANDROID_BOOT_IMAGE + This enables support for booting images which use the Android + image format header. + + CONFIG_USB_FASTBOOT_BUF_ADDR + The fastboot protocol requires a large memory buffer for + downloads. Define this to the starting RAM address to use for + downloaded images. + + CONFIG_USB_FASTBOOT_BUF_SIZE + The fastboot protocol requires a large memory buffer for + downloads. This buffer should be as large as possible for a + platform. Define this to the size available RAM for fastboot. + - Journaling Flash filesystem support: CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE, CONFIG_JFFS2_NAND_DEV diff --git a/common/Makefile b/common/Makefile index bfcb466..219cb51 100644 --- a/common/Makefile +++ b/common/Makefile @@ -168,6 +168,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..83fa7bd --- /dev/null +++ b/common/cmd_fastboot.c @@ -0,0 +1,36 @@ +/* + * Copyright 2008 - 2009 Windriver, <www.windriver.com> + * Author: Tom Rix Tom.Rix@windriver.com + * + * (C) Copyright 2014 Linaro, Ltd. + * Rob Herring robh@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("usb_dnl_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 896c8d4..66becdc 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,6 +18,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..9dd85b6 --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,513 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix Tom.Rix@windriver.com + * + * Copyright 2011 Sebastian Andrzej Siewior bigeasy@linutronix.de + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring robh@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> + +#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 id; + struct usb_gadget *gadget = c->cdev->gadget; + struct f_fastboot *f_fb = func_to_fastboot(f); + + /* DYNAMIC interface numbers assignments */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + interface_desc.bInterfaceNumber = id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + 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) + return -ENODEV; + f_fb->in_ep->driver_data = c->cdev; + + f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!f_fb->out_ep) + return -ENODEV; + f_fb->out_ep->driver_data = c->cdev; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + + return 0; +} + +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"); + return ret; + } + + f_fb->out_req = fastboot_start_ep(f_fb->out_ep); + if (!f_fb->out_req) { + puts("failed to alloc out req\n"); + ret = -EINVAL; + goto err; + } + f_fb->out_req->complete = rx_handler_command; + + ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in); + if (ret) { + puts("failed to enable in ep\n"); + goto err; + } + + f_fb->in_req = fastboot_start_ep(f_fb->in_ep); + if (!f_fb->in_req) { + puts("failed alloc req in\n"); + ret = -EINVAL; + 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 ret; +} + +static 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; +} +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add); + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + struct usb_request *in_req = fastboot_func->in_req; + int ret; + + 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 0x20000 +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, + 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 (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + + 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(); + if (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + 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 }; + + 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 *cmdbuf = req->buf; + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + int i; + + 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); + } +}

From: Rob Herring robh@kernel.org
Enable Android Fastboot support on omap3_beagle board.
Signed-off-by: Rob Herring robh@kernel.org --- include/configs/omap3_beagle.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 0b57421..be39b7c 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -110,6 +110,16 @@ #define CONFIG_TWL4030_USB 1 #define CONFIG_USB_ETHER #define CONFIG_USB_ETHER_RNDIS +#define CONFIG_USB_GADGET +#define CONFIG_USB_GADGET_VBUS_DRAW 0 +#define CONFIG_USBDOWNLOAD_GADGET +#define CONFIG_G_DNL_VENDOR_NUM 0x0451 +#define CONFIG_G_DNL_PRODUCT_NUM 0xd022 +#define CONFIG_G_DNL_MANUFACTURER "TI" +#define CONFIG_CMD_FASTBOOT +#define CONFIG_ANDROID_BOOT_IMAGE +#define CONFIG_USB_FASTBOOT_BUF_ADDR CONFIG_SYS_LOAD_ADDR +#define CONFIG_USB_FASTBOOT_BUF_SIZE 0x07000000
/* USB EHCI */ #define CONFIG_CMD_USB

On Monday, May 05, 2014 at 10:08:08 PM, Rob Herring wrote:
From: Rob Herring robh@kernel.org
This is the 3nd version since I revived the fastboot patches Sebastian submitted.
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
The Android code used or referenced is BSD 2-clause license. This was originally raised by Wolfgang that it was not compatible with GPLv2+. I believe that has since been demonstrated and agreed that the BSD 2-clause license is compatible with u-boot.
As far as the history of the code, I have traced that back. The u-boot code started in 2008/2009 by Tom Rix @ Windriver. This initial support was then adopted and extended by TI (eMMC support primarily, not included here) in their OMAP u-boot tree[2]. In 2011, the TI code was used as a basis for upstream patches by Sebastian Siewior @ Linutronix. The code has been rearranged quite a bit since the original, but the content is pretty much the same. Some of the re-arranging left stale or missing copyrights in the v2 version which I have corrected.
This version is rebased on u-boot usb tree with the recent USB downloader gadget registration changes. The primary change is documentation added to README for new config options.
I've tested this series on a BeagleBoard.
Looks good.
Acked-by: Marek Vasut marex@denx.de
Best regards, Marek Vasut

Hi Rob,
From: Rob Herring robh@kernel.org
This is the 3nd version since I revived the fastboot patches Sebastian submitted.
I'm reviving the Android Fastboot support after 2+ years since the last posting[1]. The previous postings had some questions about licensing and source of some code. I believe I've traced the history sufficiently that the copyrights and source information are complete and correct.
The Android code used or referenced is BSD 2-clause license. This was originally raised by Wolfgang that it was not compatible with GPLv2+. I believe that has since been demonstrated and agreed that the BSD 2-clause license is compatible with u-boot.
As far as the history of the code, I have traced that back. The u-boot code started in 2008/2009 by Tom Rix @ Windriver. This initial support was then adopted and extended by TI (eMMC support primarily, not included here) in their OMAP u-boot tree[2]. In 2011, the TI code was used as a basis for upstream patches by Sebastian Siewior @ Linutronix. The code has been rearranged quite a bit since the original, but the content is pretty much the same. Some of the re-arranging left stale or missing copyrights in the v2 version which I have corrected.
This version is rebased on u-boot usb tree with the recent USB downloader gadget registration changes. The primary change is documentation added to README for new config options.
I've tested this series on a BeagleBoard.
Applied to u-boot-dfu tree. Rob, thanks for development.
participants (8)
-
Bo Shen
-
Lukasz Majewski
-
Marek Vasut
-
Rob Herring
-
Rob Herring
-
Sebastian Andrzej Siewior
-
Tom Rini
-
Wolfgang Denk