mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 00/19] Slices and fastboot over UDP
@ 2020-05-20  9:43 Sascha Hauer
  2020-05-20  9:43 ` [PATCH 01/19] poller: Give pollers a name Sascha Hauer
                   ` (18 more replies)
  0 siblings, 19 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

This is a combination of the slices series I posted earlier and
the fastboot over ethernet series from Daniel Glöckner. This works
quite well for me now and I am impressed that with a small fix both
fastboot over USB and over UDP now not only work after each other,
but also truely simultanously. Well, I didn't try to write the same
file from both instances, that won't work for sure ;)

This series currently has three known issues which should be fixed
before merging:

- The fastboot UDP keepalive handler has no idea if we are inside a
  fastboot session or not. It will just blindly send keepalive packets
- The only possibility to start fastboot over UDP currently is a
  Kconfig option which if enabled starts fastboot over UDP unconditionally
  Here we need at least a nv variable to control starting
- It's not yet clear if completely removing the may_send mechanism is the
  right thing.

Changes since the slices v4 series:

- rename slice_add() to slice_depends_on() which makes the intention of
  the function more clear
- slice_del() is no longer necessary and removed
- slice_exit() introduced to remove a slice. This removes the slice, it's
  dependencies and also removes the slice from other slices dependencies

Changes since fastboot over UDP v2:

- squashed 01/10 - 03/10 into patches of the slices series
- Move releasing of the idle slice to the shell and the startup code
  calling the autoboot timeout counter
- add forgotten rename of global.usbgadget.fastboot_max_download_size to
  global.fastboot.max_download_size
- Add patch to use different temporary filenames for each fastboot instance.
  With this fastboot over UDP and fastboot over USB can work in parallel
- remove may_send mechanism from fastboot over UDP
- fix wrong test in fastboot_net_exit()

Daniel Glöckner (4):
  fastboot: don't close fd 0 when downloading to ram
  Introduce idle slice
  defconfigs: update renamed fastboot options
  fastboot: rename usbgadget.fastboot_* variables to fastboot.*

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

Sascha Hauer (13):
  poller: Give pollers a name
  poller: Add a poller command
  fastboot: Use unique tempfile name
  Introduce slices
  net: Add a slice to struct eth_device
  net: mdiobus: Add slice
  usb: Add a slice to usb host controllers
  usbnet: Add slice
  net: Call net_poll() in a poller
  net: reply to ping requests
  usbnet: Be more friendly in the receive path
  poller: Allow to run pollers inside of pollers
  fastboot net: remove may_send

 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                         |  17 +
 commands/usbgadget.c                     |   2 +-
 common/Kconfig                           |  40 +
 common/Makefile                          |   2 +
 common/console_countdown.c               |   1 +
 common/fastboot.c                        | 975 +++++++++++++++++++++++
 common/hush.c                            |   5 +
 common/poller.c                          |  92 ++-
 common/ratp/ratp.c                       |   9 +-
 common/slice.c                           | 325 ++++++++
 common/startup.c                         |   2 +
 common/usbgadget.c                       |  16 +-
 drivers/input/gpio_keys.c                |   2 +-
 drivers/input/imx_keypad.c               |   2 +-
 drivers/input/input.c                    |   2 +-
 drivers/input/qt1070.c                   |   2 +-
 drivers/input/twl6030_pwrbtn.c           |   2 +-
 drivers/led/core.c                       |   2 +-
 drivers/net/phy/mdio_bus.c               |  41 +
 drivers/net/usb/ax88179_178a.c           |   2 +-
 drivers/net/usb/usbnet.c                 |  22 +-
 drivers/usb/core/usb.c                   |   6 +
 drivers/usb/gadget/Kconfig               |  36 +-
 drivers/usb/gadget/f_fastboot.c          | 970 +---------------------
 drivers/usb/gadget/multi.c               |   5 +-
 drivers/usb/gadget/udc-core.c            |   2 +-
 drivers/watchdog/wd_core.c               |   2 +-
 fs/nfs.c                                 |   2 -
 fs/tftp.c                                |   2 -
 include/fastboot.h                       |  71 ++
 include/fastboot_net.h                   |  12 +
 include/linux/phy.h                      |  38 +-
 include/net.h                            |  11 +-
 include/poller.h                         |   7 +-
 include/ratp_bb.h                        |   1 -
 include/slice.h                          |  36 +
 include/usb/fastboot.h                   |  34 +-
 include/usb/gadget-multi.h               |   2 +-
 include/usb/usb.h                        |   7 +
 include/usb/usbnet.h                     |   3 +
 lib/Kconfig                              |   1 +
 lib/readline.c                           |   7 +-
 net/Kconfig                              |  18 +
 net/Makefile                             |   1 +
 net/dhcp.c                               |   1 -
 net/dns.c                                |   1 -
 net/eth.c                                |  29 +-
 net/fastboot.c                           | 476 +++++++++++
 net/net.c                                |  60 +-
 net/netconsole.c                         |   4 +-
 net/nfs.c                                |   1 -
 net/ping.c                               |   2 -
 net/sntp.c                               |   2 -
 63 files changed, 2330 insertions(+), 1105 deletions(-)
 create mode 100644 common/fastboot.c
 create mode 100644 common/slice.c
 create mode 100644 include/fastboot.h
 create mode 100644 include/fastboot_net.h
 create mode 100644 include/slice.h
 create mode 100644 net/fastboot.c

-- 
2.26.2


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

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

* [PATCH 01/19] poller: Give pollers a name
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 02/19] poller: Add a poller command Sascha Hauer
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

It helps debugging when pollers have a name, so give them one.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/boards/mioa701/gpio0_poweroff.c | 2 +-
 arch/arm/boards/sama5d3xek/init.c        | 2 +-
 common/poller.c                          | 8 +++++---
 common/ratp/ratp.c                       | 2 +-
 drivers/input/gpio_keys.c                | 2 +-
 drivers/input/imx_keypad.c               | 2 +-
 drivers/input/input.c                    | 2 +-
 drivers/input/qt1070.c                   | 2 +-
 drivers/input/twl6030_pwrbtn.c           | 2 +-
 drivers/led/core.c                       | 2 +-
 drivers/net/usb/ax88179_178a.c           | 2 +-
 drivers/usb/gadget/udc-core.c            | 2 +-
 drivers/watchdog/wd_core.c               | 2 +-
 include/poller.h                         | 5 +++--
 14 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/arch/arm/boards/mioa701/gpio0_poweroff.c b/arch/arm/boards/mioa701/gpio0_poweroff.c
index 4b34922507..8caccebeab 100644
--- a/arch/arm/boards/mioa701/gpio0_poweroff.c
+++ b/arch/arm/boards/mioa701/gpio0_poweroff.c
@@ -73,7 +73,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 c768e98d26..d9be4c5f9d 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/common/poller.c b/common/poller.c
index 32795b641f..b1a2122f91 100644
--- a/common/poller.c
+++ b/common/poller.c
@@ -16,11 +16,12 @@
 static LIST_HEAD(poller_list);
 static int poller_active;
 
-int poller_register(struct poller_struct *poller)
+int poller_register(struct poller_struct *poller, const char *name)
 {
 	if (poller->registered)
 		return -EBUSY;
 
+	poller->name = xstrdup(name);
 	list_add_tail(&poller->list, &poller_list);
 	poller->registered = 1;
 
@@ -35,6 +36,7 @@ int poller_unregister(struct poller_struct *poller)
 
 	list_del(&poller->list);
 	poller->registered = 0;
+	free(poller->name);
 
 	return 0;
 }
@@ -92,12 +94,12 @@ int poller_call_async(struct poller_async *pa, uint64_t delay_ns,
 	return 0;
 }
 
-int poller_async_register(struct poller_async *pa)
+int poller_async_register(struct poller_async *pa, const char *name)
 {
 	pa->poller.func = poller_async_callback;
 	pa->active = 0;
 
-	return poller_register(&pa->poller);
+	return poller_register(&pa->poller, name);
 }
 
 int poller_async_unregister(struct poller_async *pa)
diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c
index ca751a30eb..556dc638d8 100644
--- a/common/ratp/ratp.c
+++ b/common/ratp/ratp.c
@@ -451,7 +451,7 @@ int barebox_ratp(struct console_device *cdev)
 	if (ret < 0)
 		goto out;
 
-	ret = poller_register(&ctx->poller);
+	ret = poller_register(&ctx->poller, "ratp");
 	if (ret)
 		goto out1;
 
diff --git a/drivers/input/gpio_keys.c b/drivers/input/gpio_keys.c
index 38c0f11535..11d598c402 100644
--- a/drivers/input/gpio_keys.c
+++ b/drivers/input/gpio_keys.c
@@ -166,7 +166,7 @@ static int __init gpio_keys_probe(struct device_d *dev)
 	if (ret)
 		return ret;
 
-	ret = poller_register(&gk->poller);
+	ret = poller_register(&gk->poller, dev_name(dev));
 	if (ret)
 		return ret;
 
diff --git a/drivers/input/imx_keypad.c b/drivers/input/imx_keypad.c
index 44ff9b7856..6757fac72b 100644
--- a/drivers/input/imx_keypad.c
+++ b/drivers/input/imx_keypad.c
@@ -410,7 +410,7 @@ static int __init imx_keypad_probe(struct device_d *dev)
 
 	keypad->poller.func = imx_keypad_check_for_events;
 
-	ret = poller_register(&keypad->poller);
+	ret = poller_register(&keypad->poller, dev_name(dev));
 	if (ret)
 		return ret;
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 1e8f6e178e..bcc8667417 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -201,7 +201,7 @@ static int input_init(void)
 	ic->fifo = kfifo_alloc(32);
 	ic->notifier.notify = input_console_notify;
 	input_register_notfier(&ic->notifier);
-	poller_async_register(&ic->poller);
+	poller_async_register(&ic->poller, "input");
 
 	return console_register(&ic->console);
 }
diff --git a/drivers/input/qt1070.c b/drivers/input/qt1070.c
index 59acee5c39..9e1dcc57ee 100644
--- a/drivers/input/qt1070.c
+++ b/drivers/input/qt1070.c
@@ -269,7 +269,7 @@ static int qt1070_probe(struct device_d *dev)
 
 	console_register(&data->cdev);
 
-	ret = poller_register(&data->poller);
+	ret = poller_register(&data->poller, dev_name(dev));
 	if (ret)
 		goto err;
 
