[U-Boot] [PATCH v4 14/14] net: fastboot: Merge AOSP UDP fastboot

Joe Hershberger joe.hershberger at ni.com
Wed May 16 19:15:11 UTC 2018


On Tue, May 15, 2018 at 9:48 AM, Alex Kiernan <alex.kiernan at gmail.com> wrote:
> Merge UDP fastboot support from AOSP:
>
>   https://android.googlesource.com/platform/external/u-boot/+/android-o-mr1-iot-preview-8
>
> Signed-off-by: Alex Kiernan <alex.kiernan at gmail.com>
> Signed-off-by: Alex Deymo <deymo at google.com>
> Signed-off-by: Jocelyn Bohr <bohr at google.com>
> Reviewed-by: Simon Glass <sjg at chromium.org>
> ---
>
> Changes in v4:
> - guard fb_getvar/fb_command with UDP_FUNCTION_FASTBOOT in Makefile
> - add docbook comments
> - remove parameter from fastboot_boot() since we always want
>   fastboot_buf_addr (and if we're using fastboot_bootcmd then it's
>   ignored)
>
> Changes in v3:
> - use FASTBOOT as our guard in Kconfig not a list of USB || UDP
> - correct mis-translation from AOSP introduced when cleaning up for
>   checkpatch - we should write when buffer is not NULL, rather than
>   erasing, and erase when buffer is NULL
> - use CMD_RET_USAGE from do_fastboot
> - remove do_fastboot_udp from cmd/net.c and rewrite using net_loop()
> - rename timed_send_info to fastboot_send_info, rename fastboot_send_info to
>   fastboot_udp_send_info
> - replace FASTBOOT_HEADER_SIZE with sizeof(struct fastboot_header)
> - move start time into timed_send_info() rather than passing it in
> - make calls to fastboot_udp_send_info a runtime dependency, not a compile
>   time one
> - set ${filesize} to size of downloaded image
> - add progress meter from USB path during download
> - add support for 'oem format' command from the USB path
> - rename 'fastbootcmd' to 'fastboot_bootcmd' to make clear that this is the
>   fastboot boot command
> - make getvar implementation table driven
> - add fastboot_buf_addr, fastboot_buf_size to override buffer address and
>   size
> - return correct filesystem type in getvar partition-type on MMC
> - process "fastboot." prefixed env variables in getvar first so you
>   can override the normal values (this also lets you set a fs type for
>   NAND devices)
> - make global variables static and add accessors for the things which
>   the transport layers need
> - squash subsequent patches which change this code into this one:
>   - If the fastboot flash/erase commands are disabled, remove that support
>     so we still build correctly.
>   - Add NAND support to fastboot UDP flash/erase commands
>   - If we don't have a partition name passed, report it as not found.
>   - Change the behaviour of the fastboot net code such that
>     "reboot-bootloader" is no longer written to CONFIG_FASTBOOT_BUF_ADDR for
>     use as a marker on reboot (the AOSP code in common/android-bootloader.c
>     uses this marker - this code could be reinstated there if that gets
>     merged).
>   - Merge USB and UDP boot code. The USB implementation stays the same, but
>     UDP no longer passes an fdt. We introduce a new environment variable
>     'fastboot_bootcmd' which if set overrides the hardcoded boot command,
>     setting this then allows the UDP implementation to remain the same. If
>     after running 'fastboot_bootcmd' the board has not booted, control is
>     returned to U-Boot and the fastboot process ends.
>   - Separate the fastboot protocol handling from the fastboot UDP code in
>     preparation for reusing it in the USB code.
>
> Changes in v2:
> - ensure fastboot syntax is backward compatible - 'fastboot 0' means
>   'fastboot usb 0'
>
>  cmd/fastboot.c                |  91 +++++++++++-
>  drivers/fastboot/Kconfig      |  15 ++
>  drivers/fastboot/Makefile     |   3 +-
>  drivers/fastboot/fb_command.c | 327 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/fastboot/fb_common.c  | 113 +++++++++++++++
>  drivers/fastboot/fb_getvar.c  | 229 +++++++++++++++++++++++++++++
>  drivers/fastboot/fb_mmc.c     |  76 +++++++++-
>  drivers/fastboot/fb_nand.c    |  12 +-
>  include/fastboot.h            | 157 ++++++++++++++++++++
>  include/fb_mmc.h              |   8 +-
>  include/fb_nand.h             |  10 +-
>  include/net.h                 |   2 +-
>  include/net/fastboot.h        |  21 +++
>  net/Makefile                  |   1 +
>  net/fastboot.c                | 312 ++++++++++++++++++++++++++++++++++++++++
>  net/net.c                     |   7 +
>  16 files changed, 1367 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/fastboot/fb_command.c
>  create mode 100644 drivers/fastboot/fb_getvar.c
>  create mode 100644 include/net/fastboot.h
>  create mode 100644 net/fastboot.c
>
> diff --git a/cmd/fastboot.c b/cmd/fastboot.c
> index a5ec5f4..557257a 100644
> --- a/cmd/fastboot.c
> +++ b/cmd/fastboot.c
> @@ -10,10 +10,32 @@
>  #include <command.h>
>  #include <console.h>
>  #include <g_dnl.h>
> +#include <fastboot.h>
> +#include <net.h>
>  #include <usb.h>
>
> -static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
> +static int do_fastboot_udp(int argc, char *const argv[],
> +                          uintptr_t buf_addr, size_t buf_size)
>  {
> +#if CONFIG_IS_ENABLED(UDP_FUNCTION_FASTBOOT)
> +       int err = net_loop(FASTBOOT);
> +
> +       if (err < 0) {
> +               printf("fastboot udp error: %d\n", err);
> +               return CMD_RET_FAILURE;
> +       }
> +
> +       return CMD_RET_SUCCESS;
> +#else
> +       pr_err("Fastboot UDP not enabled\n");
> +       return CMD_RET_FAILURE;
> +#endif
> +}
> +
> +static int do_fastboot_usb(int argc, char *const argv[],
> +                          uintptr_t buf_addr, size_t buf_size)
> +{
> +#if CONFIG_IS_ENABLED(USB_FUNCTION_FASTBOOT)
>         int controller_index;
>         char *usb_controller;
>         int ret;
> @@ -58,11 +80,70 @@ exit:
>         board_usb_cleanup(controller_index, USB_INIT_DEVICE);
>
>         return ret;
> +#else
> +       pr_err("Fastboot USB not enabled\n");
> +       return CMD_RET_FAILURE;
> +#endif
> +}
> +
> +static int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
> +{
> +       uintptr_t buf_addr = (uintptr_t)NULL;
> +       size_t buf_size = 0;
> +
> +       if (argc < 2)
> +               return CMD_RET_USAGE;
> +
> +       while (argc > 1 && **(argv + 1) == '-') {
> +               char *arg = *++argv;
> +
> +               --argc;
> +               while (*++arg) {
> +                       switch (*arg) {
> +                       case 'l':
> +                               if (--argc <= 0)
> +                                       return CMD_RET_USAGE;
> +                               buf_addr = simple_strtoul(*++argv, NULL, 16);
> +                               goto NXTARG;
> +
> +                       case 's':
> +                               if (--argc <= 0)
> +                                       return CMD_RET_USAGE;
> +                               buf_size = simple_strtoul(*++argv, NULL, 16);
> +                               goto NXTARG;
> +
> +                       default:
> +                               return CMD_RET_USAGE;
> +                       }
> +               }
> +NXTARG:
> +               ;
> +       }
> +
> +       fastboot_init((void *)buf_addr, buf_size);
> +
> +       if (!strcmp(argv[1], "udp"))
> +               return do_fastboot_udp(argc, argv, buf_addr, buf_size);
> +
> +       if (!strcmp(argv[1], "usb")) {
> +               argv++;
> +               argc--;
> +       }
> +
> +       return do_fastboot_usb(argc, argv, buf_addr, buf_size);
>  }
>
> +#ifdef CONFIG_SYS_LONGHELP
> +static char fastboot_help_text[] =
> +       "[-l addr] [-s size] usb <controller> | udp\n"
> +       "\taddr - address of buffer used during data transfers ("
> +       __stringify(CONFIG_FASTBOOT_BUF_ADDR) ")\n"
> +       "\tsize - size of buffer used during data transfers ("
> +       __stringify(CONFIG_FASTBOOT_BUF_SIZE) ")"
> +       ;
> +#endif
> +
>  U_BOOT_CMD(
> -       fastboot, 2, 1, do_fastboot,
> -       "use USB Fastboot protocol",
> -       "<USB_controller>\n"
> -       "    - run as a fastboot usb device"
> +       fastboot, CONFIG_SYS_MAXARGS, 1, do_fastboot,
> +       "run as a fastboot usb or udp device", fastboot_help_text
>  );
> diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
> index 82e1420..5844f3f 100644
> --- a/drivers/fastboot/Kconfig
> +++ b/drivers/fastboot/Kconfig
> @@ -14,6 +14,13 @@ config USB_FUNCTION_FASTBOOT
>         help
>           This enables the USB part of the fastboot gadget.
>
> +config UDP_FUNCTION_FASTBOOT
> +       depends on NET
> +       select FASTBOOT
> +       bool "Enable fastboot protocol over UDP"
> +       help
> +         This enables the fastboot protocol over UDP.
> +
>  if FASTBOOT
>
>  config FASTBOOT_BUF_ADDR
> @@ -119,6 +126,14 @@ config FASTBOOT_MBR_NAME
>           specified on the "fastboot flash" command line matches the value
>           defined here. The default target name for updating MBR is "mbr".
>
> +config FASTBOOT_CMD_OEM_FORMAT
> +       bool "Enable the 'oem format' command"
> +       depends on FASTBOOT_FLASH_MMC && CMD_GPT
> +       help
> +         Add support for the "oem format" command from a client. This
> +         relies on the env variable partitions to contain the list of
> +         partitions as required by the gpt command.

