mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/10] Support for Fastboot over UDP
@ 2020-05-14 18:21 Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 01/10] Remove CONFIG_SLICE Daniel Glöckner
                   ` (9 more replies)
  0 siblings, 10 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

This v2 of the Fastboot over UDP code is based on the slice patch series
posted on April 22. Strictly necessary for Fastboot over UDP are only the
patches 4, 6, 8, and 9. The others touch related code but can be omitted
if desired.

Changes since v1:
- Add idle slice to execute commands when barebox is idle
- Use second poller to send keep-alive messages
- Share all settings in global variables with USB gadget
  and move them to a protocol agnostic variable namespace
- Use eth_open function added recently
- Minor cleanups

Daniel Glöckner (8):
  Remove CONFIG_SLICE
  net: fixed-link phys are never acquired
  poller: adapt remaining users to API change
  Introduce idle slice
  ratp: use poller to run ratp commands
  defconfigs: update renamed fastboot options
  fastboot: rename usbgadget.fastboot_* variables to fastboot.*
  fastboot: don't close fd 0 when downloading to ram

Edmund Henniges (2):
  fastboot: split generic code from USB gadget
  fastboot net: implement fastboot over UDP

 Documentation/user/usb.rst               |   4 +-
 arch/arm/boards/mioa701/gpio0_poweroff.c |   2 +-
 arch/arm/boards/sama5d3xek/init.c        |   2 +-
 arch/arm/configs/imx23_defconfig         |   2 +-
 arch/arm/configs/imx28_defconfig         |   2 +-
 arch/arm/configs/imx_v7_defconfig        |   2 +-
 arch/arm/configs/imx_v8_defconfig        |   2 +-
 arch/arm/configs/kindle-mx50_defconfig   |   2 +-
 arch/arm/configs/omap_defconfig          |   2 +-
 arch/arm/configs/zii_vf610_dev_defconfig |   2 +-
 commands/Kconfig                         |   1 -
 commands/usbgadget.c                     |   2 +-
 common/Kconfig                           |  46 +-
 common/Makefile                          |   1 +
 common/binfmt.c                          |   3 +
 common/command.c                         |   3 +
 common/console_countdown.c               |   3 +
 common/fastboot.c                        | 972 +++++++++++++++++++++++
 common/poller.c                          |   2 +
 common/ratp/ratp.c                       |  12 +-
 common/slice.c                           |  24 +
 common/usbgadget.c                       |  16 +-
 drivers/net/usb/ax88179_178a.c           |   2 +-
 drivers/usb/gadget/Kconfig               |  36 +-
 drivers/usb/gadget/f_fastboot.c          | 970 +---------------------
 drivers/usb/gadget/multi.c               |   5 +-
 include/fastboot.h                       |  70 ++
 include/fastboot_net.h                   |  12 +
 include/linux/phy.h                      |   2 +-
 include/ratp_bb.h                        |   1 -
 include/slice.h                          |  10 +
 include/usb/fastboot.h                   |  34 +-
 include/usb/gadget-multi.h               |   2 +-
 lib/Kconfig                              |   1 +
 lib/readline.c                           |   9 +-
 net/Kconfig                              |  18 +
 net/Makefile                             |   1 +
 net/fastboot.c                           | 496 ++++++++++++
 38 files changed, 1738 insertions(+), 1038 deletions(-)
 create mode 100644 common/fastboot.c
 create mode 100644 include/fastboot.h
 create mode 100644 include/fastboot_net.h
 create mode 100644 net/fastboot.c

-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 01/10] Remove CONFIG_SLICE
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 02/10] net: fixed-link phys are never acquired Daniel Glöckner
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

Since we are unconditionally compiling and linking the slice code, it does
not make sense to have a config option that depends on CONFIG_POLLER.
The CONFIG_SLICE option was also broken because it was missing the "bool"
line to denote its type.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 commands/Kconfig | 1 -
 common/Kconfig   | 4 ----
 2 files changed, 5 deletions(-)

diff --git a/commands/Kconfig b/commands/Kconfig
index 5ff645442..33bc4743b 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -272,7 +272,6 @@ config CMD_POLLER
 config CMD_SLICE
 	tristate
 	prompt "slice"
-	depends on SLICE
 	help
 	  slices are a way to protect resources from being accessed by pollers. The slice
 	  command can be used to print informations about slices and also to manipulate
diff --git a/common/Kconfig b/common/Kconfig
index bd2aebac7..400c0553c 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -913,10 +913,6 @@ config BAREBOXCRC32_TARGET
 config POLLER
 	bool "generic polling infrastructure"
 
-config SLICE
-	depends on POLLER
-	default y
-
 config STATE
 	bool "generic state infrastructure"
 	select CRC32
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 02/10] net: fixed-link phys are never acquired
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 01/10] Remove CONFIG_SLICE Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 03/10] poller: adapt remaining users to API change Daniel Glöckner
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

Fixed-link phys have their bus field set to NULL. This causes a crash when
phy_acquired tries to check the slice of the bus.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 include/linux/phy.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/phy.h b/include/linux/phy.h
index e93f6a01f..3f0f31b35 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -370,7 +370,7 @@ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, int devad,
 
 static inline bool phy_acquired(struct phy_device *phydev)
 {
-	return phydev && slice_acquired(&phydev->bus->slice);
+	return phydev && phydev->bus && slice_acquired(&phydev->bus->slice);
 }
 
 #ifdef CONFIG_PHYLIB
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 03/10] poller: adapt remaining users to API change
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 01/10] Remove CONFIG_SLICE Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 02/10] net: fixed-link phys are never acquired Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 04/10] Introduce idle slice Daniel Glöckner
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

poller_register now has a second parameter.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 arch/arm/boards/mioa701/gpio0_poweroff.c | 2 +-
 arch/arm/boards/sama5d3xek/init.c        | 2 +-
 drivers/net/usb/ax88179_178a.c           | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boards/mioa701/gpio0_poweroff.c b/arch/arm/boards/mioa701/gpio0_poweroff.c
index 01a5d0cc6..279da5e0a 100644
--- a/arch/arm/boards/mioa701/gpio0_poweroff.c
+++ b/arch/arm/boards/mioa701/gpio0_poweroff.c
@@ -76,7 +76,7 @@ static struct poller_struct gpio0_poller = {
 
 static int gpio0_poweroff_probe(void)
 {
-	return poller_register(&gpio0_poller);
+	return poller_register(&gpio0_poller, "power-button");
 }
 
 device_initcall(gpio0_poweroff_probe);
diff --git a/arch/arm/boards/sama5d3xek/init.c b/arch/arm/boards/sama5d3xek/init.c
index c768e98d2..d9be4c5f9 100644
--- a/arch/arm/boards/sama5d3xek/init.c
+++ b/arch/arm/boards/sama5d3xek/init.c
@@ -408,7 +408,7 @@ static void ek_add_device_hdmi(void)
 	hdmi_reset_start = get_time_ns();
 	hdmi_poller.func = hdmi_off_poller;
 
-	poller_register(&hdmi_poller);
+	poller_register(&hdmi_poller, "hdmi-reset");
 }
 #else
 static void ek_add_device_hdmi(void)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 6fea5a7b1..fddb18714 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -409,7 +409,7 @@ static int ax88179_bind(struct usbnet *dev)
 
 	priv->last = get_time_ns();
 	priv->poller.func = ax88179_poller;
-	poller_register(&priv->poller);
+	poller_register(&priv->poller, dev_name(&dev->udev->dev));
 
 	return 0;
 }
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 04/10] Introduce idle slice
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (2 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 03/10] poller: adapt remaining users to API change Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-20  6:03   ` Sascha Hauer
  2020-05-14 18:21 ` [PATCH v2 05/10] ratp: use poller to run ratp commands Daniel Glöckner
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

Some code can't foresee which resources will be used by its poller. This
is the case especially in pollers that will execute arbitrary commands
input by the user. With this commit a special slice is introduced that is
released only when Barebox is waiting for console input and only when it
is doing that outside of any command. Code that wants to depend on the
idle slice must select CONFIG_IDLE_SLICE to make it available.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 common/Kconfig             |  4 ++++
 common/binfmt.c            |  3 +++
 common/command.c           |  3 +++
 common/console_countdown.c |  3 +++
 common/poller.c            |  2 ++
 common/slice.c             | 24 ++++++++++++++++++++++++
 include/slice.h            | 10 ++++++++++
 lib/readline.c             |  8 +++++++-
 8 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/common/Kconfig b/common/Kconfig
index 400c0553c..2fa9140de 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -913,6 +913,10 @@ config BAREBOXCRC32_TARGET
 config POLLER
 	bool "generic polling infrastructure"
 
+config IDLE_SLICE
+	bool
+	default n
+
 config STATE
 	bool "generic state infrastructure"
 	select CRC32
diff --git a/common/binfmt.c b/common/binfmt.c
index f2ff62458..899f270d4 100644
--- a/common/binfmt.c
+++ b/common/binfmt.c
@@ -10,6 +10,7 @@
 #include <malloc.h>
 #include <command.h>
 #include <errno.h>
+#include <slice.h>
 
 static LIST_HEAD(binfmt_hooks);
 
@@ -23,7 +24,9 @@ static int binfmt_run(char *file, int argc, char **argv)
 		if (b->type != type)
 			continue;
 
+		idle_slice_acquire();
 		ret = b->hook(b, file, argc, argv);
+		idle_slice_release();
 		if (ret != -ERESTARTNOHAND)
 			return ret;
 	}
diff --git a/common/command.c b/common/command.c
index 49845938a..2b10248f5 100644
--- a/common/command.c
+++ b/common/command.c
@@ -30,6 +30,7 @@
 #include <init.h>
 #include <complete.h>
 #include <getopt.h>
+#include <slice.h>
 
 LIST_HEAD(command_list);
 EXPORT_SYMBOL(command_list);
@@ -72,7 +73,9 @@ int execute_command(int argc, char **argv)
 	/* Look up command in command table */
 	if ((cmdtp = find_cmd(argv[0]))) {
 		/* OK - call function to do the command */
+		idle_slice_acquire();
 		ret = cmdtp->cmd(argc, argv);
+		idle_slice_release();
 		if (ret == COMMAND_ERROR_USAGE) {
 			barebox_cmd_usage(cmdtp);
 			ret = COMMAND_ERROR;
diff --git a/common/console_countdown.c b/common/console_countdown.c
index 8d09894c3..f161cf7df 100644
--- a/common/console_countdown.c
+++ b/common/console_countdown.c
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <console_countdown.h>
 #include <stdio.h>
+#include <slice.h>
 
 static bool console_countdown_timeout_abort;
 
@@ -59,6 +60,7 @@ int console_countdown(int timeout_s, unsigned flags, const char *keys,
 	if (!(flags & CONSOLE_COUNTDOWN_SILENT))
 		printf("%4d", countdown--);
 
+	idle_slice_release();
 	do {
 		if (tstc()) {
 			key = getchar();
@@ -91,6 +93,7 @@ int console_countdown(int timeout_s, unsigned flags, const char *keys,
 	ret = 0;
 
  out:
+	idle_slice_acquire();
 	if (!(flags & CONSOLE_COUNTDOWN_SILENT))
 		printf("\n");
 	if (key && out_key)
diff --git a/common/poller.c b/common/poller.c
index cccffcb88..60a3461b2 100644
--- a/common/poller.c
+++ b/common/poller.c
@@ -116,7 +116,9 @@ void poller_call(void)
 			continue;
 
 		slice_acquire(&poller->slice);
+		idle_slice_acquire();
 		poller->func(poller);
+		idle_slice_release();
 		slice_release(&poller->slice);
 	}
 }
diff --git a/common/slice.c b/common/slice.c
index b4460335d..960bb2ff8 100644
--- a/common/slice.c
+++ b/common/slice.c
@@ -3,6 +3,7 @@
 #define pr_fmt(fmt) "slice: " fmt
 
 #include <common.h>
+#include <init.h>
 #include <slice.h>
 
 /*
@@ -221,6 +222,29 @@ void slice_init(struct slice *slice, const char *name)
 	list_add_tail(&slice->list, &slices);
 }
 
+#ifdef CONFIG_IDLE_SLICE
+struct slice idle_slice;
+
+void idle_slice_acquire(void)
+{
+	slice_acquire(&idle_slice);
+}
+
+void idle_slice_release(void)
+{
+	slice_release(&idle_slice);
+}
+
+static int idle_slice_init(void)
+{
+	slice_init(&idle_slice, "idle");
+	slice_acquire(&idle_slice);
+	return 0;
+}
+
+pure_initcall(idle_slice_init);
+#endif
+
 #if defined CONFIG_CMD_SLICE
 
 #include <command.h>
diff --git a/include/slice.h b/include/slice.h
index 4874f1afc..068daaa7c 100644
--- a/include/slice.h
+++ b/include/slice.h
@@ -28,4 +28,14 @@ void slice_init(struct slice *slice, const char *name);
 
 void slice_debug_acquired(struct slice *slice);
 
+#ifdef CONFIG_IDLE_SLICE
+extern struct slice idle_slice;
+
+void idle_slice_acquire(void);
+void idle_slice_release(void);
+#else
+static inline void idle_slice_acquire(void) {}
+static inline void idle_slice_release(void) {}
+#endif
+
 #endif /* __SLICE_H */
diff --git a/lib/readline.c b/lib/readline.c
index 3d16c1838..709efb375 100644
--- a/lib/readline.c
+++ b/lib/readline.c
@@ -3,6 +3,7 @@
 #include <init.h>
 #include <libbb.h>
 #include <poller.h>
+#include <slice.h>
 #include <ratp_bb.h>
 #include <xfuncs.h>
 #include <complete.h>
@@ -200,11 +201,16 @@ int readline(const char *prompt, char *buf, int len)
 	puts (prompt);
 
 	while (1) {
+		idle_slice_release();
 		while (!tstc()) {
 			poller_call();
-			if (IS_ENABLED(CONFIG_CONSOLE_RATP))
+			if (IS_ENABLED(CONFIG_CONSOLE_RATP)) {
+				idle_slice_acquire();
 				barebox_ratp_command_run();
+				idle_slice_release();
+			}
 		}
+		idle_slice_acquire();
 
 		ichar = read_key();
 
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 05/10] ratp: use poller to run ratp commands
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (3 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 04/10] Introduce idle slice Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 06/10] fastboot: split generic code from USB gadget Daniel Glöckner
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

With the new idle slice there is no longer a need to have a dedicated hook
for RATP inside readline. Switch to a poller that depends on the idle
slice to execute the commands received over RATP.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 common/ratp/ratp.c | 12 +++++++++++-
 include/ratp_bb.h  |  1 -
 lib/Kconfig        |  1 +
 lib/readline.c     |  9 +--------
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c
index 9625a31b1..9b3ea0f98 100644
--- a/common/ratp/ratp.c
+++ b/common/ratp/ratp.c
@@ -53,6 +53,7 @@ struct ratp_ctx {
 	struct ratp_bb_pkt *fs_rx;
 
 	struct poller_struct poller;
+	struct poller_struct execution_poller;
 };
 
 static int compare_ratp_command(struct list_head *a, struct list_head *b)
@@ -302,7 +303,7 @@ static int ratp_console_register(struct ratp_ctx *ctx)
 	return 0;
 }
 
-void barebox_ratp_command_run(void)
+static void barebox_ratp_command_run(struct poller_struct *poller)
 {
 	int ret;
 
@@ -336,6 +337,7 @@ static void ratp_console_unregister(struct ratp_ctx *ctx)
 	int ret;
 
 	console_set_active(&ctx->ratp_console, 0);
+	poller_unregister(&ctx->execution_poller);
 	poller_unregister(&ctx->poller);
 	ratp_close(&ctx->ratp);
 	console_set_active(ctx->cdev, ctx->old_active);
@@ -436,6 +438,7 @@ int barebox_ratp(struct console_device *cdev)
 		ctx->console_recv_fifo = kfifo_alloc(512);
 		ctx->console_transmit_fifo = kfifo_alloc(SZ_128K);
 		ctx->poller.func = ratp_poller;
+		ctx->execution_poller.func = barebox_ratp_command_run;
 		ratp_console_register(ctx);
 	}
 
@@ -458,11 +461,18 @@ int barebox_ratp(struct console_device *cdev)
 	if (ret)
 		goto out1;
 
+	ret = poller_register(&ctx->execution_poller, "ratp-exec");
+	if (ret)
+		goto out2;
+
+	slice_add(&ctx->execution_poller.slice, &idle_slice);
 	console_set_active(&ctx->ratp_console, CONSOLE_STDOUT | CONSOLE_STDERR |
 			CONSOLE_STDIN);
 
 	return 0;
 
+out2:
+	poller_unregister(&ctx->poller);
 out1:
 	ratp_close(ratp);
 out:
diff --git a/include/ratp_bb.h b/include/ratp_bb.h
index a4f28c642..b710f99bf 100644
--- a/include/ratp_bb.h
+++ b/include/ratp_bb.h
@@ -41,7 +41,6 @@ struct ratp_bb_pkt {
 };
 
 int  barebox_ratp(struct console_device *cdev);
-void barebox_ratp_command_run(void);
 int  barebox_ratp_fs_call(struct ratp_bb_pkt *tx, struct ratp_bb_pkt **rx);
 int  barebox_ratp_fs_mount(const char *path);
 
diff --git a/lib/Kconfig b/lib/Kconfig
index 9a8078018..d60c6ce2d 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -108,6 +108,7 @@ config RATP
 	select COMPILE_MEMORY
 	select COMMAND_SUPPORT
 	select POLLER
+	select IDLE_SLICE
 	depends on CONSOLE_FULL
 	depends on !SHELL_NONE
 	bool "RATP protocol support"
diff --git a/lib/readline.c b/lib/readline.c
index 709efb375..79bac6f42 100644
--- a/lib/readline.c
+++ b/lib/readline.c
@@ -4,7 +4,6 @@
 #include <libbb.h>
 #include <poller.h>
 #include <slice.h>
-#include <ratp_bb.h>
 #include <xfuncs.h>
 #include <complete.h>
 #include <linux/ctype.h>
@@ -202,14 +201,8 @@ int readline(const char *prompt, char *buf, int len)
 
 	while (1) {
 		idle_slice_release();
-		while (!tstc()) {
+		while (!tstc())
 			poller_call();
-			if (IS_ENABLED(CONFIG_CONSOLE_RATP)) {
-				idle_slice_acquire();
-				barebox_ratp_command_run();
-				idle_slice_release();
-			}
-		}
 		idle_slice_acquire();
 
 		ichar = read_key();
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 06/10] fastboot: split generic code from USB gadget
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (4 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 05/10] ratp: use poller to run ratp commands Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 07/10] defconfigs: update renamed fastboot options Daniel Glöckner
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Edmund Henniges, Daniel Glöckner

From: Edmund Henniges <eh@emlix.com>

The fastboot specification describes other protocols beyond USB. Allow
these to reuse the generic parts of the existing fastboot code when they
are implemented.

Most of the changes in common/fastboot.c are due to the renaming of struct
f_fastboot *f_fb to struct fastboot *fb.

Signed-off-by: Edmund Henniges <eh@emlix.com>
Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 common/Kconfig                  |  40 ++
 common/Makefile                 |   1 +
 common/fastboot.c               | 957 +++++++++++++++++++++++++++++++
 drivers/usb/gadget/Kconfig      |  36 +-
 drivers/usb/gadget/f_fastboot.c | 970 ++------------------------------
 drivers/usb/gadget/multi.c      |   5 +-
 include/fastboot.h              |  66 +++
 include/usb/fastboot.h          |  34 +-
 include/usb/gadget-multi.h      |   2 +-
 9 files changed, 1112 insertions(+), 999 deletions(-)
 create mode 100644 common/fastboot.c
 create mode 100644 include/fastboot.h

diff --git a/common/Kconfig b/common/Kconfig
index 2fa9140de..5388030be 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -114,6 +114,9 @@ config USBGADGET_START
 config BOOT
 	bool
 
+config FASTBOOT_BASE
+	bool
+
 menu "General Settings"
 
 config LOCALVERSION
@@ -1017,6 +1020,43 @@ config PBL_OPTEE
 
 endmenu
 
+if FASTBOOT_BASE
+
+menu "Android Fastboot"
+
+config FASTBOOT_SPARSE
+	bool
+	select IMAGE_SPARSE
+	prompt "Enable Fastboot sparse image support"
+	help
+	  Sparse images are a way for the fastboot protocol to write
+	  images that are bigger than the available memory. If unsure,
+	  say yes here.
+
+config FASTBOOT_BUF
+	bool
+	prompt "Download files to temporary buffer instead of file"
+	help
+	  With this option enabled the fastboot code will download files to a
+	  temporary buffer instead of a temporary file. Normally you want to
+	  use a file as this also works when your memory is fragmented. However,
+	  in some special cases, when the file consumer also better copes with
+	  a buffer, then using a buffer might be better.
+
+	  Say no here unless you know what you are doing.
+
+config FASTBOOT_CMD_OEM
+	bool
+	prompt "Enable OEM commands"
+	help
+	  This option enables the fastboot "oem" group of commands. They allow to
+	  executing arbitrary barebox commands and may be disabled in secure
+	  environments.
+
+endmenu
+
+endif
+
 endmenu
 
 menu "Debugging"
diff --git a/common/Makefile b/common/Makefile
index 16f14db41..f7834c6a5 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SERIAL_DEV_BUS)	+= serdev.o
 obj-$(CONFIG_USBGADGET_START)	+= usbgadget.o
 pbl-$(CONFIG_PBL_OPTEE)		+= optee.o
 obj-$(CONFIG_BOOTM_OPTEE)	+= optee.o
+obj-$(CONFIG_FASTBOOT_BASE)	+= fastboot.o
 
 ifdef CONFIG_PASSWORD
 
diff --git a/common/fastboot.c b/common/fastboot.c
new file mode 100644
index 000000000..d58f68f1b
--- /dev/null
+++ b/common/fastboot.c
@@ -0,0 +1,957 @@
+/*
+ * (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>
+ *
+ * Copyright 2014 Sascha Hauer <s.hauer@pengutronix.de>
+ * Ported to barebox
+ *
+ * Copyright 2020 Edmund Henniges <eh@emlix.com>
+ * Copyright 2020 Daniel Glöckner <dg@emlix.com>
+ * Split off of generic parts
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#define pr_fmt(fmt) "fastboot: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <ioctl.h>
+#include <bbu.h>
+#include <bootm.h>
+#include <fs.h>
+#include <init.h>
+#include <libfile.h>
+#include <ubiformat.h>
+#include <unistd.h>
+#include <magicvar.h>
+#include <linux/sizes.h>
+#include <progress.h>
+#include <environment.h>
+#include <globalvar.h>
+#include <restart.h>
+#include <console_countdown.h>
+#include <image-sparse.h>
+#include <linux/types.h>
+#include <linux/stat.h>
+#include <linux/mtd/mtd.h>
+#include <fastboot.h>
+
+#define FASTBOOT_VERSION		"0.4"
+
+#define FASTBOOT_TMPFILE		"/.fastboot.img"
+
+static unsigned int fastboot_max_download_size = SZ_8M;
+
+struct fb_variable {
+	char *name;
+	char *value;
+	struct list_head list;
+};
+
+static inline bool fastboot_download_to_buf(struct fastboot *fb)
+{
+	if (IS_ENABLED(CONFIG_FASTBOOT_BUF))
+		return true;
+	else
+		return false;
+}
+
+static void fb_setvar(struct fb_variable *var, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	var->value = bvasprintf(fmt, ap);
+	va_end(ap);
+}
+
+static struct fb_variable *fb_addvar(struct fastboot *fb, const char *fmt, ...)
+{
+	struct fb_variable *var = xzalloc(sizeof(*var));
+	va_list ap;
+
+	va_start(ap, fmt);
+	var->name = bvasprintf(fmt, ap);
+	va_end(ap);
+
+	list_add_tail(&var->list, &fb->variables);
+
+	return var;
+}
+
+static int fastboot_add_partition_variables(struct fastboot *fb,
+		struct file_list_entry *fentry)
+{
+	struct stat s;
+	size_t size = 0;
+	int fd, ret;
+	struct mtd_info_user mtdinfo;
+	char *type = NULL;
+	struct fb_variable *var;
+
+	ret = stat(fentry->filename, &s);
+	if (ret) {
+		device_detect_by_name(devpath_to_name(fentry->filename));
+		ret = stat(fentry->filename, &s);
+	}
+
+	if (ret) {
+		if (fentry->flags & FILE_LIST_FLAG_CREATE) {
+			ret = 0;
+			type = "file";
+			goto out;
+		}
+
+		goto out;
+	}
+
+	fd = open(fentry->filename, O_RDWR);
+	if (fd < 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	size = s.st_size;
+
+	ret = ioctl(fd, MEMGETINFO, &mtdinfo);
+
+	close(fd);
+
+	if (!ret) {
+		switch (mtdinfo.type) {
+		case MTD_NANDFLASH:
+			type = "NAND-flash";
+			break;
+		case MTD_NORFLASH:
+			type = "NOR-flash";
+			break;
+		case MTD_UBIVOLUME:
+			type = "UBI";
+			break;
+		default:
+			type = "flash";
+			break;
+		}
+
+		goto out;
+	}
+
+	type = "basic";
+	ret = 0;
+
+out:
+	if (ret)
+		return ret;
+
+	var = fb_addvar(fb, "partition-size:%s", fentry->name);
+	fb_setvar(var, "%08zx", size);
+	var = fb_addvar(fb, "partition-type:%s", fentry->name);
+	fb_setvar(var, "%s", type);
+
+	return ret;
+}
+
+static int fastboot_add_bbu_variables(struct bbu_handler *handler, void *ctx)
+{
+	struct fastboot *fb = ctx;
+	char *name;
+	int ret;
+
+	name = basprintf("bbu-%s", handler->name);
+
+	ret = file_list_add_entry(fb->files, name, handler->devicefile, 0);
+
+	free(name);
+
+	return ret;
+}
+
+int fastboot_generic_init(struct fastboot *fb, bool export_bbu)
+{
+	int ret;
+	struct file_list_entry *fentry;
+	struct fb_variable *var;
+
+	var = fb_addvar(fb, "version");
+	fb_setvar(var, "0.4");
+	var = fb_addvar(fb, "bootloader-version");
+	fb_setvar(var, release_string);
+	if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE)) {
+		var = fb_addvar(fb, "max-download-size");
+		fb_setvar(var, "%u", fastboot_max_download_size);
+	}
+
+	if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && export_bbu)
+		bbu_handlers_iterate(fastboot_add_bbu_variables, fb);
+
+	file_list_for_each_entry(fb->files, fentry) {
+		ret = fastboot_add_partition_variables(fb, fentry);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+void fastboot_generic_free(struct fastboot *fb)
+{
+	struct fb_variable *var, *tmp;
+
+	list_for_each_entry_safe(var, tmp, &fb->variables, list) {
+		free(var->name);
+		free(var->value);
+		list_del(&var->list);
+		free(var);
+	}
+
+	fb->active = false;
+}
+
+static struct fastboot *g_fb;
+
+void fastboot_generic_close(struct fastboot *fb)
+{
+	if (g_fb == fb)
+		g_fb = NULL;
+}
+
+/*
+ * A "oem exec bootm" or similar commands will stop barebox. Tell the
+ * fastboot command on the other side so that it doesn't run into a
+ * timeout.
+ */
+static void fastboot_shutdown(void)
+{
+	struct fastboot *fb = g_fb;
+
+	if (!fb || !fb->active)
+		return;
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "barebox shutting down");
+	fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+early_exitcall(fastboot_shutdown);
+
+static char *fastboot_msg[] = {
+	[FASTBOOT_MSG_OKAY] = "OKAY",
+	[FASTBOOT_MSG_FAIL] = "FAIL",
+	[FASTBOOT_MSG_INFO] = "INFO",
+	[FASTBOOT_MSG_DATA] = "DATA",
+};
+
+int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type,
+		      const char *fmt, ...)
+{
+	struct va_format vaf;
+	char buf[64];
+	va_list ap;
+	int n;
+	const char *msg = fastboot_msg[type];
+
+	va_start(ap, fmt);
+	vaf.fmt = fmt;
+	vaf.va = &ap;
+
+	n = snprintf(buf, 64, "%s%pV", msg, &vaf);
+
+	switch (type) {
+	case FASTBOOT_MSG_OKAY:
+		fb->active = false;
+		break;
+	case FASTBOOT_MSG_FAIL:
+		fb->active = false;
+		pr_err("%pV\n", &vaf);
+		break;
+	case FASTBOOT_MSG_INFO:
+		pr_info("%pV\n", &vaf);
+		break;
+	case FASTBOOT_MSG_DATA:
+		break;
+	}
+
+	va_end(ap);
+
+	if (n > 64)
+		n = 64;
+
+	return fb->write(fb, buf, n);
+}
+
+static void cb_reboot(struct fastboot *fb, const char *cmd)
+{
+	restart_machine();
+}
+
+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 fastboot *fb, const char *cmd)
+{
+	struct fb_variable *var;
+
+	pr_debug("getvar: \"%s\"\n", cmd);
+
+	if (!strcmp_l1(cmd, "all")) {
+		list_for_each_entry(var, &fb->variables, list) {
+			fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "%s: %s",
+					  var->name, var->value);
+		}
+		fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+		return;
+	}
+
+	list_for_each_entry(var, &fb->variables, list) {
+		if (!strcmp(cmd, var->name)) {
+			fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, var->value);
+			return;
+		}
+	}
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+int fastboot_handle_download_data(struct fastboot *fb, const void *buffer,
+				  unsigned int len)
+{
+	int ret;
+
+	if (fastboot_download_to_buf(fb)) {
+		memcpy(fb->buf + fb->download_bytes, buffer, len);
+	} else {
+		ret = write(fb->download_fd, buffer, len);
+		if (ret < 0)
+			return ret;
+	}
+
+	fb->download_bytes += len;
+	show_progress(fb->download_bytes);
+	return 0;
+}
+
+void fastboot_download_finished(struct fastboot *fb)
+{
+	close(fb->download_fd);
+
+	printf("\n");
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished",
+			  fb->download_bytes);
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static void cb_download(struct fastboot *fb, const char *cmd)
+{
+	fb->download_size = simple_strtoul(cmd, NULL, 16);
+	fb->download_bytes = 0;
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...",
+			  fb->download_size);
+
+	init_progression_bar(fb->download_size);
+
+	if (fastboot_download_to_buf(fb)) {
+		free(fb->buf);
+		fb->buf = malloc(fb->download_size);
+		if (!fb->buf) {
+			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "not enough memory");
+			return;
+		}
+	} else {
+		fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC);
+		if (fb->download_fd < 0) {
+			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "internal error");
+			return;
+		}
+	}
+
+	if (!fb->download_size)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "data invalid size");
+	else
+		fb->start_download(fb);
+}
+
+void fastboot_start_download_generic(struct fastboot *fb)
+{
+	fastboot_tx_print(fb, FASTBOOT_MSG_DATA, "%08x", fb->download_size);
+}
+
+static void __maybe_unused cb_boot(struct fastboot *fb, const char *opt)
+{
+	int ret;
+	struct bootm_data data = {
+		.initrd_address = UIMAGE_INVALID_ADDRESS,
+		.os_address = UIMAGE_SOME_ADDRESS,
+	};
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Booting kernel..\n");
+
+	globalvar_set_match("linux.bootargs.dyn.", "");
+	globalvar_set_match("bootm.image", "");
+
+	data.os_file = FASTBOOT_TMPFILE;
+
+	ret = bootm_boot(&data);
+
+	if (ret)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "Booting failed: %s",
+				   strerror(-ret));
+	else
+		fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static struct mtd_info *get_mtd(struct fastboot *fb, const char *filename)
+{
+	int fd, ret;
+	struct mtd_info_user meminfo;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return ERR_PTR(-errno);
+
+	ret = ioctl(fd, MEMGETINFO, &meminfo);
+
+	close(fd);
+
+	if (ret)
+		return ERR_PTR(ret);
+
+	return meminfo.mtd;
+}
+
+static int do_ubiformat(struct fastboot *fb, struct mtd_info *mtd,
+			const char *file, const void *buf, size_t len)
+{
+	struct ubiformat_args args = {
+		.yes = 1,
+		.image = file,
+		.image_buf = buf,
+		.image_size = len,
+	};
+
+	if (!file)
+		args.novtbl = 1;
+
+	if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "ubiformat is not available");
+		return -ENODEV;
+	}
+
+	return ubiformat(mtd, &args);
+}
+
+
+static int check_ubi(struct fastboot *fb, struct file_list_entry *fentry,
+		     enum filetype filetype)
+{
+	struct mtd_info *mtd;
+
+	mtd = get_mtd(fb, fentry->filename);
+
+	/*
+	 * Issue a warning when we are about to write a UBI image to a MTD device
+	 * and the FILE_LIST_FLAG_UBI is not given as this means we loose all
+	 * erase counters.
+	 */
+	if (!IS_ERR(mtd) && filetype == filetype_ubi &&
+	    !(fentry->flags & FILE_LIST_FLAG_UBI)) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+				  "writing UBI image to MTD device, "
+				  "add the 'u' ");
+		fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+				  "flag to the partition description");
+		return 0;
+	}
+
+	if (!(fentry->flags & FILE_LIST_FLAG_UBI))
+		return 0;
+
+	if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "ubiformat not available");
+		return -ENOSYS;
+	}
+
+	if (IS_ERR(mtd)) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "UBI flag given on non-MTD device");
+		return -EINVAL;
+	}
+
+	if (filetype == filetype_ubi) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+				  "This is a UBI image...");
+		return 1;
+	} else {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "This is no UBI image but %s",
+				 file_type_to_string(filetype));
+		return -EINVAL;
+	}
+}
+
+static int fastboot_handle_sparse(struct fastboot *fb,
+				  struct file_list_entry *fentry)
+{
+	struct sparse_image_ctx *sparse;
+	void *buf = NULL;
+	int ret, fd;
+	unsigned int flags = O_RDWR;
+	int bufsiz = SZ_128K;
+	struct stat s;
+	struct mtd_info *mtd = NULL;
+
+	ret = stat(fentry->filename, &s);
+	if (ret) {
+		if (fentry->flags & FILE_LIST_FLAG_CREATE)
+			flags |= O_CREAT;
+		else
+			return ret;
+	}
+
+	fd = open(fentry->filename, flags);
+	if (fd < 0)
+		return -errno;
+
+	ret = fstat(fd, &s);
+	if (ret)
+		goto out_close_fd;
+
+	sparse = sparse_image_open(FASTBOOT_TMPFILE);
+	if (IS_ERR(sparse)) {
+		pr_err("Cannot open sparse image\n");
+		ret = PTR_ERR(sparse);
+		goto out_close_fd;
+	}
+
+	if (S_ISREG(s.st_mode)) {
+		ret = ftruncate(fd, sparse_image_size(sparse));
+		if (ret)
+			goto out;
+	}
+
+	buf = malloc(bufsiz);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (fentry->flags & FILE_LIST_FLAG_UBI) {
+		mtd = get_mtd(fb, fentry->filename);
+		if (IS_ERR(mtd)) {
+			ret = PTR_ERR(mtd);
+			goto out;
+		}
+	}
+
+	while (1) {
+		int retlen;
+		loff_t pos;
+
+		ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen);
+		if (ret)
+			goto out;
+		if (!retlen)
+			break;
+
+		if (pos == 0) {
+			ret = check_ubi(fb, fentry, file_detect_type(buf, retlen));
+			if (ret < 0)
+				goto out;
+		}
+
+		if (fentry->flags & FILE_LIST_FLAG_UBI) {
+			if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
+				ret = -ENOSYS;
+				goto out;
+			}
+
+			if (pos == 0) {
+				ret = do_ubiformat(fb, mtd, NULL, NULL, 0);
+				if (ret)
+					goto out;
+			}
+
+			ret = ubiformat_write(mtd, buf, retlen, pos);
+			if (ret)
+				goto out;
+		} else {
+			discard_range(fd, retlen, pos);
+
+			pos = lseek(fd, pos, SEEK_SET);
+			if (pos == -1) {
+				ret = -errno;
+				goto out;
+			}
+
+			ret = write_full(fd, buf, retlen);
+			if (ret < 0)
+				goto out;
+		}
+	}
+
+	ret = 0;
+
+out:
+	free(buf);
+	sparse_image_close(sparse);
+out_close_fd:
+	close(fd);
+
+	return ret;
+}
+
+static void cb_flash(struct fastboot *fb, const char *cmd)
+{
+	struct file_list_entry *fentry;
+	int ret;
+	const char *filename = NULL, *sourcefile;
+	enum filetype filetype;
+
+	if (fastboot_download_to_buf(fb)) {
+		sourcefile = NULL;
+		filetype = file_detect_type(fb->buf, fb->download_bytes);
+	} else {
+		sourcefile = FASTBOOT_TMPFILE;
+		filetype = file_name_detect_type(FASTBOOT_TMPFILE);
+	}
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Copying file to %s...",
+			  cmd);
+
+	fentry = file_list_entry_by_name(fb->files, cmd);
+
+	if (!fentry) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "No such partition: %s",
+				  cmd);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (fb->cmd_flash) {
+		ret = fb->cmd_flash(fb, fentry, sourcefile, fb->buf,
+				      fb->download_size);
+		if (ret != FASTBOOT_CMD_FALLTHROUGH)
+			goto out;
+	}
+
+	filename = fentry->filename;
+
+	if (filetype == filetype_android_sparse) {
+		if (!IS_ENABLED(CONFIG_FASTBOOT_SPARSE) ||
+		    fastboot_download_to_buf(fb)) {
+			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "sparse image not supported");
+			ret = -EOPNOTSUPP;
+			goto out;
+		}
+
+		ret = fastboot_handle_sparse(fb, fentry);
+		if (ret)
+			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "writing sparse image: %s",
+					  strerror(-ret));
+
+		goto out;
+	}
+
+	ret = check_ubi(fb, fentry, filetype);
+	if (ret < 0)
+		goto out;
+
+	if (ret > 0) {
+		struct mtd_info *mtd;
+
+		mtd = get_mtd(fb, fentry->filename);
+
+		ret = do_ubiformat(fb, mtd, sourcefile, fb->buf,
+				   fb->download_size);
+		if (ret) {
+			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+					  "write partition: %s",
+					  strerror(-ret));
+			goto out;
+		}
+
+		goto out;
+	}
+
+	if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) {
+		struct bbu_handler *handler;
+		struct bbu_data data = {
+			.devicefile = filename,
+			.flags = BBU_FLAG_YES,
+		};
+
+		handler = bbu_find_handler_by_device(data.devicefile);
+		if (!handler)
+			goto copy;
+
+		fastboot_tx_print(fb, FASTBOOT_MSG_INFO,
+				  "This is a barebox image...");
+
+		if (fastboot_download_to_buf(fb)) {
+			data.len = fb->download_size;
+		} else {
+			ret = read_file_2(sourcefile, &data.len, &fb->buf,
+					fb->download_size);
+			if (ret) {
+				fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+						  "reading barebox");
+				goto out;
+			}
+		}
+
+		data.image = fb->buf;
+		data.imagefile = sourcefile;
+
+		ret = barebox_update(&data, handler);
+
+		if (ret)
+			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "update barebox: %s", strerror(-ret));
+
+		goto out;
+	}
+
+copy:
+	if (fastboot_download_to_buf(fb))
+		ret = write_file(filename, fb->buf, fb->download_size);
+	else
+		ret = copy_file(FASTBOOT_TMPFILE, filename, 1);
+
+	if (ret)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "write partition: %s", strerror(-ret));
+
+out:
+	if (!ret)
+		fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+
+	free(fb->buf);
+	fb->buf = NULL;
+
+	if (!fastboot_download_to_buf(fb))
+		unlink(FASTBOOT_TMPFILE);
+}
+
+static void cb_erase(struct fastboot *fb, const char *cmd)
+{
+	struct file_list_entry *fentry;
+	int ret;
+	const char *filename = NULL;
+	int fd;
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd);
+
+	file_list_for_each_entry(fb->files, fentry) {
+		if (!strcmp(cmd, fentry->name)) {
+			filename = fentry->filename;
+			break;
+		}
+	}
+
+	if (!filename) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "No such partition: %s", cmd);
+		return;
+	}
+
+	fd = open(filename, O_RDWR);
+	if (fd < 0)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-fd));
+
+	ret = erase(fd, ERASE_SIZE_ALL, 0);
+
+	close(fd);
+
+	if (ret)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "cannot erase partition %s: %s",
+				  filename, strerror(-ret));
+	else
+		fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+struct cmd_dispatch_info {
+	char *cmd;
+	void (*cb)(struct fastboot *fb, const char *opt);
+};
+
+static void fb_run_command(struct fastboot *fb, const char *cmdbuf,
+		const struct cmd_dispatch_info *cmds, int num_commands)
+{
+	const struct cmd_dispatch_info *cmd;
+	int i;
+
+	console_countdown_abort();
+
+	for (i = 0; i < num_commands; i++) {
+		cmd = &cmds[i];
+
+		if (!strcmp_l1(cmd->cmd, cmdbuf)) {
+			cmd->cb(fb, cmdbuf + strlen(cmd->cmd));
+
+			return;
+		}
+	}
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "unknown command %s",
+			  cmdbuf);
+}
+
+static void cb_oem_getenv(struct fastboot *fb, const char *cmd)
+{
+	const char *value;
+
+	pr_debug("%s: \"%s\"\n", __func__, cmd);
+
+	value = getenv(cmd);
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, value ? value : "");
+	fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static void cb_oem_setenv(struct fastboot *fb, const char *cmd)
+{
+	char *var = xstrdup(cmd);
+	char *value;
+	int ret;
+
+	pr_debug("%s: \"%s\"\n", __func__, cmd);
+
+	value = strchr(var, '=');
+	if (!value) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	*value++ = 0;
+
+	ret = setenv(var, value);
+	if (ret)
+		goto out;
+
+	fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+out:
+	free(var);
+
+	if (ret)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-ret));
+}
+
+static void cb_oem_exec(struct fastboot *fb, const char *cmd)
+{
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) {
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
+				  "no command support available");
+		return;
+	}
+
+	ret = run_command(cmd);
+	if (ret < 0)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, strerror(-ret));
+	else if (ret > 0)
+		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL, "");
+	else
+		fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
+}
+
+static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = {
+	{
+		.cmd = "getenv ",
+		.cb = cb_oem_getenv,
+	}, {
+		.cmd = "setenv ",
+		.cb = cb_oem_setenv,
+	}, {
+		.cmd = "exec ",
+		.cb = cb_oem_exec,
+	},
+};
+
+static void __maybe_unused cb_oem(struct fastboot *fb, const char *cmd)
+{
+	pr_debug("%s: \"%s\"\n", __func__, cmd);
+
+	fb_run_command(fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info));
+}
+
+static const struct cmd_dispatch_info cmd_dispatch_info[] = {
+	{
+		.cmd = "reboot",
+		.cb = cb_reboot,
+	}, {
+		.cmd = "getvar:",
+		.cb = cb_getvar,
+	}, {
+		.cmd = "download:",
+		.cb = cb_download,
+#if defined(CONFIG_BOOTM)
+	}, {
+		.cmd = "boot",
+		.cb = cb_boot,
+#endif
+	}, {
+		.cmd = "flash:",
+		.cb = cb_flash,
+	}, {
+		.cmd = "erase:",
+		.cb = cb_erase,
+#if defined(CONFIG_FASTBOOT_CMD_OEM)
+	}, {
+		.cmd = "oem ",
+		.cb = cb_oem,
+#endif
+	},
+};
+
+void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf)
+{
+	int ret;
+
+	g_fb = fb;
+	fb->active = true;
+
+	if (fb->cmd_exec) {
+		ret = fb->cmd_exec(fb, cmdbuf);
+		if (ret != FASTBOOT_CMD_FALLTHROUGH)
+			return;
+	}
+
+	fb_run_command(fb, cmdbuf, cmd_dispatch_info,
+				ARRAY_SIZE(cmd_dispatch_info));
+}
+
+static int fastboot_globalvars_init(void)
+{
+	if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE))
+		globalvar_add_simple_int("usbgadget.fastboot_max_download_size",
+				 &fastboot_max_download_size, "%u");
+
+	return 0;
+}
+
+device_initcall(fastboot_globalvars_init);
+
+BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size,
+		       global.usbgadget.fastboot_max_download_size,
+		       "Fastboot maximum download size");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 6e60c7aee..977f6c0db 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -57,38 +57,6 @@ config USB_GADGET_FASTBOOT
 	bool
 	select BANNER
 	select FILE_LIST
-	prompt "Android Fastboot support"
-
-config USB_GADGET_FASTBOOT_SPARSE
-	bool
-	depends on USB_GADGET_FASTBOOT
-	select IMAGE_SPARSE
-	prompt "Enable Fastboot sparse image support"
-	help
-	  Sparse images are a way for the fastboot protocol to write
-	  images that are bigger than the available memory. If unsure,
-	  say yes here.
-
-config USB_GADGET_FASTBOOT_BUF
-	bool
-	depends on USB_GADGET_FASTBOOT
-	prompt "Download files to temporary buffer instead of file"
-	help
-	  With this option enabled the fastboot code will download files to a
-	  temporary buffer instead of a temporary file. Normally you want to
-	  use a file as this also works when your memory is fragmented. However,
-	  in some special cases, when the file consumer also better copes with
-	  a buffer, then using a buffer might be better.
-
-	  Say no here unless you know what you are doing.
-
-config USB_GADGET_FASTBOOT_CMD_OEM
-	bool
-	depends on USB_GADGET_FASTBOOT
-	prompt "Enable OEM commands"
-	help
-	  This option enables the fastboot "oem" group of commands. They allow to
-	  executing arbitrary barebox commands and may be disabled in secure
-	  environments.
-
+	select FASTBOOT_BASE
+	prompt "Android Fastboot USB Gadget"
 endif
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index cf3cc6dac..f8a9c3253 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -11,90 +11,36 @@
  * Copyright 2014 Sascha Hauer <s.hauer@pengutronix.de>
  * Ported to barebox
  *
+ * Copyright 2020 Edmund Henniges <eh@emlix.com>
+ * Copyright 2020 Daniel Glöckner <dg@emlix.com>
+ * Split off of generic parts
+ *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #define pr_fmt(fmt) "fastboot: " fmt
 
-#include <common.h>
-#include <command.h>
-#include <errno.h>
-#include <malloc.h>
-#include <fcntl.h>
-#include <clock.h>
-#include <ioctl.h>
-#include <libbb.h>
-#include <bbu.h>
-#include <bootm.h>
 #include <dma.h>
-#include <fs.h>
-#include <libfile.h>
-#include <ubiformat.h>
-#include <stdlib.h>
-#include <file-list.h>
-#include <magicvar.h>
-#include <linux/sizes.h>
+#include <unistd.h>
 #include <progress.h>
-#include <environment.h>
-#include <globalvar.h>
-#include <restart.h>
-#include <console_countdown.h>
-#include <image-sparse.h>
-#include <usb/ch9.h>
-#include <usb/gadget.h>
+#include <fastboot.h>
 #include <usb/fastboot.h>
-#include <usb/composite.h>
-#include <linux/err.h>
-#include <linux/compiler.h>
-#include <linux/stat.h>
-#include <linux/mtd/mtd-abi.h>
-#include <linux/mtd/mtd.h>
-
-#define FASTBOOT_VERSION		"0.4"
 
 #define FASTBOOT_INTERFACE_CLASS	0xff
 #define FASTBOOT_INTERFACE_SUB_CLASS	0x42
 #define FASTBOOT_INTERFACE_PROTOCOL	0x03
 
-#define FASTBOOT_TMPFILE		"/.fastboot.img"
-
 #define EP_BUFFER_SIZE			4096
 
-static unsigned int fastboot_max_download_size = SZ_8M;
-
-struct fb_variable {
-	char *name;
-	char *value;
-	struct list_head list;
-};
-
 struct f_fastboot {
+	struct fastboot fastboot;
 	struct usb_function func;
 
 	/* IN/OUT EP's and corresponding requests */
 	struct usb_ep *in_ep, *out_ep;
 	struct usb_request *in_req, *out_req;
-	struct file_list *files;
-	int (*cmd_exec)(struct f_fastboot *, const char *cmd);
-	int (*cmd_flash)(struct f_fastboot *, struct file_list_entry *entry,
-			 const char *filename, const void *buf, size_t len);
-	int download_fd;
-	void *buf;
-	bool active;
-
-	size_t download_bytes;
-	size_t download_size;
-	struct list_head variables;
 };
 
-static inline bool fastboot_download_to_buf(struct f_fastboot *f_fb)
-{
-	if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_BUF))
-		return true;
-	else
-		return false;
-}
-
 static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
 {
 	return container_of(f, struct f_fastboot, func);
@@ -182,6 +128,9 @@ static struct usb_gadget_strings *fastboot_strings[] = {
 };
 
 static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+static int fastboot_write_usb(struct fastboot *fb, const char *buffer,
+			      unsigned int buffer_size);
+static void fastboot_start_download_usb(struct fastboot *fb);
 
 static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
 {
@@ -206,116 +155,6 @@ static struct usb_request *fastboot_alloc_request(struct usb_ep *ep)
 	return req;
 }
 
-static void fb_setvar(struct fb_variable *var, const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	var->value = bvasprintf(fmt, ap);
-	va_end(ap);
-}
-
-static struct fb_variable *fb_addvar(struct f_fastboot *f_fb, const char *fmt, ...)
-{
-	struct fb_variable *var = xzalloc(sizeof(*var));
-	va_list ap;
-
-	va_start(ap, fmt);
-	var->name = bvasprintf(fmt, ap);
-	va_end(ap);
-
-	list_add_tail(&var->list, &f_fb->variables);
-
-	return var;
-}
-
-static int fastboot_add_partition_variables(struct f_fastboot *f_fb,
-		struct file_list_entry *fentry)
-{
-	struct stat s;
-	size_t size = 0;
-	int fd, ret;
-	struct mtd_info_user mtdinfo;
-	char *type = NULL;
-	struct fb_variable *var;
-
-	ret = stat(fentry->filename, &s);
-	if (ret) {
-		device_detect_by_name(devpath_to_name(fentry->filename));
-		ret = stat(fentry->filename, &s);
-	}
-
-	if (ret) {
-		if (fentry->flags & FILE_LIST_FLAG_CREATE) {
-			ret = 0;
-			type = "file";
-			goto out;
-		}
-
-		goto out;
-	}
-
-	fd = open(fentry->filename, O_RDWR);
-	if (fd < 0) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	size = s.st_size;
-
-	ret = ioctl(fd, MEMGETINFO, &mtdinfo);
-
-	close(fd);
-
-	if (!ret) {
-		switch (mtdinfo.type) {
-		case MTD_NANDFLASH:
-			type = "NAND-flash";
-			break;
-		case MTD_NORFLASH:
-			type = "NOR-flash";
-			break;
-		case MTD_UBIVOLUME:
-			type = "UBI";
-			break;
-		default:
-			type = "flash";
-			break;
-		}
-
-		goto out;
-	}
-
-	type = "basic";
-	ret = 0;
-
-out:
-	if (ret)
-		return ret;
-
-	var = fb_addvar(f_fb, "partition-size:%s", fentry->name);
-	fb_setvar(var, "%08zx", size);
-	var = fb_addvar(f_fb, "partition-type:%s", fentry->name);
-	fb_setvar(var, "%s", type);
-
-	return ret;
-}
-
-static int fastboot_add_bbu_variables(struct bbu_handler *handler, void *ctx)
-{
-	struct f_fastboot *f_fb = ctx;
-	char *name;
-	int ret;
-
-	name = basprintf("bbu-%s", handler->name);
-
-	ret = file_list_add_entry(f_fb->files, name, handler->devicefile, 0);
-
-	free(name);
-
-	return ret;
-}
-
 static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
@@ -325,30 +164,17 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
 	struct usb_string *us;
 	const struct usb_function_instance *fi = f->fi;
 	struct f_fastboot_opts *opts = container_of(fi, struct f_fastboot_opts, func_inst);
-	struct file_list_entry *fentry;
-	struct fb_variable *var;
-
-	f_fb->files = opts->files;
-	f_fb->cmd_exec = opts->cmd_exec;
-	f_fb->cmd_flash = opts->cmd_flash;
-
-	var = fb_addvar(f_fb, "version");
-	fb_setvar(var, "0.4");
-	var = fb_addvar(f_fb, "bootloader-version");
-	fb_setvar(var, release_string);
-	if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE)) {
-		var = fb_addvar(f_fb, "max-download-size");
-		fb_setvar(var, "%u", fastboot_max_download_size);
-	}
 
-	if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && opts->export_bbu)
-		bbu_handlers_iterate(fastboot_add_bbu_variables, f_fb);
+	f_fb->fastboot.write = fastboot_write_usb;
+	f_fb->fastboot.start_download = fastboot_start_download_usb;
 
-	file_list_for_each_entry(f_fb->files, fentry) {
-		ret = fastboot_add_partition_variables(f_fb, fentry);
-		if (ret)
-			return ret;
-	}
+	f_fb->fastboot.files = opts->common.files;
+	f_fb->fastboot.cmd_exec = opts->common.cmd_exec;
+	f_fb->fastboot.cmd_flash = opts->common.cmd_flash;
+
+	ret = fastboot_generic_init(&f_fb->fastboot, opts->common.export_bbu);
+	if (ret)
+		return ret;
 
 	/* DYNAMIC interface numbers assignments */
 	id = usb_interface_id(c, f);
@@ -409,7 +235,6 @@ static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
 static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct f_fastboot *f_fb = func_to_fastboot(f);
-	struct fb_variable *var, *tmp;
 
 	usb_ep_dequeue(f_fb->in_ep, f_fb->in_req);
 	free(f_fb->in_req->buf);
@@ -421,14 +246,7 @@ static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
 	usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
 	f_fb->out_req = NULL;
 
-	list_for_each_entry_safe(var, tmp, &f_fb->variables, list) {
-		free(var->name);
-		free(var->value);
-		list_del(&var->list);
-		free(var);
-	}
-
-	f_fb->active = false;
+	fastboot_generic_free(&f_fb->fastboot);
 }
 
 static void fastboot_disable(struct usb_function *f)
@@ -481,43 +299,21 @@ err:
 	return ret;
 }
 
-static struct f_fastboot *g_f_fb;
-
 static void fastboot_free_func(struct usb_function *f)
 {
 	struct f_fastboot *f_fb = container_of(f, struct f_fastboot, func);
 
-	if (g_f_fb == f_fb)
-		g_f_fb = NULL;
-
+	fastboot_generic_close(&f_fb->fastboot);
 	free(f_fb);
 }
 
-/*
- * A "oem exec bootm" or similar commands will stop barebox. Tell the
- * fastboot command on the other side so that it doesn't run into a
- * timeout.
- */
-static void fastboot_shutdown(void)
-{
-	struct f_fastboot *f_fb = g_f_fb;
-
-	if (!f_fb || !f_fb->active)
-		return;
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "barebox shutting down");
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-}
-
-early_exitcall(fastboot_shutdown);
-
 static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi)
 {
 	struct f_fastboot *f_fb;
 
 	f_fb = xzalloc(sizeof(*f_fb));
 
-	INIT_LIST_HEAD(&f_fb->variables);
+	INIT_LIST_HEAD(&f_fb->fastboot.variables);
 
 	f_fb->func.name = "fastboot";
 	f_fb->func.strings = fastboot_strings;
@@ -527,9 +323,6 @@ static struct usb_function *fastboot_alloc_func(struct usb_function_instance *fi
 	f_fb->func.unbind = fastboot_unbind;
 	f_fb->func.free_func = fastboot_free_func;
 
-	if (!g_f_fb)
-		g_f_fb = f_fb;
-
 	return &f_fb->func;
 }
 
@@ -553,8 +346,9 @@ static struct usb_function_instance *fastboot_alloc_instance(void)
 
 DECLARE_USB_FUNCTION_INIT(fastboot, fastboot_alloc_instance, fastboot_alloc_func);
 
-static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsigned int buffer_size)
+static int fastboot_write_usb(struct fastboot *fb, const char *buffer, unsigned int buffer_size)
 {
+	struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot);
 	struct usb_request *in_req = f_fb->in_req;
 	uint64_t start;
 	int ret;
@@ -580,91 +374,10 @@ static int fastboot_tx_write(struct f_fastboot *f_fb, const char *buffer, unsign
 	return 0;
 }
 
-static char *fastboot_msg[] = {
-	[FASTBOOT_MSG_OKAY] = "OKAY",
-	[FASTBOOT_MSG_FAIL] = "FAIL",
-	[FASTBOOT_MSG_INFO] = "INFO",
-	[FASTBOOT_MSG_DATA] = "DATA",
-};
-
-int fastboot_tx_print(struct f_fastboot *f_fb, enum fastboot_msg_type type,
-		      const char *fmt, ...)
-{
-	struct va_format vaf;
-	char buf[64];
-	va_list ap;
-	int n;
-	const char *msg = fastboot_msg[type];
-
-	va_start(ap, fmt);
-	vaf.fmt = fmt;
-	vaf.va = &ap;
-
-	n = snprintf(buf, 64, "%s%pV", msg, &vaf);
-
-	switch (type) {
-	case FASTBOOT_MSG_OKAY:
-		f_fb->active = false;
-		break;
-	case FASTBOOT_MSG_FAIL:
-		f_fb->active = false;
-		pr_err("%pV\n", &vaf);
-		break;
-	case FASTBOOT_MSG_INFO:
-		pr_info("%pV\n", &vaf);
-		break;
-	case FASTBOOT_MSG_DATA:
-		break;
-	}
-
-	va_end(ap);
-
-	if (n > 64)
-		n = 64;
-
-	return fastboot_tx_write(f_fb, buf, n);
-}
-
-static void cb_reboot(struct f_fastboot *f_fb, const char *cmd)
-{
-	restart_machine();
-}
-
-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 f_fastboot *f_fb, const char *cmd)
-{
-	struct fb_variable *var;
-
-	pr_debug("getvar: \"%s\"\n", cmd);
-
-	if (!strcmp_l1(cmd, "all")) {
-		list_for_each_entry(var, &f_fb->variables, list) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "%s: %s",
-					  var->name, var->value);
-		}
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-		return;
-	}
-
-	list_for_each_entry(var, &f_fb->variables, list) {
-		if (!strcmp(cmd, var->name)) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, var->value);
-			return;
-		}
-	}
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-}
-
 static int rx_bytes_expected(struct f_fastboot *f_fb)
 {
-	int remaining = f_fb->download_size - f_fb->download_bytes;
+	int remaining = f_fb->fastboot.download_size
+		      - f_fb->fastboot.download_bytes;
 
 	if (remaining >= EP_BUFFER_SIZE)
 		return EP_BUFFER_SIZE;
@@ -683,651 +396,50 @@ static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
 		return;
 	}
 
-	if (fastboot_download_to_buf(f_fb)) {
-		memcpy(f_fb->buf + f_fb->download_bytes, buffer, req->actual);
-	} else {
-		ret = write(f_fb->download_fd, buffer, req->actual);
-		if (ret < 0) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret));
-			return;
-		}
+	ret = fastboot_handle_download_data(&f_fb->fastboot, buffer,
+					    req->actual);
+	if (ret < 0) {
+		fastboot_tx_print(&f_fb->fastboot, FASTBOOT_MSG_FAIL,
+				  strerror(-ret));
+		return;
 	}
 
-	f_fb->download_bytes += req->actual;
-
 	req->length = rx_bytes_expected(f_fb);
 
-	show_progress(f_fb->download_bytes);
-
 	/* Check if transfer is done */
-	if (f_fb->download_bytes >= f_fb->download_size) {
+	if (f_fb->fastboot.download_bytes >= f_fb->fastboot.download_size) {
 		req->complete = rx_handler_command;
 		req->length = EP_BUFFER_SIZE;
-		close(f_fb->download_fd);
-
-		printf("\n");
 
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes finished",
-				  f_fb->download_bytes);
-
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
+		fastboot_download_finished(&f_fb->fastboot);
 	}
 
 	req->actual = 0;
 	usb_ep_queue(ep, req);
 }
 
-static void cb_download(struct f_fastboot *f_fb, const char *cmd)
-{
-	f_fb->download_size = simple_strtoul(cmd, NULL, 16);
-	f_fb->download_bytes = 0;
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Downloading %d bytes...",
-			  f_fb->download_size);
-
-	init_progression_bar(f_fb->download_size);
-
-	if (fastboot_download_to_buf(f_fb)) {
-		free(f_fb->buf);
-		f_fb->buf = malloc(f_fb->download_size);
-		if (!f_fb->buf) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "not enough memory");
-			return;
-		}
-	} else {
-		f_fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC);
-		if (f_fb->download_fd < 0) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "internal error");
-			return;
-		}
-	}
-
-	if (!f_fb->download_size) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "data invalid size");
-	} else {
-		struct usb_request *req = f_fb->out_req;
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_DATA,
-					  "%08x", f_fb->download_size);
-		req->complete = rx_handler_dl_image;
-		req->length = rx_bytes_expected(f_fb);
-	}
-}
-
-static void __maybe_unused cb_boot(struct f_fastboot *f_fb, const char *opt)
-{
-	int ret;
-	struct bootm_data data = {
-		.initrd_address = UIMAGE_INVALID_ADDRESS,
-		.os_address = UIMAGE_SOME_ADDRESS,
-	};
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Booting kernel..\n");
-
-	globalvar_set_match("linux.bootargs.dyn.", "");
-	globalvar_set_match("bootm.image", "");
-
-	data.os_file = FASTBOOT_TMPFILE;
-
-	ret = bootm_boot(&data);
-
-	if (ret)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "Booting failed: %s",
-				   strerror(-ret));
-	else
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-}
-
-static struct mtd_info *get_mtd(struct f_fastboot *f_fb, const char *filename)
-{
-	int fd, ret;
-	struct mtd_info_user meminfo;
-
-	fd = open(filename, O_RDONLY);
-	if (fd < 0)
-		return ERR_PTR(-errno);
-
-	ret = ioctl(fd, MEMGETINFO, &meminfo);
-
-	close(fd);
-
-	if (ret)
-		return ERR_PTR(ret);
-
-	return meminfo.mtd;
-}
-
-static int do_ubiformat(struct f_fastboot *f_fb, struct mtd_info *mtd,
-			const char *file, const void *buf, size_t len)
-{
-	struct ubiformat_args args = {
-		.yes = 1,
-		.image = file,
-		.image_buf = buf,
-		.image_size = len,
-	};
-
-	if (!file)
-		args.novtbl = 1;
-
-	if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "ubiformat is not available");
-		return -ENODEV;
-	}
-
-	return ubiformat(mtd, &args);
-}
-
-
-static int check_ubi(struct f_fastboot *f_fb, struct file_list_entry *fentry,
-		     enum filetype filetype)
-{
-	struct mtd_info *mtd;
-
-	mtd = get_mtd(f_fb, fentry->filename);
-
-	/*
-	 * Issue a warning when we are about to write a UBI image to a MTD device
-	 * and the FILE_LIST_FLAG_UBI is not given as this means we loose all
-	 * erase counters.
-	 */
-	if (!IS_ERR(mtd) && filetype == filetype_ubi &&
-	    !(fentry->flags & FILE_LIST_FLAG_UBI)) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
-				  "writing UBI image to MTD device, "
-				  "add the 'u' ");
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
-				  "flag to the partition description");
-		return 0;
-	}
-
-	if (!(fentry->flags & FILE_LIST_FLAG_UBI))
-		return 0;
-
-	if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "ubiformat not available");
-		return -ENOSYS;
-	}
-
-	if (IS_ERR(mtd)) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "UBI flag given on non-MTD device");
-		return -EINVAL;
-	}
-
-	if (filetype == filetype_ubi) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
-				  "This is a UBI image...");
-		return 1;
-	} else {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "This is no UBI image but %s",
-				 file_type_to_string(filetype));
-		return -EINVAL;
-	}
-}
-
-static int fastboot_handle_sparse(struct f_fastboot *f_fb,
-				  struct file_list_entry *fentry)
-{
-	struct sparse_image_ctx *sparse;
-	void *buf = NULL;
-	int ret, fd;
-	unsigned int flags = O_RDWR;
-	int bufsiz = SZ_128K;
-	struct stat s;
-	struct mtd_info *mtd = NULL;
-
-	ret = stat(fentry->filename, &s);
-	if (ret) {
-		if (fentry->flags & FILE_LIST_FLAG_CREATE)
-			flags |= O_CREAT;
-		else
-			return ret;
-	}
-
-	fd = open(fentry->filename, flags);
-	if (fd < 0)
-		return -errno;
-
-	ret = fstat(fd, &s);
-	if (ret)
-		goto out_close_fd;
-
-	sparse = sparse_image_open(FASTBOOT_TMPFILE);
-	if (IS_ERR(sparse)) {
-		pr_err("Cannot open sparse image\n");
-		ret = PTR_ERR(sparse);
-		goto out_close_fd;
-	}
-
-	if (S_ISREG(s.st_mode)) {
-		ret = ftruncate(fd, sparse_image_size(sparse));
-		if (ret)
-			goto out;
-	}
-
-	buf = malloc(bufsiz);
-	if (!buf) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	if (fentry->flags & FILE_LIST_FLAG_UBI) {
-		mtd = get_mtd(f_fb, fentry->filename);
-		if (IS_ERR(mtd)) {
-			ret = PTR_ERR(mtd);
-			goto out;
-		}
-	}
-
-	while (1) {
-		int retlen;
-		loff_t pos;
-
-		ret = sparse_image_read(sparse, buf, &pos, bufsiz, &retlen);
-		if (ret)
-			goto out;
-		if (!retlen)
-			break;
-
-		if (pos == 0) {
-			ret = check_ubi(f_fb, fentry, file_detect_type(buf, retlen));
-			if (ret < 0)
-				goto out;
-		}
-
-		if (fentry->flags & FILE_LIST_FLAG_UBI) {
-			if (!IS_ENABLED(CONFIG_UBIFORMAT)) {
-				ret = -ENOSYS;
-				goto out;
-			}
-
-			if (pos == 0) {
-				ret = do_ubiformat(f_fb, mtd, NULL, NULL, 0);
-				if (ret)
-					goto out;
-			}
-
-			ret = ubiformat_write(mtd, buf, retlen, pos);
-			if (ret)
-				goto out;
-		} else {
-			discard_range(fd, retlen, pos);
-
-			pos = lseek(fd, pos, SEEK_SET);
-			if (pos == -1) {
-				ret = -errno;
-				goto out;
-			}
-
-			ret = write_full(fd, buf, retlen);
-			if (ret < 0)
-				goto out;
-		}
-	}
-
-	ret = 0;
-
-out:
-	free(buf);
-	sparse_image_close(sparse);
-out_close_fd:
-	close(fd);
-
-	return ret;
-}
-
-static void cb_flash(struct f_fastboot *f_fb, const char *cmd)
-{
-	struct file_list_entry *fentry;
-	int ret;
-	const char *filename = NULL, *sourcefile;
-	enum filetype filetype;
-
-	if (fastboot_download_to_buf(f_fb)) {
-		sourcefile = NULL;
-		filetype = file_detect_type(f_fb->buf, f_fb->download_bytes);
-	} else {
-		sourcefile = FASTBOOT_TMPFILE;
-		filetype = file_name_detect_type(FASTBOOT_TMPFILE);
-	}
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Copying file to %s...",
-			  cmd);
-
-	fentry = file_list_entry_by_name(f_fb->files, cmd);
-
-	if (!fentry) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "No such partition: %s",
-				  cmd);
-		ret = -ENOENT;
-		goto out;
-	}
-
-	if (f_fb->cmd_flash) {
-		ret = f_fb->cmd_flash(f_fb, fentry, sourcefile, f_fb->buf,
-				      f_fb->download_size);
-		if (ret != FASTBOOT_CMD_FALLTHROUGH)
-			goto out;
-	}
-
-	filename = fentry->filename;
-
-	if (filetype == filetype_android_sparse) {
-		if (!IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE) ||
-		    fastboot_download_to_buf(f_fb)) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "sparse image not supported");
-			ret = -EOPNOTSUPP;
-			goto out;
-		}
-
-		ret = fastboot_handle_sparse(f_fb, fentry);
-		if (ret)
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "writing sparse image: %s",
-					  strerror(-ret));
-
-		goto out;
-	}
-
-	ret = check_ubi(f_fb, fentry, filetype);
-	if (ret < 0)
-		goto out;
-
-	if (ret > 0) {
-		struct mtd_info *mtd;
-
-		mtd = get_mtd(f_fb, fentry->filename);
-
-		ret = do_ubiformat(f_fb, mtd, sourcefile, f_fb->buf,
-				   f_fb->download_size);
-		if (ret) {
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-					  "write partition: %s",
-					  strerror(-ret));
-			goto out;
-		}
-
-		goto out;
-	}
-
-	if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && filetype_is_barebox_image(filetype)) {
-		struct bbu_handler *handler;
-		struct bbu_data data = {
-			.devicefile = filename,
-			.flags = BBU_FLAG_YES,
-		};
-
-		handler = bbu_find_handler_by_device(data.devicefile);
-		if (!handler)
-			goto copy;
-
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO,
-				  "This is a barebox image...");
-
-		if (fastboot_download_to_buf(f_fb)) {
-			data.len = f_fb->download_size;
-		} else {
-			ret = read_file_2(sourcefile, &data.len, &f_fb->buf,
-					f_fb->download_size);
-			if (ret) {
-				fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-						  "reading barebox");
-				goto out;
-			}
-		}
-
-		data.image = f_fb->buf;
-		data.imagefile = sourcefile;
-
-		ret = barebox_update(&data, handler);
-
-		if (ret)
-			fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "update barebox: %s", strerror(-ret));
-
-		goto out;
-	}
-
-copy:
-	if (fastboot_download_to_buf(f_fb))
-		ret = write_file(filename, f_fb->buf, f_fb->download_size);
-	else
-		ret = copy_file(FASTBOOT_TMPFILE, filename, 1);
-
-	if (ret)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "write partition: %s", strerror(-ret));
-
-out:
-	if (!ret)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-
-	free(f_fb->buf);
-	f_fb->buf = NULL;
-
-	if (!fastboot_download_to_buf(f_fb))
-		unlink(FASTBOOT_TMPFILE);
-}
-
-static void cb_erase(struct f_fastboot *f_fb, const char *cmd)
-{
-	struct file_list_entry *fentry;
-	int ret;
-	const char *filename = NULL;
-	int fd;
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, "Erasing %s...", cmd);
-
-	file_list_for_each_entry(f_fb->files, fentry) {
-		if (!strcmp(cmd, fentry->name)) {
-			filename = fentry->filename;
-			break;
-		}
-	}
-
-	if (!filename) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "No such partition: %s", cmd);
-		return;
-	}
-
-	fd = open(filename, O_RDWR);
-	if (fd < 0)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-fd));
-
-	ret = erase(fd, ERASE_SIZE_ALL, 0);
-
-	close(fd);
-
-	if (ret)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "cannot erase partition %s: %s",
-				  filename, strerror(-ret));
-	else
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-}
-
-struct cmd_dispatch_info {
-	char *cmd;
-	void (*cb)(struct f_fastboot *f_fb, const char *opt);
-};
-
-static void fb_run_command(struct f_fastboot *f_fb, const char *cmdbuf,
-		const struct cmd_dispatch_info *cmds, int num_commands)
-{
-	const struct cmd_dispatch_info *cmd;
-	int i;
-
-	console_countdown_abort();
-
-	for (i = 0; i < num_commands; i++) {
-		cmd = &cmds[i];
-
-		if (!strcmp_l1(cmd->cmd, cmdbuf)) {
-			cmd->cb(f_fb, cmdbuf + strlen(cmd->cmd));
-
-			return;
-		}
-	}
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "unknown command %s",
-			  cmdbuf);
-}
-
-static void cb_oem_getenv(struct f_fastboot *f_fb, const char *cmd)
-{
-	const char *value;
-
-	pr_debug("%s: \"%s\"\n", __func__, cmd);
-
-	value = getenv(cmd);
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_INFO, value ? value : "");
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-}
-
-static void cb_oem_setenv(struct f_fastboot *f_fb, const char *cmd)
-{
-	char *var = xstrdup(cmd);
-	char *value;
-	int ret;
-
-	pr_debug("%s: \"%s\"\n", __func__, cmd);
-
-	value = strchr(var, '=');
-	if (!value) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	*value++ = 0;
-
-	ret = setenv(var, value);
-	if (ret)
-		goto out;
-
-	fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-out:
-	free(var);
-
-	if (ret)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret));
-}
-
-static void cb_oem_exec(struct f_fastboot *f_fb, const char *cmd)
+static void fastboot_start_download_usb(struct fastboot *fb)
 {
-	int ret;
+	struct f_fastboot *f_fb = container_of(fb, struct f_fastboot, fastboot);
+	struct usb_request *req = f_fb->out_req;
 
-	if (!IS_ENABLED(CONFIG_COMMAND_SUPPORT)) {
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL,
-				  "no command support available");
-		return;
-	}
-
-	ret = run_command(cmd);
-	if (ret < 0)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, strerror(-ret));
-	else if (ret > 0)
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_FAIL, "");
-	else
-		fastboot_tx_print(f_fb, FASTBOOT_MSG_OKAY, "");
-}
-
-static const struct cmd_dispatch_info cmd_oem_dispatch_info[] = {
-	{
-		.cmd = "getenv ",
-		.cb = cb_oem_getenv,
-	}, {
-		.cmd = "setenv ",
-		.cb = cb_oem_setenv,
-	}, {
-		.cmd = "exec ",
-		.cb = cb_oem_exec,
-	},
-};
-
-static void __maybe_unused cb_oem(struct f_fastboot *f_fb, const char *cmd)
-{
-	pr_debug("%s: \"%s\"\n", __func__, cmd);
-
-	fb_run_command(f_fb, cmd, cmd_oem_dispatch_info, ARRAY_SIZE(cmd_oem_dispatch_info));
+	req->complete = rx_handler_dl_image;
+	req->length = rx_bytes_expected(f_fb);
+	fastboot_start_download_generic(fb);
 }
 
-static const struct cmd_dispatch_info cmd_dispatch_info[] = {
-	{
-		.cmd = "reboot",
-		.cb = cb_reboot,
-	}, {
-		.cmd = "getvar:",
-		.cb = cb_getvar,
-	}, {
-		.cmd = "download:",
-		.cb = cb_download,
-#if defined(CONFIG_BOOTM)
-	}, {
-		.cmd = "boot",
-		.cb = cb_boot,
-#endif
-	}, {
-		.cmd = "flash:",
-		.cb = cb_flash,
-	}, {
-		.cmd = "erase:",
-		.cb = cb_erase,
-#if defined(CONFIG_USB_GADGET_FASTBOOT_CMD_OEM)
-	}, {
-		.cmd = "oem ",
-		.cb = cb_oem,
-#endif
-	},
-};
-
 static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
 {
 	char *cmdbuf = req->buf;
 	struct f_fastboot *f_fb = req->context;
-	int ret;
 
 	if (req->status != 0)
 		return;
 
-	f_fb->active = true;
-
 	*(cmdbuf + req->actual) = 0;
-
-	if (f_fb->cmd_exec) {
-		ret = f_fb->cmd_exec(f_fb, cmdbuf);
-		if (ret != FASTBOOT_CMD_FALLTHROUGH)
-			goto done;
-	}
-
-	fb_run_command(f_fb, cmdbuf, cmd_dispatch_info,
-				ARRAY_SIZE(cmd_dispatch_info));
-done:
+	fastboot_exec_cmd(&f_fb->fastboot, cmdbuf);
 	*cmdbuf = '\0';
 	req->actual = 0;
 	memset(req->buf, 0, EP_BUFFER_SIZE);
 	usb_ep_queue(ep, req);
 }
-
-static int fastboot_globalvars_init(void)
-{
-	if (IS_ENABLED(CONFIG_USB_GADGET_FASTBOOT_SPARSE))
-		globalvar_add_simple_int("usbgadget.fastboot_max_download_size",
-				 &fastboot_max_download_size, "%u");
-
-	return 0;
-}
-
-device_initcall(fastboot_globalvars_init);
-
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size,
-		       global.usbgadget.fastboot_max_download_size,
-		       "Fastboot maximum download size");
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index d6edfb8cf..445cc3242 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -127,10 +127,7 @@ static int multi_bind_fastboot(struct usb_composite_dev *cdev)
 	}
 
 	opts = container_of(fi_fastboot, struct f_fastboot_opts, func_inst);
-	opts->files = gadget_multi_opts->fastboot_opts.files;
-	opts->cmd_exec = gadget_multi_opts->fastboot_opts.cmd_exec;
-	opts->cmd_flash = gadget_multi_opts->fastboot_opts.cmd_flash;
-	opts->export_bbu = gadget_multi_opts->fastboot_opts.export_bbu;
+	opts->common = gadget_multi_opts->fastboot_opts;
 
 	f_fastboot = usb_get_function(fi_fastboot);
 	if (IS_ERR(f_fastboot)) {
diff --git a/include/fastboot.h b/include/fastboot.h
new file mode 100644
index 000000000..3b6cae8a5
--- /dev/null
+++ b/include/fastboot.h
@@ -0,0 +1,66 @@
+#ifndef __FASTBOOT__
+#define __FASTBOOT__
+
+#include <common.h>
+#include <file-list.h>
+#include <net.h>
+
+/*
+ * Return codes for the exec_cmd callback above:
+ *
+ * FASTBOOT_CMD_FALLTHROUGH - Not handled by the external command dispatcher,
+ *                            handle it with internal dispatcher
+ * Other than these negative error codes mean errors handling the command and
+ * zero means the command has been successfully handled.
+ */
+#define FASTBOOT_CMD_FALLTHROUGH	1
+
+struct fastboot {
+	int (*write)(struct fastboot *fb, const char *buf, unsigned int n);
+	void (*start_download)(struct fastboot *fb);
+
+	struct file_list *files;
+	int (*cmd_exec)(struct fastboot *fb, const char *cmd);
+	int (*cmd_flash)(struct fastboot *fb, struct file_list_entry *entry,
+			 const char *filename, const void *buf, size_t len);
+	int download_fd;
+	void *buf;
+
+	bool active;
+
+	size_t download_bytes;
+	size_t download_size;
+	struct list_head variables;
+};
+
+/**
+ * struct fastboot_opts - options to configure fastboot
+ * @files:	A file_list containing the files (partitions) to export via fastboot
+ * @export_bbu:	Automatically include the partitions provided by barebox update (bbu)
+ */
+struct fastboot_opts {
+	struct file_list *files;
+	bool export_bbu;
+	int (*cmd_exec)(struct fastboot *fb, const char *cmd);
+	int (*cmd_flash)(struct fastboot *fb, struct file_list_entry *entry,
+			 const char *filename, const void *buf, size_t len);
+};
+
+enum fastboot_msg_type {
+	FASTBOOT_MSG_OKAY,
+	FASTBOOT_MSG_FAIL,
+	FASTBOOT_MSG_INFO,
+	FASTBOOT_MSG_DATA,
+};
+
+int fastboot_generic_init(struct fastboot *fb, bool export_bbu);
+void fastboot_generic_close(struct fastboot *fb);
+void fastboot_generic_free(struct fastboot *fb);
+int fastboot_handle_download_data(struct fastboot *fb, const void *buffer,
+				  unsigned int len);
+int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type,
+		      const char *fmt, ...);
+void fastboot_start_download_generic(struct fastboot *fb);
+void fastboot_download_finished(struct fastboot *fb);
+void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf);
+#endif
diff --git a/include/usb/fastboot.h b/include/usb/fastboot.h
index c0775c67d..a3609ba5d 100644
--- a/include/usb/fastboot.h
+++ b/include/usb/fastboot.h
@@ -1,45 +1,17 @@
 #ifndef _USB_FASTBOOT_H
 #define _USB_FASTBOOT_H
 
-#include <linux/types.h>
-#include <file-list.h>
 #include <usb/composite.h>
-
-struct f_fastboot;
+#include <fastboot.h>
 
 /**
  * struct f_fastboot_opts - options to configure the fastboot gadget
+ * @common:	Options common to all fastboot protocol variants
  * @func_inst:	The USB function instance to register on
- * @files:	A file_list containing the files (partitions) to export via fastboot
- * @export_bbu:	Automatically include the partitions provided by barebox update (bbu)
  */
 struct f_fastboot_opts {
+	struct fastboot_opts common;
 	struct usb_function_instance func_inst;
-	struct file_list *files;
-	bool export_bbu;
-	int (*cmd_exec)(struct f_fastboot *, const char *cmd);
-	int (*cmd_flash)(struct f_fastboot *, struct file_list_entry *entry,
-			 const char *filename, const void *buf, size_t len);
-};
-
-/*
- * Return codes for the exec_cmd callback above:
- *
- * FASTBOOT_CMD_FALLTHROUGH - Not handled by the external command dispatcher,
- *                            handle it with internal dispatcher
- * Other than these negative error codes mean errors handling the command and
- * zero means the command has been successfully handled.
- */
-#define FASTBOOT_CMD_FALLTHROUGH	1
-
-enum fastboot_msg_type {
-	FASTBOOT_MSG_OKAY,
-	FASTBOOT_MSG_FAIL,
-	FASTBOOT_MSG_INFO,
-	FASTBOOT_MSG_DATA,
 };
 
-int fastboot_tx_print(struct f_fastboot *f_fb, enum fastboot_msg_type type,
-		      const char *fmt, ...);
-
 #endif /* _USB_FASTBOOT_H */
diff --git a/include/usb/gadget-multi.h b/include/usb/gadget-multi.h
index 030e604fe..9bb6c889f 100644
--- a/include/usb/gadget-multi.h
+++ b/include/usb/gadget-multi.h
@@ -6,7 +6,7 @@
 #include <usb/usbserial.h>
 
 struct f_multi_opts {
-	struct f_fastboot_opts fastboot_opts;
+	struct fastboot_opts fastboot_opts;
 	struct f_dfu_opts dfu_opts;
 	int create_acm;
 	void (*release)(struct f_multi_opts *opts);
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 07/10] defconfigs: update renamed fastboot options
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (5 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 06/10] fastboot: split generic code from USB gadget Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 08/10] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Daniel Glöckner
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

The split of the generic Fastboot code from the Fastboot USB gadget
included renaming the CONFIG_USB_GADGET_FASTBOOT_* options to
CONFIG_FASTBOOT_*. Update all defconfigs to use the new names.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 arch/arm/configs/imx23_defconfig         | 2 +-
 arch/arm/configs/imx28_defconfig         | 2 +-
 arch/arm/configs/imx_v7_defconfig        | 2 +-
 arch/arm/configs/imx_v8_defconfig        | 2 +-
 arch/arm/configs/kindle-mx50_defconfig   | 2 +-
 arch/arm/configs/omap_defconfig          | 2 +-
 arch/arm/configs/zii_vf610_dev_defconfig | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/arm/configs/imx23_defconfig b/arch/arm/configs/imx23_defconfig
index bff9c08c4..48bf14a39 100644
--- a/arch/arm/configs/imx23_defconfig
+++ b/arch/arm/configs/imx23_defconfig
@@ -22,6 +22,7 @@ CONFIG_BLSPEC=y
 CONFIG_CONSOLE_ALLOW_COLOR=y
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_RESET_SOURCE=y
+CONFIG_FASTBOOT_CMD_OEM=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
@@ -90,7 +91,6 @@ CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DFU=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_CMD_OEM=y
 CONFIG_VIDEO=y
 CONFIG_DRIVER_VIDEO_STM=y
 CONFIG_MCI=y
diff --git a/arch/arm/configs/imx28_defconfig b/arch/arm/configs/imx28_defconfig
index 1acc8dd94..b35694f50 100644
--- a/arch/arm/configs/imx28_defconfig
+++ b/arch/arm/configs/imx28_defconfig
@@ -23,6 +23,7 @@ CONFIG_BLSPEC=y
 CONFIG_CONSOLE_ALLOW_COLOR=y
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_RESET_SOURCE=y
+CONFIG_FASTBOOT_CMD_OEM=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
@@ -93,7 +94,6 @@ CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DFU=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_CMD_OEM=y
 CONFIG_VIDEO=y
 CONFIG_DRIVER_VIDEO_STM=y
 CONFIG_MCI=y
diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig
index 5bf908ee8..fb7872434 100644
--- a/arch/arm/configs/imx_v7_defconfig
+++ b/arch/arm/configs/imx_v7_defconfig
@@ -66,6 +66,7 @@ CONFIG_CONSOLE_ACTIVATE_NONE=y
 CONFIG_PARTITION_DISK_EFI=y
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_RESET_SOURCE=y
+CONFIG_FASTBOOT_CMD_OEM=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
@@ -160,7 +161,6 @@ CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DFU=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_CMD_OEM=y
 CONFIG_VIDEO=y
 CONFIG_DRIVER_VIDEO_IMX_IPUV3=y
 CONFIG_DRIVER_VIDEO_IMX_IPUV3_LVDS=y
diff --git a/arch/arm/configs/imx_v8_defconfig b/arch/arm/configs/imx_v8_defconfig
index 06fb40608..b96867ad0 100644
--- a/arch/arm/configs/imx_v8_defconfig
+++ b/arch/arm/configs/imx_v8_defconfig
@@ -25,6 +25,7 @@ CONFIG_CONSOLE_RATP=y
 CONFIG_PARTITION_DISK_EFI=y
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_RESET_SOURCE=y
+CONFIG_FASTBOOT_SPARSE=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
@@ -106,7 +107,6 @@ CONFIG_USB_STORAGE=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_SPARSE=y
 CONFIG_MCI=y
 CONFIG_MCI_MMC_BOOT_PARTITIONS=y
 CONFIG_MCI_IMX_ESDHC=y
diff --git a/arch/arm/configs/kindle-mx50_defconfig b/arch/arm/configs/kindle-mx50_defconfig
index 855daef71..552b2d6d3 100644
--- a/arch/arm/configs/kindle-mx50_defconfig
+++ b/arch/arm/configs/kindle-mx50_defconfig
@@ -20,6 +20,7 @@ CONFIG_BOOTM_OFTREE=y
 CONFIG_CONSOLE_ACTIVATE_ALL=y
 CONFIG_CONSOLE_ALLOW_COLOR=y
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
+CONFIG_FASTBOOT_CMD_OEM=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_MEMINFO=y
@@ -50,7 +51,6 @@ CONFIG_USB_EHCI=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_CMD_OEM=y
 CONFIG_MCI=y
 CONFIG_MCI_STARTUP=y
 CONFIG_MCI_MMC_BOOT_PARTITIONS=y
diff --git a/arch/arm/configs/omap_defconfig b/arch/arm/configs/omap_defconfig
index 9d71d0274..59892cb23 100644
--- a/arch/arm/configs/omap_defconfig
+++ b/arch/arm/configs/omap_defconfig
@@ -34,6 +34,7 @@ CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_STATE=y
 CONFIG_BOOTCHOOSER=y
 CONFIG_RESET_SOURCE=y
+CONFIG_FASTBOOT_CMD_OEM=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
 CONFIG_CMD_MEMINFO=y
@@ -121,7 +122,6 @@ CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DFU=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_CMD_OEM=y
 CONFIG_USB_MUSB=y
 CONFIG_USB_MUSB_AM335X=y
 CONFIG_USB_MUSB_HOST=y
diff --git a/arch/arm/configs/zii_vf610_dev_defconfig b/arch/arm/configs/zii_vf610_dev_defconfig
index 7161d740a..45c24d6df 100644
--- a/arch/arm/configs/zii_vf610_dev_defconfig
+++ b/arch/arm/configs/zii_vf610_dev_defconfig
@@ -22,6 +22,7 @@ CONFIG_CONSOLE_RATP=y
 CONFIG_PARTITION_DISK_EFI=y
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_RESET_SOURCE=y
+CONFIG_FASTBOOT_CMD_OEM=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
@@ -113,7 +114,6 @@ CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DFU=y
 CONFIG_USB_GADGET_SERIAL=y
 CONFIG_USB_GADGET_FASTBOOT=y
-CONFIG_USB_GADGET_FASTBOOT_CMD_OEM=y
 CONFIG_MCI=y
 CONFIG_MCI_MMC_BOOT_PARTITIONS=y
 CONFIG_MCI_IMX_ESDHC=y
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 08/10] fastboot: rename usbgadget.fastboot_* variables to fastboot.*
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (6 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 07/10] defconfigs: update renamed fastboot options Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-20  4:55   ` Sascha Hauer
  2020-05-14 18:21 ` [PATCH v2 09/10] fastboot net: implement fastboot over UDP Daniel Glöckner
  2020-05-14 18:21 ` [PATCH v2 10/10] fastboot: don't close fd 0 when downloading to ram Daniel Glöckner
  9 siblings, 1 reply; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

