net: fastboot: Merge AOSP UDP fastboot
authorAlex Kiernan <alex.kiernan@gmail.com>
Tue, 29 May 2018 15:30:53 +0000 (15:30 +0000)
committerMarek Vasut <marex@denx.de>
Wed, 30 May 2018 09:59:21 +0000 (11:59 +0200)
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@gmail.com>
Signed-off-by: Alex Deymo <deymo@google.com>
Signed-off-by: Jocelyn Bohr <bohr@google.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
18 files changed:
cmd/Kconfig
cmd/fastboot.c
drivers/fastboot/Kconfig
drivers/fastboot/Makefile
drivers/fastboot/fb_command.c [new file with mode: 0644]
drivers/fastboot/fb_common.c
drivers/fastboot/fb_getvar.c [new file with mode: 0644]
drivers/fastboot/fb_mmc.c
drivers/fastboot/fb_nand.c
include/fastboot-internal.h [new file with mode: 0644]
include/fastboot.h
include/fb_mmc.h
include/fb_nand.h
include/net.h
include/net/fastboot.h [new file with mode: 0644]
net/Makefile
net/fastboot.c [new file with mode: 0644]
net/net.c

index 30cf63f..17ca0bb 100644 (file)
@@ -655,8 +655,8 @@ config CMD_FASTBOOT
          This enables the command "fastboot" which enables the Android
          fastboot mode for the platform. Fastboot is a protocol for
          downloading images, flashing and device control used on
-         Android devices. Fastboot requires support for acting as a USB
-         device.
+         Android devices. Fastboot requires either the network stack
+         enabled or support for acting as a USB device.
 
          See doc/README.android-fastboot for more information.
 
index a5ec5f4..557257a 100644 (file)
 #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
 );