I think this (feature) deserves its own patch.

> +
>  endif # FASTBOOT
>
>  endmenu
> diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile
> index e4bd389..8831096 100644
> --- a/drivers/fastboot/Makefile
> +++ b/drivers/fastboot/Makefile
> @@ -1,6 +1,7 @@
>  # SPDX-License-Identifier:      GPL-2.0+
>
>  obj-y += fb_common.o
> -
> +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_getvar.o
> +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_command.o
>  obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o
>  obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
> diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
> new file mode 100644
> index 0000000..0ca05ec
> --- /dev/null
> +++ b/drivers/fastboot/fb_command.c
> @@ -0,0 +1,327 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * Copyright (C) 2016 The Android Open Source Project
> + */
> +
> +#include <common.h>
> +#include <fastboot.h>
> +#include <fb_mmc.h>
> +#include <fb_nand.h>
> +#include <part.h>
> +#include <stdlib.h>
> +
> +/**
> + * image_size - final fastboot image size
> + */
> +static u32 image_size;
> +
> +/**
> + * fastboot_bytes_received - number of bytes received in the current download
> + */
> +static u32 fastboot_bytes_received;
> +
> +/**
> + * fastboot_bytes_expected - number of bytes expected in the current download
> + */
> +static u32 fastboot_bytes_expected;
> +
> +static void fb_okay(char *, char *);
> +static void fb_getvar(char *, char *);
> +static void fb_download(char *, char *);
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +static void fb_flash(char *, char *);
> +static void fb_erase(char *, char *);
> +#endif
> +static void fb_reboot_bootloader(char *, char *);
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
> +static void fb_oem_format(char *, char *);
> +#endif
> +
> +static const struct {
> +       const char *command;
> +       void (*dispatch)(char *cmd_parameter, char *response);
> +} fb_commands[FASTBOOT_COMMAND_COUNT] = {
> +       [FASTBOOT_COMMAND_GETVAR] = {
> +               .command = "getvar",
> +               .dispatch = fb_getvar
> +       },
> +       [FASTBOOT_COMMAND_DOWNLOAD] = {
> +               .command = "download",
> +               .dispatch = fb_download
> +       },
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +       [FASTBOOT_COMMAND_FLASH] =  {
> +               .command = "flash",
> +               .dispatch = fb_flash
> +       },
> +       [FASTBOOT_COMMAND_ERASE] =  {
> +               .command = "erase",
> +               .dispatch = fb_erase
> +       },
> +#endif
> +       [FASTBOOT_COMMAND_BOOT] =  {
> +               .command = "boot",
> +               .dispatch = fb_okay
> +       },
> +       [FASTBOOT_COMMAND_CONTINUE] =  {
> +               .command = "continue",
> +               .dispatch = fb_okay
> +       },
> +       [FASTBOOT_COMMAND_REBOOT] =  {
> +               .command = "reboot",
> +               .dispatch = fb_okay
> +       },
> +       [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] =  {
> +               .command = "reboot-bootloader",
> +               .dispatch = fb_reboot_bootloader
> +       },
> +       [FASTBOOT_COMMAND_SET_ACTIVE] =  {
> +               .command = "set_active",
> +               .dispatch = fb_okay
> +       },
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
> +       [FASTBOOT_COMMAND_OEM_FORMAT] = {
> +               .command = "oem format",
> +               .dispatch = fb_oem_format,
> +       },
> +#endif
> +};
> +
> +/**
> + * fastboot_get_bytes_remaining() - return bytes remaining in current transfer
> + *
> + * Return: Number of bytes left in the current download
> + */
> +u32 fastboot_get_bytes_remaining(void)
> +{
> +       return fastboot_bytes_expected - fastboot_bytes_received;
> +}
> +
> +/**
> + * fastboot_handle_command - Handle fastboot command
> + *
> + * @cmd_string: Pointer to command string
> + * @response: Pointer to fastboot response buffer
> + *
> + * Return: Executed command, or -1 if not recognized
> + */
> +int fastboot_handle_command(char *cmd_string, char *response)
> +{
> +       int i;
> +       char *cmd_parameter;
> +
> +       cmd_parameter = cmd_string;
> +       strsep(&cmd_parameter, ":");
> +
> +       for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) {
> +               if (!strcmp(fb_commands[i].command, cmd_string)) {
> +                       if (fb_commands[i].dispatch) {
> +                               fb_commands[i].dispatch(cmd_parameter,
> +                                                       response);
> +                               return i;
> +                       } else {
> +                               break;
> +                       }
> +               }
> +       }
> +
> +       pr_err("command %s not recognized.\n", cmd_string);
> +       fastboot_fail("unrecognized command", response);
> +       return -1;
> +}
> +
> +/**
> + * fb_okay() - Send bare OKAY response

It would be good to settle on "fastboot" or "fb" and not use both. It
seems many of the "fb" are static, but some are not. Static really
don't need any scoping like "fb_".

> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + *
> + * Send a bare OKAY fastboot response. This is used where the command is
> + * valid, but all the work is done after the response has been sent (e.g.
> + * boot, reboot etc.)
> + */
> +static void fb_okay(char *cmd_parameter, char *response)
> +{
> +       fastboot_okay(NULL, response);
> +}
> +
> +/**
> + * fb_getvar() - Read a config/version variable
> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + */
> +static void fb_getvar(char *cmd_parameter, char *response)
> +{
> +       fastboot_getvar(cmd_parameter, response);
> +}
> +
> +/**
> + * fastboot_download() - Start a download transfer from the client
> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + */
> +static void fb_download(char *cmd_parameter, char *response)
> +{
> +       char *tmp;
> +
> +       if (!cmd_parameter) {
> +               fastboot_fail("Expected command parameter", response);
> +               return;
> +       }
> +       fastboot_bytes_received = 0;
> +       fastboot_bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16);
> +       if (fastboot_bytes_expected == 0) {
> +               fastboot_fail("Expected nonzero image size", response);
> +               return;
> +       }
> +       /*
> +        * Nothing to download yet. Response is of the form:
> +        * [DATA|FAIL]$cmd_parameter
> +        *
> +        * where cmd_parameter is an 8 digit hexadecimal number
> +        */
> +       if (fastboot_bytes_expected > fastboot_get_buf_size()) {
> +               fastboot_fail(cmd_parameter, response);
> +       } else {
> +               printf("Starting download of %d bytes\n",
> +                      fastboot_bytes_expected);
> +               fastboot_response("DATA", response, "%s", cmd_parameter);
> +       }
> +}
> +
> +/**
> + * fastboot_download_data() - Copy image data to fastboot_buf_addr.
> + *
> + * @fastboot_data: Pointer to received fastboot data
> + * @fastboot_data_len: Length of received fastboot data
> + * @response: Pointer to fastboot response buffer
> + *
> + * Copies image data from fastboot_data to fastboot_buf_addr. Writes to
> + * response. fastboot_bytes_received is updated to indicate the number
> + * of bytes that have been transferred.
> + *
> + * On completion sets image_size and ${filesize} to the total size of the
> + * downloaded image.
> + */
> +void fastboot_download_data(const void *fastboot_data,
> +                           unsigned int fastboot_data_len,
> +                           char *response)
> +{
> +       if (fastboot_data_len == 0 &&
> +           fastboot_bytes_received >= fastboot_bytes_expected) {
> +               /* Download complete. Respond with "OKAY" */
> +               fastboot_okay(NULL, response);
> +               printf("\ndownloading of %d bytes finished\n",
> +                      fastboot_bytes_received);
> +               image_size = fastboot_bytes_received;
> +               env_set_hex("filesize", image_size);
> +               fastboot_bytes_expected = 0;
> +               fastboot_bytes_received = 0;
> +       } else {
> +#define BYTES_PER_DOT  0x20000
> +               u32 pre_dot_num, now_dot_num;
> +
> +               if (fastboot_data_len == 0 ||
> +                   (fastboot_bytes_received + fastboot_data_len) >
> +                   fastboot_bytes_expected) {
> +                       fastboot_fail("Received invalid data length",
> +                                     response);
> +                       return;
> +               }
> +               /* Download data to fastboot_buf_addr */
> +               memcpy(fastboot_get_buf_addr() + fastboot_bytes_received,
> +                      fastboot_data, fastboot_data_len);
> +
> +               pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
> +               fastboot_bytes_received += fastboot_data_len;
> +               now_dot_num = fastboot_bytes_received / BYTES_PER_DOT;
> +
> +               if (pre_dot_num != now_dot_num) {
> +                       putc('.');
> +                       if (!(now_dot_num % 74))
> +                               putc('\n');
> +               }
> +       }
> +}
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +/**
> + * fb_flash() - write the downloaded image to the indicated partition.
> + *
> + * @cmd_parameter: Pointer to partition name
> + * @response: Pointer to fastboot response buffer
> + *
> + * Writes the previously downloaded image to the partition indicated by
> + * cmd_parameter. Writes to response.
> + */
> +static void fb_flash(char *cmd_parameter, char *response)
> +{
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
> +       fb_mmc_flash_write(cmd_parameter, fastboot_get_buf_addr(), image_size,
> +                          response);
> +#endif
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
> +       fb_nand_flash_write(cmd_parameter, fastboot_get_buf_addr(), image_size,
> +                           response);
> +#endif
> +}
> +
> +/**
> + * fb_erase() - erase the indicated partition.
> + *
> + * @cmd_parameter: Pointer to partition name
> + * @response: Pointer to fastboot response buffer
> + *
> + * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
> + * to response.
> + */
> +static void fb_erase(char *cmd_parameter, char *response)
> +{
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
> +       fb_mmc_erase(cmd_parameter, response);
> +#endif
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
> +       fb_nand_erase(cmd_parameter, response);
> +#endif
> +}
> +#endif
> +
> +/**
> + * fb_reboot_bootloader() - Sets reboot bootloader flag.
> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + */
> +static void fb_reboot_bootloader(char *cmd_parameter, char *response)
> +{
> +       if (fastboot_set_reboot_flag())
> +               fastboot_fail("Cannot set reboot flag", response);
> +       else
> +               fastboot_okay(NULL, response);
> +}
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
> +/**
> + * fb_oem_format() - Execute the OEM format command
> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + */
> +static void fb_oem_format(char *cmd_parameter, char *response)
> +{
> +       char cmdbuf[32];
> +
> +       if (!env_get("partitions")) {
> +               fastboot_fail("partitions not set", response);
> +       } else {
> +               sprintf(cmdbuf, "gpt write mmc %x $partitions",
> +                       CONFIG_FASTBOOT_FLASH_MMC_DEV);
> +               if (run_command(cmdbuf, 0))
> +                       fastboot_fail("", response);
> +               else
> +                       fastboot_okay(NULL, response);
> +       }
> +}
> +#endif
> diff --git a/drivers/fastboot/fb_common.c b/drivers/fastboot/fb_common.c
> index 853ad9c..3a2aaa2 100644
> --- a/drivers/fastboot/fb_common.c
> +++ b/drivers/fastboot/fb_common.c
> @@ -12,6 +12,22 @@
>
>  #include <common.h>
>  #include <fastboot.h>
> +#include <net/fastboot.h>
> +
> +/**
> + * fastboot_buf_addr - base address of the fastboot download buffer
> + */
> +static void *fastboot_buf_addr;
> +
> +/**
> + * fastboot_buf_size - size of the fastboot download buffer
> + */
> +static u32 fastboot_buf_size;
> +
> +/**
> + * fastboot_progress_callback - callback executed during long operations
> + */
> +static void (*fastboot_progress_callback)(const char *msg);
>
>  /**
>   * fastboot_response() - Writes a response of the form "$tag$reason".
> @@ -75,3 +91,100 @@ int __weak fastboot_set_reboot_flag(void)
>  {
>         return -ENOSYS;
>  }
> +
> +/**
> + * fastboot_get_buf_addr() - Return address of the fastboot transfer buffer
> + *
> + * Return: Address of the fastboot transfer buffer
> + */
> +void *fastboot_get_buf_addr(void)
> +{
> +       return fastboot_buf_addr;
> +}
> +
> +/**
> + * fastboot_get_buf_size() - Return size of the fastboot transfer buffer
> + *
> + * Return: Size of the fastboot transfer buffer
> + */
> +u32 fastboot_get_buf_size(void)
> +{
> +       return fastboot_buf_size;
> +}
> +
> +/**
> + * fastboot_get_progress_callback() - Return progress callback
> + *
> + * Return: Pointer to function called during long operations
> + */
> +void (*fastboot_get_progress_callback(void))(const char *)
> +{
> +       return fastboot_progress_callback;
> +}
> +
> +/**
> + * fastboot_boot() - Execute fastboot boot command
> + *
> + * If ${fastboot_bootcmd} is set, run that command to execute the boot
> + * process, if that returns, then exit the fastboot server and return
> + * control to the caller.
> + *
> + * Otherwise execute "bootm <fastboot_buf_addr>", if that fails, reset
> + * the board.
> + */
> +void fastboot_boot(void)
> +{
> +       char *s;
> +
> +       s = env_get("fastboot_bootcmd");
> +       if (s) {
> +               run_command(s, CMD_FLAG_ENV);
> +       } else {
> +               static char boot_addr_start[12];
> +               static char *const bootm_args[] = {
> +                       "bootm", boot_addr_start, NULL
> +               };
> +
> +               snprintf(boot_addr_start, sizeof(boot_addr_start) - 1,
> +                        "0x%p", fastboot_buf_addr);
> +               printf("Booting kernel at %s...\n\n\n", boot_addr_start);
> +
> +               do_bootm(NULL, 0, 2, bootm_args);
> +
> +               /*
> +                * This only happens if image is somehow faulty so we start
> +                * over. We deliberately leave this policy to the invocation
> +                * of fastbootcmd if that's what's being run
> +                */
> +               do_reset(NULL, 0, 0, NULL);
> +       }
> +}
> +
> +/**
> + * fastboot_set_progress_callback() - set progress callback
> + *
> + * @progress: Pointer to progress callback
> + *
> + * Set a callback which is invoked periodically during long running operations
> + * (flash and erase). This can be used (for example) by the UDP transport to
> + * send INFO responses to keep the client alive whilst those commands are
> + * executing.
> + */
> +void fastboot_set_progress_callback(void (*progress)(const char *msg))
> +{
> +       fastboot_progress_callback = progress;
> +}
> +
> +/*
> + * fastboot_init() - initialise new fastboot protocol session
> + *
> + * @buf_addr: Pointer to download buffer, or NULL for default
> + * @buf_size: Size of download buffer, or zero for default
> + */
> +void fastboot_init(void *buf_addr, u32 buf_size)
> +{
> +       fastboot_buf_addr = buf_addr ? buf_addr :
> +                                      (void *)CONFIG_FASTBOOT_BUF_ADDR;
> +       fastboot_buf_size = buf_size ? buf_size : CONFIG_FASTBOOT_BUF_SIZE;
> +       fastboot_set_progress_callback(NULL);
> +}
> diff --git a/drivers/fastboot/fb_getvar.c b/drivers/fastboot/fb_getvar.c
> new file mode 100644
> index 0000000..736efbf
> --- /dev/null
> +++ b/drivers/fastboot/fb_getvar.c
> @@ -0,0 +1,229 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * Copyright (C) 2016 The Android Open Source Project
> + */
> +
> +#include <common.h>
> +#include <fastboot.h>
> +#include <fb_mmc.h>
> +#include <fb_nand.h>
> +#include <fs.h>
> +#include <version.h>
> +
> +static void fb_getvar_version(char *var_parameter, char *response);
> +static void fb_getvar_bootloader_version(char *var_parameter, char *response);
> +static void fb_getvar_downloadsize(char *var_parameter, char *response);
> +static void fb_getvar_serialno(char *var_parameter, char *response);
> +static void fb_getvar_version_baseband(char *var_parameter, char *response);
> +static void fb_getvar_product(char *var_parameter, char *response);
> +static void fb_getvar_current_slot(char *var_parameter, char *response);
> +static void fb_getvar_slot_suffixes(char *var_parameter, char *response);
> +static void fb_getvar_has_slot(char *var_parameter, char *response);
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
> +static void fb_getvar_partition_type(char *part_name, char *response);
> +#endif
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +static void fb_getvar_partition_size(char *part_name, char *response);
> +#endif
> +
> +static const struct {
> +       const char *variable;
> +       void (*dispatch)(char *var_parameter, char *response);
> +} fb_getvar_dispatch[] = {
> +       {
> +               .variable = "version",
> +               .dispatch = fb_getvar_version
> +       }, {
> +               .variable = "bootloader-version",
> +               .dispatch = fb_getvar_bootloader_version
> +       }, {
> +               .variable = "version-bootloader",
> +               .dispatch = fb_getvar_bootloader_version
> +       }, {
> +               .variable = "downloadsize",
> +               .dispatch = fb_getvar_downloadsize
> +       }, {
> +               .variable = "max-download-size",
> +               .dispatch = fb_getvar_downloadsize
> +       }, {
> +               .variable = "serialno",
> +               .dispatch = fb_getvar_serialno
> +       }, {
> +               .variable = "version-baseband",
> +               .dispatch = fb_getvar_version_baseband
> +       }, {
> +               .variable = "product",
> +               .dispatch = fb_getvar_product
> +       }, {
> +               .variable = "current-slot",
> +               .dispatch = fb_getvar_current_slot
> +       }, {
> +               .variable = "slot-suffixes",
> +               .dispatch = fb_getvar_slot_suffixes
> +       }, {
> +               .variable = "has_slot",
> +               .dispatch = fb_getvar_has_slot
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
> +       }, {
> +               .variable = "partition-type",
> +               .dispatch = fb_getvar_partition_type
> +#endif
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +       }, {
> +               .variable = "partition-size",
> +               .dispatch = fb_getvar_partition_size
> +#endif
> +       }
> +};
> +
> +static void fb_getvar_version(char *var_parameter, char *response)
> +{
> +       fastboot_okay(FASTBOOT_VERSION, response);
> +}
> +
> +static void fb_getvar_bootloader_version(char *var_parameter, char *response)
> +{
> +       fastboot_okay(U_BOOT_VERSION, response);
> +}
> +
> +static void fb_getvar_downloadsize(char *var_parameter, char *response)
> +{
> +       fastboot_response("OKAY", response, "0x%08x", fastboot_get_buf_size());
> +}
> +
> +static void fb_getvar_serialno(char *var_parameter, char *response)
> +{
> +       const char *tmp = env_get("serial#");
> +
> +       if (tmp)
> +               fastboot_okay(tmp, response);
> +       else
> +               fastboot_fail("Value not set", response);
> +}
> +
> +static void fb_getvar_version_baseband(char *var_parameter, char *response)
> +{
> +       fastboot_okay("N/A", response);
> +}
> +
> +static void fb_getvar_product(char *var_parameter, char *response)
> +{
> +       const char *board = env_get("board");
> +
> +       if (board)
> +               fastboot_okay(board, response);
> +       else
> +               fastboot_fail("Board not set", response);
> +}
> +
> +static void fb_getvar_current_slot(char *var_parameter, char *response)
> +{
> +       /* A/B not implemented, for now always return _a */
> +       fastboot_okay("_a", response);
> +}
> +
> +static void fb_getvar_slot_suffixes(char *var_parameter, char *response)
> +{
> +       fastboot_okay("_a,_b", response);
> +}
> +
> +static void fb_getvar_has_slot(char *part_name, char *response)
> +{
> +       if (part_name && (!strcmp(part_name, "boot") ||
> +                         !strcmp(part_name, "system")))
> +               fastboot_okay("yes", response);
> +       else
> +               fastboot_okay("no", response);
> +}
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
> +static void fb_getvar_partition_type(char *part_name, char *response)
> +{
> +       int r;
> +       struct blk_desc *dev_desc;
> +       disk_partition_t part_info;
> +
> +       r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info,
> +                                      response);
> +       if (r >= 0) {
> +               r = fs_set_blk_dev_with_part(dev_desc, r);
> +               if (r < 0)
> +                       fastboot_fail("failed to set partition", response);
> +               else
> +                       fastboot_okay(fs_get_type_name(), response);
> +       }
> +}
> +#endif
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +static void fb_getvar_partition_size(char *part_name, char *response)
> +{
> +       int r;
> +       size_t size;
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
> +       struct blk_desc *dev_desc;
> +       disk_partition_t part_info;
> +
> +       r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info,
> +                                      response);
> +       if (r >= 0)
> +               size = part_info.size;
> +#endif
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
> +       struct part_info *part_info;
> +
> +       r = fastboot_nand_get_part_info(part_name, &part_info, response);
> +       if (r >= 0)
> +               size = part_info->size;
> +#endif
> +       if (r >= 0)
> +               fastboot_response("OKAY", response, "0x%016zx", size);
> +}
> +#endif
> +
> +/**
> + * fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + *
> + * Look up cmd_parameter first as an environment variable of the form
> + * fastboot.<cmd_parameter>, if that exists return use its value to set
> + * response.
> + *
> + * Otherwise lookup the name of variable and execute the appropriate
> + * function to return the requested value.
> + */
> +void fastboot_getvar(char *cmd_parameter, char *response)
> +{
> +       if (!cmd_parameter) {
> +               fastboot_fail("missing var", response);
> +       } else {
> +#define FASTBOOT_ENV_PREFIX    "fastboot."
> +               int i;
> +               char *var_parameter = cmd_parameter;
> +               char envstr[FASTBOOT_RESPONSE_LEN];
> +               const char *s;
> +
> +               snprintf(envstr, sizeof(envstr) - 1,
> +                        FASTBOOT_ENV_PREFIX "%s", cmd_parameter);
> +               s = env_get(envstr);
> +               if (s) {
> +                       fastboot_response("OKAY", response, "%s", s);
> +                       return;
> +               }
> +
> +               strsep(&var_parameter, ":");
> +               for (i = 0; i < ARRAY_SIZE(fb_getvar_dispatch); ++i) {
> +                       if (!strcmp(fb_getvar_dispatch[i].variable,
> +                                   cmd_parameter)) {
> +                               fb_getvar_dispatch[i].dispatch(var_parameter,
> +                                                              response);
> +                               return;
> +                       }
> +               }
> +               pr_warn("WARNING: unknown variable: %s\n", cmd_parameter);
> +               fastboot_fail("Variable not implemented", response);
> +       }
> +}
> diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c
> index 038905f..0b8794a 100644
> --- a/drivers/fastboot/fb_mmc.c
> +++ b/drivers/fastboot/fb_mmc.c
> @@ -15,6 +15,8 @@
>  #include <linux/compat.h>
>  #include <android_image.h>
>
> +#define FASTBOOT_MAX_BLK_WRITE 16384
> +
>  #define BOOT_PARTITION_NAME "boot"
>
>  struct fb_mmc_sparse {
> @@ -43,13 +45,49 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc,
>         return ret;
>  }
>
> +/**
> + * fb_mmc_blk_write() - Write/erase MMC in chunks of FASTBOOT_MAX_BLK_WRITE
> + *
> + * @block_dev: Pointer to block device
> + * @start: First block to write/erase
> + * @blkcnt: Count of blocks
> + * @buffer: Pointer to data buffer for write or NULL for erase
> + */
> +static lbaint_t fb_mmc_blk_write(struct blk_desc *block_dev, lbaint_t start,
> +                                lbaint_t blkcnt, const void *buffer)
> +{
> +       lbaint_t blk = start;
> +       lbaint_t blks_written;
> +       lbaint_t cur_blkcnt;
> +       lbaint_t blks = 0;
> +       int i;
> +       void (*progress)(const char *) = fastboot_get_progress_callback();
> +
> +       for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) {
> +               cur_blkcnt = min((int)blkcnt - i, FASTBOOT_MAX_BLK_WRITE);
> +               if (buffer) {
> +                       if (progress)
> +                               progress("writing");
> +                       blks_written = blk_dwrite(block_dev, blk, cur_blkcnt,
> +                                                 buffer + (i * block_dev->blksz));
> +               } else {
> +                       if (progress)
> +                               progress("erasing");
> +                       blks_written = blk_derase(block_dev, blk, cur_blkcnt);
> +               }
> +               blk += blks_written;
> +               blks += blks_written;
> +       }
> +       return blks;
> +}
> +
>  static lbaint_t fb_mmc_sparse_write(struct sparse_storage *info,
>                 lbaint_t blk, lbaint_t blkcnt, const void *buffer)
>  {
>         struct fb_mmc_sparse *sparse = info->priv;
>         struct blk_desc *dev_desc = sparse->dev_desc;
>
> -       return blk_dwrite(dev_desc, blk, blkcnt, buffer);
> +       return fb_mmc_blk_write(dev_desc, blk, blkcnt, buffer);
>  }
>
>  static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info,
> @@ -60,7 +98,7 @@ static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info,
>
>  static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info,
>                 const char *part_name, void *buffer,
> -               unsigned int download_bytes, char *response)
> +               u32 download_bytes, char *response)
>  {
>         lbaint_t blkcnt;
>         lbaint_t blks;
> @@ -77,7 +115,8 @@ static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info,
>
>         puts("Flashing Raw Image\n");
>
> -       blks = blk_dwrite(dev_desc, info->start, blkcnt, buffer);
> +       blks = fb_mmc_blk_write(dev_desc, info->start, blkcnt, buffer);
> +
>         if (blks != blkcnt) {
>                 pr_err("failed writing to device %d\n", dev_desc->devnum);
>                 fastboot_fail("failed writing to device", response);
> @@ -148,7 +187,7 @@ static lbaint_t fb_mmc_get_boot_header(struct blk_desc *dev_desc,
>   */
>  static int fb_mmc_update_zimage(struct blk_desc *dev_desc,
>                                 void *download_buffer,
> -                               unsigned int download_bytes,
> +                               u32 download_bytes,
>                                 char *response)
>  {
>         uintptr_t hdr_addr;                     /* boot image header address */
> @@ -251,8 +290,32 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc,
>  }
>  #endif
>
> +int fastboot_mmc_get_part_info(char *part_name, struct blk_desc **dev_desc,
> +                              disk_partition_t *part_info, char *response)
> +{
> +       int r;
> +
> +       *dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
> +       if (!*dev_desc) {
> +               fastboot_fail("block device not found", response);
> +               return -ENOENT;
> +       }
> +       if (!part_name) {
> +               fastboot_fail("partition not found", response);
> +               return -ENOENT;
> +       }
> +
> +       r = part_get_info_by_name_or_alias(*dev_desc, part_name, part_info);
> +       if (r < 0) {
> +               fastboot_fail("partition not found", response);
> +               return r;
> +       }
> +
> +       return r;
> +}
> +
>  void fb_mmc_flash_write(const char *cmd, void *download_buffer,
> -                       unsigned int download_bytes, char *response)
> +                       u32 download_bytes, char *response)
>  {
>         struct blk_desc *dev_desc;
>         disk_partition_t info;
> @@ -389,7 +452,8 @@ void fb_mmc_erase(const char *cmd, char *response)
>         printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n",
>                blks_start, blks_start + blks_size);
>
> -       blks = blk_derase(dev_desc, blks_start, blks_size);
> +       blks = fb_mmc_blk_write(dev_desc, blks_start, blks_size, NULL);
> +
>         if (blks != blks_size) {
>                 pr_err("failed erasing from device %d\n", dev_desc->devnum);
>                 fastboot_fail("failed erasing from device", response);
> diff --git a/drivers/fastboot/fb_nand.c b/drivers/fastboot/fb_nand.c
> index 849a6f1..a7cbc34 100644
> --- a/drivers/fastboot/fb_nand.c
> +++ b/drivers/fastboot/fb_nand.c
> @@ -88,7 +88,7 @@ static int _fb_nand_erase(struct mtd_info *mtd, struct part_info *part)
>  }
>
>  static int _fb_nand_write(struct mtd_info *mtd, struct part_info *part,
> -                         void *buffer, unsigned int offset,
> +                         void *buffer, u32 offset,
>                           size_t length, size_t *written)
>  {
>         int flags = WITH_WR_VERIFY;
> @@ -145,8 +145,16 @@ static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info,
>         return blkcnt + bad_blocks;
>  }
>
> +int fastboot_nand_get_part_info(char *part_name, struct part_info **part_info,
> +                               char *response)
> +{
> +       struct mtd_info *mtd = NULL;
> +
> +       return fb_nand_lookup(part_name, &mtd, part_info, response);
> +}
> +
>  void fb_nand_flash_write(const char *cmd, void *download_buffer,
> -                        unsigned int download_bytes, char *response)
> +                        u32 download_bytes, char *response)
>  {
>         struct part_info *part;
>         struct mtd_info *mtd = NULL;
> diff --git a/include/fastboot.h b/include/fastboot.h
> index 593e6a7..6473ec9 100644
> --- a/include/fastboot.h
> +++ b/include/fastboot.h
> @@ -15,13 +15,170 @@
>  #define FASTBOOT_VERSION       "0.4"
>
>  /* The 64 defined bytes plus \0 */
> +#define FASTBOOT_COMMAND_LEN   (64 + 1)
>  #define FASTBOOT_RESPONSE_LEN  (64 + 1)
>
> +/**
> + * All known commands to fastboot
> + */
> +enum {
> +       FASTBOOT_COMMAND_GETVAR = 0,
> +       FASTBOOT_COMMAND_DOWNLOAD,
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +       FASTBOOT_COMMAND_FLASH,
> +       FASTBOOT_COMMAND_ERASE,
> +#endif
> +       FASTBOOT_COMMAND_BOOT,
> +       FASTBOOT_COMMAND_CONTINUE,
> +       FASTBOOT_COMMAND_REBOOT,
> +       FASTBOOT_COMMAND_REBOOT_BOOTLOADER,
> +       FASTBOOT_COMMAND_SET_ACTIVE,
> +#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
> +       FASTBOOT_COMMAND_OEM_FORMAT,
> +#endif
> +
> +       FASTBOOT_COMMAND_COUNT
> +};
> +
> +/**
> + * fastboot_response() - Writes a response of the form "$tag$reason".
> + *
> + * @param tag          The first part of the response
> + * @param response     Pointer to fastboot response buffer
> + * @param format       printf style format string
> + */
>  void fastboot_response(const char *tag, char *response,
>                        const char *format, ...)
>         __attribute__ ((format (__printf__, 3, 4)));
>
> +/**
> + * fastboot_fail() - Write a FAIL response of the form "FAIL$reason".
> + *
> + * @param reason       Pointer to returned reason string
> + * @param response     Pointer to fastboot response buffer
> + */
>  void fastboot_fail(const char *reason, char *response);
> +
> +/**
> + * fastboot_okay() - Write an OKAY response of the form "OKAY$reason".
> + *
> + * @param reason       Pointer to returned reason string.
> + *                     Can be NULL to send a bare "OKAY" response.
> + * @param response     Pointer to fastboot response buffer
> + */
>  void fastboot_okay(const char *reason, char *response);
> +
> +/**
> + * fastboot_set_reboot_flag() - Set flag to indicate reboot-bootloader
> + *
> + * Set flag which indicates that we should reboot into the bootloader
> + * following the reboot that fastboot executes after this function.
> + *
> + * This function should be overridden in your board file with one
> + * which sets whatever flag your board specific Android bootloader flow
> + * requires in order to re-enter the bootloader.
> + */
>  int fastboot_set_reboot_flag(void);
> +
> +/**
> + * fastboot_get_buf_addr() - Return address of the fastboot transfer buffer
> + *
> + * Return: Address of the fastboot transfer buffer
> + */
> +void *fastboot_get_buf_addr(void);
> +
> +/**
> + * fastboot_get_buf_size() - Return size of the fastboot transfer buffer
> + *
> + * Return: Size of the fastboot transfer buffer
> + */
> +u32 fastboot_get_buf_size(void);
> +
> +/**
> + * fastboot_get_bytes_remaining() - return bytes remaining in current transfer
> + *
> + * Return: Number of bytes left in the current download
> + */
> +u32 fastboot_get_bytes_remaining(void);
> +
> +/**
> + * fastboot_get_progress_callback() - Return progress callback
> + *
> + * Return: Pointer to function called during long operations
> + */
> +void (*fastboot_get_progress_callback(void))(const char *);
> +
> +/**
> + * fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
> + *
> + * @cmd_parameter: Pointer to command parameter
> + * @response: Pointer to fastboot response buffer
> + *
> + * Look up cmd_parameter first as an environment variable of the form
> + * fastboot.<cmd_parameter>, if that exists return use its value to set
> + * response.
> + *
> + * Otherwise lookup the name of variable and execute the appropriate
> + * function to return the requested value.
> + */
> +void fastboot_getvar(char *cmd_parameter, char *response);
> +
> +/**
> + * fastboot_set_progress_callback() - set progress callback
> + *
> + * @progress: Pointer to progress callback
> + *
> + * Set a callback which is invoked periodically during long running operations
> + * (flash and erase). This can be used (for example) by the UDP transport to
> + * send INFO responses to keep the client alive whilst those commands are
> + * executing.
> + */
> +void fastboot_set_progress_callback(void (*progress)(const char *msg));
> +
> +/*
> + * fastboot_init() - initialise new fastboot protocol session
> + *
> + * @buf_addr: Pointer to download buffer, or NULL for default
> + * @buf_size: Size of download buffer, or zero for default
> + */
> +void fastboot_init(void *buf_addr, u32 buf_size);
> +
> +/**
> + * fastboot_boot() - Execute fastboot boot command
> + *
> + * If ${fastboot_bootcmd} is set, run that command to execute the boot
> + * process, if that returns, then exit the fastboot server and return
> + * control to the caller.
> + *
> + * Otherwise execute "bootm <fastboot_buf_adder>", if that fails, reset
> + * the board.
> + */
> +void fastboot_boot(void);
> +
> +/**
> + * fastboot_handle_command() - Handle fastboot command
> + *
> + * @cmd_string: Pointer to command string
> + * @response: Pointer to fastboot response buffer
> + *
> + * Return: Executed command, or -1 if not recognized
> + */
> +int fastboot_handle_command(char *cmd_string, char *response);
> +
> +/**
> + * fastboot_download_data() - Copy image data to fastboot_buf_addr.
> + *
> + * @fastboot_data: Pointer to received fastboot data
> + * @fastboot_data_len: Length of received fastboot data
> + * @response: Pointer to fastboot response buffer
> + *
> + * Copies image data from fastboot_data to fastboot_buf_addr. Writes to
> + * response. fastboot_bytes_received is updated to indicate the number
> + * of bytes that have been transferred.
> + *
> + * On completion sets image_size and ${filesize} to the total size of the
> + * downloaded image.
> + */
> +void fastboot_download_data(const void *fastboot_data,
> +                           unsigned int fastboot_data_len, char *response);
>  #endif /* _FASTBOOT_H_ */
> diff --git a/include/fb_mmc.h b/include/fb_mmc.h
> index 39a960c..ae01f99 100644
> --- a/include/fb_mmc.h
> +++ b/include/fb_mmc.h
> @@ -3,6 +3,12 @@
>   * Copyright 2014 Broadcom Corporation.
>   */
>
> +#ifndef _FB_MMC_H_
> +#define _FB_MMC_H_
> +
> +int fastboot_mmc_get_part_info(char *part_name, struct blk_desc **dev_desc,
> +                              disk_partition_t *part_info, char *response);
>  void fb_mmc_flash_write(const char *cmd, void *download_buffer,
> -                       unsigned int download_bytes, char *response);
> +                       u32 download_bytes, char *response);
>  void fb_mmc_erase(const char *cmd, char *response);
> +#endif
> diff --git a/include/fb_nand.h b/include/fb_nand.h
> index 2c92a4e..937f9e4 100644
> --- a/include/fb_nand.h
> +++ b/include/fb_nand.h
> @@ -4,6 +4,14 @@
>   * Copyright 2015 Free Electrons.
>   */
>
> +#ifndef _FB_NAND_H_
> +#define _FB_NAND_H_
> +
> +#include <jffs2/load_kernel.h>
> +
> +int fastboot_nand_get_part_info(char *part_name, struct part_info **part_info,
> +                               char *response);
>  void fb_nand_flash_write(const char *cmd, void *download_buffer,
> -                        unsigned int download_bytes, char *response);
> +                        u32 download_bytes, char *response);
>  void fb_nand_erase(const char *cmd, char *response);
> +#endif
> diff --git a/include/net.h b/include/net.h
> index 65f51d7..5760685 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -535,7 +535,7 @@ extern int          net_restart_wrap;       /* Tried all network devices */
>
>  enum proto_t {
>         BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
> -       TFTPSRV, TFTPPUT, LINKLOCAL
> +       TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT
>  };
>
>  extern char    net_boot_file_name[1024];/* Boot File name */
> diff --git a/include/net/fastboot.h b/include/net/fastboot.h
> new file mode 100644
> index 0000000..6860209
> --- /dev/null
> +++ b/include/net/fastboot.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (C) 2016 The Android Open Source Project
> + */
> +
> +#ifndef __NET_FASTBOOT_H__
> +#define __NET_FASTBOOT_H__
> +
> +/**********************************************************************/
> +/*
> + *     Global functions and variables.
> + */
> +
> +/**
> + * Wait for incoming fastboot comands.
> + */
> +void fastboot_start_server(void);
> +
> +/**********************************************************************/
> +
> +#endif /* __NET_FASTBOOT_H__ */
> diff --git a/net/Makefile b/net/Makefile
> index d1e8e01..0746687 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_CMD_PING) += ping.o
>  obj-$(CONFIG_CMD_RARP) += rarp.o
>  obj-$(CONFIG_CMD_SNTP) += sntp.o
>  obj-$(CONFIG_CMD_TFTPBOOT) += tftp.o
> +obj-$(CONFIG_UDP_FUNCTION_FASTBOOT)  += fastboot.o
>
>  # Disable this warning as it is triggered by:
>  # sprintf(buf, index ? "foo%d" : "foo", index)
> diff --git a/net/fastboot.c b/net/fastboot.c
> new file mode 100644
> index 0000000..3c9f71e
> --- /dev/null
> +++ b/net/fastboot.c
> @@ -0,0 +1,312 @@
> +// SPDX-License-Identifier: BSD-2-Clause
> +/*
> + * Copyright (C) 2016 The Android Open Source Project
> + */
> +
> +#include <common.h>
> +#include <fastboot.h>
> +#include <net.h>
> +#include <net/fastboot.h>
> +
> +/* Fastboot port # defined in spec */
> +#define WELL_KNOWN_PORT 5554
> +
> +enum {
> +       FASTBOOT_ERROR = 0,
> +       FASTBOOT_QUERY = 1,
> +       FASTBOOT_INIT = 2,
> +       FASTBOOT_FASTBOOT = 3,
> +};
> +
> +struct __packed fastboot_header {
> +       uchar id;
> +       uchar flags;
> +       unsigned short seq;
> +};
> +
> +#define PACKET_SIZE 1024
> +#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header))
> +
> +/* Sequence number sent for every packet */
> +static unsigned short fb_sequence_number = 1;
> +static const unsigned short fb_packet_size = PACKET_SIZE;
> +static const unsigned short fb_udp_version = 1;
> +
> +/* Keep track of last packet for resubmission */
> +static uchar last_packet[PACKET_SIZE];
> +static unsigned int last_packet_len;
> +
> +static struct in_addr fastboot_remote_ip;
> +/* The UDP port at their end */
> +static int fastboot_remote_port;
> +/* The UDP port at our end */
> +static int fastboot_our_port;
> +
> +static void boot_downloaded_image(void);
> +
> +#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
> +/**
> + * fastboot_udp_send_info() - Send an INFO packet during long commands.
> + *
> + * @msg: String describing the reason for waiting
> + */
> +static void fastboot_udp_send_info(const char *msg)
> +{
> +       uchar *packet;
> +       uchar *packet_base;
> +       int len = 0;
> +       char response[FASTBOOT_RESPONSE_LEN] = {0};
> +
> +       struct fastboot_header fb_response_header = {
> +               .id = FASTBOOT_FASTBOOT,
> +               .flags = 0,
> +               .seq = htons(fb_sequence_number)
> +       };
> +       ++fb_sequence_number;
> +       packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
> +       packet_base = packet;
> +
> +       /* Write headers */
> +       memcpy(packet, &fb_response_header, sizeof(fb_response_header));
> +       packet += sizeof(fb_response_header);
> +       /* Write response */
> +       fastboot_response("INFO", response, "%s", msg);
> +       memcpy(packet, response, strlen(response));
> +       packet += strlen(response);
> +
> +       len = packet - packet_base;
> +
> +       /* Save packet for retransmitting */
> +       last_packet_len = len;
> +       memcpy(last_packet, packet_base, last_packet_len);
> +
> +       net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
> +                           fastboot_remote_port, fastboot_our_port, len);
> +}
> +
> +/**
> + * fastboot_timed_send_info() - Send INFO packet every 30 seconds
> + *
> + * @msg: String describing the reason for waiting
> + *
> + * Send an INFO packet during long commands based on timer. An INFO packet
> + * is sent if the time is 30 seconds after start. Else, noop.
> + */
> +static void fastboot_timed_send_info(const char *msg)
> +{
> +       static ulong start;
> +
> +       /* Initialize timer */
> +       if (start == 0)
> +               start = get_timer(0);
> +       ulong time = get_timer(start);
> +       /* Send INFO packet to host every 30 seconds */
> +       if (time >= 30000) {
> +               start = get_timer(0);
> +               fastboot_udp_send_info(msg);
> +       }
> +}
> +#endif
> +
> +/**
> + * fastboot_send() - Sends a packet in response to received fastboot packet
> + *
> + * @fb_header: Header for response packet
> + * @fastboot_data: Pointer to received fastboot data
> + * @fastboot_data_len: Length of received fastboot data
> + * @retransmit: Nonzero if sending last sent packet
> + */
> +static void fastboot_send(struct fastboot_header fb_header, char *fastboot_data,
> +                         unsigned int fastboot_data_len, uchar retransmit)
> +{
> +       uchar *packet;
> +       uchar *packet_base;
> +       int len = 0;
> +       const char *error_msg = "An error occurred.";
> +       short tmp;
> +       struct fastboot_header fb_response_header = fb_header;
> +       static char command[FASTBOOT_COMMAND_LEN];
> +       static int cmd = -1;
> +       static bool pending_command;
> +       char response[FASTBOOT_RESPONSE_LEN] = {0};
> +
> +       /*
> +        * We will always be sending some sort of packet, so
> +        * cobble together the packet headers now.
> +        */
> +       packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
> +       packet_base = packet;
> +
> +       /* Resend last packet */
> +       if (retransmit) {
> +               memcpy(packet, last_packet, last_packet_len);
> +               net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
> +                                   fastboot_remote_port, fastboot_our_port,
> +                                   last_packet_len);
> +               return;
> +       }
> +
> +       fb_response_header.seq = htons(fb_response_header.seq);
> +       memcpy(packet, &fb_response_header, sizeof(fb_response_header));
> +       packet += sizeof(fb_response_header);
> +
> +       switch (fb_header.id) {
> +       case FASTBOOT_QUERY:
> +               tmp = htons(fb_sequence_number);
> +               memcpy(packet, &tmp, sizeof(tmp));
> +               packet += sizeof(tmp);
> +               break;
> +       case FASTBOOT_INIT:
> +               tmp = htons(fb_udp_version);
> +               memcpy(packet, &tmp, sizeof(tmp));
> +               packet += sizeof(tmp);
> +               tmp = htons(fb_packet_size);
> +               memcpy(packet, &tmp, sizeof(tmp));
> +               packet += sizeof(tmp);
> +               break;
> +       case FASTBOOT_ERROR:
> +               memcpy(packet, error_msg, strlen(error_msg));
> +               packet += strlen(error_msg);
> +               break;
> +       case FASTBOOT_FASTBOOT:
> +               if (cmd == FASTBOOT_COMMAND_DOWNLOAD) {
> +                       fastboot_download_data(fastboot_data, fastboot_data_len,
> +                                              response);
> +               } else if (!pending_command) {
> +                       strlcpy(command, fastboot_data,
> +                               min((size_t)fastboot_data_len + 1,
> +                                   sizeof(command)));
> +                       pending_command = true;
> +               } else {
> +                       cmd = fastboot_handle_command(command, response);
> +                       pending_command = false;
> +               }
> +               /*
> +                * Sent some INFO packets, need to update sequence number in
> +                * header
> +                */
> +               if (fb_header.seq != fb_sequence_number) {
> +                       fb_response_header.seq = htons(fb_sequence_number);
> +                       memcpy(packet_base, &fb_response_header,
> +                              sizeof(fb_response_header));
> +               }
> +               /* Write response to packet */
> +               memcpy(packet, response, strlen(response));
> +               packet += strlen(response);
> +               break;
> +       default:
> +               pr_err("ID %d not implemented.\n", fb_header.id);
> +               return;
> +       }
> +
> +       len = packet - packet_base;
> +
> +       /* Save packet for retransmitting */
> +       last_packet_len = len;
> +       memcpy(last_packet, packet_base, last_packet_len);
> +
> +       net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip,
> +                           fastboot_remote_port, fastboot_our_port, len);
> +
> +       /* Continue boot process after sending response */
> +       if (!strncmp("OKAY", response, 4)) {
> +               switch (cmd) {
> +               case FASTBOOT_COMMAND_BOOT:
> +                       boot_downloaded_image();
> +                       break;
> +
> +               case FASTBOOT_COMMAND_CONTINUE:
> +                       net_set_state(NETLOOP_SUCCESS);
> +                       break;
> +
> +               case FASTBOOT_COMMAND_REBOOT:
> +               case FASTBOOT_COMMAND_REBOOT_BOOTLOADER:
> +                       do_reset(NULL, 0, 0, NULL);
> +                       break;
> +               }
> +       }
> +
> +       if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4))
> +               cmd = -1;
> +}
> +
> +/**
> + * boot_downloaded_image() - Boots into downloaded image.
> + */
> +static void boot_downloaded_image(void)
> +{
> +       fastboot_boot();
> +       net_set_state(NETLOOP_SUCCESS);
> +}
> +
> +/**
> + * fastboot_handler() - Incoming UDP packet handler.
> + *
> + * @packet: Pointer to incoming UDP packet
> + * @dport: Destination UDP port
> + * @sip: Source IP address
> + * @sport: Source UDP port
> + * @len: Packet length
> + */
> +static void fastboot_handler(uchar *packet, unsigned int dport,
> +                            struct in_addr sip, unsigned int sport,
> +                            unsigned int len)
> +{
> +       struct fastboot_header fb_header;
> +       char fastboot_data[DATA_SIZE] = {0};
> +       unsigned int fastboot_data_len = 0;
> +
> +       if (dport != fastboot_our_port)
> +               return;
> +
> +       fastboot_remote_ip = sip;
> +       fastboot_remote_port = sport;
> +
> +       if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE)
> +               return;
> +       memcpy(&fb_header, packet, sizeof(fb_header));
> +       fb_header.flags = 0;
> +       fb_header.seq = ntohs(fb_header.seq);
> +       packet += sizeof(fb_header);
> +       len -= sizeof(fb_header);
> +
> +       switch (fb_header.id) {
> +       case FASTBOOT_QUERY:
> +               fastboot_send(fb_header, fastboot_data, 0, 0);
> +               break;
> +       case FASTBOOT_INIT:
> +       case FASTBOOT_FASTBOOT:
> +               fastboot_data_len = len;
> +               if (len > 0)
> +                       memcpy(fastboot_data, packet, len);
> +               if (fb_header.seq == fb_sequence_number) {
> +                       fastboot_send(fb_header, fastboot_data,
> +                                     fastboot_data_len, 0);
> +                       fb_sequence_number++;
> +               } else if (fb_header.seq == fb_sequence_number - 1) {
> +                       /* Retransmit last sent packet */
> +                       fastboot_send(fb_header, fastboot_data,
> +                                     fastboot_data_len, 1);
> +               }
> +               break;
> +       default:
> +               pr_err("ID %d not implemented.\n", fb_header.id);
> +               fb_header.id = FASTBOOT_ERROR;
> +               fastboot_send(fb_header, fastboot_data, 0, 0);
> +               break;
> +       }
> +}
> +
> +void fastboot_start_server(void)
> +{
> +       printf("Using %s device\n", eth_get_name());
> +       printf("Listening for fastboot command on %pI4\n", &net_ip);
> +
> +       fastboot_our_port = WELL_KNOWN_PORT;
> +
> +       fastboot_set_progress_callback(fastboot_timed_send_info);
> +       net_set_udp_handler(fastboot_handler);
> +
> +       /* zero out server ether in case the server ip has changed */
> +       memset(net_server_ethaddr, 0, 6);
> +}
> diff --git a/net/net.c b/net/net.c
> index 7f85211..a4932f4 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -87,6 +87,7 @@
>  #include <environment.h>
>  #include <errno.h>
>  #include <net.h>
> +#include <net/fastboot.h>
>  #include <net/tftp.h>
>  #if defined(CONFIG_LED_STATUS)
>  #include <miiphy.h>
> @@ -451,6 +452,11 @@ restart:
>                         tftp_start_server();
>                         break;
>  #endif
> +#ifdef CONFIG_UDP_FUNCTION_FASTBOOT
> +               case FASTBOOT:
> +                       fastboot_start_server();
> +                       break;
> +#endif
>  #if defined(CONFIG_CMD_DHCP)
>                 case DHCP:
>                         bootp_reset();
> @@ -1322,6 +1328,7 @@ common:
>                 /* Fall through */
>
>         case NETCONS:
> +       case FASTBOOT:
>         case TFTPSRV:
>                 if (net_ip.s_addr == 0) {
>                         puts("*** ERROR: `ipaddr' not set\n");
> --
> 2.7.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot


More information about the U-Boot mailing list