There is nothing USB-specific in the defined usbgadget.fastboot_*
variables. Rename them to be usable also for the UDP fastboot transport.

The usbgadget.fastboot_function variable is used to define the files and
devices accessible with the erase and flash commands. Since "function" is
a term from the USB specification and the Fastboot specification uses the
term "partition", we rename that variable to "fastboot.partitions".

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 Documentation/user/usb.rst |  4 ++--
 commands/usbgadget.c       |  2 +-
 common/fastboot.c          | 15 +++++++++++++--
 common/usbgadget.c         | 16 ++--------------
 include/fastboot.h         |  3 +++
 5 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
index 4c1b2925f..ca5f8138d 100644
--- a/Documentation/user/usb.rst
+++ b/Documentation/user/usb.rst
@@ -266,7 +266,7 @@ USB Gadget autostart Options
   See :ref:`command_usbgadget` -a. (Default 0).
 ``global.usbgadget.dfu_function``
   Function description for DFU. See :ref:`command_usbgadget` -D [desc].
-``global.usbgadget.fastboot_function``
+``global.fastboot.partitions``
   Function description for fastboot. See :ref:`command_usbgadget` -A [desc].
-``global.usbgadget.fastboot_bbu``
+``global.fastboot.bbu``
   Export barebox update handlers. See :ref:`command_usbgadget` -b. (Default 0).