index 0c9ced5..625f016 100644 (file)
@@ -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
index e4bd389..8831096 100644 (file)
@@ -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 (file)
index 0000000..af4f500
--- /dev/null
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#include <common.h>
+#include <fastboot.h>
+#include <fastboot-internal.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 okay(char *, char *);
+static void getvar(char *, char *);
+static void download(char *, char *);
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+static void flash(char *, char *);
+static void erase(char *, char *);
+#endif
+static void reboot_bootloader(char *, char *);
+
+static const struct {
+       const char *command;
+       void (*dispatch)(char *cmd_parameter, char *response);
+} commands[FASTBOOT_COMMAND_COUNT] = {
+       [FASTBOOT_COMMAND_GETVAR] = {
+               .command = "getvar",
+               .dispatch = getvar
+       },
+       [FASTBOOT_COMMAND_DOWNLOAD] = {
+               .command = "download",
+               .dispatch = download
+       },
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+       [FASTBOOT_COMMAND_FLASH] =  {
+               .command = "flash",
+               .dispatch = flash
+       },
+       [FASTBOOT_COMMAND_ERASE] =  {
+               .command = "erase",
+               .dispatch = erase
+       },
+#endif
+       [FASTBOOT_COMMAND_BOOT] =  {
+               .command = "boot",
+               .dispatch = okay
+       },
+       [FASTBOOT_COMMAND_CONTINUE] =  {
+               .command = "continue",
+               .dispatch = okay
+       },
+       [FASTBOOT_COMMAND_REBOOT] =  {
+               .command = "reboot",
+               .dispatch = okay
+       },
+       [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] =  {
+               .command = "reboot-bootloader",
+               .dispatch = reboot_bootloader
+       },
+       [FASTBOOT_COMMAND_SET_ACTIVE] =  {
+               .command = "set_active",
+               .dispatch = okay
+       },
+};
+
+/**
+ * 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(commands[i].command, cmd_string)) {
+                       if (commands[i].dispatch) {
+                               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;
+}
+
+/**
+ * okay() - Send bare OKAY response
+ *
+ * @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 okay(char *cmd_parameter, char *response)
+{
+       fastboot_okay(NULL, response);
+}
+
+/**
+ * getvar() - Read a config/version variable
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @response: Pointer to fastboot response buffer
+ */
+static void 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 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_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_data_remaining() - return bytes remaining in current transfer
+ *
+ * Return: Number of bytes left in the current download
+ */
+u32 fastboot_data_remaining(void)
+{
+       return fastboot_bytes_expected - fastboot_bytes_received;
+}
+
+/**
+ * fastboot_data_download() - 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_data_download(const void *fastboot_data,
+                           unsigned int fastboot_data_len,
+                           char *response)
+{
+#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_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');
+       }
+       *response = '\0';
+}
+
+/**
+ * fastboot_data_complete() - Mark current transfer complete
+ *
+ * @response: Pointer to fastboot response buffer
+ *
+ * Set image_size and ${filesize} to the total size of the downloaded image.
+ */
+void fastboot_data_complete(char *response)
+{
+       /* 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;
+}
+
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+/**
+ * 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 flash(char *cmd_parameter, char *response)
+{
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
+       fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
+                                response);
+#endif
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
+       fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
+                                 response);
+#endif
+}
+
+/**
+ * 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 erase(char *cmd_parameter, char *response)
+{
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
+       fastboot_mmc_erase(cmd_parameter, response);
+#endif
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
+       fastboot_nand_erase(cmd_parameter, response);
+#endif
+}
+#endif
+
+/**
+ * reboot_bootloader() - Sets reboot bootloader flag.
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @response: Pointer to fastboot response buffer
+ */
+static void reboot_bootloader(char *cmd_parameter, char *response)
+{
+       if (fastboot_set_reboot_flag())
+               fastboot_fail("Cannot set reboot flag", response);
+       else
+               fastboot_okay(NULL, response);
+}
index 79e080a..c6e06aa 100644 (file)
 
 #include <common.h>
 #include <fastboot.h>
+#include <net/fastboot.h>
+
+/**
+ * fastboot_buf_addr - base address of the fastboot download buffer
+ */
+void *fastboot_buf_addr;
+
+/**
+ * fastboot_buf_size - size of the fastboot download buffer
+ */
+u32 fastboot_buf_size;
+
+/**
+ * fastboot_progress_callback - callback executed during long operations
+ */
+void (*fastboot_progress_callback)(const char *msg);
 
 /**
  * fastboot_response() - Writes a response of the form "$tag$reason".
@@ -74,3 +90,80 @@ int __weak fastboot_set_reboot_flag(void)
 {
        return -ENOSYS;
 }
+
+/**
+ * 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 (file)
index 0000000..4d264c9
--- /dev/null
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#include <common.h>
+#include <fastboot.h>
+#include <fastboot-internal.h>
+#include <fb_mmc.h>
+#include <fb_nand.h>
+#include <fs.h>
+#include <version.h>
+
+static void getvar_version(char *var_parameter, char *response);
+static void getvar_bootloader_version(char *var_parameter, char *response);
+static void getvar_downloadsize(char *var_parameter, char *response);
+static void getvar_serialno(char *var_parameter, char *response);
+static void getvar_version_baseband(char *var_parameter, char *response);
+static void getvar_product(char *var_parameter, char *response);
+static void getvar_current_slot(char *var_parameter, char *response);
+static void getvar_slot_suffixes(char *var_parameter, char *response);
+static void getvar_has_slot(char *var_parameter, char *response);
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
+static void getvar_partition_type(char *part_name, char *response);
+#endif
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+static void getvar_partition_size(char *part_name, char *response);
+#endif
+
+static const struct {
+       const char *variable;
+       void (*dispatch)(char *var_parameter, char *response);
+} getvar_dispatch[] = {
+       {
+               .variable = "version",
+               .dispatch = getvar_version
+       }, {
+               .variable = "bootloader-version",
+               .dispatch = getvar_bootloader_version
+       }, {
+               .variable = "version-bootloader",
+               .dispatch = getvar_bootloader_version
+       }, {
+               .variable = "downloadsize",
+               .dispatch = getvar_downloadsize
+       }, {
+               .variable = "max-download-size",
+               .dispatch = getvar_downloadsize
+       }, {
+               .variable = "serialno",
+               .dispatch = getvar_serialno
+       }, {
+               .variable = "version-baseband",
+               .dispatch = getvar_version_baseband
+       }, {
+               .variable = "product",
+               .dispatch = getvar_product
+       }, {
+               .variable = "current-slot",
+               .dispatch = getvar_current_slot
+       }, {
+               .variable = "slot-suffixes",
+               .dispatch = getvar_slot_suffixes
+       }, {
+               .variable = "has_slot",
+               .dispatch = getvar_has_slot
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
+       }, {
+               .variable = "partition-type",
+               .dispatch = getvar_partition_type
+#endif
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH)
+       }, {
+               .variable = "partition-size",
+               .dispatch = getvar_partition_size
+#endif
+       }
+};
+
+static void getvar_version(char *var_parameter, char *response)
+{
+       fastboot_okay(FASTBOOT_VERSION, response);
+}
+
+static void getvar_bootloader_version(char *var_parameter, char *response)
+{
+       fastboot_okay(U_BOOT_VERSION, response);
+}
+
+static void getvar_downloadsize(char *var_parameter, char *response)
+{
+       fastboot_response("OKAY", response, "0x%08x", fastboot_buf_size);
+}
+
+static void 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 getvar_version_baseband(char *var_parameter, char *response)
+{
+       fastboot_okay("N/A", response);
+}
+
+static void 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 getvar_current_slot(char *var_parameter, char *response)
+{
+       /* A/B not implemented, for now always return _a */
+       fastboot_okay("_a", response);
+}
+
+static void getvar_slot_suffixes(char *var_parameter, char *response)
+{
+       fastboot_okay("_a,_b", response);
+}
+
+static void 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 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 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(getvar_dispatch); ++i) {
+                       if (!strcmp(getvar_dispatch[i].variable,
+                                   cmd_parameter)) {
+                               getvar_dispatch[i].dispatch(var_parameter,
+                                                           response);
+                               return;
+                       }
+               }
+               pr_warn("WARNING: unknown variable: %s\n", cmd_parameter);
+               fastboot_fail("Variable not implemented", response);
+       }
+}
index 47fcf74..4c1c7fd 100644 (file)
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <blk.h>
 #include <fastboot.h>
