summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2018-06-01 09:52:15 -0400
committerTom Rini <trini@konsulko.com>2018-06-01 09:52:15 -0400
commitcaa2a2e5ab44d87faf51fafc780ecc985e0c05d6 (patch)
treeda4f3cba6cdfbeae5f723a9ebf00bd29c8ae31bb /drivers
parentc90c43cda8c376f949266f920bbb49119aef0b00 (diff)
parent277b1333b780acd8ddb761c9160c06ffdf1c3901 (diff)
Merge branch 'master' of git://git.denx.de/u-boot-usb
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/fastboot/Kconfig137
-rw-r--r--drivers/fastboot/Makefile7
-rw-r--r--drivers/fastboot/fb_command.c335
-rw-r--r--drivers/fastboot/fb_common.c169
-rw-r--r--drivers/fastboot/fb_getvar.c230
-rw-r--r--drivers/fastboot/fb_mmc.c488
-rw-r--r--drivers/fastboot/fb_nand.c261
-rw-r--r--drivers/usb/gadget/f_fastboot.c347
-rw-r--r--drivers/usb/gadget/f_thor.c65
-rw-r--r--drivers/usb/host/xhci-rcar.c5
-rw-r--r--drivers/usb/host/xhci-rockchip.c16
-rw-r--r--drivers/usb/host/xhci.c14
14 files changed, 1714 insertions, 363 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c2e813f5ad..8424898dbd 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -28,6 +28,8 @@ source "drivers/dfu/Kconfig"
source "drivers/dma/Kconfig"
+source "drivers/fastboot/Kconfig"
+
source "drivers/firmware/Kconfig"
source "drivers/fpga/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index b3f1b600a5..d29a6e467b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -71,6 +71,7 @@ obj-y += block/
obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/
obj-$(CONFIG_CPU) += cpu/
obj-y += crypto/
+obj-$(CONFIG_FASTBOOT) += fastboot/
obj-y += firmware/
obj-$(CONFIG_FPGA) += fpga/
obj-y += misc/
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
new file mode 100644
index 0000000000..bc25ea1d9c
--- /dev/null
+++ b/drivers/fastboot/Kconfig
@@ -0,0 +1,137 @@
+menu "Fastboot support"
+
+config FASTBOOT
+ bool
+ imply ANDROID_BOOT_IMAGE
+ imply CMD_FASTBOOT
+
+config USB_FUNCTION_FASTBOOT
+ bool "Enable USB fastboot gadget"
+ depends on USB_GADGET
+ default y if ARCH_SUNXI && USB_MUSB_GADGET
+ select FASTBOOT
+ select USB_GADGET_DOWNLOAD
+ 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
+ hex "Define FASTBOOT buffer address"
+ default 0x82000000 if MX6SX || MX6SL || MX6UL || MX6SLL
+ default 0x81000000 if ARCH_OMAP2PLUS
+ default 0x42000000 if ARCH_SUNXI && !MACH_SUN9I
+ default 0x22000000 if ARCH_SUNXI && MACH_SUN9I
+ default 0x60800800 if ROCKCHIP_RK3036 || ROCKCHIP_RK3188 || \
+ ROCKCHIP_RK322X
+ default 0x800800 if ROCKCHIP_RK3288 || ROCKCHIP_RK3329 || \
+ ROCKCHIP_RK3399
+ default 0x280000 if ROCKCHIP_RK3368
+ default 0x100000 if ARCH_ZYNQMP
+ help
+ The fastboot protocol requires a large memory buffer for
+ downloads. Define this to the starting RAM address to use for
+ downloaded images.
+
+config FASTBOOT_BUF_SIZE
+ hex "Define FASTBOOT buffer size"
+ default 0x8000000 if ARCH_ROCKCHIP
+ default 0x6000000 if ARCH_ZYNQMP
+ default 0x2000000 if ARCH_SUNXI
+ default 0x7000000
+ help
+ The fastboot protocol requires a large memory buffer for
+ downloads. This buffer should be as large as possible for a
+ platform. Define this to the size available RAM for fastboot.
+
+config FASTBOOT_USB_DEV
+ int "USB controller number"
+ depends on USB_FUNCTION_FASTBOOT
+ default 0
+ help
+ Some boards have USB OTG controller other than 0. Define this
+ option so it can be used in compiled environment (e.g. in
+ CONFIG_BOOTCOMMAND).
+
+config FASTBOOT_FLASH
+ bool "Enable FASTBOOT FLASH command"
+ default y if ARCH_SUNXI
+ depends on MMC || (NAND && CMD_MTDPARTS)
+ select IMAGE_SPARSE
+ help
+ The fastboot protocol includes a "flash" command for writing
+ the downloaded image to a non-volatile storage device. Define
+ this to enable the "fastboot flash" command.
+
+choice
+ prompt "Flash provider for FASTBOOT"
+ depends on FASTBOOT_FLASH
+
+config FASTBOOT_FLASH_MMC
+ bool "FASTBOOT on MMC"
+ depends on MMC
+
+config FASTBOOT_FLASH_NAND
+ bool "FASTBOOT on NAND"
+ depends on NAND && CMD_MTDPARTS
+
+endchoice
+
+config FASTBOOT_FLASH_MMC_DEV
+ int "Define FASTBOOT MMC FLASH default device"
+ depends on FASTBOOT_FLASH_MMC
+ default 0 if ARCH_SUNXI && MMC_SUNXI_SLOT_EXTRA = -1
+ default 1 if ARCH_SUNXI && MMC_SUNXI_SLOT_EXTRA != -1
+ help
+ The fastboot "flash" command requires additional information
+ regarding the non-volatile storage device. Define this to
+ the eMMC device that fastboot should use to store the image.
+
+config FASTBOOT_FLASH_NAND_TRIMFFS
+ bool "Skip empty pages when flashing NAND"
+ depends on FASTBOOT_FLASH_NAND
+ help
+ When flashing NAND enable the DROP_FFS flag to drop trailing all-0xff
+ pages.
+
+config FASTBOOT_GPT_NAME
+ string "Target name for updating GPT"
+ depends on FASTBOOT_FLASH_MMC && EFI_PARTITION
+ default "gpt"
+ help
+ The fastboot "flash" command supports writing the downloaded
+ image to the Protective MBR and the Primary GUID Partition
+ Table. (Additionally, this downloaded image is post-processed
+ to generate and write the Backup GUID Partition Table.)
+ This occurs when the specified "partition name" on the
+ "fastboot flash" command line matches the value defined here.
+ The default target name for updating GPT is "gpt".
+
+config FASTBOOT_MBR_NAME
+ string "Target name for updating MBR"
+ depends on FASTBOOT_FLASH_MMC && DOS_PARTITION
+ default "mbr"
+ help
+ The fastboot "flash" command allows to write the downloaded image
+ to the Master Boot Record. This occurs when the "partition 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
new file mode 100644
index 0000000000..a2421565e2
--- /dev/null
+++ b/drivers/fastboot/Makefile
@@ -0,0 +1,7 @@
+# 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 0000000000..200f9910c5
--- /dev/null
+++ b/drivers/fastboot/fb_command.c
@@ -0,0 +1,335 @@
+// 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 *);
+#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
+static void oem_format(char *, char *);
+#endif
+
+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
+ },
+#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
+ [FASTBOOT_COMMAND_OEM_FORMAT] = {
+ .command = "oem format",
+ .dispatch = oem_format,
+ },
+#endif
+};
+
+/**
+ * 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);
+}
+
+#if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT)
+/**
+ * oem_format() - Execute the OEM format command
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @response: Pointer to fastboot response buffer
+ */
+static void 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
new file mode 100644
index 0000000000..c6e06aab7a
--- /dev/null
+++ b/drivers/fastboot/fb_common.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2008 - 2009
+ * Windriver, <www.windriver.com>
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * Copyright 2014 Linaro, Ltd.
+ * Rob Herring <robh@kernel.org>
+ */
+
+#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".
+ *
+ * @tag: The first part of the response
+ * @response: Pointer to fastboot response buffer
+ * @format: printf style format string
+ */
+void fastboot_response(const char *tag, char *response,
+ const char *format, ...)
+{
+ va_list args;
+
+ strlcpy(response, tag, FASTBOOT_RESPONSE_LEN);
+ if (format) {
+ va_start(args, format);
+ vsnprintf(response + strlen(response),
+ FASTBOOT_RESPONSE_LEN - strlen(response) - 1,
+ format, args);
+ va_end(args);
+ }
+}
+
+/**
+ * fastboot_fail() - Write a FAIL response of the form "FAIL$reason".
+ *
+ * @reason: Pointer to returned reason string
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_fail(const char *reason, char *response)
+{
+ fastboot_response("FAIL", response, "%s", reason);
+}
+
+/**
+ * fastboot_okay() - Write an OKAY response of the form "OKAY$reason".
+ *
+ * @reason: Pointer to returned reason string, or NULL to send a bare "OKAY"
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_okay(const char *reason, char *response)
+{
+ if (reason)
+ fastboot_response("OKAY", response, "%s", reason);
+ else
+ fastboot_response("OKAY", response, NULL);
+}
+
+/**
+ * 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 __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
index 0000000000..4d264c985d
--- /dev/null
+++ b/drivers/fastboot/fb_getvar.c
@@ -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);
+ }
+}
diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c
new file mode 100644
index 0000000000..4c1c7fd2cd
--- /dev/null
+++ b/drivers/fastboot/fb_mmc.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2014 Broadcom Corporation.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <blk.h>
+#include <fastboot.h>
+#include <fastboot-internal.h>
+#include <fb_mmc.h>
+#include <image-sparse.h>
+#include <part.h>
+#include <mmc.h>
+#include <div64.h>
+#include <linux/compat.h>
+#include <android_image.h>
+
+#define FASTBOOT_MAX_BLK_WRITE 16384
+
+#define BOOT_PARTITION_NAME "boot"
+
+struct fb_mmc_sparse {
+ struct blk_desc *dev_desc;
+};
+
+static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc,
+ const char *name, disk_partition_t *info)
+{
+ int ret;
+
+ ret = part_get_info_by_name(dev_desc, name, info);
+ if (ret < 0) {
+ /* strlen("fastboot_partition_alias_") + 32(part_name) + 1 */
+ char env_alias_name[25 + 32 + 1];
+ char *aliased_part_name;
+
+ /* check for alias */
+ strcpy(env_alias_name, "fastboot_partition_alias_");
+ strncat(env_alias_name, name, 32);
+ aliased_part_name = env_get(env_alias_name);
+ if (aliased_part_name != NULL)
+ ret = part_get_info_by_name(dev_desc,
+ aliased_part_name, info);
+ }
+ 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 fb_mmc_blk_write(dev_desc, blk, blkcnt, buffer);
+}
+
+static lbaint_t fb_mmc_sparse_reserve(struct sparse_storage *info,
+ lbaint_t blk, lbaint_t blkcnt)
+{
+ return blkcnt;
+}
+
+static void write_raw_image(struct blk_desc *dev_desc, disk_partition_t *info,
+ const char *part_name, void *buffer,
+ u32 download_bytes, char *response)
+{
+ lbaint_t blkcnt;
+ lbaint_t blks;
+
+ /* determine number of blocks to write */
+ blkcnt = ((download_bytes + (info->blksz - 1)) & ~(info->blksz - 1));
+ blkcnt = lldiv(blkcnt, info->blksz);
+
+ if (blkcnt > info->size) {
+ pr_err("too large for partition: '%s'\n", part_name);
+ fastboot_fail("too large for partition", response);
+ return;
+ }
+
+ puts("Flashing Raw Image\n");
+
+ 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);
+ return;
+ }
+
+ printf("........ wrote " LBAFU " bytes to '%s'\n", blkcnt * info->blksz,
+ part_name);
+ fastboot_okay(NULL, response);
+}
+
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+/**
+ * Read Android boot image header from boot partition.
+ *
+ * @param[in] dev_desc MMC device descriptor
+ * @param[in] info Boot partition info
+ * @param[out] hdr Where to store read boot image header
+ *
+ * @return Boot image header sectors count or 0 on error
+ */
+static lbaint_t fb_mmc_get_boot_header(struct blk_desc *dev_desc,
+ disk_partition_t *info,
+ struct andr_img_hdr *hdr,
+ char *response)
+{
+ ulong sector_size; /* boot partition sector size */
+ lbaint_t hdr_sectors; /* boot image header sectors count */
+ int res;
+
+ /* Calculate boot image sectors count */
+ sector_size = info->blksz;
+ hdr_sectors = DIV_ROUND_UP(sizeof(struct andr_img_hdr), sector_size);
+ if (hdr_sectors == 0) {
+ pr_err("invalid number of boot sectors: 0\n");
+ fastboot_fail("invalid number of boot sectors: 0", response);
+ return 0;
+ }
+
+ /* Read the boot image header */
+ res = blk_dread(dev_desc, info->start, hdr_sectors, (void *)hdr);
+ if (res != hdr_sectors) {
+ pr_err("cannot read header from boot partition\n");
+ fastboot_fail("cannot read header from boot partition",
+ response);
+ return 0;
+ }
+
+ /* Check boot header magic string */
+ res = android_image_check_header(hdr);
+ if (res != 0) {
+ pr_err("bad boot image magic\n");
+ fastboot_fail("boot partition not initialized", response);
+ return 0;
+ }
+
+ return hdr_sectors;
+}
+
+/**
+ * Write downloaded zImage to boot partition and repack it properly.
+ *
+ * @param dev_desc MMC device descriptor
+ * @param download_buffer Address to fastboot buffer with zImage in it
+ * @param download_bytes Size of fastboot buffer, in bytes
+ *
+ * @return 0 on success or -1 on error
+ */
+static int fb_mmc_update_zimage(struct blk_desc *dev_desc,
+ void *download_buffer,
+ u32 download_bytes,
+ char *response)
+{
+ uintptr_t hdr_addr; /* boot image header address */
+ struct andr_img_hdr *hdr; /* boot image header */
+ lbaint_t hdr_sectors; /* boot image header sectors */
+ u8 *ramdisk_buffer;
+ u32 ramdisk_sector_start;
+ u32 ramdisk_sectors;
+ u32 kernel_sector_start;
+ u32 kernel_sectors;
+ u32 sectors_per_page;
+ disk_partition_t info;
+ int res;
+
+ puts("Flashing zImage\n");
+
+ /* Get boot partition info */
+ res = part_get_info_by_name(dev_desc, BOOT_PARTITION_NAME, &info);
+ if (res < 0) {
+ pr_err("cannot find boot partition\n");
+ fastboot_fail("cannot find boot partition", response);
+ return -1;
+ }
+
+ /* Put boot image header in fastboot buffer after downloaded zImage */
+ hdr_addr = (uintptr_t)download_buffer + ALIGN(download_bytes, PAGE_SIZE);
+ hdr = (struct andr_img_hdr *)hdr_addr;
+
+ /* Read boot image header */
+ hdr_sectors = fb_mmc_get_boot_header(dev_desc, &info, hdr, response);
+ if (hdr_sectors == 0) {
+ pr_err("unable to read boot image header\n");
+ fastboot_fail("unable to read boot image header", response);
+ return -1;
+ }
+
+ /* Check if boot image has second stage in it (we don't support it) */
+ if (hdr->second_size > 0) {
+ pr_err("moving second stage is not supported yet\n");
+ fastboot_fail("moving second stage is not supported yet",
+ response);
+ return -1;
+ }
+
+ /* Extract ramdisk location */
+ sectors_per_page = hdr->page_size / info.blksz;
+ ramdisk_sector_start = info.start + sectors_per_page;
+ ramdisk_sector_start += DIV_ROUND_UP(hdr->kernel_size, hdr->page_size) *
+ sectors_per_page;
+ ramdisk_sectors = DIV_ROUND_UP(hdr->ramdisk_size, hdr->page_size) *
+ sectors_per_page;
+
+ /* Read ramdisk and put it in fastboot buffer after boot image header */
+ ramdisk_buffer = (u8 *)hdr + (hdr_sectors * info.blksz);
+ res = blk_dread(dev_desc, ramdisk_sector_start, ramdisk_sectors,
+ ramdisk_buffer);
+ if (res != ramdisk_sectors) {
+ pr_err("cannot read ramdisk from boot partition\n");
+ fastboot_fail("cannot read ramdisk from boot partition",
+ response);
+ return -1;
+ }
+
+ /* Write new kernel size to boot image header */
+ hdr->kernel_size = download_bytes;
+ res = blk_dwrite(dev_desc, info.start, hdr_sectors, (void *)hdr);
+ if (res == 0) {
+ pr_err("cannot writeback boot image header\n");
+ fastboot_fail("cannot write back boot image header", response);
+ return -1;
+ }
+
+ /* Write the new downloaded kernel */
+ kernel_sector_start = info.start + sectors_per_page;
+ kernel_sectors = DIV_ROUND_UP(hdr->kernel_size, hdr->page_size) *
+ sectors_per_page;
+ res = blk_dwrite(dev_desc, kernel_sector_start, kernel_sectors,
+ download_buffer);
+ if (res == 0) {
+ pr_err("cannot write new kernel\n");
+ fastboot_fail("cannot write new kernel", response);
+ return -1;
+ }
+
+ /* Write the saved ramdisk back */
+ ramdisk_sector_start = info.start + sectors_per_page;
+ ramdisk_sector_start += DIV_ROUND_UP(hdr->kernel_size, hdr->page_size) *
+ sectors_per_page;
+ res = blk_dwrite(dev_desc, ramdisk_sector_start, ramdisk_sectors,
+ ramdisk_buffer);
+ if (res == 0) {
+ pr_err("cannot write back original ramdisk\n");
+ fastboot_fail("cannot write back original ramdisk", response);
+ return -1;
+ }
+
+ puts("........ zImage was updated in boot partition\n");
+ fastboot_okay(NULL, response);
+ return 0;
+}
+#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
+ * @download_buffer: Pointer to image data
+ * @download_bytes: Size of image data
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_mmc_flash_write(const char *cmd, void *download_buffer,
+ u32 download_bytes, char *response)
+{
+ struct blk_desc *dev_desc;
+ disk_partition_t info;
+
+ dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
+ if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
+ pr_err("invalid mmc device\n");
+ fastboot_fail("invalid mmc device", response);
+ return;
+ }
+
+#if CONFIG_IS_ENABLED(EFI_PARTITION)
+ if (strcmp(cmd, CONFIG_FASTBOOT_GPT_NAME) == 0) {
+ printf("%s: updating MBR, Primary and Backup GPT(s)\n",
+ __func__);
+ if (is_valid_gpt_buf(dev_desc, download_buffer)) {
+ printf("%s: invalid GPT - refusing to write to flash\n",
+ __func__);
+ fastboot_fail("invalid GPT partition", response);
+ return;
+ }
+ if (write_mbr_and_gpt_partitions(dev_desc, download_buffer)) {
+ printf("%s: writing GPT partitions failed\n", __func__);
+ fastboot_fail("writing GPT partitions failed",
+ response);
+ return;
+ }
+ printf("........ success\n");
+ fastboot_okay(NULL, response);
+ return;
+ }
+#endif
+
+#if CONFIG_IS_ENABLED(DOS_PARTITION)
+ if (strcmp(cmd, CONFIG_FASTBOOT_MBR_NAME) == 0) {
+ printf("%s: updating MBR\n", __func__);
+ if (is_valid_dos_buf(download_buffer)) {
+ printf("%s: invalid MBR - refusing to write to flash\n",
+ __func__);
+ fastboot_fail("invalid MBR partition", response);
+ return;
+ }
+ if (write_mbr_partition(dev_desc, download_buffer)) {
+ printf("%s: writing MBR partition failed\n", __func__);
+ fastboot_fail("writing MBR partition failed",
+ response);
+ return;
+ }
+ printf("........ success\n");
+ fastboot_okay(NULL, response);
+ return;
+ }
+#endif
+
+#ifdef CONFIG_ANDROID_BOOT_IMAGE
+ if (strncasecmp(cmd, "zimage", 6) == 0) {
+ fb_mmc_update_zimage(dev_desc, download_buffer,
+ download_bytes, response);
+ return;
+ }
+#endif
+
+ if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) {
+ pr_err("cannot find partition: '%s'\n", cmd);
+ fastboot_fail("cannot find partition", response);
+ return;
+ }
+
+ if (is_sparse_image(download_buffer)) {
+ struct fb_mmc_sparse sparse_priv;
+ struct sparse_storage sparse;
+ int err;
+
+ sparse_priv.dev_desc = dev_desc;
+
+ sparse.blksz = info.blksz;
+ sparse.start = info.start;
+ sparse.size = info.size;
+ sparse.write = fb_mmc_sparse_write;
+ sparse.reserve = fb_mmc_sparse_reserve;
+ sparse.mssg = fastboot_fail;
+
+ printf("Flashing sparse image at offset " LBAFU "\n",
+ sparse.start);
+
+ sparse.priv = &sparse_priv;
+ err = write_sparse_image(&sparse, cmd, download_buffer,
+ response);
+ if (!err)
+ fastboot_okay(NULL, response);
+ } else {
+ write_raw_image(dev_desc, &info, cmd, download_buffer,
+ download_bytes, response);
+ }
+}
+
+/**
+ * fastboot_mmc_flash_erase() - Erase eMMC for fastboot
+ *
+ * @cmd: Named partition to erase
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_mmc_erase(const char *cmd, char *response)
+{
+ int ret;
+ struct blk_desc *dev_desc;
+ disk_partition_t info;
+ lbaint_t blks, blks_start, blks_size, grp_size;
+ struct mmc *mmc = find_mmc_device(CONFIG_FASTBOOT_FLASH_MMC_DEV);
+
+ if (mmc == NULL) {
+ pr_err("invalid mmc device\n");
+ fastboot_fail("invalid mmc device", response);
+ return;
+ }
+
+ dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
+ if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
+ pr_err("invalid mmc device\n");
+ fastboot_fail("invalid mmc device", response);
+ return;
+ }
+
+ ret = part_get_info_by_name_or_alias(dev_desc, cmd, &info);
+ if (ret < 0) {
+ pr_err("cannot find partition: '%s'\n", cmd);
+ fastboot_fail("cannot find partition", response);
+ return;
+ }
+
+ /* Align blocks to erase group size to avoid erasing other partitions */
+ grp_size = mmc->erase_grp_size;
+ blks_start = (info.start + grp_size - 1) & ~(grp_size - 1);
+ if (info.size >= grp_size)
+ blks_size = (info.size - (blks_start - info.start)) &
+ (~(grp_size - 1));
+ else
+ blks_size = 0;
+
+ printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n",
+ blks_start, 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);
+ return;
+ }
+
+ printf("........ erased " LBAFU " bytes from '%s'\n",
+ blks_size * info.blksz, cmd);
+ fastboot_okay(NULL, response);
+}
diff --git a/drivers/fastboot/fb_nand.c b/drivers/fastboot/fb_nand.c
new file mode 100644
index 0000000000..526bc12307
--- /dev/null
+++ b/drivers/fastboot/fb_nand.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2014 Broadcom Corporation.
+ * Copyright 2015 Free Electrons.
+ */
+
+#include <config.h>
+#include <common.h>
+
+#include <fastboot.h>
+#include <image-sparse.h>
+
+#include <linux/mtd/mtd.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+
+struct fb_nand_sparse {
+ struct mtd_info *mtd;
+ struct part_info *part;
+};
+
+__weak int board_fastboot_erase_partition_setup(char *name)
+{
+ return 0;
+}
+
+__weak int board_fastboot_write_partition_setup(char *name)
+{
+ return 0;
+}
+
+static int fb_nand_lookup(const char *partname,
+ struct mtd_info **mtd,
+ struct part_info **part,
+ char *response)
+{
+ struct mtd_device *dev;
+ int ret;
+ u8 pnum;
+
+ ret = mtdparts_init();
+ if (ret) {
+ pr_err("Cannot initialize MTD partitions\n");
+ fastboot_fail("cannot init mtdparts", response);
+ return ret;
+ }
+
+ ret = find_dev_and_part(partname, &dev, &pnum, part);
+ if (ret) {
+ pr_err("cannot find partition: '%s'", partname);
+ fastboot_fail("cannot find partition", response);
+ return ret;
+ }
+
+ if (dev->id->type != MTD_DEV_TYPE_NAND) {
+ pr_err("partition '%s' is not stored on a NAND device",
+ partname);
+ fastboot_fail("not a NAND device", response);
+ return -EINVAL;
+ }
+
+ *mtd = get_nand_dev_by_index(dev->id->num);
+
+ return 0;
+}
+
+static int _fb_nand_erase(struct mtd_info *mtd, struct part_info *part)
+{
+ nand_erase_options_t opts;
+ int ret;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.offset = part->offset;
+ opts.length = part->size;
+ opts.quiet = 1;
+
+ printf("Erasing blocks 0x%llx to 0x%llx\n",
+ part->offset, part->offset + part->size);
+
+ ret = nand_erase_opts(mtd, &opts);
+ if (ret)
+ return ret;
+
+ printf("........ erased 0x%llx bytes from '%s'\n",
+ part->size, part->name);
+
+ return 0;
+}
+
+static int _fb_nand_write(struct mtd_info *mtd, struct part_info *part,
+ void *buffer, u32 offset,
+ size_t length, size_t *written)
+{
+ int flags = WITH_WR_VERIFY;
+
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS
+ flags |= WITH_DROP_FFS;
+#endif
+
+ return nand_write_skip_bad(mtd, offset, &length, written,
+ part->size - (offset - part->offset),
+ buffer, flags);
+}
+
+static lbaint_t fb_nand_sparse_write(struct sparse_storage *info,
+ lbaint_t blk, lbaint_t blkcnt, const void *buffer)
+{
+ struct fb_nand_sparse *sparse = info->priv;
+ size_t written;
+ int ret;
+
+ ret = _fb_nand_write(sparse->mtd, sparse->part, (void *)buffer,
+ blk * info->blksz,
+ blkcnt * info->blksz, &written);
+ if (ret < 0) {
+ printf("Failed to write sparse chunk\n");
+ return ret;
+ }
+
+/* TODO - verify that the value "written" includes the "bad-blocks" ... */
+
+ /*
+ * the return value must be 'blkcnt' ("good-blocks") plus the
+ * number of "bad-blocks" encountered within this space...
+ */
+ return written / info->blksz;
+}
+
+static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info,
+ lbaint_t blk, lbaint_t blkcnt)
+{
+ int bad_blocks = 0;
+
+/*
+ * TODO - implement a function to determine the total number
+ * of blocks which must be used in order to reserve the specified
+ * number ("blkcnt") of "good-blocks", starting at "blk"...
+ * ( possibly something like the "check_skip_len()" function )
+ */
+
+ /*
+ * the return value must be 'blkcnt' ("good-blocks") plus the
+ * number of "bad-blocks" encountered within this space...
+ */
+ return blkcnt + bad_blocks;
+}
+
+/**
+ * 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
+ * @download_buffer: Pointer to image data
+ * @download_bytes: Size of image data
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_nand_flash_write(const char *cmd, void *download_buffer,
+ u32 download_bytes, char *response)
+{
+ struct part_info *part;
+ struct mtd_info *mtd = NULL;
+ int ret;
+
+ ret = fb_nand_lookup(cmd, &mtd, &part, response);
+ if (ret) {
+ pr_err("invalid NAND device");
+ fastboot_fail("invalid NAND device", response);
+ return;
+ }
+
+ ret = board_fastboot_write_partition_setup(part->name);
+ if (ret)
+ return;
+
+ if (is_sparse_image(download_buffer)) {
+ struct fb_nand_sparse sparse_priv;
+ struct sparse_storage sparse;
+
+ sparse_priv.mtd = mtd;
+ sparse_priv.part = part;
+
+ sparse.blksz = mtd->writesize;
+ sparse.start = part->offset / sparse.blksz;
+ sparse.size = part->size / sparse.blksz;
+ sparse.write = fb_nand_sparse_write;
+ sparse.reserve = fb_nand_sparse_reserve;
+ sparse.mssg = fastboot_fail;
+
+ printf("Flashing sparse image at offset " LBAFU "\n",
+ sparse.start);
+
+ sparse.priv = &sparse_priv;
+ ret = write_sparse_image(&sparse, cmd, download_buffer,
+ response);
+ if (!ret)
+ fastboot_okay(NULL, response);
+ } else {
+ printf("Flashing raw image at offset 0x%llx\n",
+ part->offset);
+
+ ret = _fb_nand_write(mtd, part, download_buffer, part->offset,
+ download_bytes, NULL);
+
+ printf("........ wrote %u bytes to '%s'\n",
+ download_bytes, part->name);
+ }
+
+ if (ret) {
+ fastboot_fail("error writing the image", response);
+ return;
+ }
+
+ fastboot_okay(NULL, response);
+}
+
+/**
+ * fastboot_nand_flash_erase() - Erase NAND for fastboot
+ *
+ * @cmd: Named device to erase
+ * @response: Pointer to fastboot response buffer
+ */
+void fastboot_nand_erase(const char *cmd, char *response)
+{
+ struct part_info *part;
+ struct mtd_info *mtd = NULL;
+ int ret;
+
+ ret = fb_nand_lookup(cmd, &mtd, &part, response);
+ if (ret) {
+ pr_err("invalid NAND device");
+ fastboot_fail("invalid NAND device", response);
+ return;
+ }
+
+ ret = board_fastboot_erase_partition_setup(part->name);
+ if (ret)
+ return;
+
+ ret = _fb_nand_erase(mtd, part);
+ if (ret) {
+ pr_err("failed erasing from device %s", mtd->name);
+ fastboot_fail("failed erasing from device", response);
+ return;
+ }
+
+ fastboot_okay(NULL, response);
+}
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 3acadae8b1..3ad4346f2d 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -18,16 +18,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include <linux/compiler.h>
-#include <version.h>
#include <g_dnl.h>
-#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
-#include <fb_mmc.h>
-#endif
-#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
-#include <fb_nand.h>
-#endif
-
-#define FASTBOOT_VERSION "0.4"
#define FASTBOOT_INTERFACE_CLASS 0xff
#define FASTBOOT_INTERFACE_SUB_CLASS 0x42
@@ -58,8 +49,6 @@ static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
}
static struct f_fastboot *fastboot_func;
-static unsigned int download_size;
-static unsigned int download_bytes;
static struct usb_endpoint_descriptor fs_ep_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -147,22 +136,6 @@ static struct usb_gadget_strings *fastboot_strings[] = {
};
static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
-static int strcmp_l1(const char *s1, const char *s2);
-
-
-static char *fb_response_str;
-
-void fastboot_fail(const char *reason)
-{
- strncpy(fb_response_str, "FAIL\0", 5);
- strncat(fb_response_str, reason, FASTBOOT_RESPONSE_LEN - 4 - 1);
-}
-
-void fastboot_okay(const char *reason)
-{
- strncpy(fb_response_str, "OKAY\0", 5);
- strncat(fb_response_str, reason, FASTBOOT_RESPONSE_LEN - 4 - 1);
-}
static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -372,90 +345,9 @@ static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
do_reset(NULL, 0, 0, NULL);
}
-int __weak fb_set_reboot_flag(void)
-{
- return -ENOSYS;
-}
-
-static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
-{
- char *cmd = req->buf;
- if (!strcmp_l1("reboot-bootloader", cmd)) {
- if (fb_set_reboot_flag()) {
- fastboot_tx_write_str("FAILCannot set reboot flag");
- return;
- }
- }
- fastboot_func->in_req->complete = compl_do_reset;
- fastboot_tx_write_str("OKAY");
-}
-
-static int strcmp_l1(const char *s1, const char *s2)
-{
- if (!s1 || !s2)
- return -1;
- return strncmp(s1, s2, strlen(s1));
-}
-
-static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
-{
- char *cmd = req->buf;
- char response[FASTBOOT_RESPONSE_LEN];
- const char *s;
- size_t chars_left;
-
- strcpy(response, "OKAY");
- chars_left = sizeof(response) - strlen(response) - 1;
-
- strsep(&cmd, ":");
- if (!cmd) {
- pr_err("missing variable");
- fastboot_tx_write_str("FAILmissing var");
- return;
- }
-
- if (!strcmp_l1("version", cmd)) {
- strncat(response, FASTBOOT_VERSION, chars_left);
- } else if (!strcmp_l1("bootloader-version", cmd)) {
- strncat(response, U_BOOT_VERSION, chars_left);
- } else if (!strcmp_l1("downloadsize", cmd) ||
- !strcmp_l1("max-download-size", cmd)) {
- char str_num[12];
-
- sprintf(str_num, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE);
- strncat(response, str_num, chars_left);
- } else if (!strcmp_l1("serialno", cmd)) {
- s = env_get("serial#");
- if (s)
- strncat(response, s, chars_left);
- else
- strcpy(response, "FAILValue not set");
- } else {
- char *envstr;
-
- envstr = malloc(strlen("fastboot.") + strlen(cmd) + 1);
- if (!envstr) {
- fastboot_tx_write_str("FAILmalloc error");
- return;
- }
-
- sprintf(envstr, "fastboot.%s", cmd);
- s = env_get(envstr);
- if (s) {
- strncat(response, s, chars_left);
- } else {
- printf("WARNING: unknown variable: %s\n", cmd);
- strcpy(response, "FAILVariable not implemented");
- }
-
- free(envstr);
- }
- fastboot_tx_write_str(response);
-}
-
static unsigned int rx_bytes_expected(struct usb_ep *ep)
{
- int rx_remain = download_size - download_bytes;
+ int rx_remain = fastboot_data_remaining();
unsigned int rem;
unsigned int maxpacket = ep->maxpacket;
@@ -477,14 +369,12 @@ static unsigned int rx_bytes_expected(struct usb_ep *ep)
return rx_remain;
}
-#define BYTES_PER_DOT 0x20000
static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
{
- char response[FASTBOOT_RESPONSE_LEN];
- unsigned int transfer_size = download_size - download_bytes;
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+ unsigned int transfer_size = fastboot_data_remaining();
const unsigned char *buffer = req->buf;
unsigned int buffer_size = req->actual;
- unsigned int pre_dot_num, now_dot_num;
if (req->status != 0) {
printf("Bad status: %d\n", req->status);
@@ -494,33 +384,19 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
if (buffer_size < transfer_size)
transfer_size = buffer_size;
- memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes,
- buffer, transfer_size);
-
- pre_dot_num = download_bytes / BYTES_PER_DOT;
- download_bytes += transfer_size;
- now_dot_num = download_bytes / BYTES_PER_DOT;
-
- if (pre_dot_num != now_dot_num) {
- putc('.');
- if (!(now_dot_num % 74))
- putc('\n');
- }
+ fastboot_data_download(buffer, transfer_size, response);
+ if (response[0]) {
+ fastboot_tx_write_str(response);
+ } else if (!fastboot_data_remaining()) {
+ fastboot_data_complete(response);
- /* Check if transfer is done */
- if (download_bytes >= download_size) {
/*
- * Reset global transfer variable, keep download_bytes because
- * it will be used in the next possible flashing command
+ * Reset global transfer variable
*/
- download_size = 0;
req->complete = rx_handler_command;
req->length = EP_BUFFER_SIZE;
- strcpy(response, "OKAY");
fastboot_tx_write_str(response);
-
- printf("\ndownloading of %d bytes finished\n", download_bytes);
} else {
req->length = rx_bytes_expected(ep);
}
@@ -529,204 +405,55 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
usb_ep_queue(ep, req, 0);
}
-static void cb_download(struct usb_ep *ep, struct usb_request *req)
-{
- char *cmd = req->buf;
- char response[FASTBOOT_RESPONSE_LEN];
-
- strsep(&cmd, ":");
- download_size = simple_strtoul(cmd, NULL, 16);
- download_bytes = 0;
-
- printf("Starting download of %d bytes\n", download_size);
-
- if (0 == download_size) {
- strcpy(response, "FAILdata invalid size");
- } else if (download_size > CONFIG_FASTBOOT_BUF_SIZE) {
- download_size = 0;
- strcpy(response, "FAILdata too large");
- } else {
- sprintf(response, "DATA%08x", download_size);
- req->complete = rx_handler_dl_image;
- req->length = rx_bytes_expected(ep);
- }
- fastboot_tx_write_str(response);
-}
-
-static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
-{
- char boot_addr_start[12];
- char *bootm_args[] = { "bootm", boot_addr_start, NULL };
-
- puts("Booting kernel..\n");
-
- sprintf(boot_addr_start, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR);
- do_bootm(NULL, 0, 2, bootm_args);
-
- /* This only happens if image is somehow faulty so we start over */
- do_reset(NULL, 0, 0, NULL);
-}
-
-static void cb_boot(struct usb_ep *ep, struct usb_request *req)
-{
- fastboot_func->in_req->complete = do_bootm_on_complete;
- fastboot_tx_write_str("OKAY");
-}
-
static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req)
{
g_dnl_trigger_detach();
}
-static void cb_continue(struct usb_ep *ep, struct usb_request *req)
+static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
{
- fastboot_func->in_req->complete = do_exit_on_complete;
- fastboot_tx_write_str("OKAY");
+ fastboot_boot();
+ do_exit_on_complete(ep, req);
}
-#ifdef CONFIG_FASTBOOT_FLASH
-static void cb_flash(struct usb_ep *ep, struct usb_request *req)
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
{
- char *cmd = req->buf;
- char response[FASTBOOT_RESPONSE_LEN];
+ char *cmdbuf = req->buf;
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+ int cmd = -1;
- strsep(&cmd, ":");
- if (!cmd) {
- pr_err("missing partition name");
- fastboot_tx_write_str("FAILmissing partition name");
+ if (req->status != 0 || req->length == 0)
return;
- }
-
- /* initialize the response buffer */
- fb_response_str = response;
-
- fastboot_fail("no flash device defined");
-#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
- fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
- download_bytes);
-#endif
-#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
- fb_nand_flash_write(cmd,
- (void *)CONFIG_FASTBOOT_BUF_ADDR,
- download_bytes);
-#endif
- fastboot_tx_write_str(response);
-}
-#endif
-static void cb_oem(struct usb_ep *ep, struct usb_request *req)
-{
- char *cmd = req->buf;
-#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
- if (strncmp("format", cmd + 4, 6) == 0) {
- char cmdbuf[32];
- sprintf(cmdbuf, "gpt write mmc %x $partitions",
- CONFIG_FASTBOOT_FLASH_MMC_DEV);
- if (run_command(cmdbuf, 0))
- fastboot_tx_write_str("FAIL");
- else
- fastboot_tx_write_str("OKAY");
- } else
-#endif
- if (strncmp("unlock", cmd + 4, 8) == 0) {
- fastboot_tx_write_str("FAILnot implemented");
- }
- else {
- fastboot_tx_write_str("FAILunknown oem command");
+ if (req->actual < req->length) {
+ cmdbuf[req->actual] = '\0';
+ cmd = fastboot_handle_command(cmdbuf, response);
+ } else {
+ pr_err("buffer overflow");
+ fastboot_fail("buffer overflow", response);
}
-}
-
-#ifdef CONFIG_FASTBOOT_FLASH
-static void cb_erase(struct usb_ep *ep, struct usb_request *req)
-{
- char *cmd = req->buf;
- char response[FASTBOOT_RESPONSE_LEN];
- strsep(&cmd, ":");
- if (!cmd) {
- pr_err("missing partition name");
- fastboot_tx_write_str("FAILmissing partition name");
- return;
+ if (!strncmp("DATA", response, 4)) {
+ req->complete = rx_handler_dl_image;
+ req->length = rx_bytes_expected(ep);
}
- /* initialize the response buffer */
- fb_response_str = response;
-
- fastboot_fail("no flash device defined");
-#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
- fb_mmc_erase(cmd);
-#endif
-#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
- fb_nand_erase(cmd);
-#endif
fastboot_tx_write_str(response);
-}
-#endif
-
-struct cmd_dispatch_info {
- char *cmd;
- void (*cb)(struct usb_ep *ep, struct usb_request *req);
-};
-
-static const struct cmd_dispatch_info cmd_dispatch_info[] = {
- {
- .cmd = "reboot",
- .cb = cb_reboot,
- }, {
- .cmd = "getvar:",
- .cb = cb_getvar,
- }, {
- .cmd = "download:",
- .cb = cb_download,
- }, {
- .cmd = "boot",
- .cb = cb_boot,
- }, {
- .cmd = "continue",
- .cb = cb_continue,
- },
-#ifdef CONFIG_FASTBOOT_FLASH
- {
- .cmd = "flash",
- .cb = cb_flash,
- }, {
- .cmd = "erase",
- .cb = cb_erase,
- },
-#endif
- {
- .cmd = "oem",
- .cb = cb_oem,
- },
-};
-
-static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
-{
- char *cmdbuf = req->buf;
- void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
- int i;
- if (req->status != 0 || req->length == 0)
- return;
+ if (!strncmp("OKAY", response, 4)) {
+ switch (cmd) {
+ case FASTBOOT_COMMAND_BOOT:
+ fastboot_func->in_req->complete = do_bootm_on_complete;
+ break;
- for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
- if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
- func_cb = cmd_dispatch_info[i].cb;
+ case FASTBOOT_COMMAND_CONTINUE:
+ fastboot_func->in_req->complete = do_exit_on_complete;
break;
- }
- }
- if (!func_cb) {
- pr_err("unknown command: %.*s", req->actual, cmdbuf);
- fastboot_tx_write_str("FAILunknown command");
- } else {
- if (req->actual < req->length) {
- u8 *buf = (u8 *)req->buf;
- buf[req->actual] = 0;
- func_cb(ep, req);
- } else {
- pr_err("buffer overflow");
- fastboot_tx_write_str("FAILbuffer overflow");
+ case FASTBOOT_COMMAND_REBOOT:
+ case FASTBOOT_COMMAND_REBOOT_BOOTLOADER:
+ fastboot_func->in_req->complete = compl_do_reset;
+ break;
}
}
diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c
index c8eda05832..1aa6be44bb 100644
--- a/drivers/usb/gadget/f_thor.c
+++ b/drivers/usb/gadget/f_thor.c
@@ -620,22 +620,6 @@ static void thor_rx_tx_complete(struct usb_ep *ep, struct usb_request *req)
status, req->actual, req->length);
}
-static struct usb_request *thor_start_ep(struct usb_ep *ep)
-{
- struct usb_request *req;
-
- req = alloc_ep_req(ep, THOR_PACKET_SIZE);
- debug("%s: ep:%p req:%p\n", __func__, ep, req);
-
- if (!req)
- return NULL;
-
- memset(req->buf, 0, req->length);
- req->complete = thor_rx_tx_complete;
-
- return req;
-}
-
static void thor_setup_complete(struct usb_ep *ep, struct usb_request *req)
{
if (req->status || req->actual != req->length)
@@ -752,6 +736,13 @@ int thor_handle(void)
return 0;
}
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->buf)
+ free(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
static int thor_func_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_gadget *gadget = c->cdev->gadget;
@@ -860,21 +851,18 @@ static int thor_func_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
fail:
+ if (dev->req)
+ free_ep_req(gadget->ep0, dev->req);
free(dev);
return status;
}
-static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
- free(req->buf);
- usb_ep_free_request(ep, req);
-}
-
static void thor_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_thor *f_thor = func_to_thor(f);
struct thor_dev *dev = f_thor->dev;
+ free_ep_req(dev->gadget->ep0, dev->req);
free(dev);
memset(thor_func, 0, sizeof(*thor_func));
thor_func = NULL;
@@ -895,8 +883,6 @@ static void thor_func_disable(struct usb_function *f)
}
if (dev->out_ep->driver_data) {
- free(dev->out_req->buf);
- dev->out_req->buf = NULL;
usb_ep_free_request(dev->out_ep, dev->out_req);
usb_ep_disable(dev->out_ep);
dev->out_ep->driver_data = NULL;
@@ -924,16 +910,17 @@ static int thor_eps_setup(struct usb_function *f)
result = usb_ep_enable(ep, d);
if (result)
- goto exit;
+ goto err;
ep->driver_data = cdev; /* claim */
- req = thor_start_ep(ep);
+ req = alloc_ep_req(ep, THOR_PACKET_SIZE);
if (!req) {
- usb_ep_disable(ep);
result = -EIO;
- goto exit;
+ goto err_disable_in_ep;
}
+ memset(req->buf, 0, req->length);
+ req->complete = thor_rx_tx_complete;
dev->in_req = req;
ep = dev->out_ep;
d = ep_desc(gadget, &hs_out_desc, &fs_out_desc);
@@ -941,22 +928,34 @@ static int thor_eps_setup(struct usb_function *f)
result = usb_ep_enable(ep, d);
if (result)
- goto exit;
+ goto err_free_in_req;
ep->driver_data = cdev; /* claim */
- req = thor_start_ep(ep);
+ req = usb_ep_alloc_request(ep, 0);
if (!req) {
- usb_ep_disable(ep);
result = -EIO;
- goto exit;
+ goto err_disable_out_ep;
}
+ req->complete = thor_rx_tx_complete;
dev->out_req = req;
/* ACM control EP */
ep = dev->int_ep;
ep->driver_data = cdev; /* claim */
- exit:
+ return 0;
+
+ err_disable_out_ep:
+ usb_ep_disable(dev->out_ep);
+
+ err_free_in_req:
+ free_ep_req(dev->in_ep, dev->in_req);
+ dev->in_req = NULL;
+
+ err_disable_in_ep:
+ usb_ep_disable(dev->in_ep);
+
+ err:
return result;
}
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index a837afc483..f2e91ef0fe 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -117,12 +117,15 @@ err_clk:
static int xhci_rcar_deregister(struct udevice *dev)
{
+ int ret;
struct rcar_xhci_platdata *plat = dev_get_platdata(dev);
+ ret = xhci_deregister(dev);
+
clk_disable(&plat->clk);
clk_free(&plat->clk);
- return xhci_deregister(dev);
+ return ret;
}
static int xhci_rcar_ofdata_to_platdata(struct udevice *dev)
diff --git a/drivers/usb/host/xhci-rockchip.c b/drivers/usb/host/xhci-rockchip.c
index 060a6c4309..f19bea3a91 100644
--- a/drivers/usb/host/xhci-rockchip.c
+++ b/drivers/usb/host/xhci-rockchip.c
@@ -17,7 +17,6 @@
struct rockchip_xhci_platdata {
fdt_addr_t hcd_base;
- fdt_addr_t phy_base;
struct udevice *vbus_supply;
};
@@ -35,7 +34,6 @@ struct rockchip_xhci {
static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
{
struct rockchip_xhci_platdata *plat = dev_get_platdata(dev);
- struct udevice *child;
int ret = 0;
/*
@@ -47,20 +45,6 @@ static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
return -ENXIO;
}
- /* Get the base address for usbphy from the device node */
- for (device_find_first_child(dev, &child); child;
- device_find_next_child(&child)) {
- if (!device_is_compatible(child, "rockchip,rk3399-usb3-phy"))
- continue;
- plat->phy_base = devfdt_get_addr(child);
- break;
- }
-
- if (plat->phy_base == FDT_ADDR_T_NONE) {
- pr_err("Can't get the usbphy register address\n");
- return -ENXIO;
- }
-
/* Vbus regulator */
ret = device_get_supply_regulator(dev, "vbus-supply",
&plat->vbus_supply);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 3adb0028f2..9ded14cc3c 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -536,7 +536,7 @@ static int xhci_set_configuration(struct usb_device *udev)
/* slot context */
xhci_slot_copy(ctrl, in_ctx, out_ctx);
slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
- slot_ctx->dev_info &= ~(LAST_CTX_MASK);
+ slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK));
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0);
xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0);
@@ -1424,7 +1424,7 @@ static int xhci_update_hub_device(struct udevice *dev, struct usb_device *udev)
ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
/* Initialize the input context control */
- ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
+ ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
ctrl_ctx->drop_flags = 0;
xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
@@ -1435,8 +1435,15 @@ static int xhci_update_hub_device(struct udevice *dev, struct usb_device *udev)
/* Update hub related fields */
slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
- if (hub->tt.multi && udev->speed == USB_SPEED_HIGH)
+ /*
+ * refer to section 6.2.2: MTT should be 0 for full speed hub,
+ * but it may be already set to 1 when setup an xHCI virtual
+ * device, so clear it anyway.
+ */
+ if (hub->tt.multi)
slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+ else if (udev->speed == USB_SPEED_FULL)
+ slot_ctx->dev_info &= cpu_to_le32(~DEV_MTT);
slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(udev->maxchild));
/*
* Set TT think time - convert from ns to FS bit times.
@@ -1452,6 +1459,7 @@ static int xhci_update_hub_device(struct udevice *dev, struct usb_device *udev)
think_time = (think_time / 666) - 1;
if (udev->speed == USB_SPEED_HIGH)
slot_ctx->tt_info |= cpu_to_le32(TT_THINK_TIME(think_time));
+ slot_ctx->dev_state = 0;
return xhci_configure_endpoints(udev, false);
}