diff --git a/commands/usbgadget.c b/commands/usbgadget.c
index e8e1e9ada..4d3810f6f 100644
--- a/commands/usbgadget.c
+++ b/commands/usbgadget.c
@@ -71,7 +71,7 @@ BAREBOX_CMD_HELP_TEXT("")
 BAREBOX_CMD_HELP_TEXT("Options:")
 BAREBOX_CMD_HELP_OPT ("-a\t", "Create CDC ACM function")
 BAREBOX_CMD_HELP_OPT ("-A <desc>", "Create Android Fastboot function. If 'desc' is not provided, "
-				   "try to use 'global.usbgadget.fastboot_function' variable.")
+				   "try to use 'global.fastboot.partitions' variable.")
 BAREBOX_CMD_HELP_OPT ("-b\t", "include registered barebox update handlers (fastboot specific)")
 BAREBOX_CMD_HELP_OPT ("-D <desc>", "Create DFU function. If 'desc' is not provided, "
 				   "try to use 'global.usbgadget.dfu_function' variable.")
diff --git a/common/fastboot.c b/common/fastboot.c
index d58f68f1b..d50f61ff2 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -48,6 +48,8 @@
 #define FASTBOOT_TMPFILE		"/.fastboot.img"
 
 static unsigned int fastboot_max_download_size = SZ_8M;