+#include <fastboot-internal.h>
 #include <fb_mmc.h>
 #include <image-sparse.h>
 #include <part.h>
@@ -15,6 +16,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 +46,48 @@ 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;
+
+       for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) {
+               cur_blkcnt = min((int)blkcnt - i, FASTBOOT_MAX_BLK_WRITE);
+               if (buffer) {
+                       if (fastboot_progress_callback)
+                               fastboot_progress_callback("writing");
+                       blks_written = blk_dwrite(block_dev, blk, cur_blkcnt,
+                                                 buffer + (i * block_dev->blksz));
+               } else {
+                       if (fastboot_progress_callback)
+                               fastboot_progress_callback("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 */
@@ -252,6 +291,38 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc,
 #endif
 
 /**
+ * fastboot_mmc_get_part_info() - Lookup eMMC partion by name
+ *
+ * @part_name: Named partition to lookup
+ * @dev_desc: Pointer to returned blk_desc pointer
+ * @part_info: Pointer to returned disk_partition_t
+ * @response: Pointer to fastboot response buffer
+ */
+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;
+}
+
+/**
  * fastboot_mmc_flash_write() - Write image to eMMC for fastboot
  *
  * @cmd: Named partition to write image to
@@ -260,7 +331,7 @@ static int fb_mmc_update_zimage(struct blk_desc *dev_desc,
  * @response: Pointer to fastboot response buffer
  */
 void fastboot_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;
@@ -403,7 +474,8 @@ void fastboot_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);
index 535f541..526bc12 100644 (file)
@@ -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;
@@ -146,6 +146,21 @@ static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info,
 }
 
 /**
+ * fastboot_nand_get_part_info() - Lookup NAND partion by name
+ *
+ * @part_name: Named device to lookup
+ * @part_info: Pointer to returned part_info pointer
+ * @response: Pointer to fastboot response buffer
+ */
+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);
+}
+
+/**
  * fastboot_nand_flash_write() - Write image to NAND for fastboot
  *
  * @cmd: Named device to write image to
@@ -154,7 +169,7 @@ static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info,
  * @response: Pointer to fastboot response buffer
  */
 void fastboot_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-internal.h b/include/fastboot-internal.h
