[U-Boot] [PATCH v3 13/13] net: fastboot: Merge AOSP UDP fastboot

Alex Kiernan alex.kiernan at gmail.com
Mon May 14 09:09:08 UTC 2018


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>
---

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)
- 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     |   2 +
 drivers/fastboot/fb_command.c | 258 +++++++++++++++++++++++++++++++++++
 drivers/fastboot/fb_common.c  |  48 +++++++
 drivers/fastboot/fb_getvar.c  | 222 ++++++++++++++++++++++++++++++
 drivers/fastboot/fb_mmc.c     |  67 ++++++++-
 drivers/fastboot/fb_nand.c    |  12 +-
 include/fastboot.h            |  47 +++++++
 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                | 310 ++++++++++++++++++++++++++++++++++++++++++
 net/net.c                     |   7 +
 16 files changed, 1105 insertions(+), 16 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 51c5789..a79f456 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
@@ -118,6 +125,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.
+
 endif # FASTBOOT
 
 endmenu
diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile
index e4bd389..149bd02 100644
--- a/drivers/fastboot/Makefile
+++ b/drivers/fastboot/Makefile
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier:      GPL-2.0+
 
 obj-y += fb_common.o
+obj-y += fb_getvar.o
+obj-y += 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..34bf9a2
--- /dev/null
+++ b/drivers/fastboot/fb_command.c
@@ -0,0 +1,258 @@
+// 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>
+
+/* Fastboot download parameters */
+static u32 image_size;
+u32 fastboot_bytes_received;
+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
+};
+
+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;
+}
+
+static void fb_okay(char *cmd_parameter, char *response)
+{
+	fastboot_okay(NULL, response);
+}
+
+static void fb_getvar(char *cmd_parameter, char *response)
+{
+	fastboot_getvar(cmd_parameter, response);
+}
+
+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_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);
+	}
+}
+
+/**
+ * Copies image data from fastboot_data to fastboot_buf_addr.
+ * Writes to response.
+ *
+ * @param fastboot_data        Pointer to received fastboot data
+ * @param fastboot_data_len    Length of received fastboot data
+ * @param response             Pointer to fastboot response buffer
+ */
+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_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)
+/**
+ * Writes the previously downloaded image to the partition indicated by
+ * cmd_parameter. Writes to response.
+ *
+ * @param response    Pointer to fastboot response buffer
+ */
+static void fb_flash(char *cmd_parameter, char *response)
+{
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
+	fb_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
+			   response);
+#endif
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND)
+	fb_nand_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
+			    response);
+#endif
+}
+
+/**
+ * Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes
+ * to response.
+ *
+ * @param response    Pointer to fastboot response buffer
+ */
+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
+
+/**
+ * Sets reboot bootloader flag if requested. Writes to response.
+ *
+ * @param 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)
+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 e74f3a0..4cbcbb1 100644
--- a/drivers/fastboot/fb_common.c
+++ b/drivers/fastboot/fb_common.c
@@ -12,6 +12,13 @@
 
 #include <common.h>
 #include <fastboot.h>
+#include <net/fastboot.h>
+
+/* fastboot download buffer */
+void *fastboot_buf_addr;
+u32 fastboot_buf_size;
+
+void (*fastboot_progress_callback)(const char *msg);
 
 /**
  * Writes a response to response buffer of the form "$tag$reason".
@@ -52,3 +59,44 @@ int __weak fastboot_set_reboot_flag(void)
 {
 	return -ENOSYS;
 }
+
+void fastboot_boot(void *addr)
+{
+	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%lx", (long)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);
+	}
+}
+
+void fastboot_set_progress_callback(void (*progress)(const char *msg))
+{
+	fastboot_progress_callback = progress;
+}
+
+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..7fc46c3
--- /dev/null
+++ b/drivers/fastboot/fb_getvar.c
@@ -0,0 +1,222 @@
+// 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_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
+
+/**
+ * Writes ascii string specified by cmd_parameter to response.
+ *
+ * @param cmd_parameter	Point to command parameter
+ * @param response	Pointer to fastboot response buffer
+ */
+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..69a78be 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,40 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc,
 	return ret;
 }
 
+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 +89,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 +106,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 +178,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 +281,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 +443,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..3d9ffc6 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -15,8 +15,39 @@
 #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
+};
+
+extern void *fastboot_buf_addr;
+extern u32 fastboot_buf_size;
+
+extern u32 fastboot_bytes_received;
+extern u32 fastboot_bytes_expected;
+
+extern void (*fastboot_progress_callback)(const char *msg);
+
 void fastboot_response(const char *tag, char *response,
 		       const char *format, ...)
 	__attribute__ ((format (__printf__, 3, 4)));
@@ -24,4 +55,20 @@ void fastboot_response(const char *tag, char *response,
 void fastboot_fail(const char *reason, char *response);
 void fastboot_okay(const char *reason, char *response);
 int fastboot_set_reboot_flag(void);
+
+/**
+ * Writes ascii string specified by cmd_parameter to response.
+ *
+ * @param cmd_parameter	Pointer to variable name
+ * @param response	Pointer to fastboot response buffer
+ */
+void fastboot_getvar(char *cmd_parameter, char *response);
+
+void fastboot_init(void *buf_addr, u32 buf_size);
+void fastboot_set_progress_callback(void (*progress)(const char *msg));
+
+void fastboot_boot(void *addr);
+int fastboot_handle_command(char *cmd_string, char *response);
+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..8ec2aa5
--- /dev/null
+++ b/net/fastboot.c
@@ -0,0 +1,310 @@
+// 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)
+/**
+ * Send an INFO packet during long commands
+ *
+ * @param 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);
+}
+
+/**
+ * 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.
+ *
+ * @param msg:    String describing the reason for waiting
+ */
+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
+
+/**
+ * Constructs and sends a packet in response to received fastboot packet
+ *
+ * @param fb_header            Header for response packet
+ * @param fastboot_data        Pointer to received fastboot data
+ * @param fastboot_data_len    Length of received fastboot data
+ * @param 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;
+}
+
+/**
+ * Boots into downloaded image.
+ */
+static void boot_downloaded_image(void)
+{
+	fastboot_boot(fastboot_buf_addr);
+	net_set_state(NETLOOP_SUCCESS);
+}
+
+/**
+ * Incoming UDP packet handler.
+ *
+ * @param packet  Pointer to incoming UDP packet
+ * @param dport   Destination UDP port
+ * @param sip     Source IP address
+ * @param sport   Source UDP port
+ * @param 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



More information about the U-Boot mailing list