+int fastboot_bbu;
+char *fastboot_partitions;
 
 struct fb_variable {
 	char *name;
@@ -946,12 +948,21 @@ static int fastboot_globalvars_init(void)
 	if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE))
 		globalvar_add_simple_int("usbgadget.fastboot_max_download_size",
 				 &fastboot_max_download_size, "%u");
+	globalvar_add_simple_bool("fastboot.bbu", &fastboot_bbu);
+	globalvar_add_simple_string("fastboot.partitions",
+				    &fastboot_partitions);
 
 	return 0;
 }
 
 device_initcall(fastboot_globalvars_init);
 
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size,
-		       global.usbgadget.fastboot_max_download_size,
+BAREBOX_MAGICVAR_NAMED(global_fastboot_max_download_size,
+		       global.fastboot.max_download_size,
 		       "Fastboot maximum download size");
+BAREBOX_MAGICVAR_NAMED(global_fastboot_partitions,
+		       global.fastboot.partitions,
+		       "Partitions exported for update via fastboot");
+BAREBOX_MAGICVAR_NAMED(global_fastboot_bbu,
+		       global.fastboot.bbu,
+		       "Export barebox update handlers via fastboot");
diff --git a/common/usbgadget.c b/common/usbgadget.c
index a8f104cf1..25fd99493 100644
--- a/common/usbgadget.c
+++ b/common/usbgadget.c
@@ -30,8 +30,6 @@
 static int autostart;
 static int acm;
 static char *dfu_function;