new file mode 100644 (file)
index 0000000..bf2f2b3
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _FASTBOOT_INTERNAL_H_
+#define _FASTBOOT_INTERNAL_H_
+
+/**
+ * fastboot_buf_addr - base address of the fastboot download buffer
+ */
+extern void *fastboot_buf_addr;
+
+/**
+ * fastboot_buf_size - size of the fastboot download buffer
+ */
+extern u32 fastboot_buf_size;
+
+/**
+ * fastboot_progress_callback - callback executed during long operations
+ */
+extern void (*fastboot_progress_callback)(const char *msg);
+
+/**
+ * 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);
+
+#endif
index bf3d9e2..9a3d5ba 100644 (file)
 #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,
+
+       FASTBOOT_COMMAND_COUNT
+};
+
+/**
  * fastboot_response() - Writes a response of the form "$tag$reason".
  *
  * @tag: The first part of the response
@@ -43,5 +63,89 @@ void fastboot_fail(const char *reason, char *response);
  * @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_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_addr>", 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_data_remaining() - return bytes remaining in current transfer
+ *
+ * Return: Number of bytes left in the current download
+ */
+u32 fastboot_data_remaining(void);
+
+/**
+ * fastboot_data_download() - 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.
+ */
+void fastboot_data_download(const void *fastboot_data,
+                           unsigned int fastboot_data_len, char *response);
+
+/**
+ * fastboot_data_complete() - Mark current transfer complete
+ *
+ * @response: Pointer to fastboot response buffer
+ *
+ * Set image_size and ${filesize} to the total size of the downloaded image.
+ */
+void fastboot_data_complete(char *response);
+
 #endif /* _FASTBOOT_H_ */
index daca81a..fd5db9e 100644 (file)
@@ -3,6 +3,20 @@
  * Copyright 2014 Broadcom Corporation.
  */
 
+#ifndef _FB_MMC_H_
+#define _FB_MMC_H_
+
+/**
+ * fastboot_mmc_get_part_info() - Lookup eMMC partion by name
+ *
+ * @part_name: Named partition to lookup
+ * @dev_desc: Pointer to returned blk_desc pointer
+ * @part_info: Pointer to returned disk_partition_t
+ * @response: Pointer to fastboot response buffer
+ */
+int fastboot_mmc_get_part_info(char *part_name, struct blk_desc **dev_desc,
+                              disk_partition_t *part_info, char *response);
+
 /**
  * fastboot_mmc_flash_write() - Write image to eMMC for fastboot
  *
@@ -12,8 +26,7 @@
  * @response: Pointer to fastboot response buffer
  */
 void fastboot_mmc_flash_write(const char *cmd, void *download_buffer,
-                             unsigned int download_bytes, char *response);
-
+                             u32 download_bytes, char *response);
 /**
  * fastboot_mmc_flash_erase() - Erase eMMC for fastboot
  *
@@ -21,3 +34,4 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer,
  * @response: Pointer to fastboot response buffer
  */
 void fastboot_mmc_erase(const char *cmd, char *response);
+#endif
index b5d4c0c..08ab0e2 100644 (file)
@@ -4,6 +4,21 @@
  * Copyright 2015 Free Electrons.
  */
 