diff --git a/drivers/input/twl6030_pwrbtn.c b/drivers/input/twl6030_pwrbtn.c
index fc4c728778..481688b4a9 100644
--- a/drivers/input/twl6030_pwrbtn.c
+++ b/drivers/input/twl6030_pwrbtn.c
@@ -97,7 +97,7 @@ static int __init twl6030_pwrbtn_probe(struct device_d *dev)
 	idata->cdev.getc = twl6030_pwrbtn_getc;
 	console_register(&idata->cdev);
 
-	return poller_register(&idata->poller);
+	return poller_register(&idata->poller, dev_name(dev));
 }
 
 static struct driver_d twl6030_pwrbtn_driver = {
diff --git a/drivers/led/core.c b/drivers/led/core.c
index eeeeaa0ff3..ab171c6184 100644
--- a/drivers/led/core.c
+++ b/drivers/led/core.c
@@ -209,7 +209,7 @@ static struct poller_struct led_poller = {
 
 static int led_blink_init(void)
 {
-	return poller_register(&led_poller);
+	return poller_register(&led_poller, "led");
 }
 late_initcall(led_blink_init);
 
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 6fea5a7b1d..fddb187144 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;
 }
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 096f05ed48..126d76e8bb 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -319,7 +319,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
 
 	if (udc->gadget->ops->udc_poll) {
 		udc->poller.func = udc_poll_driver;
-		ret = poller_register(&udc->poller);
+		ret = poller_register(&udc->poller, dev_name(&udc->dev));
 		if (ret)
 			return ret;
 	}
diff --git a/drivers/watchdog/wd_core.c b/drivers/watchdog/wd_core.c
index 34040408f7..a17234f4b6 100644
--- a/drivers/watchdog/wd_core.c
+++ b/drivers/watchdog/wd_core.c
@@ -116,7 +116,7 @@ static int watchdog_register_poller(struct watchdog *wd)
 	struct param_d *p;
 	int ret;
 
-	ret = poller_async_register(&wd->poller);
+	ret = poller_async_register(&wd->poller, dev_name(&wd->dev));
 	if (ret)
 		return ret;
 
diff --git a/include/poller.h b/include/poller.h
index b22b8a1b89..886557252b 100644
--- a/include/poller.h
+++ b/include/poller.h
@@ -12,9 +12,10 @@ struct poller_struct {
 	void (*func)(struct poller_struct *poller);
 	int registered;
 	struct list_head list;
+	char *name;
 };
 
-int poller_register(struct poller_struct *poller);
+int poller_register(struct poller_struct *poller, const char *name);
 int poller_unregister(struct poller_struct *poller);
 
 struct poller_async;
@@ -27,7 +28,7 @@ struct poller_async {
 	int active;
 };
 
-int poller_async_register(struct poller_async *pa);
+int poller_async_register(struct poller_async *pa, const char *name);
 int poller_async_unregister(struct poller_async *pa);
 
 int poller_call_async(struct poller_async *pa, uint64_t delay_ns,
-- 
2.26.2


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

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

* [PATCH 02/19] poller: Add a poller command
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
  2020-05-20  9:43 ` [PATCH 01/19] poller: Give pollers a name Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 03/19] fastboot: split generic code from USB gadget Sascha Hauer
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

The poller command allows to print which pollers are registered and also
how many times we can run the registered pollers in one second.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 commands/Kconfig |  9 ++++++
 common/poller.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/commands/Kconfig b/commands/Kconfig
index 8de98ef3ee..3789f33c3b 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -244,6 +244,15 @@ config CMD_MMC_EXTCSD
 		  -y      don't request when writing to one time programmable fields
 		  __CAUTION__: this could damage the device!
 
+config CMD_POLLER
+	tristate
+	prompt "poller"
+	depends on POLLER
+	help
+	  Pollers are functions that are running in the background whenever code executes
+	  is_timeout() or one of the various delay functions. The poller command prints
+	  informations about registered pollers.
+
 # end Information commands
 endmenu
 
diff --git a/common/poller.c b/common/poller.c
index b1a2122f91..95f828b439 100644
--- a/common/poller.c
+++ b/common/poller.c
@@ -121,3 +121,74 @@ void poller_call(void)
 
 	poller_active = 0;
 }
+
+#if defined CONFIG_CMD_POLLER
+
+#include <command.h>
+#include <getopt.h>
+
+static void poller_time(void)
+{
+	uint64_t start = get_time_ns();
+	int i = 0;
+
+	/*
+	 * How many times we can run the registered pollers in one second?
+	 *
+	 * A low number here may point to problems with pollers taking too
+	 * much time.
+	 */
+	while (!is_timeout(start, SECOND))
+		i++;
+
+	printf("%d poller calls in 1s\n", i);
+}
+
+static void poller_info(void)
+{
+	struct poller_struct *poller;
+
+	printf("Registered pollers:\n");
+
+	if (list_empty(&poller_list)) {
+		printf("<none>\n");
+		return;
+	}
+
+	list_for_each_entry(poller, &poller_list, list)
+		printf("%s\n", poller->name);
+}
+
+BAREBOX_CMD_HELP_START(poller)
+BAREBOX_CMD_HELP_TEXT("print info about registered pollers")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-i", "Print information about registered pollers")
+BAREBOX_CMD_HELP_OPT ("-t", "measure how many pollers we run in 1s")
+BAREBOX_CMD_HELP_END
+
+static int do_poller(int argc, char *argv[])
+{
+	int opt;
+
+	while ((opt = getopt(argc, argv, "it")) > 0) {
+		switch (opt) {
+		case 'i':
+			poller_info();
+			return 0;
+		case 't':
+			poller_time();
+			return 0;
+		}
+	}
+
+	return COMMAND_ERROR_USAGE;
+}
+
+BAREBOX_CMD_START(poller)
+	.cmd = do_poller,
+	BAREBOX_CMD_DESC("print info about registered pollers")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_HELP(cmd_poller_help)
+BAREBOX_CMD_END
+#endif
-- 
2.26.2


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

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

* [PATCH 03/19] fastboot: split generic code from USB gadget
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
  2020-05-20  9:43 ` [PATCH 01/19] poller: Give pollers a name Sascha Hauer
  2020-05-20  9:43 ` [PATCH 02/19] poller: Add a poller command Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 04/19] fastboot: don't close fd 0 when downloading to ram Sascha Hauer
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 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 460ac487cb..18796c6888 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -113,6 +113,9 @@ config USBGADGET_START
 config BOOT
 	bool
 
+config FASTBOOT_BASE
+	bool
+
 menu "General Settings"
 
 config LOCALVERSION
@@ -1009,6 +1012,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 c14af692f9..53859d8d14 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -69,6 +69,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 0000000000..d58f68f1bb
--- /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 6e60c7aee8..977f6c0dba 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 cf3cc6dac7..f8a9c32530 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 d6edfb8cf2..445cc32420 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 0000000000..3b6cae8a58
--- /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 c0775c67dd..a3609ba5db 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 030e604fe7..9bb6c889f3 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.26.2


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

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

* [PATCH 04/19] fastboot: don't close fd 0 when downloading to ram
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (2 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 03/19] fastboot: split generic code from USB gadget Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 05/19] fastboot: Use unique tempfile name Sascha Hauer
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

From: Daniel Glöckner <dg@emlix.com>

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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/fastboot.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/common/fastboot.c b/common/fastboot.c
index d58f68f1bb..1e12f99915 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -342,7 +342,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.26.2


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

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

* [PATCH 05/19] fastboot: Use unique tempfile name
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (3 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 04/19] fastboot: don't close fd 0 when downloading to ram Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 06/19] Introduce slices Sascha Hauer
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

With fastboot over ethernet we now can have two instances of fastboot
running in parallel. Make sure that both instances use a different
temporary file to store data so that they do not influence each other.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/fastboot.c  | 21 ++++++++++++---------
 include/fastboot.h |  1 +
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/common/fastboot.c b/common/fastboot.c
index 1e12f99915..6f54e939e7 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -45,8 +45,6 @@
 
 #define FASTBOOT_VERSION		"0.4"
 
-#define FASTBOOT_TMPFILE		"/.fastboot.img"
-
 static unsigned int fastboot_max_download_size = SZ_8M;
 
 struct fb_variable {
@@ -178,6 +176,7 @@ int fastboot_generic_init(struct fastboot *fb, bool export_bbu)
 	int ret;
 	struct file_list_entry *fentry;
 	struct fb_variable *var;
+	static int instance;
 
 	var = fb_addvar(fb, "version");
 	fb_setvar(var, "0.4");
@@ -188,6 +187,8 @@ int fastboot_generic_init(struct fastboot *fb, bool export_bbu)
 		fb_setvar(var, "%u", fastboot_max_download_size);
 	}
 
+	fb->tempname = basprintf(".fastboot.%d.img", instance++);
+
 	if (IS_ENABLED(CONFIG_BAREBOX_UPDATE) && export_bbu)
 		bbu_handlers_iterate(fastboot_add_bbu_variables, fb);
 
@@ -211,6 +212,8 @@ void fastboot_generic_free(struct fastboot *fb)
 		free(var);
 	}
 
+	free(fb->tempname);
+
 	fb->active = false;
 }
 
@@ -372,7 +375,7 @@ static void cb_download(struct fastboot *fb, const char *cmd)
 			return;
 		}
 	} else {
-		fb->download_fd = open(FASTBOOT_TMPFILE, O_WRONLY | O_CREAT | O_TRUNC);
+		fb->download_fd = open(fb->tempname, O_WRONLY | O_CREAT | O_TRUNC);
 		if (fb->download_fd < 0) {
 			fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
 					  "internal error");
@@ -405,7 +408,7 @@ static void __maybe_unused cb_boot(struct fastboot *fb, const char *opt)
 	globalvar_set_match("linux.bootargs.dyn.", "");
 	globalvar_set_match("bootm.image", "");
 
-	data.os_file = FASTBOOT_TMPFILE;
+	data.os_file = fb->tempname;
 
 	ret = bootm_boot(&data);
 
@@ -534,7 +537,7 @@ static int fastboot_handle_sparse(struct fastboot *fb,
 	if (ret)
 		goto out_close_fd;
 
-	sparse = sparse_image_open(FASTBOOT_TMPFILE);
+	sparse = sparse_image_open(fb->tempname);
 	if (IS_ERR(sparse)) {
 		pr_err("Cannot open sparse image\n");
 		ret = PTR_ERR(sparse);
@@ -629,8 +632,8 @@ static void cb_flash(struct fastboot *fb, const char *cmd)
 		sourcefile = NULL;
 		filetype = file_detect_type(fb->buf, fb->download_bytes);
 	} else {
-		sourcefile = FASTBOOT_TMPFILE;
-		filetype = file_name_detect_type(FASTBOOT_TMPFILE);
+		sourcefile = fb->tempname;
+		filetype = file_name_detect_type(fb->tempname);
 	}
 
 	fastboot_tx_print(fb, FASTBOOT_MSG_INFO, "Copying file to %s...",
@@ -735,7 +738,7 @@ copy:
 	if (fastboot_download_to_buf(fb))
 		ret = write_file(filename, fb->buf, fb->download_size);
 	else
-		ret = copy_file(FASTBOOT_TMPFILE, filename, 1);
+		ret = copy_file(fb->tempname, filename, 1);
 
 	if (ret)
 		fastboot_tx_print(fb, FASTBOOT_MSG_FAIL,
@@ -749,7 +752,7 @@ out:
 	fb->buf = NULL;
 
 	if (!fastboot_download_to_buf(fb))
-		unlink(FASTBOOT_TMPFILE);
+		unlink(fb->tempname);
 }
 
 static void cb_erase(struct fastboot *fb, const char *cmd)
diff --git a/include/fastboot.h b/include/fastboot.h
index 3b6cae8a58..b3e7155efa 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -25,6 +25,7 @@ struct fastboot {
 			 const char *filename, const void *buf, size_t len);
 	int download_fd;
 	void *buf;
+	char *tempname;
 
 	bool active;
 
-- 
2.26.2


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

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

* [PATCH 06/19] Introduce slices
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (4 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 05/19] fastboot: Use unique tempfile name Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 07/19] Introduce idle slice Sascha Hauer
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

slices, the barebox idea of locking

barebox has pollers which execute code in the background whenever one of the
delay functions (udelay, mdelay, ...) or is_timeout() are called. This
introduces resource problems when some device triggers a poller by calling
a delay function and then the poller code calls into the same device again.

As an example consider a I2C GPIO expander which drives a LED which shall
be used as a heartbeat LED:

poller -> LED on/off -> GPIO high/low -> I2C transfer

The I2C controller has a timeout loop using is_timeout() and thus can trigger
a poller run. With this the following can happen during an unrelated I2C
transfer:

I2C transfer -> is_timeout() -> poller -> LED on/off -> GPIO high/low -> I2C transfer

We end up with issuing an I2C transfer during another I2C transfer and
things go downhill.

Due to the lack of interrupts we can't do real locking in barebox. We use
a mechanism called slices instead. A slice describes a resource to which
other slices can be attached. Whenever a slice is needed it must be acquired.
Acquiring a slice never fails, it just increases the acquired counter of
the slice and its dependent slices. when a slice shall be used inside a
poller it must first be tested if the slice is already in use. If it is,
we can't do the operation on the slice now and must return and hope that
we have more luck in the next poller call.

slices can be attached other slices as dependencies. In the example above
LED driver would add a dependency to the GPIO controller and the GPIO driver
would add a dependency to the I2C bus:

GPIO driver probe:

slice_add(&gpio->slice, i2c_device_slice(i2cdev));

LED driver probe:

slice_add(&led->slice, gpio_slice(gpio));

The GPIO code would call slice_acquire(&gpio->slice) before doing any
operation on the GPIO chip providing this GPIO, likewise the I2C core
would call slice_acquire(&i2cbus->slice) before doing an operation on
this I2C bus.

The heartbeat poller code would call slice_acquired(led_slice(led)) and
only continue when the slice is not acquired.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 commands/Kconfig |   8 ++
 common/Makefile  |   1 +
 common/slice.c   | 303 +++++++++++++++++++++++++++++++++++++++++++++++
 include/slice.h  |  31 +++++
 4 files changed, 343 insertions(+)
 create mode 100644 common/slice.c
 create mode 100644 include/slice.h

diff --git a/commands/Kconfig b/commands/Kconfig
index 3789f33c3b..608643fceb 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -253,6 +253,14 @@ config CMD_POLLER
 	  is_timeout() or one of the various delay functions. The poller command prints
 	  informations about registered pollers.
 
+config CMD_SLICE
+	tristate
+	prompt "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
+	  them on the command line for debugging purposes.
+
 # end Information commands
 endmenu
 
diff --git a/common/Makefile b/common/Makefile
index 53859d8d14..141109b23a 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -11,6 +11,7 @@ obj-y				+= bootsource.o
 obj-$(CONFIG_ELF)		+= elf.o
 obj-y				+= restart.o
 obj-y				+= poweroff.o
+obj-y				+= slice.o
 obj-$(CONFIG_MACHINE_ID)	+= machine_id.o
 obj-$(CONFIG_AUTO_COMPLETE)	+= complete.o
 obj-y				+= version.o
diff --git a/common/slice.c b/common/slice.c
new file mode 100644
index 0000000000..085d67604f
--- /dev/null
+++ b/common/slice.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) "slice: " fmt
+
+#include <common.h>
+#include <slice.h>
+
+/*
+ * slices, the barebox idea of locking
+ *
+ * barebox has pollers which execute code in the background whenever one of the
+ * delay functions (udelay, mdelay, ...) or is_timeout() are called. This
+ * introduces resource problems when some device triggers a poller by calling
+ * a delay function and then the poller code calls into the same device again.
+ *
+ * As an example consider a I2C GPIO expander which drives a LED which shall
+ * be used as a heartbeat LED:
+ *
+ * poller -> LED on/off -> GPIO high/low -> I2C transfer
+ *
+ * The I2C controller has a timeout loop using is_timeout() and thus can trigger
+ * a poller run. With this the following can happen during an unrelated I2C
+ * transfer:
+ *
+ * I2C transfer -> is_timeout() -> poller -> LED on/off -> GPIO high/low -> I2C transfer
+ *
+ * We end up with issuing an I2C transfer during another I2C transfer and
+ * things go downhill.
+ *
+ * Due to the lack of interrupts we can't do real locking in barebox. We use
+ * a mechanism called slices instead. A slice describes a resource to which
+ * other slices can be attached. Whenever a slice is needed it must be acquired.
+ * Acquiring a slice never fails, it just increases the acquired counter of
+ * the slice and its dependent slices. when a slice shall be used inside a
+ * poller it must first be tested if the slice is already in use. If it is,
+ * we can't do the operation on the slice now and must return and hope that
+ * we have more luck in the next poller call.
+ *
+ * slices can be attached other slices as dependencies. In the example above
+ * LED driver would add a dependency to the GPIO controller and the GPIO driver
+ * would add a dependency to the I2C bus:
+ *
+ * GPIO driver probe:
+ *
+ * slice_depends_on(&gpio->slice, i2c_device_slice(i2cdev));
+ *
+ * LED driver probe:
+ *
+ * slice_depends_on(&led->slice, gpio_slice(gpio));
+ *
+ * The GPIO code would call slice_acquire(&gpio->slice) before doing any
+ * operation on the GPIO chip providing this GPIO, likewise the I2C core
+ * would call slice_acquire(&i2cbus->slice) before doing an operation on
+ * this I2C bus.
+ *
+ * The heartbeat poller code would call slice_acquired(led_slice(led)) and
+ * only continue when the slice is not acquired.
+ */
+
+/*
+ * slices are not required to have a device and thus a name, but it really
+ * helps debugging when it has one.
+ */
+static const char *slice_name(struct slice *slice)
+{
+	return slice->name;
+}
+
+static void __slice_acquire(struct slice *slice, int add)
+{
+	slice->acquired += add;
+
+	if (slice->acquired < 0) {
+		pr_err("Slice %s acquired count drops below zero\n",
+			slice_name(slice));
+		dump_stack();
+		slice->acquired = 0;
+		return;
+	}
+}
+
+/**
+ * slice_acquire: acquire a slice
+ * @slice: The slice to acquire
+ *
+ * This acquires a slice and its dependencies
+ */
+void slice_acquire(struct slice *slice)
+{
+	__slice_acquire(slice, 1);
+}
+
+/**
+ * slice_release: release a slice
+ * @slice: The slice to release
+ *
+ * This releases a slice and its dependencies
+ */
+void slice_release(struct slice *slice)
+{
+	__slice_acquire(slice, -1);
+}
+
+/**
+ * slice_acquired: test if a slice is acquired
+ * @slice: The slice to test
+ *
+ * This tests if a slice is acquired. Returns true if it is, false otherwise
+ */
+bool slice_acquired(struct slice *slice)
+{
+	struct slice_entry *se;
+	bool ret = false;
+
+	if (slice->acquired > 0)
+		return true;
+
+	if (slice->acquired < 0) {
+		pr_err("Recursive dependency detected in slice %s\n",
+		       slice_name(slice));
+		panic("Cannot continue");
+	}
+
+	slice->acquired = -1;
+
+	list_for_each_entry(se, &slice->deps, list)
+		if (slice_acquired(se->slice)) {
+			ret = true;
+			break;
+		}
+
+	slice->acquired = 0;
+
+	return ret;
+}
+
+static void __slice_debug_acquired(struct slice *slice, int level)
+{
+	struct slice_entry *se;
+
+	pr_debug("%*s%s: %d\n", level * 4, "",
+		 slice_name(slice),
+		 slice->acquired);
+
+	list_for_each_entry(se, &slice->deps, list)
+		__slice_debug_acquired(se->slice, level + 1);
+}
+
+/**
+ * slice_debug_acquired: print the acquired state of a slice
+ *
+ * @slice: The slice to print
+ *
+ * This prints the acquired state of a slice and its dependencies.
+ */
+void slice_debug_acquired(struct slice *slice)
+{
+	if (!slice_acquired(slice))
+		return;
+
+	__slice_debug_acquired(slice, 0);
+}
+
+/**
+ * slice_depends_on: Add a dependency to a slice
+ *
+ * @slice: The slice to add the dependency to
+ * @dep:   The slice @slice depends on
+ *
+ * This makes @slice dependent on @dep. In other words, acquiring @slice
+ * will lead to also acquiring @dep.
+ */
+void slice_depends_on(struct slice *slice, struct slice *dep)
+{
+	struct slice_entry *se = xzalloc(sizeof(*se));
+
+	pr_debug("Adding dependency %s (%d) to slice %s (%d)\n",
+		 slice_name(dep), dep->acquired,
+		 slice_name(slice), slice->acquired);
+
+	se->slice = dep;
+
+	list_add_tail(&se->list, &slice->deps);
+}
+
+static LIST_HEAD(slices);
+
+/**
+ * slice_init - initialize a slice before usage
+ * @slice: The slice to initialize
+ * @name:  The name of the slice
+ *
+ * This initializes a slice before usage. @name should be a meaningful name, when
+ * a device context is available to the caller it is recommended to pass its
+ * dev_name() as name argument.
+ */
+void slice_init(struct slice *slice, const char *name)
+{
+	INIT_LIST_HEAD(&slice->deps);
+	slice->name = xstrdup(name);
+	list_add_tail(&slice->list, &slices);
+}
+
+/**
+ * slice_exit: Remove a slice
+ * @slice: The slice to remove the dependency from
+ */
+void slice_exit(struct slice *slice)
+{
+	struct slice *s;
+	struct slice_entry *se, *tmp;
+
+	list_del(&slice->list);
+
+	/* remove our dependencies */
+	list_for_each_entry_safe(se, tmp, &slice->deps, list) {
+		list_del(&se->list);
+		free(se);
+	}
+
+	/* remove this slice from other slices dependencies */
+	list_for_each_entry(s, &slices, list) {
+		list_for_each_entry_safe(se, tmp, &s->deps, list) {
+			if (se->slice == slice) {
+				list_del(&se->list);
+				free(se);
+			}
+		}
+	}
+
+	free(slice->name);
+}
+
+#if defined CONFIG_CMD_SLICE
+
+#include <command.h>
+#include <getopt.h>
+
+static void slice_info(void)
+{
+	struct slice *slice;
+	struct slice_entry *se;
+
+	list_for_each_entry(slice, &slices, list) {
+		printf("slice %s: acquired=%d\n",
+		       slice_name(slice), slice->acquired);
+		list_for_each_entry(se, &slice->deps, list)
+			printf("    dep: %s\n", slice_name(se->slice));
+	}
+}
+
+static int __slice_acquire_name(const char *name, int add)
+{
+	struct slice *slice;
+
+	list_for_each_entry(slice, &slices, list) {
+		if (!strcmp(slice->name, name)) {
+			__slice_acquire(slice, add);
+			return 0;
+		}
+	}
+
+	printf("No such slice: %s\n", name);
+
+	return 1;
+}
+
+BAREBOX_CMD_HELP_START(slice)
+BAREBOX_CMD_HELP_TEXT("slice debugging command")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-i", "Print information about slices")
+BAREBOX_CMD_HELP_OPT ("-a <devname>", "acquire a slice")
+BAREBOX_CMD_HELP_OPT ("-r <devname>", "release a slice")
+BAREBOX_CMD_HELP_END
+
+static int do_slice(int argc, char *argv[])
+{
+	int opt;
+
+	while ((opt = getopt(argc, argv, "a:r:i")) > 0) {
+		switch (opt) {
+		case 'a':
+			return __slice_acquire_name(optarg, 1);
+		case 'r':
+			return __slice_acquire_name(optarg, -1);
+		case 'i':
+			slice_info();
+			return 0;
+		}
+	}
+
+	return COMMAND_ERROR_USAGE;
+}
+
+BAREBOX_CMD_START(slice)
+	.cmd = do_slice,
+	BAREBOX_CMD_DESC("debug slices")
+	BAREBOX_CMD_OPTS("[-ari]")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_HELP(cmd_slice_help)
+BAREBOX_CMD_END
+#endif
diff --git a/include/slice.h b/include/slice.h
new file mode 100644
index 0000000000..5538fc434a
--- /dev/null
+++ b/include/slice.h
@@ -0,0 +1,31 @@
+#ifndef __SLICE_H
+#define __SLICE_H
+
+enum slice_action {
+	SLICE_ACQUIRE = 1,
+	SLICE_RELEASE = -1,
+	SLICE_TEST = 0,
+};
+
+struct slice {
+	int acquired;
+	struct list_head deps;
+	char *name;
+	struct list_head list;
+};
+
+struct slice_entry {
+	struct slice *slice;
+	struct list_head list;
+};
+
+void slice_acquire(struct slice *slice);
+void slice_release(struct slice *slice);
+bool slice_acquired(struct slice *slice);
+void slice_depends_on(struct slice *slice, struct slice *dep);
+void slice_init(struct slice *slice, const char *name);
+void slice_exit(struct slice *slice);
+
+void slice_debug_acquired(struct slice *slice);
+
+#endif /* __SLICE_H */
-- 
2.26.2


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

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

* [PATCH 07/19] Introduce idle slice
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (5 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 06/19] Introduce slices Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-22 11:56   ` Daniel Glöckner
  2020-05-20  9:43 ` [PATCH 08/19] net: Add a slice to struct eth_device Sascha Hauer
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

From: Daniel Glöckner <dg@emlix.com>

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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/console_countdown.c |  1 +
 common/hush.c              |  5 +++++
 common/ratp/ratp.c         |  7 ++++++-
 common/slice.c             | 22 ++++++++++++++++++++++
 common/startup.c           |  2 ++
 include/ratp_bb.h          |  1 -
 include/slice.h            |  5 +++++
 lib/Kconfig                |  1 +
 lib/readline.c             |  7 ++-----
 9 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/common/console_countdown.c b/common/console_countdown.c
index 74dc382795..c69029dc0e 100644
--- a/common/console_countdown.c
+++ b/common/console_countdown.c
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <console_countdown.h>
 #include <stdio.h>
+#include <slice.h>
 
 static bool console_countdown_timeout_abort;
 
diff --git a/common/hush.c b/common/hush.c
index c24b2c7cd2..61424d98e7 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -460,7 +460,12 @@ static void get_user_input(struct in_str *i)
 	else
 		prompt = CONFIG_PROMPT_HUSH_PS2;
 
+	idle_slice_release();
+
 	n = readline(prompt, console_buffer, CONFIG_CBSIZE);
+
+	idle_slice_acquire();
+
 	if (n == -1 ) {
 		i->interrupt = 1;
 		n = 0;
diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c
index 556dc638d8..ec665803bb 100644
--- a/common/ratp/ratp.c
+++ b/common/ratp/ratp.c
@@ -299,10 +299,13 @@ static int ratp_console_register(struct ratp_ctx *ctx)
 	return 0;
 }
 
-void barebox_ratp_command_run(void)
+static void barebox_ratp_command_run(void)
 {
 	int ret;
 
+	if (slice_acquired(&idle_slice))
+		return;
+
 	if (!ratp_command)
 		return;
 
@@ -369,6 +372,8 @@ static void ratp_poller(struct poller_struct *poller)
 
 	free(buf);
 
+	barebox_ratp_command_run();
+
 	return;
 
 out:
diff --git a/common/slice.c b/common/slice.c
index 085d67604f..b64b822ec1 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>
 
 /*
@@ -231,6 +232,27 @@ void slice_exit(struct slice *slice)
 	free(slice->name);
 }
 
+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);
+
 #if defined CONFIG_CMD_SLICE
 
 #include <command.h>
diff --git a/common/startup.c b/common/startup.c
index 511675ed55..aecb045f45 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -268,8 +268,10 @@ enum autoboot_state do_autoboot_countdown(void)
 		break;
 	}
 
+	idle_slice_release();
 	ret = console_countdown(global_autoboot_timeout, flags, abortkeys,
 				&outkey);
+	idle_slice_acquire();
 
 	if (ret == 0)
 		autoboot_state = AUTOBOOT_BOOT;
diff --git a/include/ratp_bb.h b/include/ratp_bb.h
index a4f28c642c..b710f99bf9 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/include/slice.h b/include/slice.h
index 5538fc434a..37d5350ebe 100644
--- a/include/slice.h
+++ b/include/slice.h
@@ -28,4 +28,9 @@ void slice_exit(struct slice *slice);
 
 void slice_debug_acquired(struct slice *slice);
 
+extern struct slice idle_slice;
+
+void idle_slice_acquire(void);
+void idle_slice_release(void);
+
 #endif /* __SLICE_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index b4a8079700..053ac15cfc 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -111,6 +111,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 3d16c1838c..cafdc58b1b 100644
--- a/lib/readline.c
+++ b/lib/readline.c
@@ -3,7 +3,7 @@
 #include <init.h>
 #include <libbb.h>
 #include <poller.h>
-#include <ratp_bb.h>
+#include <slice.h>
 #include <xfuncs.h>
 #include <complete.h>
 #include <linux/ctype.h>
@@ -200,11 +200,8 @@ int readline(const char *prompt, char *buf, int len)
 	puts (prompt);
 
 	while (1) {
-		while (!tstc()) {
+		while (!tstc())
 			poller_call();
-			if (IS_ENABLED(CONFIG_CONSOLE_RATP))
-				barebox_ratp_command_run();
-		}
 
 		ichar = read_key();
 
-- 
2.26.2


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

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

* [PATCH 08/19] net: Add a slice to struct eth_device
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (6 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 07/19] Introduce idle slice Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 09/19] net: mdiobus: Add slice Sascha Hauer
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

Add ethernet code safe for being called from a poller.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/net.h |  8 ++++++++
 net/eth.c     | 20 +++++++++++++++++---
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/include/net.h b/include/net.h
index 54db8a179a..8d2b4923de 100644
--- a/include/net.h
+++ b/include/net.h
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <clock.h>
 #include <led.h>
+#include <slice.h>
 #include <xfuncs.h>
 #include <linux/phy.h>
 #include <linux/string.h>	/* memcpy */
@@ -63,6 +64,8 @@ struct eth_device {
 	char *bootarg;
 	char *linuxdevname;
 
+	struct slice slice;
+
 	bool ifup;
 #define ETH_MODE_DHCP 0
 #define ETH_MODE_STATIC 1
@@ -72,6 +75,11 @@ struct eth_device {
 
 #define dev_to_edev(d) container_of(d, struct eth_device, dev)
 
+static inline struct slice *eth_device_slice(struct eth_device *edev)
+{
+	return &edev->slice;
+}
+
 static inline const char *eth_name(struct eth_device *edev)
 {
 	return edev->devname;
diff --git a/net/eth.c b/net/eth.c
index e3d0d06efe..888075da61 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -219,20 +219,32 @@ int eth_send(struct eth_device *edev, void *packet, int length)
 	if (ret)
 		return ret;
 
+	slice_acquire(eth_device_slice(edev));
+
 	led_trigger_network(LED_TRIGGER_NET_TX);
 
-	return edev->send(edev, packet, length);
+	ret = edev->send(edev, packet, length);
+
+	slice_release(eth_device_slice(edev));
+
+	return ret;
 }
 
 static int __eth_rx(struct eth_device *edev)
 {
 	int ret;
 
+	slice_acquire(eth_device_slice(edev));
+
 	ret = eth_carrier_check(edev, 0);
 	if (ret)
-		return ret;
+		goto out;
+
+	ret = edev->recv(edev);
+out:
+	slice_release(eth_device_slice(edev));
 
-	return edev->recv(edev);
+	return ret;
 }
 
 int eth_rx(void)
@@ -353,6 +365,8 @@ int eth_register(struct eth_device *edev)
 	if (ret)
 		return ret;
 
+	slice_init(&edev->slice, dev_name(dev));
+
 	edev->devname = xstrdup(dev_name(&edev->dev));
 
 	dev_add_param_ip(dev, "ipaddr", NULL, NULL, &edev->ipaddr, edev);
-- 
2.26.2


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

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

* [PATCH 09/19] net: mdiobus: Add slice
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (7 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 08/19] net: Add a slice to struct eth_device Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 10/19] usb: Add a slice to usb host controllers Sascha Hauer
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

By adding a slice to the mdio bus we make the mdio code safe for being
called in a poller.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/net/phy/mdio_bus.c | 41 ++++++++++++++++++++++++++++++++++++++
 include/linux/phy.h        | 38 ++++++++++++++---------------------
 2 files changed, 56 insertions(+), 23 deletions(-)

diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 3480e2ffb4..9a76db6ad4 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -239,6 +239,8 @@ int mdiobus_register(struct mii_bus *bus)
 		return -EINVAL;
 	}
 
+	slice_init(&bus->slice, dev_name(&bus->dev));
+
 	if (bus->reset)
 		bus->reset(bus);
 
@@ -357,6 +359,45 @@ static int mdio_bus_match(struct device_d *dev, struct driver_d *drv)
 	return 1;
 }
 
+/**
+ * mdiobus_read - Convenience function for reading a given MII mgmt register
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+ * @regnum: register number to read
+ */
+int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
+{
+	int ret;
+
+	slice_acquire(&bus->slice);
+
+	ret = bus->read(bus, addr, regnum);
+
+	slice_release(&bus->slice);
+
+	return ret;
+}
+
+/**
+ * mdiobus_write - Convenience function for writing a given MII mgmt register
+ * @bus: the mii_bus struct
+ * @addr: the phy address
+ * @regnum: register number to write
+ * @val: value to write to @regnum
+ */
+int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
+{
+	int ret;
+
+	slice_acquire(&bus->slice);
+
+	ret = bus->write(bus, addr, regnum, val);
+
+	slice_release(&bus->slice);
+
+	return ret;
+}
+
 static ssize_t phydev_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags)
 {
 	int i = count;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index a9fdf44f1a..3f0f31b35c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -16,6 +16,7 @@
 #define __PHY_H
 
 #include <driver.h>
+#include <slice.h>
 #include <linux/list.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
@@ -116,9 +117,16 @@ struct mii_bus {
 	struct list_head list;
 
 	bool is_multiplexed;
+
+	struct slice slice;
 };
 #define to_mii_bus(d) container_of(d, struct mii_bus, dev)
 
+static inline struct slice *mdiobus_slice(struct mii_bus *bus)
+{
+	return &bus->slice;
+}
+
 int mdiobus_register(struct mii_bus *bus);
 void mdiobus_unregister(struct mii_bus *bus);
 struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
@@ -134,28 +142,8 @@ struct mii_bus *mdiobus_get_bus(int busnum);
 
 struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np);
 
-/**
- * mdiobus_read - Convenience function for reading a given MII mgmt register
- * @bus: the mii_bus struct
- * @addr: the phy address
- * @regnum: register number to read
- */
-static inline int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
-{
-	return bus->read(bus, addr, regnum);
-}
-
-/**
- * mdiobus_write - Convenience function for writing a given MII mgmt register
- * @bus: the mii_bus struct
- * @addr: the phy address
- * @regnum: register number to write
- * @val: value to write to @regnum
- */
-static inline int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
-{
-	return bus->write(bus, addr, regnum, val);
-}
+int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
 
 /* phy_device: An instance of a PHY
  *
@@ -376,11 +364,15 @@ int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
 int phy_register_fixup_for_id(const char *bus_id,
 		int (*run)(struct phy_device *));
 int phy_scan_fixups(struct phy_device *phydev);
-
 int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad);
 void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, int devad,
 				   u16 data);
 
+static inline bool phy_acquired(struct phy_device *phydev)
+{
+	return phydev && phydev->bus && slice_acquired(&phydev->bus->slice);
+}
+
 #ifdef CONFIG_PHYLIB
 int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
 		int (*run)(struct phy_device *));
-- 
2.26.2


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

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

* [PATCH 10/19] usb: Add a slice to usb host controllers
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (8 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 09/19] net: mdiobus: Add slice Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 11/19] usbnet: Add slice Sascha Hauer
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/usb/core/usb.c | 6 ++++++
 include/usb/usb.h      | 7 +++++++
 2 files changed, 13 insertions(+)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 30c251f405..f25e350f15 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -78,11 +78,16 @@ static inline int usb_host_acquire(struct usb_host *host)
 	if (host->sem)
 		return -EAGAIN;
 	host->sem++;
+
+	slice_acquire(&host->slice);
+
 	return 0;
 }
 
 static inline void usb_host_release(struct usb_host *host)
 {
+	slice_release(&host->slice);
+
 	if (host->sem > 0)
 		host->sem--;
 }
@@ -92,6 +97,7 @@ int usb_register_host(struct usb_host *host)
 	list_add_tail(&host->list, &host_list);
 	host->busnum = host_busnum++;
 	host->sem = 0;
+	slice_init(&host->slice, dev_name(host->hw_dev));
 	return 0;
 }
 
diff --git a/include/usb/usb.h b/include/usb/usb.h
index c2085eae87..1b51a00ddc 100644
--- a/include/usb/usb.h
+++ b/include/usb/usb.h
@@ -20,6 +20,7 @@
 #define _USB_H_
 
 #include <driver.h>
+#include <slice.h>
 #include <usb/ch9.h>
 #include <usb/ch11.h>
 #include <usb/usb_defs.h>
@@ -165,11 +166,17 @@ struct usb_host {
 	struct usb_device *root_dev;
 	int sem;
 	struct usb_phy *usbphy;
+	struct slice slice;
 };
 
 int usb_register_host(struct usb_host *);
 void usb_unregister_host(struct usb_host *host);
 
+static inline struct slice *usb_device_slice(struct usb_device *udev)
+{
+	return &udev->host->slice;
+}
+
 int usb_host_detect(struct usb_host *host);
 
 int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol);
-- 
2.26.2


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

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

* [PATCH 11/19] usbnet: Add slice
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (9 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 10/19] usb: Add a slice to usb host controllers Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 12/19] net: Call net_poll() in a poller Sascha Hauer
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

Both the ethernet device and the mdio bus of a USB network controller
need the USB bus. Add dependencies to it.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/net/usb/usbnet.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 406b8c964f..d2518c6e4a 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -233,6 +233,9 @@ int usbnet_probe(struct usb_device *usbdev, const struct usb_device_id *prod)
 
 	eth_register(edev);
 
+	slice_depends_on(eth_device_slice(edev), usb_device_slice(usbdev));
+	slice_depends_on(mdiobus_slice(&undev->miibus), usb_device_slice(usbdev));
+
 	return 0;
 out1:
 	dev_dbg(&edev->dev, "err: %d\n", status);
-- 
2.26.2


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

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

* [PATCH 12/19] net: Call net_poll() in a poller
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (10 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 11/19] usbnet: Add slice Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 13/19] net: reply to ping requests Sascha Hauer
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

This patch moves the ethernet receive loop into a poller. With this
network protocols no longer have to call net_loop() explicitly but
can do it implicitly by calling is_timeout() when waiting for packets.

Having the network receive loop running in a poller has the advantage
that we have it running continuously and not only when we explicitly
expect packets to come in. With this we can reasonably well answer to
ping requests which is implemented in the next patch. This also helps
protocols that run in the background like fastboot over UDP.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 fs/nfs.c         |  2 --
 fs/tftp.c        |  2 --
 include/net.h    |  3 ---
 net/dhcp.c       |  1 -
 net/dns.c        |  1 -
 net/eth.c        | 15 ++++++++++-----
 net/net.c        | 14 +++++++++++---
 net/netconsole.c |  4 +---
 net/nfs.c        |  1 -
 net/ping.c       |  2 --
 net/sntp.c       |  2 --
 11 files changed, 22 insertions(+), 25 deletions(-)

diff --git a/fs/nfs.c b/fs/nfs.c
index 15ddab7915..ebf851f656 100644
--- a/fs/nfs.c
+++ b/fs/nfs.c
@@ -447,8 +447,6 @@ again:
 	nfs_timer_start = get_time_ns();
 
 	while (1) {
-		net_poll();
-
 		if (is_timeout(nfs_timer_start, NFS_TIMEOUT)) {
 			tries++;
 			if (tries == NFS_MAX_RESEND)
diff --git a/fs/tftp.c b/fs/tftp.c
index d186e7983a..e5a58a4dae 100644
--- a/fs/tftp.c
+++ b/fs/tftp.c
@@ -207,8 +207,6 @@ static int tftp_poll(struct file_priv *priv)
 		return -ETIMEDOUT;
 	}
 
-	net_poll();
-
 	return 0;
 }
 
diff --git a/include/net.h b/include/net.h
index 8d2b4923de..583dc14ed5 100644
--- a/include/net.h
+++ b/include/net.h
@@ -244,9 +244,6 @@ IPaddr_t net_get_nameserver(void);
 const char *net_get_domainname(void);
 struct eth_device *net_route(IPaddr_t ip);
 
-/* Do the work */
-void net_poll(void);
-
 static inline struct iphdr *net_eth_to_iphdr(char *pkt)
 {
 	return (struct iphdr *)(pkt + ETHER_HDR_SIZE);
diff --git a/net/dhcp.c b/net/dhcp.c
index a27fa89996..ef22d2cef0 100644
--- a/net/dhcp.c
+++ b/net/dhcp.c
@@ -513,7 +513,6 @@ int dhcp_request(struct eth_device *edev, const struct dhcp_req_param *param,
 			ret = -ETIMEDOUT;
 			goto out1;
 		}
-		net_poll();
 		if (is_timeout(dhcp_start, 3 * SECOND)) {
 			dhcp_start = get_time_ns();
 			printf("T ");
diff --git a/net/dns.c b/net/dns.c
index ffe98ef9e3..851bf3722e 100644
--- a/net/dns.c
+++ b/net/dns.c
@@ -231,7 +231,6 @@ int resolv(const char *host, IPaddr_t *ip)
 		if (ctrlc()) {
 			break;
 		}
-		net_poll();
 		if (is_timeout(dns_timer_start, SECOND)) {
 			dns_timer_start = get_time_ns();
 			printf("T ");
diff --git a/net/eth.c b/net/eth.c
index 888075da61..e224f90947 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -234,14 +234,19 @@ static int __eth_rx(struct eth_device *edev)
 {
 	int ret;
 
-	slice_acquire(eth_device_slice(edev));
+	if (!phy_acquired(edev->phydev)) {
+		ret = eth_carrier_check(edev, 0);
+		if (ret)
+			return ret;
+	}
 
-	ret = eth_carrier_check(edev, 0);
-	if (ret)
-		goto out;
+	if (slice_acquired(eth_device_slice(edev)))
+		return 0;
+
+	slice_acquire(eth_device_slice(edev));
 
 	ret = edev->recv(edev);
-out:
+
 	slice_release(eth_device_slice(edev));
 
 	return ret;
diff --git a/net/net.c b/net/net.c
index 5fd6246ab2..197b551e72 100644
--- a/net/net.c
+++ b/net/net.c
@@ -239,8 +239,6 @@ static int arp_request(struct eth_device *edev, IPaddr_t dest, unsigned char *et
 
 		if (retries > PKT_NUM_RETRIES)
 			return -ETIMEDOUT;
-
-		net_poll();
 	}
 
 	pr_debug("Got ARP REPLY for %pI4: %02x:%02x:%02x:%02x:%02x:%02x\n",
@@ -249,11 +247,21 @@ static int arp_request(struct eth_device *edev, IPaddr_t dest, unsigned char *et
 	return 0;
 }
 
-void net_poll(void)
+static void net_poll(struct poller_struct *poller)
 {
 	eth_rx();
 }
 
+static struct poller_struct net_poller = {
+	.func = net_poll,
+};
+
+static int init_net_poll(void)
+{
+	return poller_register(&net_poller, "net");
+}
+device_initcall(init_net_poll);
+
 static uint16_t net_udp_new_localport(void)
 {
 	static uint16_t localport;
diff --git a/net/netconsole.c b/net/netconsole.c
index 0fece65a23..c692db942f 100644
--- a/net/netconsole.c
+++ b/net/netconsole.c
@@ -59,7 +59,7 @@ static int nc_getc(struct console_device *cdev)
 		return 0;
 
 	while (!kfifo_len(priv->fifo))
-		net_poll();
+		udelay(1);
 
 	kfifo_getc(priv->fifo, &c);
 
@@ -77,8 +77,6 @@ static int nc_tstc(struct console_device *cdev)
 	if (priv->busy)
 		return kfifo_len(priv->fifo) ? 1 : 0;
 
-	net_poll();
-
 	return kfifo_len(priv->fifo) ? 1 : 0;
 }
 
diff --git a/net/nfs.c b/net/nfs.c
index 63573098d7..e0479ef692 100644
--- a/net/nfs.c
+++ b/net/nfs.c
@@ -708,7 +708,6 @@ static int do_nfs(int argc, char *argv[])
 			nfs_err = -EINTR;
 			break;
 		}
-		net_poll();
 		if (is_timeout(nfs_timer_start, NFS_TIMEOUT * SECOND)) {
 			show_progress(-1);
 			nfs_send();
diff --git a/net/ping.c b/net/ping.c
index a71f59a805..f3de0c27a4 100644
--- a/net/ping.c
+++ b/net/ping.c
@@ -89,8 +89,6 @@ static int do_ping(int argc, char *argv[])
 			break;
 		}
 
-		net_poll();
-
 		if (is_timeout(ping_start, SECOND)) {
 			/* No answer, send another packet */
 			ping_start = get_time_ns();
diff --git a/net/sntp.c b/net/sntp.c
index b4e6d6439c..f693a2e8af 100644
--- a/net/sntp.c
+++ b/net/sntp.c
@@ -145,8 +145,6 @@ s64 sntp(const char *server)
 			break;
 		}
 
-		net_poll();
-
 		if (is_timeout(sntp_start, 1 * SECOND)) {
 			sntp_start = get_time_ns();
 			ret = sntp_send();
-- 
2.26.2


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

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

* [PATCH 13/19] net: reply to ping requests
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (11 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 12/19] net: Call net_poll() in a poller Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 14/19] usbnet: Be more friendly in the receive path Sascha Hauer
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

Now that we have the network receive function running in a poller we
can reasonably well answer to ping requests. Implement this feature.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 net/net.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/net/net.c b/net/net.c
index 197b551e72..cb0013519c 100644
--- a/net/net.c
+++ b/net/net.c
@@ -570,12 +570,54 @@ static int net_handle_udp(unsigned char *pkt, int len)
 	return -EINVAL;
 }
 
-static int net_handle_icmp(unsigned char *pkt, int len)
+static int ping_reply(struct eth_device *edev, unsigned char *pkt, int len)
+{
+	struct ethernet *et = (struct ethernet *)pkt;
+	struct icmphdr *icmp;
+	struct iphdr *ip = (struct iphdr *)(pkt + ETHER_HDR_SIZE);
+	unsigned char *packet;
+	int ret;
+
+	memcpy(et->et_dest, et->et_src, 6);
+	memcpy(et->et_src, edev->ethaddr, 6);
+	et->et_protlen = htons(PROT_IP);
+
+	icmp = net_eth_to_icmphdr(pkt);
+
+	icmp->type = ICMP_ECHO_REPLY;
+	icmp->checksum = 0;
+	icmp->checksum = ~net_checksum((unsigned char *)icmp,
+				       len - sizeof(struct iphdr) - ETHER_HDR_SIZE);
+	ip->check = 0;
+	ip->frag_off = 0;
+	net_copy_ip((void *)&ip->daddr, &ip->saddr);
+	net_copy_ip((void *)&ip->saddr, &edev->ipaddr);
+	ip->check = ~net_checksum((unsigned char *)ip, sizeof(struct iphdr));
+
+	packet = net_alloc_packet();
+	if (!packet)
+		return 0;
+
+	memcpy(packet, pkt, ETHER_HDR_SIZE + len);
+
+	ret = eth_send(edev, packet, ETHER_HDR_SIZE + len);
+
+	free(packet);
+
+	return 0;
+}
+
+static int net_handle_icmp(struct eth_device *edev, unsigned char *pkt, int len)
 {
 	struct net_connection *con;
+	struct icmphdr *icmp;
 
 	pr_debug("%s\n", __func__);
 
+	icmp = net_eth_to_icmphdr(pkt);
+	if (icmp->type == ICMP_ECHO_REQUEST)
+		ping_reply(edev, pkt, len);
+
 	list_for_each_entry(con, &connection_list, list) {
 		if (con->proto == IPPROTO_ICMP) {
 			con->handler(con->priv, pkt, len);
@@ -612,7 +654,7 @@ static int net_handle_ip(struct eth_device *edev, unsigned char *pkt, int len)
 
 	switch (ip->protocol) {
 	case IPPROTO_ICMP:
-		return net_handle_icmp(pkt, len);
+		return net_handle_icmp(edev, pkt, len);
 	case IPPROTO_UDP:
 		return net_handle_udp(pkt, len);
 	}
-- 
2.26.2


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

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

* [PATCH 14/19] usbnet: Be more friendly in the receive path
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (12 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 13/19] net: reply to ping requests Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:43 ` [PATCH 15/19] poller: Allow to run pollers inside of pollers Sascha Hauer
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

To recognize if we have a receive packet pending we must set up a USB
bulk transfer. When there's no incoming packet we must wait until the
transfer times out. We do this with every poller call which can
considerably slow down the system. With this patch we do two things
against this:

- lower the timeout for the bulk transfer
- When we haven't received a packet for longer then lower the frequency
  of calling into the USB stack to once every 100ms

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/net/usb/usbnet.c | 19 ++++++++++++++++++-
 include/usb/usbnet.h     |  3 +++
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index d2518c6e4a..25252fbcc2 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -122,12 +122,29 @@ static int usbnet_recv(struct eth_device *edev)
 
 	dev_dbg(&edev->dev, "%s\n",__func__);
 
+	/*
+	 * we must let the usb_bulk_msg below timeout before we realize
+	 * that we have no packet received. Since this function runs
+	 * inside a poller we considerably slow down barebox when we
+	 * wait for the timeout too often. To improve this we only poll
+	 * with full speed when we actually have received a packet in the
+	 * last 100ms.
+	 */
+	if (is_timeout(dev->last_pkt_received, 100 * MSECOND) &&
+	    !is_timeout(dev->last_recv_call, 100 * MSECOND)) {
+		return 0;
+	}
+
+	dev->last_recv_call = get_time_ns();
+
 	len = dev->rx_urb_size;
 
-	ret = usb_bulk_msg(dev->udev, dev->in, dev->rx_buf, len, &alen, 100);
+	ret = usb_bulk_msg(dev->udev, dev->in, dev->rx_buf, len, &alen, 2);
 	if (ret)
 		return ret;
 
+	dev->last_pkt_received = get_time_ns();
+
 	if (alen) {
 		if (info->rx_fixup)
 			return info->rx_fixup(dev, dev->rx_buf, alen);
diff --git a/include/usb/usbnet.h b/include/usb/usbnet.h
index 450db47b40..7ff32f280a 100644
--- a/include/usb/usbnet.h
+++ b/include/usb/usbnet.h
@@ -54,6 +54,9 @@ struct usbnet {
 #		define EVENT_RX_MEMORY	2
 #		define EVENT_STS_SPLIT	3
 #		define EVENT_LINK_RESET	4
+
+	uint64_t last_pkt_received;
+	uint64_t last_recv_call;
 };
 
 #if 0
-- 
2.26.2


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

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

* [PATCH 15/19] poller: Allow to run pollers inside of pollers
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (13 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 14/19] usbnet: Be more friendly in the receive path Sascha Hauer
@ 2020-05-20  9:43 ` Sascha Hauer
  2020-05-20  9:44 ` [PATCH 16/19] defconfigs: update renamed fastboot options Sascha Hauer
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:43 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

This adds a slice to each poller which is acquired before the poller is
executed. This allows us to run pollers inside of other pollers.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/poller.c  | 17 ++++++++---------
 include/poller.h |  2 ++
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/common/poller.c b/common/poller.c
index 95f828b439..ce4175fb3d 100644
--- a/common/poller.c
+++ b/common/poller.c
@@ -14,7 +14,6 @@
 #include <clock.h>
 
 static LIST_HEAD(poller_list);
-static int poller_active;
 
 int poller_register(struct poller_struct *poller, const char *name)
 {
@@ -22,6 +21,7 @@ int poller_register(struct poller_struct *poller, const char *name)
 		return -EBUSY;
 
 	poller->name = xstrdup(name);
+	slice_init(&poller->slice, poller->name);
 	list_add_tail(&poller->list, &poller_list);
 	poller->registered = 1;
 
@@ -33,7 +33,7 @@ int poller_unregister(struct poller_struct *poller)
 	if (!poller->registered)
 		return -ENODEV;
 
-
+	slice_exit(&poller->slice);
 	list_del(&poller->list);
 	poller->registered = 0;
 	free(poller->name);
@@ -111,15 +111,14 @@ void poller_call(void)
 {
 	struct poller_struct *poller, *tmp;
 
-	if (poller_active)
-		return;
-
-	poller_active = 1;
+	list_for_each_entry_safe(poller, tmp, &poller_list, list) {
+		if (slice_acquired(&poller->slice))
+			continue;
 
-	list_for_each_entry_safe(poller, tmp, &poller_list, list)
+		slice_acquire(&poller->slice);
 		poller->func(poller);
-
-	poller_active = 0;
+		slice_release(&poller->slice);
+	}
 }
 
 #if defined CONFIG_CMD_POLLER
diff --git a/include/poller.h b/include/poller.h
index 886557252b..c8b6829685 100644
--- a/include/poller.h
+++ b/include/poller.h
@@ -7,11 +7,13 @@
 #define POLLER_H
 
 #include <linux/list.h>
+#include <slice.h>
 
 struct poller_struct {
 	void (*func)(struct poller_struct *poller);
 	int registered;
 	struct list_head list;
+	struct slice slice;
 	char *name;
 };
 
-- 
2.26.2


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

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

* [PATCH 16/19] defconfigs: update renamed fastboot options
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (14 preceding siblings ...)
  2020-05-20  9:43 ` [PATCH 15/19] poller: Allow to run pollers inside of pollers Sascha Hauer
@ 2020-05-20  9:44 ` Sascha Hauer
  2020-05-20  9:44 ` [PATCH 17/19] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Sascha Hauer
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:44 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

From: Daniel Glöckner <dg@emlix.com>

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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 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 bff9c08c40..48bf14a390 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 4442c79cc4..beb0bc2f76 100644
--- a/arch/arm/configs/imx28_defconfig
+++ b/arch/arm/configs/imx28_defconfig
@@ -25,6 +25,7 @@ CONFIG_CONSOLE_ALLOW_COLOR=y
 CONFIG_PBL_CONSOLE=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
@@ -105,7 +106,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 5bf908ee85..fb7872434e 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 06fb406084..b96867ad0c 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 855daef71a..552b2d6d33 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 9d71d02744..59892cb231 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 7161d740ac..45c24d6df4 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.26.2


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

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

* [PATCH 17/19] fastboot: rename usbgadget.fastboot_* variables to fastboot.*
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (15 preceding siblings ...)
  2020-05-20  9:44 ` [PATCH 16/19] defconfigs: update renamed fastboot options Sascha Hauer
@ 2020-05-20  9:44 ` Sascha Hauer
  2020-05-20  9:44 ` [PATCH 18/19] fastboot net: implement fastboot over UDP Sascha Hauer
  2020-05-20  9:44 ` [PATCH 19/19] fastboot net: remove may_send Sascha Hauer
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:44 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

From: Daniel Glöckner <dg@emlix.com>

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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 Documentation/user/usb.rst |  4 ++--
 commands/usbgadget.c       |  2 +-
 common/fastboot.c          | 17 ++++++++++++++---
 common/usbgadget.c         | 16 ++--------------
 include/fastboot.h         |  3 +++
 5 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/Documentation/user/usb.rst b/Documentation/user/usb.rst
index 4c1b2925f2..ca5f8138de 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 193da86dee..9133402f52 100644
--- a/commands/usbgadget.c
+++ b/commands/usbgadget.c
@@ -57,7 +57,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 6f54e939e7..7a2f31a3e2 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -46,6 +46,8 @@
 #define FASTBOOT_VERSION		"0.4"
 
 static unsigned int fastboot_max_download_size = SZ_8M;
+int fastboot_bbu;
+char *fastboot_partitions;
 
 struct fb_variable {
 	char *name;
@@ -948,14 +950,23 @@ void fastboot_exec_cmd(struct fastboot *fb, const char *cmdbuf)
 static int fastboot_globalvars_init(void)
 {
 	if (IS_ENABLED(CONFIG_FASTBOOT_SPARSE))
-		globalvar_add_simple_int("usbgadget.fastboot_max_download_size",
+		globalvar_add_simple_int("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 a8f104cf1c..25fd994930 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 b3e7155efa..88ec478499 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -54,6 +54,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.26.2


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

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

* [PATCH 18/19] fastboot net: implement fastboot over UDP
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (16 preceding siblings ...)
  2020-05-20  9:44 ` [PATCH 17/19] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Sascha Hauer
@ 2020-05-20  9:44 ` Sascha Hauer
  2020-05-20  9:44 ` [PATCH 19/19] fastboot net: remove may_send Sascha Hauer
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:44 UTC (permalink / raw)
  To: Barebox List; +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>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 common/fastboot.c      |   3 +
 include/fastboot.h     |   1 +
 include/fastboot_net.h |  12 +
 net/Kconfig            |  18 ++
 net/Makefile           |   1 +
 net/fastboot.c         | 494 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 529 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 7a2f31a3e2..cd8647c338 100644
--- a/common/fastboot.c
+++ b/common/fastboot.c
@@ -250,6 +250,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,
@@ -278,6 +279,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;
 	}
@@ -292,6 +294,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 88ec478499..5c3e2894df 100644
--- a/include/fastboot.h
+++ b/include/fastboot.h
@@ -52,6 +52,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 0000000000..e4b9d98091
--- /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 12b6bdb56d..4ed5c6de9d 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 eb8d439150..962b2dec58 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 0000000000..9ea1a8c5b4
--- /dev/null
+++ b/net/fastboot.c
@@ -0,0 +1,494 @@
+// 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);
+	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_depends_on(&fbn->poller.slice, &idle_slice);
+	slice_depends_on(&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.26.2


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

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

* [PATCH 19/19] fastboot net: remove may_send
  2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
                   ` (17 preceding siblings ...)
  2020-05-20  9:44 ` [PATCH 18/19] fastboot net: implement fastboot over UDP Sascha Hauer
@ 2020-05-20  9:44 ` Sascha Hauer
  18 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-20  9:44 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

This removes the may_send mechanism from fastboot over UDP. Without this
fastboot transfers get stuck when on the command line something like
"sleep 10" is executed before or during a fastboot session. In this case
while (fbn->may_send == MAY_NOT_SEND) will never become false and
barebox is cought in that loop.

It is not yet clear if this is the right approach, so this is an extra
patch currently.

FIXME: This patch has the effect that the keepalive poller doesn't know
if we are inside a fastboot session or not, it will just blindly send a
keepalive every 30s, no matter what. This must be fixed before merging.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 net/fastboot.c | 28 +++++-----------------------
 1 file changed, 5 insertions(+), 23 deletions(-)

diff --git a/net/fastboot.c b/net/fastboot.c
index 9ea1a8c5b4..c4ea481c19 100644
--- a/net/fastboot.c
+++ b/net/fastboot.c
@@ -54,7 +54,6 @@ struct fastboot_net {
 	bool active_download;
 	bool download_needs_cleanup;
 	bool reinit;
-	u8 may_send;
 	char command[65];
 
 	IPaddr_t host_addr;
@@ -146,28 +145,11 @@ static int fastboot_write_net(struct fastboot *fb, const char *buf,
 	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;
@@ -232,7 +214,6 @@ static void fastboot_handle_type_fastboot(struct fastboot_net *fbn,
 {
 	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) {
@@ -389,11 +370,12 @@ static void fastboot_send_keep_alive(struct poller_struct *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,
+	if (!is_timeout_non_interruptible(fbn->host_waits_since,
 					 30ULL * NSEC_PER_SEC))
-		fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_INFO,
-				  "still busy");
+		return;
+
+	fastboot_tx_print(&fbn->fastboot, FASTBOOT_MSG_INFO, "still busy");
+	fbn->host_waits_since = get_time_ns();
 }
 
 void fastboot_net_free(struct fastboot_net *fbn)
-- 
2.26.2


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

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

* Re: [PATCH 07/19] Introduce idle slice
  2020-05-20  9:43 ` [PATCH 07/19] Introduce idle slice Sascha Hauer
@ 2020-05-22 11:56   ` Daniel Glöckner
  2020-05-25  8:09     ` Sascha Hauer
  0 siblings, 1 reply; 23+ messages in thread
From: Daniel Glöckner @ 2020-05-22 11:56 UTC (permalink / raw)
  To: Sascha Hauer, Barebox List; +Cc: Edmund Henniges

Hello Sascha,

Am 20.05.20 um 11:43 schrieb Sascha Hauer:
> diff --git a/common/console_countdown.c b/common/console_countdown.c
> index 74dc382795..c69029dc0e 100644
> --- a/common/console_countdown.c
> +++ b/common/console_countdown.c
> @@ -19,6 +19,7 @@
>  #include <errno.h>
>  #include <console_countdown.h>
>  #include <stdio.h>
> +#include <slice.h>
>  
>  static bool console_countdown_timeout_abort;
>  

with your changes we no longer need that include in here.

> diff --git a/common/hush.c b/common/hush.c
> index c24b2c7cd2..61424d98e7 100644
> --- a/common/hush.c
> +++ b/common/hush.c
> @@ -460,7 +460,12 @@ static void get_user_input(struct in_str *i)
>  	else
>  		prompt = CONFIG_PROMPT_HUSH_PS2;
>  
> +	idle_slice_release();
> +
>  	n = readline(prompt, console_buffer, CONFIG_CBSIZE);
> +
> +	idle_slice_acquire();
> +
>  	if (n == -1 ) {
>  		i->interrupt = 1;
>  		n = 0;

Ok, you moved the release/acquire from readline and console_countdown to
get_user_input and do_autoboot_countdown. That is actually more strict
than what I had in mind and avoids having to acquire/release in binfmt_run
and execute_command.

But I'm not so happy about the removal of the acquire/release in poller.c.
Now the fastboot poller might be run from within another poller and neither
do we know which resources that other poller uses nor do we know which
resources the commands need that we are told over to execute in our poller.

> @@ -369,6 +372,8 @@ static void ratp_poller(struct poller_struct *poller)
>  
>  	free(buf);
>  
> +	barebox_ratp_command_run();
> +
>  	return;
>  
>  out:

Ok, you reused the existing RATP poller instead of adding a second one.
Should work.

> diff --git a/lib/Kconfig b/lib/Kconfig
> index b4a8079700..053ac15cfc 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -111,6 +111,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"

This has no effect. You removed CONFIG_IDLE_SLICE.

> diff --git a/lib/readline.c b/lib/readline.c
> index 3d16c1838c..cafdc58b1b 100644
> --- a/lib/readline.c
> +++ b/lib/readline.c
> @@ -3,7 +3,7 @@
>  #include <init.h>
>  #include <libbb.h>
>  #include <poller.h>
> -#include <ratp_bb.h>
> +#include <slice.h>
>  #include <xfuncs.h>
>  #include <complete.h>
>  #include <linux/ctype.h>

Again, with your changes no include for slice.h needed.

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] 23+ messages in thread

* Re: [PATCH 07/19] Introduce idle slice
  2020-05-22 11:56   ` Daniel Glöckner
@ 2020-05-25  8:09     ` Sascha Hauer
  0 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-05-25  8:09 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: Barebox List, Edmund Henniges

On Fri, May 22, 2020 at 01:56:33PM +0200, Daniel Glöckner wrote:
> Hello Sascha,
> 
> Am 20.05.20 um 11:43 schrieb Sascha Hauer:
> > diff --git a/common/console_countdown.c b/common/console_countdown.c
> > index 74dc382795..c69029dc0e 100644
> > --- a/common/console_countdown.c
> > +++ b/common/console_countdown.c
> > @@ -19,6 +19,7 @@
> >  #include <errno.h>
> >  #include <console_countdown.h>
> >  #include <stdio.h>
> > +#include <slice.h>
> >  
> >  static bool console_countdown_timeout_abort;
> >  
> 
> with your changes we no longer need that include in here.
> 
> > diff --git a/common/hush.c b/common/hush.c
> > index c24b2c7cd2..61424d98e7 100644
> > --- a/common/hush.c
> > +++ b/common/hush.c
> > @@ -460,7 +460,12 @@ static void get_user_input(struct in_str *i)
> >  	else
> >  		prompt = CONFIG_PROMPT_HUSH_PS2;
> >  
> > +	idle_slice_release();
> > +
> >  	n = readline(prompt, console_buffer, CONFIG_CBSIZE);
> > +
> > +	idle_slice_acquire();
> > +
> >  	if (n == -1 ) {
> >  		i->interrupt = 1;
> >  		n = 0;
> 
> Ok, you moved the release/acquire from readline and console_countdown to
> get_user_input and do_autoboot_countdown. That is actually more strict
> than what I had in mind and avoids having to acquire/release in binfmt_run
> and execute_command.
> 
> But I'm not so happy about the removal of the acquire/release in poller.c.
> Now the fastboot poller might be run from within another poller and neither
> do we know which resources that other poller uses nor do we know which
> resources the commands need that we are told over to execute in our poller.

Ok, what you mean here is:

- idle_slice is released
- poller_call() calls some unrelated poller
- this unrelated poller triggers execution of poller_call() again
- poller_call() then triggers pollers which depend on the idle_slice

In this situation we end with pollers depending on the idle_slice to be
executed inside of pollers. Yes, we don't want that, so let's add
acquiring the idle_slice before calling into poller functions back. I
hadn't thought about this situation.

I fixed the other things you mentioned as well.

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] 23+ messages in thread

* [PATCH 13/19] net: reply to ping requests
  2020-03-12  8:35 [PATCH v3 00/19] Protect code from pollers Sascha Hauer
@ 2020-03-12  8:35 ` Sascha Hauer
  0 siblings, 0 replies; 23+ messages in thread
From: Sascha Hauer @ 2020-03-12  8:35 UTC (permalink / raw)
  To: Barebox List; +Cc: Edmund Henniges, Daniel Glöckner

Now that we have the network receive function running in a poller we
can reasonably well answer to ping requests. Implement this feature.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 net/net.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/net/net.c b/net/net.c
index 6eb604e5dc..e0cded5783 100644
--- a/net/net.c
+++ b/net/net.c
@@ -573,12 +573,54 @@ static int net_handle_udp(unsigned char *pkt, int len)
 	return -EINVAL;
 }
 
-static int net_handle_icmp(unsigned char *pkt, int len)
+static int ping_reply(struct eth_device *edev, unsigned char *pkt, int len)
+{
+	struct ethernet *et = (struct ethernet *)pkt;
+	struct icmphdr *icmp;
+	struct iphdr *ip = (struct iphdr *)(pkt + ETHER_HDR_SIZE);
+	unsigned char *packet;
+	int ret;
+
+	memcpy(et->et_dest, et->et_src, 6);
+	memcpy(et->et_src, edev->ethaddr, 6);
+	et->et_protlen = htons(PROT_IP);
+
+	icmp = net_eth_to_icmphdr(pkt);
+
+	icmp->type = ICMP_ECHO_REPLY;
+	icmp->checksum = 0;
+	icmp->checksum = ~net_checksum((unsigned char *)icmp,
+				       len - sizeof(struct iphdr) - ETHER_HDR_SIZE);
+	ip->check = 0;
+	ip->frag_off = 0;
+	net_copy_ip((void *)&ip->daddr, &ip->saddr);
+	net_copy_ip((void *)&ip->saddr, &edev->ipaddr);
+	ip->check = ~net_checksum((unsigned char *)ip, sizeof(struct iphdr));
+
+	packet = net_alloc_packet();
+	if (!packet)
+		return 0;
+
+	memcpy(packet, pkt, ETHER_HDR_SIZE + len);
+
+	ret = eth_send(edev, packet, ETHER_HDR_SIZE + len);
+
+	free(packet);
+
+	return 0;
+}
+
+static int net_handle_icmp(struct eth_device *edev, unsigned char *pkt, int len)
 {
 	struct net_connection *con;
+	struct icmphdr *icmp;
 
 	pr_debug("%s\n", __func__);
 
+	icmp = net_eth_to_icmphdr(pkt);
+	if (icmp->type == ICMP_ECHO_REQUEST)
+		ping_reply(edev, pkt, len);
+
 	list_for_each_entry(con, &connection_list, list) {
 		if (con->proto == IPPROTO_ICMP) {
 			con->handler(con->priv, pkt, len);
@@ -615,7 +657,7 @@ static int net_handle_ip(struct eth_device *edev, unsigned char *pkt, int len)
 
 	switch (ip->protocol) {
 	case IPPROTO_ICMP:
-		return net_handle_icmp(pkt, len);
+		return net_handle_icmp(edev, pkt, len);
 	case IPPROTO_UDP:
 		return net_handle_udp(pkt, len);
 	}
-- 
2.25.1


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

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

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

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-20  9:43 [PATCH 00/19] Slices and fastboot over UDP Sascha Hauer
2020-05-20  9:43 ` [PATCH 01/19] poller: Give pollers a name Sascha Hauer
2020-05-20  9:43 ` [PATCH 02/19] poller: Add a poller command Sascha Hauer
2020-05-20  9:43 ` [PATCH 03/19] fastboot: split generic code from USB gadget Sascha Hauer
2020-05-20  9:43 ` [PATCH 04/19] fastboot: don't close fd 0 when downloading to ram Sascha Hauer
2020-05-20  9:43 ` [PATCH 05/19] fastboot: Use unique tempfile name Sascha Hauer
2020-05-20  9:43 ` [PATCH 06/19] Introduce slices Sascha Hauer
2020-05-20  9:43 ` [PATCH 07/19] Introduce idle slice Sascha Hauer
2020-05-22 11:56   ` Daniel Glöckner
2020-05-25  8:09     ` Sascha Hauer
2020-05-20  9:43 ` [PATCH 08/19] net: Add a slice to struct eth_device Sascha Hauer
2020-05-20  9:43 ` [PATCH 09/19] net: mdiobus: Add slice Sascha Hauer
2020-05-20  9:43 ` [PATCH 10/19] usb: Add a slice to usb host controllers Sascha Hauer
2020-05-20  9:43 ` [PATCH 11/19] usbnet: Add slice Sascha Hauer
2020-05-20  9:43 ` [PATCH 12/19] net: Call net_poll() in a poller Sascha Hauer
2020-05-20  9:43 ` [PATCH 13/19] net: reply to ping requests Sascha Hauer
2020-05-20  9:43 ` [PATCH 14/19] usbnet: Be more friendly in the receive path Sascha Hauer
2020-05-20  9:43 ` [PATCH 15/19] poller: Allow to run pollers inside of pollers Sascha Hauer
2020-05-20  9:44 ` [PATCH 16/19] defconfigs: update renamed fastboot options Sascha Hauer
2020-05-20  9:44 ` [PATCH 17/19] fastboot: rename usbgadget.fastboot_* variables to fastboot.* Sascha Hauer
2020-05-20  9:44 ` [PATCH 18/19] fastboot net: implement fastboot over UDP Sascha Hauer
2020-05-20  9:44 ` [PATCH 19/19] fastboot net: remove may_send Sascha Hauer
  -- strict thread matches above, loose matches on Subject: below --
2020-03-12  8:35 [PATCH v3 00/19] Protect code from pollers Sascha Hauer
2020-03-12  8:35 ` [PATCH 13/19] net: reply to ping requests Sascha Hauer

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