-static char *fastboot_function;
-static int fastboot_bbu;
 
 static struct file_list *parse(const char *files)
 {
@@ -56,8 +54,8 @@ int usbgadget_register(bool dfu, const char *dfu_opts,
 		dfu_opts = dfu_function;
 
 	if (fastboot && !fastboot_opts &&
-	    fastboot_function && *fastboot_function)
-		fastboot_opts = fastboot_function;
+	    fastboot_partitions && *fastboot_partitions)
+		fastboot_opts = fastboot_partitions;
 
 	if (!dfu_opts && !fastboot_opts && !acm)
 		return COMMAND_ERROR_USAGE;
@@ -116,12 +114,8 @@ static int usbgadget_globalvars_init(void)
 	if (IS_ENABLED(CONFIG_USB_GADGET_AUTOSTART)) {
 		globalvar_add_simple_bool("usbgadget.autostart", &autostart);
 		globalvar_add_simple_bool("usbgadget.acm", &acm);
-		globalvar_add_simple_bool("usbgadget.fastboot_bbu",
-					  &fastboot_bbu);
 	}
 	globalvar_add_simple_string("usbgadget.dfu_function", &dfu_function);
-	globalvar_add_simple_string("usbgadget.fastboot_function",
-				    &fastboot_function);
 
 	return 0;
 }
