fastboot: add UUU command UCmd and ACmd support

add support for the UUU commands ACmd and UCmd.

Enable them through the Kconfig option
CONFIG_FASTBOOT_UUU_SUPPORT

base was commit in NXP kernel
9b149c2a2882: ("MLK-18591-3 android: Add FSL android fastboot support")

and ported it to current mainline. Tested this patch
on imx6ul based board.

Signed-off-by: Heiko Schocher <hs@denx.de>
Acked-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
diff --git a/doc/android/fastboot-protocol.rst b/doc/android/fastboot-protocol.rst
index e723659..e8cbd7f 100644
--- a/doc/android/fastboot-protocol.rst
+++ b/doc/android/fastboot-protocol.rst
@@ -144,6 +144,11 @@
 
   "powerdown"          Power off the device.
 
+  "ucmd"               execute any bootloader command and wait until it
+                       finishs.
+
+  "acmd"               execute any bootloader command, do not wait.
+
 Client Variables
 ----------------
 
diff --git a/doc/android/fastboot.rst b/doc/android/fastboot.rst
index ce513a2..7611f07 100644
--- a/doc/android/fastboot.rst
+++ b/doc/android/fastboot.rst
@@ -19,6 +19,8 @@
 - ``reboot``
 - ``reboot-bootloader``
 - ``set_active`` (only a stub implementation which always succeeds)
+- ``ucmd`` (if enabled)
+- ``acmd`` (if enabled)
 
 The following OEM commands are supported (if enabled):
 
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
index a17e488..2d1836a 100644
--- a/drivers/fastboot/Kconfig
+++ b/drivers/fastboot/Kconfig
@@ -72,6 +72,15 @@
 	  the downloaded image to a non-volatile storage device. Define
 	  this to enable the "fastboot flash" command.
 
+config FASTBOOT_UUU_SUPPORT
+	bool "Enable FASTBOOT i.MX UUU special command"
+	default n
+	help
+	  The fastboot protocol includes "UCmd" and "ACmd" command.
+	  Be aware that you provide full access to any U-Boot command,
+	  including working with memory and may open a huge backdoor,
+	  when enabling this option.
+
 choice
 	prompt "Flash provider for FASTBOOT"
 	depends on FASTBOOT_FLASH
diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
index 41fc8d7..3a5db5b 100644
--- a/drivers/fastboot/fb_command.c
+++ b/drivers/fastboot/fb_command.c
@@ -49,6 +49,11 @@
 static void oem_bootbus(char *, char *);
 #endif
 
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+static void run_ucmd(char *, char *);
+static void run_acmd(char *, char *);
+#endif
+
 static const struct {
 	const char *command;
 	void (*dispatch)(char *cmd_parameter, char *response);
@@ -117,6 +122,16 @@
 		.dispatch = oem_bootbus,
 	},
 #endif
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+	[FASTBOOT_COMMAND_UCMD] = {
+		.command = "UCmd",
+		.dispatch = run_ucmd,
+	},
+	[FASTBOOT_COMMAND_ACMD] = {
+		.command = "ACmd",
+		.dispatch = run_acmd,
+	},
+#endif
 };
 
 /**
@@ -327,6 +342,59 @@
 }
 #endif
 
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+/**
+ * run_ucmd() - Execute the UCmd command
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @response: Pointer to fastboot response buffer
+ */
+static void run_ucmd(char *cmd_parameter, char *response)
+{
+	if (!cmd_parameter) {
+		pr_err("missing slot suffix\n");
+		fastboot_fail("missing command", response);
+		return;
+	}
+
+	if (run_command(cmd_parameter, 0))
+		fastboot_fail("", response);
+	else
+		fastboot_okay(NULL, response);
+}
+
+static char g_a_cmd_buff[64];
+
+void fastboot_acmd_complete(void)
+{
+	run_command(g_a_cmd_buff, 0);
+}
+
+/**
+ * run_acmd() - Execute the ACmd command
+ *
+ * @cmd_parameter: Pointer to command parameter
+ * @response: Pointer to fastboot response buffer
+ */
+static void run_acmd(char *cmd_parameter, char *response)
+{
+	if (!cmd_parameter) {
+		pr_err("missing slot suffix\n");
+		fastboot_fail("missing command", response);
+		return;
+	}
+
+	if (strlen(cmd_parameter) > sizeof(g_a_cmd_buff)) {
+		pr_err("too long command\n");
+		fastboot_fail("too long command", response);
+		return;
+	}
+
+	strcpy(g_a_cmd_buff, cmd_parameter);
+	fastboot_okay(NULL, response);
+}
+#endif
+
 /**
  * reboot_bootloader() - Sets reboot bootloader flag.
  *
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 950cc11..8ba55aa 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -494,6 +494,18 @@
 	do_exit_on_complete(ep, req);
 }
 
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+static void do_acmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* When usb dequeue complete will be called
+	 *  Need status value before call run_command.
+	 * otherwise, host can't get last message.
+	 */
+	if (req->status == 0)
+		fastboot_acmd_complete();
+}
+#endif
+
 static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
 {
 	char *cmdbuf = req->buf;
@@ -532,6 +544,11 @@
 		case FASTBOOT_COMMAND_REBOOT_RECOVERY:
 			fastboot_func->in_req->complete = compl_do_reset;
 			break;
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+		case FASTBOOT_COMMAND_ACMD:
+			fastboot_func->in_req->complete = do_acmd_complete;
+			break;
+#endif
 		}
 	}
 
diff --git a/include/fastboot.h b/include/fastboot.h
index 797d7df..57daaf1 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -44,6 +44,10 @@
 #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_BOOTBUS)
 	FASTBOOT_COMMAND_OEM_BOOTBUS,
 #endif
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+	FASTBOOT_COMMAND_ACMD,
+	FASTBOOT_COMMAND_UCMD,
+#endif
 
 	FASTBOOT_COMMAND_COUNT
 };
@@ -169,4 +173,7 @@
  */
 void fastboot_data_complete(char *response);
 
+#if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT)
+void fastboot_acmd_complete(void);
+#endif
 #endif /* _FASTBOOT_H_ */