+#ifndef _FB_NAND_H_
+#define _FB_NAND_H_
+
+#include <jffs2/load_kernel.h>
+
+/**
+ * fastboot_nand_get_part_info() - Lookup NAND partion by name
+ *
+ * @part_name: Named device to lookup
+ * @part_info: Pointer to returned part_info pointer
+ * @response: Pointer to fastboot response buffer
+ */
+int fastboot_nand_get_part_info(char *part_name, struct part_info **part_info,
+                               char *response);
+
 /**
  * fastboot_nand_flash_write() - Write image to NAND for fastboot
  *
@@ -13,7 +28,8 @@
  * @response: Pointer to fastboot response buffer
  */
 void fastboot_nand_flash_write(const char *cmd, void *download_buffer,
-                              unsigned int download_bytes, char *response);
+                              u32 download_bytes, char *response);
+
 /**
  * fastboot_nand_flash_erase() - Erase NAND for fastboot
  *
@@ -21,3 +37,4 @@ void fastboot_nand_flash_write(const char *cmd, void *download_buffer,
  * @response: Pointer to fastboot response buffer
  */
 void fastboot_nand_erase(const char *cmd, char *response);
+#endif
index 65f51d7..5760685 100644 (file)
@@ -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 (file)
index 0000000..6860209
--- /dev/null
@@ -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__ */
index d1e8e01..0746687 100644 (file)
@@ -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 (file)
index 0000000..a9f7c07
--- /dev/null
@@ -0,0 +1,317 @@
+// 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 sequence_number = 1;
+static const unsigned short packet_size = PACKET_SIZE;
+static const unsigned short 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 response_header = {
+               .id = FASTBOOT_FASTBOOT,
+               .flags = 0,
+               .seq = htons(sequence_number)
+       };
+       ++sequence_number;
+       packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE;
+       packet_base = packet;
+
+       /* Write headers */
+       memcpy(packet, &response_header, sizeof(response_header));
+       packet += sizeof(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
+ *
+ * @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 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 response_header = 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;
+       }
+
+       response_header.seq = htons(response_header.seq);
+       memcpy(packet, &response_header, sizeof(response_header));
+       packet += sizeof(response_header);
+
+       switch (header.id) {
+       case FASTBOOT_QUERY:
+               tmp = htons(sequence_number);
+               memcpy(packet, &tmp, sizeof(tmp));
+               packet += sizeof(tmp);
+               break;
+       case FASTBOOT_INIT:
+               tmp = htons(udp_version);
+               memcpy(packet, &tmp, sizeof(tmp));
+               packet += sizeof(tmp);
+               tmp = htons(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) {
+                       if (!fastboot_data_len && !fastboot_data_remaining()) {
+                               fastboot_data_complete(response);
+                       } else {
+                               fastboot_data_download(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 (header.seq != sequence_number) {
+                       response_header.seq = htons(sequence_number);
+                       memcpy(packet_base, &response_header,
+                              sizeof(response_header));
+               }
+               /* Write response to packet */
+               memcpy(packet, response, strlen(response));
+               packet += strlen(response);
+               break;
+       default:
+               pr_err("ID %d not implemented.\n", 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 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(&header, packet, sizeof(header));
+       header.flags = 0;
+       header.seq = ntohs(header.seq);
+       packet += sizeof(header);
+       len -= sizeof(header);
+
+       switch (header.id) {
+       case FASTBOOT_QUERY:
+               fastboot_send(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 (header.seq == sequence_number) {
+                       fastboot_send(header, fastboot_data,
+                                     fastboot_data_len, 0);
+                       sequence_number++;
+               } else if (header.seq == sequence_number - 1) {
+                       /* Retransmit last sent packet */
+                       fastboot_send(header, fastboot_data,
+                                     fastboot_data_len, 1);
+               }
+               break;
+       default:
+               pr_err("ID %d not implemented.\n", header.id);
+               header.id = FASTBOOT_ERROR;
+               fastboot_send(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);
+}
index 7f85211..a4932f4 100644 (file)
--- 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");