@@ -136,9 +130,3 @@ BAREBOX_MAGICVAR_NAMED(global_usbgadget_acm,
 BAREBOX_MAGICVAR_NAMED(global_usbgadget_dfu_function,
 		       global.usbgadget.dfu_function,
 		       "usbgadget: Create DFU function");
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_function,
-		       global.usbgadget.fastboot_function,
-		       "usbgadget: Create Android Fastboot function");
-BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_bbu,
-		       global.usbgadget.fastboot_bbu,
-		       "usbgadget: export barebox update handlers via fastboot");
diff --git a/include/fastboot.h b/include/fastboot.h
index 3b6cae8a5..7df137bb8 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -53,6 +53,9 @@ enum fastboot_msg_type {
 	FASTBOOT_MSG_DATA,
 };
 
+extern int fastboot_bbu;
+extern char *fastboot_partitions;
+
 int fastboot_generic_init(struct fastboot *fb, bool export_bbu);
 void fastboot_generic_close(struct fastboot *fb);
 void fastboot_generic_free(struct fastboot *fb);
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 09/10] fastboot net: implement fastboot over UDP
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (7 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 08/10] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  2020-05-20  5:52   ` Sascha Hauer
  2020-05-20  8:17   ` Sascha Hauer
  2020-05-14 18:21 ` [PATCH v2 10/10] fastboot: don't close fd 0 when downloading to ram Daniel Glöckner
  9 siblings, 2 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Edmund Henniges, Daniel Glöckner

From: Edmund Henniges <eh@emlix.com>

This implements the UDP variant of the fastboot protocol. The only way to
start the service for now is to compile with CONFIG_FASTBOOT_NET_ON_BOOT.
The service will bind to the network interface that provides the IPv4
gateway.

Sending an OKAY packet before performing a restart is necessary since
contrary to USB the host will not notice when a UDP server disappears.

Signed-off-by: Edmund Henniges <eh@emlix.com>
Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 common/fastboot.c      |   3 +
 include/fastboot.h     |   1 +
 include/fastboot_net.h |  12 +
 net/Kconfig            |  18 ++
 net/Makefile           |   1 +
 net/fastboot.c         | 496 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 531 insertions(+)
 create mode 100644 include/fastboot_net.h
 create mode 100644 net/fastboot.c

diff --git a/common/fastboot.c b/common/fastboot.c
index d50f61ff2..706547309 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -247,6 +247,7 @@ static char *fastboot_msg[] = {
 	[FASTBOOT_MSG_FAIL] = "FAIL",
 	[FASTBOOT_MSG_INFO] = "INFO",
 	[FASTBOOT_MSG_DATA] = "DATA",
+	[FASTBOOT_MSG_NONE] = "",
 };
 
 int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type,
@@ -275,6 +276,7 @@ int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type,
 	case FASTBOOT_MSG_INFO:
 		pr_info("%pV\n", &vaf);
 		break;
+	case FASTBOOT_MSG_NONE:
 	case FASTBOOT_MSG_DATA:
 		break;
 	}
@@ -289,6 +291,7 @@ int fastboot_tx_print(struct fastboot *fb, enum fastboot_msg_type type,
 
 static void cb_reboot(struct fastboot *fb, const char *cmd)
 {
+	fastboot_tx_print(fb, FASTBOOT_MSG_OKAY, "");
 	restart_machine();
 }
 
diff --git a/include/fastboot.h b/include/fastboot.h
index 7df137bb8..eb1971209 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -51,6 +51,7 @@ enum fastboot_msg_type {
 	FASTBOOT_MSG_FAIL,
 	FASTBOOT_MSG_INFO,
 	FASTBOOT_MSG_DATA,
+	FASTBOOT_MSG_NONE,
 };
 
 extern int fastboot_bbu;
diff --git a/include/fastboot_net.h b/include/fastboot_net.h
new file mode 100644
index 000000000..e4b9d9809
--- /dev/null
+++ b/include/fastboot_net.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __FASTBOOT_NET__
+#define __FASTBOOT_NET__
+
+#include <fastboot.h>
+
+struct fastboot_net;
+
+struct fastboot_net *fastboot_net_init(struct fastboot_opts *opts);
+void fastboot_net_free(struct fastboot_net *fbn);
+
+#endif
diff --git a/net/Kconfig b/net/Kconfig
index 12b6bdb56..4ed5c6de9 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -31,4 +31,22 @@ config NET_SNTP
 	bool
 	prompt "sntp support"
 
+config NET_FASTBOOT
+	bool
+	select BANNER
+	select FILE_LIST
+	select FASTBOOT_BASE
+	select IDLE_SLICE
+	prompt "Android Fastboot support"
+	help
+	  This option adds support for the UDP variant of the Fastboot
+	  protocol.
+
+config FASTBOOT_NET_ON_BOOT
+	bool
+	depends on NET_FASTBOOT
+	prompt "Start network fastboot during boot"
+	help
+	  Automatically starts the network and listens for Fastboot packets.
+	  The list of accessible files is taken from global.fastboot.partitions.
 endif
diff --git a/net/Makefile b/net/Makefile
index eb8d43915..962b2dec5 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_CMD_PING)	+= ping.o
 obj-$(CONFIG_NET_RESOLV)+= dns.o
 obj-$(CONFIG_NET_NETCONSOLE) += netconsole.o
 obj-$(CONFIG_NET_IFUP)	+= ifup.o
+obj-$(CONFIG_NET_FASTBOOT) += fastboot.o
diff --git a/net/fastboot.c b/net/fastboot.c
new file mode 100644
index 000000000..672d78164
--- /dev/null
+++ b/net/fastboot.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Copyright 2020 Edmund Henniges <eh@emlix.com>
+ * Copyright 2020 Daniel Glöckner <dg@emlix.com>
+ * Ported from U-Boot to Barebox
+ */
+
+#define pr_fmt(fmt) "net fastboot: " fmt
+
+#include <common.h>
+#include <net.h>
+#include <fastboot.h>
+#include <fastboot_net.h>
+#include <environment.h>
+#include <progress.h>
+#include <unistd.h>
+#include <init.h>
+
+#define FASTBOOT_PORT 5554
+#define MAX_MTU 1500
+#define PACKET_SIZE (min(PKTSIZE, MAX_MTU + ETHER_HDR_SIZE) \
+		      - (net_eth_to_udp_payload(0) - (char *)0))
+
+enum {
+	FASTBOOT_ERROR = 0,
+	FASTBOOT_QUERY = 1,
+	FASTBOOT_INIT = 2,
+	FASTBOOT_FASTBOOT = 3,
+};
+
+enum {
+	MAY_NOT_SEND,
+	MAY_SEND_MESSAGE,
+	MAY_SEND_ACK,
+};
+
+struct __packed fastboot_header {
+	u8 id;
+	u8 flags;
+	u16 seq;
+};
+
+struct fastboot_net {
+	struct fastboot fastboot;
+
+	struct net_connection *net_con;
+	struct fastboot_header response_header;
+	struct poller_struct poller;
+	struct poller_struct keep_alive_poller;
+	u64 host_waits_since;
+	bool sequence_number_seen;
+	bool active_download;
+	bool download_needs_cleanup;
+	bool reinit;
+	u8 may_send;
+	char command[65];
+
+	IPaddr_t host_addr;
+	u16 host_port;
+	u8 host_mac[ETH_ALEN];
+	u16 sequence_number;
+	u16 last_payload_len;
+	uchar last_payload[64 + sizeof(struct fastboot_header)];
+};
+
+static const ushort udp_version = 1;
+
+static bool is_current_connection(struct fastboot_net *fbn)
+{
+	return fbn->host_addr == net_read_ip(&fbn->net_con->ip->daddr) &&
+	       fbn->host_port == fbn->net_con->udp->uh_dport;
+}
+
+static void fastboot_send(struct fastboot_net *fbn,
+			  struct fastboot_header header,
+			  const char *error_msg)
+{
+	short tmp;
+	uchar *packet = net_udp_get_payload(fbn->net_con);
+	uchar *packet_base = packet;
+	bool current_session = false;
+
+	if (fbn->sequence_number == ntohs(header.seq) &&
+	    is_current_connection(fbn))
+		current_session = true;
+
+	if (error_msg)
+		header.id = FASTBOOT_ERROR;
+
+	/* send header */
+	memcpy(packet, &header, sizeof(header));
+	packet += sizeof(header);
+
+	switch (header.id) {
+	case FASTBOOT_QUERY:
+		/* send sequence number */
+		tmp = htons(fbn->sequence_number);
+		memcpy(packet, &tmp, sizeof(tmp));
+		packet += sizeof(tmp);
+		break;
+	case FASTBOOT_INIT:
+		/* send udp version and packet size */
+		tmp = htons(udp_version);
+		memcpy(packet, &tmp, sizeof(tmp));
+		packet += sizeof(tmp);
+		tmp = htons(PACKET_SIZE);
+		memcpy(packet, &tmp, sizeof(tmp));
+		packet += sizeof(tmp);
+		break;
+	case FASTBOOT_ERROR:
+		pr_err("%s\n", error_msg);
+
+		/* send error message */
+		tmp = strlen(error_msg);
+		memcpy(packet, error_msg, tmp);
+		packet += tmp;
+
+		if (current_session) {
+			fbn->fastboot.active = false;
+			fbn->active_download = false;
+			fbn->reinit = true;
+		}
+		break;
+	}
+
+	if (current_session && header.id != FASTBOOT_QUERY) {
+		fbn->sequence_number++;
+		fbn->sequence_number_seen = false;
+		fbn->last_payload_len = packet - packet_base;
+		memcpy(fbn->last_payload, packet_base, fbn->last_payload_len);
+	}
+	net_udp_send(fbn->net_con, packet - packet_base);
+}
+
+static int fastboot_write_net(struct fastboot *fb, const char *buf,
+			      unsigned int n)
+{
+	struct fastboot_net *fbn = container_of(fb, struct fastboot_net,
+						fastboot);
+	struct fastboot_header response_header;
+	uchar *packet;
+	uchar *packet_base;
+
+	if (fbn->reinit)
+		return 0;
+
+	while (fbn->may_send == MAY_NOT_SEND) {
+		poller_call();
+		if (fbn->reinit)
+			return 0;
+	}
+
+	if (n && fbn->may_send == MAY_SEND_ACK) {
+		fastboot_send(fbn, fbn->response_header,
+			      "Have message but only ACK allowed");
+		return -EPROTO;
+	} else if (!n && fbn->may_send == MAY_SEND_MESSAGE) {
+		fastboot_send(fbn, fbn->response_header,
+			      "Want to send ACK but message expected");
+		return -EPROTO;
+	}
+
+	response_header = fbn->response_header;
+	response_header.flags = 0;
+	response_header.seq = htons(fbn->sequence_number);
+	++fbn->sequence_number;
+	fbn->sequence_number_seen = false;
+	fbn->may_send = MAY_NOT_SEND;
+
+	packet = net_udp_get_payload(fbn->net_con);
+	packet_base = packet;
+
+	/* Write headers */
+	memcpy(packet, &response_header, sizeof(response_header));
+	packet += sizeof(response_header);
+	/* Write response */
+	memcpy(packet, buf, n);
+	packet += n;
+
+	/* Save packet for retransmitting */
+	fbn->last_payload_len = packet - packet_base;
+	memcpy(fbn->last_payload, packet_base, fbn->last_payload_len);
+
+	memcpy(fbn->net_con->et->et_dest, fbn->host_mac, ETH_ALEN);
+	net_write_ip(&fbn->net_con->ip->daddr, fbn->host_addr);
+	fbn->net_con->udp->uh_dport = fbn->host_port;
+	net_udp_send(fbn->net_con, fbn->last_payload_len);
+
+	return 0;
+}
+
+static void fastboot_start_download_net(struct fastboot *fb)
+{
+	struct fastboot_net *fbn = container_of(fb, struct fastboot_net,
+						fastboot);
+
+	fastboot_start_download_generic(fb);
+	fbn->active_download = true;
+}
+
+/* must send exactly one packet on all code paths */
+static void fastboot_data_download(struct fastboot_net *fbn,
+				   const void *fastboot_data,
+				   unsigned int fastboot_data_len)
+{
+	int ret;
+
+	if (fastboot_data_len == 0 ||
+	    (fbn->fastboot.download_bytes + fastboot_data_len) >
+	    fbn->fastboot.download_size) {
+		fastboot_send(fbn, fbn->response_header,
+			      "Received invalid data length");
+		return;
+	}
+
+	ret = fastboot_handle_download_data(&fbn->fastboot, fastboot_data,
+					    fastboot_data_len);
+	if (ret < 0) {
+		fastboot_send(fbn, fbn->response_header, strerror(-ret));
+		return;
+	}
+
+	fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_NONE, "");
+}
+
+static void fastboot_handle_type_fastboot(struct fastboot_net *fbn,
+					  struct fastboot_header header,
+					  char *fastboot_data,
+					  unsigned int fastboot_data_len)
+{
+	fbn->response_header = header;
+	fbn->host_waits_since = get_time_ns();
+	fbn->may_send = fastboot_data_len ? MAY_SEND_ACK : MAY_SEND_MESSAGE;
+	if (fbn->active_download) {
+		if (!fastboot_data_len && fbn->fastboot.download_bytes
+					   == fbn->fastboot.download_size) {
+			/*
+			 * Can't call fastboot_download_finished here
+			 * because it will call fastboot_tx_print
+			 * multiple times.
+			 */
+			fbn->active_download = false;
+		} else {
+			fastboot_data_download(fbn, fastboot_data,
+					       fastboot_data_len);
+		}
+	} else if (!fbn->command[0]) {
+		if (fastboot_data_len >= sizeof(fbn->command)) {
+			fastboot_send(fbn, header, "command too long");
+		} else {
+			memcpy(fbn->command, fastboot_data, fastboot_data_len);
+			fbn->command[fastboot_data_len] = 0;
+		}
+	}
+
+	if (!fbn->fastboot.active)
+		fbn->active_download = false;
+}
+
+static void fastboot_check_retransmit(struct fastboot_net *fbn,
+				      struct fastboot_header header)
+{
+	if (ntohs(header.seq) == fbn->sequence_number - 1 &&
+	    is_current_connection(fbn)) {
+		/* Retransmit last sent packet */
+		memcpy(net_udp_get_payload(fbn->net_con),
+		       fbn->last_payload, fbn->last_payload_len);
+		net_udp_send(fbn->net_con, fbn->last_payload_len);
+	}
+}
+
+static void fastboot_handler(void *ctx, char *packet, unsigned int raw_len)
+{
+	unsigned int len = net_eth_to_udplen(packet);
+	struct ethernet *eth_header = (struct ethernet *)packet;
+	struct iphdr *ip_header = net_eth_to_iphdr(packet);
+	struct udphdr *udp_header = net_eth_to_udphdr(packet);
+	char *payload = net_eth_to_udp_payload(packet);
+	struct fastboot_net *fbn = ctx;
+	struct fastboot_header header;
+	char *fastboot_data = payload + sizeof(header);
+	u16 tot_len = ntohs(ip_header->tot_len);
+
+	/* catch bogus tot_len values */
+	if ((char *)ip_header - packet + tot_len > raw_len)
+		return;
+
+	/* catch packets split into fragments that are too small to reply */
+	if (fastboot_data - (char *)ip_header > tot_len)
+		return;
+
+	/* catch packets too small to be valid */
+	if (len < sizeof(struct fastboot_header))
+		return;
+
+	memcpy(&header, payload, sizeof(header));
+	header.flags = 0;
+	len -= sizeof(header);
+
+	/* catch remaining fragmented packets */
+	if (fastboot_data - (char *)ip_header + len > tot_len) {
+		fastboot_send(fbn, header,
+			      "can't reassemble fragmented frames");
+		return;
+	}
+	/* catch too large packets */
+	if (len > PACKET_SIZE) {
+		fastboot_send(fbn, header, "packet too large");
+		return;
+	}
+
+	memcpy(fbn->net_con->et->et_dest, eth_header->et_src, ETH_ALEN);
+	net_copy_ip(&fbn->net_con->ip->daddr, &ip_header->saddr);
+	fbn->net_con->udp->uh_dport = udp_header->uh_sport;
+
+	switch (header.id) {
+	case FASTBOOT_QUERY:
+		fastboot_send(fbn, header, NULL);
+		break;
+	case FASTBOOT_INIT:
+		if (ntohs(header.seq) != fbn->sequence_number) {
+			fastboot_check_retransmit(fbn, header);
+			break;
+		}
+		fbn->host_addr = net_read_ip(&ip_header->saddr);
+		fbn->host_port = udp_header->uh_sport;
+		memcpy(fbn->host_mac, eth_header->et_src, ETH_ALEN);
+		fbn->reinit = true;
+		if (fbn->active_download) {
+			/*
+			 * it is safe to call
+			 * fastboot_download_finished here
+			 * because reinit is true
+			 */
+			fastboot_download_finished(&fbn->fastboot);
+			fbn->active_download = false;
+		}
+		fastboot_send(fbn, header, NULL);
+		break;
+	case FASTBOOT_FASTBOOT:
+		if (!is_current_connection(fbn))
+			break;
+		memcpy(fbn->host_mac, eth_header->et_src, ETH_ALEN);
+		if (ntohs(header.seq) != fbn->sequence_number) {
+			fastboot_check_retransmit(fbn, header);
+		} else if (!fbn->sequence_number_seen) {
+			fbn->sequence_number_seen = true;
+			fastboot_handle_type_fastboot(fbn, header,
+						      fastboot_data, len);
+		}
+		break;
+	default:
+		fastboot_send(fbn, header, "unknown packet type");
+		break;
+	}
+}
+
+static void fastboot_poll(struct poller_struct *poller)
+{
+	struct fastboot_net *fbn = container_of(poller, struct fastboot_net,
+					       poller);
+
+	if (fbn->download_needs_cleanup) {
+		if (fbn->active_download)
+			return;
+		if (!fbn->reinit)
+			fastboot_download_finished(&fbn->fastboot);
+		fbn->download_needs_cleanup = 0;
+	}
+
+	if (!fbn->command[0])
+		return;
+
+	fbn->reinit = false;
+	fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_NONE, "");
+	fastboot_exec_cmd(&fbn->fastboot, fbn->command);
+	fbn->command[0] = 0;
+	fbn->download_needs_cleanup = fbn->active_download;
+}
+
+static void fastboot_send_keep_alive(struct poller_struct *poller)
+{
+	struct fastboot_net *fbn = container_of(poller, struct fastboot_net,
+					       keep_alive_poller);
+
+	/*
+	 * Sending the message will reset may_send to MAY_NOT_SEND and the
+	 * ACK from the host will reset host_waits_since to the current time.
+	 */
+	if (fbn->may_send == MAY_SEND_MESSAGE &&
+	    is_timeout_non_interruptible(fbn->host_waits_since,
+					 30ULL * NSEC_PER_SEC))
+		fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_INFO,
+				  "still busy");
+}
+
+void fastboot_net_free(struct fastboot_net *fbn)
+{
+	fastboot_generic_close(&fbn->fastboot);
+	slice_del(&fbn->keep_alive_poller.slice, &fbn->net_con->edev->slice);
+	slice_del(&fbn->poller.slice, &idle_slice);
+	poller_unregister(&fbn->keep_alive_poller);
+	poller_unregister(&fbn->poller);
+	net_unregister(fbn->net_con);
+	fastboot_generic_free(&fbn->fastboot);
+	free(fbn);
+}
+
+struct fastboot_net *fastboot_net_init(struct fastboot_opts *opts)
+{
+	struct fastboot_net *fbn;
+	int ret;
+
+	fbn = xzalloc(sizeof(*fbn));
+	INIT_LIST_HEAD(&fbn->fastboot.variables);
+	fbn->fastboot.write = fastboot_write_net;
+	fbn->fastboot.start_download = fastboot_start_download_net;
+
+	if (opts) {
+		fbn->fastboot.files = opts->files;
+		fbn->fastboot.cmd_exec = opts->cmd_exec;
+		fbn->fastboot.cmd_flash = opts->cmd_flash;
+		ret = fastboot_generic_init(&fbn->fastboot, opts->export_bbu);
+	} else {
+		fbn->fastboot.files = file_list_parse(fastboot_partitions
+						      ? fastboot_partitions
+						      : "");
+		ret = fastboot_generic_init(&fbn->fastboot, fastboot_bbu);
+	}
+	if (ret)
+		goto fail_generic_init;
+
+	fbn->net_con = net_udp_new(IP_BROADCAST, FASTBOOT_PORT,
+				   fastboot_handler, fbn);
+	if (IS_ERR(fbn->net_con)) {
+		ret = PTR_ERR(fbn->net_con);
+		goto fail_net_con;
+	}
+	net_udp_bind(fbn->net_con, FASTBOOT_PORT);
+
+	eth_open(fbn->net_con->edev);
+	fbn->poller.func = fastboot_poll;
+	ret = poller_register(&fbn->poller, "fastboot-net");
+	if (ret)
+		goto fail_poller;
+
+	fbn->keep_alive_poller.func = fastboot_send_keep_alive;
+	ret = poller_register(&fbn->keep_alive_poller,
+			      "fastboot-net-keep-alive");
+	if (ret)
+		goto fail_poller2;
+
+	slice_add(&fbn->poller.slice, &idle_slice);
+	slice_add(&fbn->keep_alive_poller.slice, &fbn->net_con->edev->slice);
+
+	return fbn;
+
+fail_poller2:
+	poller_unregister(&fbn->poller);
+fail_poller:
+	net_unregister(fbn->net_con);
+fail_net_con:
+	fastboot_generic_free(&fbn->fastboot);
+fail_generic_init:
+	free(fbn);
+	return ERR_PTR(ret);
+}
+
+#ifdef CONFIG_FASTBOOT_NET_ON_BOOT
+static struct fastboot_net *fastboot_net_obj;
+
+static int fastboot_on_boot(void)
+{
+	struct fastboot_net *fbn;
+
+	ifup_all(0);
+	fbn = fastboot_net_init(NULL);
+
+	if (IS_ERR(fbn))
+		return PTR_ERR(fbn);
+
+	fastboot_net_obj = fbn;
+	return 0;
+}
+
+static void fastboot_net_exit(void)
+{
+	if (fastboot_net_obj)
+		fastboot_net_free(fastboot_net_obj);
+}
+
+postenvironment_initcall(fastboot_on_boot);
+predevshutdown_exitcall(fastboot_net_exit);
+#endif
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v2 10/10] fastboot: don't close fd 0 when downloading to ram
  2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
                   ` (8 preceding siblings ...)
  2020-05-14 18:21 ` [PATCH v2 09/10] fastboot net: implement fastboot over UDP Daniel Glöckner
@ 2020-05-14 18:21 ` Daniel Glöckner
  9 siblings, 0 replies; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-14 18:21 UTC (permalink / raw)
  To: barebox; +Cc: Daniel Glöckner

When downloading to a buffer, download_fd is never assigned a valid file
descriptor. We should therefore avoid calling close on the value found
in that variable.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 common/fastboot.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/common/fastboot.c b/common/fastboot.c
index 706547309..3fcc69f62 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -347,7 +347,8 @@ int fastboot_handle_download_data(struct fastboot *fb, const void *buffer,
 
 void fastboot_download_finished(struct fastboot *fb)
 {
-	close(fb->download_fd);
+	if (!fastboot_download_to_buf(fb))
+		close(fb->download_fd);
 
 	printf("\n");
 
-- 
2.17.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 08/10] fastboot: rename usbgadget.fastboot_* variables to fastboot.*
  2020-05-14 18:21 ` [PATCH v2 08/10] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Daniel Glöckner
@ 2020-05-20  4:55   ` Sascha Hauer
  0 siblings, 0 replies; 17+ messages in thread
From: Sascha Hauer @ 2020-05-20  4:55 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: barebox

On Thu, May 14, 2020 at 08:21:56PM +0200, Daniel Glöckner wrote:
> There is nothing USB-specific in the defined usbgadget.fastboot_*
> variables. Rename them to be usable also for the UDP fastboot transport.
> 
> The usbgadget.fastboot_function variable is used to define the files and
> devices accessible with the erase and flash commands. Since "function" is
> a term from the USB specification and the Fastboot specification uses the
> term "partition", we rename that variable to "fastboot.partitions".
> 
> Signed-off-by: Daniel Glöckner <dg@emlix.com>
> ---
>  Documentation/user/usb.rst |  4 ++--
>  commands/usbgadget.c       |  2 +-
>  common/fastboot.c          | 15 +++++++++++++--
>  common/usbgadget.c         | 16 ++--------------
>  include/fastboot.h         |  3 +++
>  5 files changed, 21 insertions(+), 19 deletions(-)
> 
> diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
>  device_initcall(fastboot_globalvars_init);
>  
> -BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_max_download_size,
> -		       global.usbgadget.fastboot_max_download_size,
> +BAREBOX_MAGICVAR_NAMED(global_fastboot_max_download_size,
> +		       global.fastboot.max_download_size,
>  		       "Fastboot maximum download size");

This renames the magicvar description for this variable, but not the
variable itself.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 09/10] fastboot net: implement fastboot over UDP
  2020-05-14 18:21 ` [PATCH v2 09/10] fastboot net: implement fastboot over UDP Daniel Glöckner
@ 2020-05-20  5:52   ` Sascha Hauer
  2020-05-20  6:57     ` Daniel Glöckner
  2020-05-20  8:17   ` Sascha Hauer
  1 sibling, 1 reply; 17+ messages in thread
From: Sascha Hauer @ 2020-05-20  5:52 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: barebox, Edmund Henniges

On Thu, May 14, 2020 at 08:21:57PM +0200, Daniel Glöckner wrote:
> From: Edmund Henniges <eh@emlix.com>
> 
> This implements the UDP variant of the fastboot protocol. The only way to
> start the service for now is to compile with CONFIG_FASTBOOT_NET_ON_BOOT.
> The service will bind to the network interface that provides the IPv4
> gateway.
> 
> Sending an OKAY packet before performing a restart is necessary since
> contrary to USB the host will not notice when a UDP server disappears.
> 
> Signed-off-by: Edmund Henniges <eh@emlix.com>
> Signed-off-by: Daniel Glöckner <dg@emlix.com>
> ---
>  common/fastboot.c      |   3 +
>  include/fastboot.h     |   1 +
>  include/fastboot_net.h |  12 +
>  net/Kconfig            |  18 ++
>  net/Makefile           |   1 +
>  net/fastboot.c         | 496 +++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 531 insertions(+)
>  create mode 100644 include/fastboot_net.h
>  create mode 100644 net/fastboot.c
> 
> diff --git a/common/fastboot.c b/common/fastboot.c
> index d50f61ff2..706547309 100644
> --- a/common/fastboot.c
> +++ b/common/fastboot.c
> +static int fastboot_write_net(struct fastboot *fb, const char *buf,
> +			      unsigned int n)
> +{
> +	struct fastboot_net *fbn = container_of(fb, struct fastboot_net,
> +						fastboot);
> +	struct fastboot_header response_header;
> +	uchar *packet;
> +	uchar *packet_base;
> +
> +	if (fbn->reinit)
> +		return 0;
> +
> +	while (fbn->may_send == MAY_NOT_SEND) {
> +		poller_call();
> +		if (fbn->reinit)
> +			return 0;
> +	}

This is a potentially endless loop I can reproducibly hit here. When I
do a "sleep 10" on the command line and at the same time start a
fastboot transfer then there's no progress. This is expected because
fastboot depends on the idle slice. However, when the "sleep 10" is over
then I get a prompt and then we are stuck in this loop.

I replaced this loop with a one second timeout loop. With this we are no
longer stuck in the loop, but the current transfer doesn't continue
anymore.

Afterwards I dropped everything around fbn->may_send completely and now
everything works fluently.

Removing these send restrictions seems the right thing to me. In the end
it's UDP, so the remote host shouldn't make any assumptions of how fast
and if at all packets arrive there.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 04/10] Introduce idle slice
  2020-05-14 18:21 ` [PATCH v2 04/10] Introduce idle slice Daniel Glöckner
@ 2020-05-20  6:03   ` Sascha Hauer
  0 siblings, 0 replies; 17+ messages in thread
From: Sascha Hauer @ 2020-05-20  6:03 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: barebox

On Thu, May 14, 2020 at 08:21:52PM +0200, Daniel Glöckner wrote:
> Some code can't foresee which resources will be used by its poller. This
> is the case especially in pollers that will execute arbitrary commands
> input by the user. With this commit a special slice is introduced that is
> released only when Barebox is waiting for console input and only when it
> is doing that outside of any command. Code that wants to depend on the
> idle slice must select CONFIG_IDLE_SLICE to make it available.
> 
> Signed-off-by: Daniel Glöckner <dg@emlix.com>
> ---
>  common/Kconfig             |  4 ++++
>  common/binfmt.c            |  3 +++
>  common/command.c           |  3 +++
>  common/console_countdown.c |  3 +++
>  common/poller.c            |  2 ++
>  common/slice.c             | 24 ++++++++++++++++++++++++
>  include/slice.h            | 10 ++++++++++
>  lib/readline.c             |  8 +++++++-
>  8 files changed, 56 insertions(+), 1 deletion(-)
> 
> diff --git a/common/Kconfig b/common/Kconfig
> index 400c0553c..2fa9140de 100644
> --- a/common/Kconfig
> +++ b/common/Kconfig
> @@ -913,6 +913,10 @@ config BAREBOXCRC32_TARGET
>  config POLLER
>  	bool "generic polling infrastructure"
>  
> +config IDLE_SLICE
> +	bool
> +	default n

We discussed config SLICE away, is there a reason to introduce config
IDLE_SLICE?

> +
>  config STATE
>  	bool "generic state infrastructure"
>  	select CRC32
> diff --git a/common/binfmt.c b/common/binfmt.c
> index f2ff62458..899f270d4 100644
> --- a/common/binfmt.c
> +++ b/common/binfmt.c
> @@ -10,6 +10,7 @@
>  #include <malloc.h>
>  #include <command.h>
>  #include <errno.h>
> +#include <slice.h>
>  
>  static LIST_HEAD(binfmt_hooks);
>  
> @@ -23,7 +24,9 @@ static int binfmt_run(char *file, int argc, char **argv)
>  		if (b->type != type)
>  			continue;
>  
> +		idle_slice_acquire();
>  		ret = b->hook(b, file, argc, argv);
> +		idle_slice_release();
>  		if (ret != -ERESTARTNOHAND)
>  			return ret;
>  	}
> diff --git a/common/command.c b/common/command.c
> index 49845938a..2b10248f5 100644
> --- a/common/command.c
> +++ b/common/command.c
> @@ -30,6 +30,7 @@
>  #include <init.h>
>  #include <complete.h>
>  #include <getopt.h>
> +#include <slice.h>
>  
>  LIST_HEAD(command_list);
>  EXPORT_SYMBOL(command_list);
> @@ -72,7 +73,9 @@ int execute_command(int argc, char **argv)
>  	/* Look up command in command table */
>  	if ((cmdtp = find_cmd(argv[0]))) {
>  		/* OK - call function to do the command */
> +		idle_slice_acquire();
>  		ret = cmdtp->cmd(argc, argv);
> +		idle_slice_release();

The command could be a shell in which case we would never be able to
release the idle slice as long this shell is running.

> @@ -200,11 +201,16 @@ int readline(const char *prompt, char *buf, int len)
>  	puts (prompt);
>  
>  	while (1) {
> +		idle_slice_release();
>  		while (!tstc()) {
>  			poller_call();
> -			if (IS_ENABLED(CONFIG_CONSOLE_RATP))
> +			if (IS_ENABLED(CONFIG_CONSOLE_RATP)) {
> +				idle_slice_acquire();
>  				barebox_ratp_command_run();
> +				idle_slice_release();
> +			}
>  		}
> +		idle_slice_acquire();

I am not sure this is the right place to release the idle slice, I would
rather do it in the caller. readline() may be called from different
places, not all of them seem to be appropriate for releasing the idle
slice.

I think the idle slice should be released right before the shell calls
into readline and acquired again right afterwards.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 09/10] fastboot net: implement fastboot over UDP
  2020-05-20  5:52   ` Sascha Hauer
@ 2020-05-20  6:57     ` Daniel Glöckner
  2020-05-20  8:14       ` Sascha Hauer
  0 siblings, 1 reply; 17+ messages in thread
From: Daniel Glöckner @ 2020-05-20  6:57 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: barebox, Edmund Henniges

Hello Sascha,

On Wed, May 20, 2020 at 07:52:32AM +0200, Sascha Hauer wrote:
> On Thu, May 14, 2020 at 08:21:57PM +0200, Daniel Glöckner wrote:
> > From: Edmund Henniges <eh@emlix.com>
> > 
> > This implements the UDP variant of the fastboot protocol. The only way to
> > start the service for now is to compile with CONFIG_FASTBOOT_NET_ON_BOOT.
> > The service will bind to the network interface that provides the IPv4
> > gateway.
> > 
> > Sending an OKAY packet before performing a restart is necessary since
> > contrary to USB the host will not notice when a UDP server disappears.
> > 
> > Signed-off-by: Edmund Henniges <eh@emlix.com>
> > Signed-off-by: Daniel Glöckner <dg@emlix.com>
> > ---
> >  common/fastboot.c      |   3 +
> >  include/fastboot.h     |   1 +
> >  include/fastboot_net.h |  12 +
> >  net/Kconfig            |  18 ++
> >  net/Makefile           |   1 +
> >  net/fastboot.c         | 496 +++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 531 insertions(+)
> >  create mode 100644 include/fastboot_net.h
> >  create mode 100644 net/fastboot.c
> > 
> > diff --git a/common/fastboot.c b/common/fastboot.c
> > index d50f61ff2..706547309 100644
> > --- a/common/fastboot.c
> > +++ b/common/fastboot.c
> > +static int fastboot_write_net(struct fastboot *fb, const char *buf,
> > +			      unsigned int n)
> > +{
> > +	struct fastboot_net *fbn = container_of(fb, struct fastboot_net,
> > +						fastboot);
> > +	struct fastboot_header response_header;
> > +	uchar *packet;
> > +	uchar *packet_base;
> > +
> > +	if (fbn->reinit)
> > +		return 0;
> > +
> > +	while (fbn->may_send == MAY_NOT_SEND) {
> > +		poller_call();
> > +		if (fbn->reinit)
> > +			return 0;
> > +	}
> 
> This is a potentially endless loop I can reproducibly hit here. When I
> do a "sleep 10" on the command line and at the same time start a
> fastboot transfer then there's no progress. This is expected because
> fastboot depends on the idle slice. However, when the "sleep 10" is over
> then I get a prompt and then we are stuck in this loop.
> 
> I replaced this loop with a one second timeout loop. With this we are no
> longer stuck in the loop, but the current transfer doesn't continue
> anymore.

The official fastboot tool from Google has a 60 second timeout and
retransmits packets every 0.5 seconds. A 1 second timeout on the device
feels too short. We should also retry for at least 60 seconds.

I would expect that the host retransmits its last packet beyond the 10
second sleep and the transfer to immediately continue when barebox is
no longer busy. Also keep in mind that a device will most likely never
be used over fastboot and serial console at the same time. A user who
interactively started the transfer can always restart fastboot on the
host, which will abort this loop by setting reinit.

> Afterwards I dropped everything around fbn->may_send completely and now
> everything works fluently.
> 
> Removing these send restrictions seems the right thing to me. In the end
> it's UDP, so the remote host shouldn't make any assumptions of how fast
> and if at all packets arrive there.

I haven't seen your changes, but I fear you have broken something else.
Without may_send it may be possible for a message to be sent before the
previous message has been acked by the host. Since we can retransmit
only the last message, the connection will be stuck until the host times
out. In the protocol each side has to wait for reception of a packet
with the correct sequence number before it can send the next packet. We
can't change that just be cause we think it's the right thing.

Best regards,

  Daniel


-- 
Dipl.-Math. Daniel Glöckner, emlix GmbH, http://www.emlix.com
Fon +49 551 30664-0, Fax +49 551 30664-11,
Gothaer Platz 3, 37083 Göttingen, Germany
Sitz der Gesellschaft: Göttingen, Amtsgericht Göttingen HR B 3160
Geschäftsführung: Heike Jordan, Dr. Uwe Kracke
Ust-IdNr.: DE 205 198 055

emlix - your embedded linux partner

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 09/10] fastboot net: implement fastboot over UDP
  2020-05-20  6:57     ` Daniel Glöckner
@ 2020-05-20  8:14       ` Sascha Hauer
  0 siblings, 0 replies; 17+ messages in thread
From: Sascha Hauer @ 2020-05-20  8:14 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: barebox, Edmund Henniges

Hi Daniel,

On Wed, May 20, 2020 at 08:57:44AM +0200, Daniel Glöckner wrote:
> Hello Sascha,
> 
> On Wed, May 20, 2020 at 07:52:32AM +0200, Sascha Hauer wrote:
> > On Thu, May 14, 2020 at 08:21:57PM +0200, Daniel Glöckner wrote:
> > > From: Edmund Henniges <eh@emlix.com>
> > > 
> > > This implements the UDP variant of the fastboot protocol. The only way to
> > > start the service for now is to compile with CONFIG_FASTBOOT_NET_ON_BOOT.
> > > The service will bind to the network interface that provides the IPv4
> > > gateway.
> > > 
> > > Sending an OKAY packet before performing a restart is necessary since
> > > contrary to USB the host will not notice when a UDP server disappears.
> > > 
> > > Signed-off-by: Edmund Henniges <eh@emlix.com>
> > > Signed-off-by: Daniel Glöckner <dg@emlix.com>
> > > ---
> > >  common/fastboot.c      |   3 +
> > >  include/fastboot.h     |   1 +
> > >  include/fastboot_net.h |  12 +
> > >  net/Kconfig            |  18 ++
> > >  net/Makefile           |   1 +
> > >  net/fastboot.c         | 496 +++++++++++++++++++++++++++++++++++++++++
> > >  6 files changed, 531 insertions(+)
> > >  create mode 100644 include/fastboot_net.h
> > >  create mode 100644 net/fastboot.c
> > > 
> > > diff --git a/common/fastboot.c b/common/fastboot.c
> > > index d50f61ff2..706547309 100644
> > > --- a/common/fastboot.c
> > > +++ b/common/fastboot.c
> > > +static int fastboot_write_net(struct fastboot *fb, const char *buf,
> > > +			      unsigned int n)
> > > +{
> > > +	struct fastboot_net *fbn = container_of(fb, struct fastboot_net,
> > > +						fastboot);
> > > +	struct fastboot_header response_header;
> > > +	uchar *packet;
> > > +	uchar *packet_base;
> > > +
> > > +	if (fbn->reinit)
> > > +		return 0;
> > > +
> > > +	while (fbn->may_send == MAY_NOT_SEND) {
> > > +		poller_call();
> > > +		if (fbn->reinit)
> > > +			return 0;
> > > +	}
> > 
> > This is a potentially endless loop I can reproducibly hit here. When I
> > do a "sleep 10" on the command line and at the same time start a
> > fastboot transfer then there's no progress. This is expected because
> > fastboot depends on the idle slice. However, when the "sleep 10" is over
> > then I get a prompt and then we are stuck in this loop.
> > 
> > I replaced this loop with a one second timeout loop. With this we are no
> > longer stuck in the loop, but the current transfer doesn't continue
> > anymore.
> 
> The official fastboot tool from Google has a 60 second timeout and
> retransmits packets every 0.5 seconds. A 1 second timeout on the device
> feels too short. We should also retry for at least 60 seconds.
> 
> I would expect that the host retransmits its last packet beyond the 10
> second sleep and the transfer to immediately continue when barebox is
> no longer busy. Also keep in mind that a device will most likely never
> be used over fastboot and serial console at the same time. A user who
> interactively started the transfer can always restart fastboot on the
> host, which will abort this loop by setting reinit.
> 
> > Afterwards I dropped everything around fbn->may_send completely and now
> > everything works fluently.
> > 
> > Removing these send restrictions seems the right thing to me. In the end
> > it's UDP, so the remote host shouldn't make any assumptions of how fast
> > and if at all packets arrive there.
> 
> I haven't seen your changes, but I fear you have broken something else.
> Without may_send it may be possible for a message to be sent before the
> previous message has been acked by the host. Since we can retransmit
> only the last message, the connection will be stuck until the host times
> out.

We are probably implementing UDP Protocol V1, right?

Here it says:

1. The host drives all communication; the device may only send a packet as
   a response to a host packet.
2. If the host does not receive a response in 500ms it will re-transmit.

This means the host doesn't ack anything, it just sends the next packet
if it received our response or the same packet again if it hasn't. For
us this means we don't have to retransmit anything, we only have to
respond to the last packet seen.

I am not sure how INFO responses are handled, there seems to be no way
for the client to notice that the host hasn't seen them. It looks like
these are just lost then and simply won't be displayed on the host.

That said, it's absolutely possible that I have broken something else
with my changes ;)

I have quite some changes also in my slice series which I'll post later
the day. Maybe we can take this as a base for further discussions.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v2 09/10] fastboot net: implement fastboot over UDP
  2020-05-14 18:21 ` [PATCH v2 09/10] fastboot net: implement fastboot over UDP Daniel Glöckner
  2020-05-20  5:52   ` Sascha Hauer
@ 2020-05-20  8:17   ` Sascha Hauer
  1 sibling, 0 replies; 17+ messages in thread
From: Sascha Hauer @ 2020-05-20  8:17 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: barebox, Edmund Henniges

On Thu, May 14, 2020 at 08:21:57PM +0200, Daniel Glöckner wrote:
> From: Edmund Henniges <eh@emlix.com>
> 
> This implements the UDP variant of the fastboot protocol. The only way to
> start the service for now is to compile with CONFIG_FASTBOOT_NET_ON_BOOT.
> The service will bind to the network interface that provides the IPv4
> gateway.
> 
> Sending an OKAY packet before performing a restart is necessary since
> contrary to USB the host will not notice when a UDP server disappears.
> 
> Signed-off-by: Edmund Henniges <eh@emlix.com>
> Signed-off-by: Daniel Glöckner <dg@emlix.com>
> ---
> +
> +#ifdef CONFIG_FASTBOOT_NET_ON_BOOT
> +static struct fastboot_net *fastboot_net_obj;
> +
> +static int fastboot_on_boot(void)
> +{
> +	struct fastboot_net *fbn;
> +
> +	ifup_all(0);
> +	fbn = fastboot_net_init(NULL);
> +
> +	if (IS_ERR(fbn))
> +		return PTR_ERR(fbn);
> +
> +	fastboot_net_obj = fbn;
> +	return 0;
> +}
> +
> +static void fastboot_net_exit(void)
> +{
> +	if (fastboot_net_obj)
> +		fastboot_net_free(fastboot_net_obj);
> +}

fastboot_net_obj won't be NULL here, the check should be if(!IS_ERR())
instead.

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2020-05-20  8:18 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-14 18:21 [PATCH v2 00/10] Support for Fastboot over UDP Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 01/10] Remove CONFIG_SLICE Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 02/10] net: fixed-link phys are never acquired Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 03/10] poller: adapt remaining users to API change Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 04/10] Introduce idle slice Daniel Glöckner
2020-05-20  6:03   ` Sascha Hauer
2020-05-14 18:21 ` [PATCH v2 05/10] ratp: use poller to run ratp commands Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 06/10] fastboot: split generic code from USB gadget Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 07/10] defconfigs: update renamed fastboot options Daniel Glöckner
2020-05-14 18:21 ` [PATCH v2 08/10] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Daniel Glöckner
2020-05-20  4:55   ` Sascha Hauer
2020-05-14 18:21 ` [PATCH v2 09/10] fastboot net: implement fastboot over UDP Daniel Glöckner
2020-05-20  5:52   ` Sascha Hauer
2020-05-20  6:57     ` Daniel Glöckner
2020-05-20  8:14       ` Sascha Hauer
2020-05-20  8:17   ` Sascha Hauer
2020-05-14 18:21 ` [PATCH v2 10/10] fastboot: don't close fd 0 when downloading to ram Daniel Glöckner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox