bootstd: Add a return code to bootflow menu

Return an error when the user does not select an OS, so we know whether
to boot or not.

Move calling of bootflow_menu_run() into a separate function so we can
call it from other places.

Expand the test to cover these cases.

Add some documentation also, while we are here.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/cmd/bootflow.c b/cmd/bootflow.c
index ad39ebe..3aeb40d 100644
--- a/cmd/bootflow.c
+++ b/cmd/bootflow.c
@@ -89,6 +89,44 @@
 	       num_valid);
 }
 
+/**
+ * bootflow_handle_menu() - Handle running the menu and updating cur bootflow
+ *
+ * This shows the menu, allows the user to select something and then prints
+ * what happened
+ *
+ * @std: bootstd information
+ * @text_mode: true to run the menu in text mode
+ * @bflowp: Returns selected bootflow, on success
+ * Return: 0 on success (a bootflow was selected), -EAGAIN if nothing was
+ *	chosen, other -ve value on other error
+ */
+__maybe_unused static int bootflow_handle_menu(struct bootstd_priv *std,
+					       bool text_mode,
+					       struct bootflow **bflowp)
+{
+	struct bootflow *bflow;
+	int ret;
+
+	ret = bootflow_menu_run(std, text_mode, &bflow);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			printf("Nothing chosen\n");
+			std->cur_bootflow = NULL;
+		} else {
+			printf("Menu failed (err=%d)\n", ret);
+		}
+
+		return ret;
+	}
+
+	printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
+	std->cur_bootflow = bflow;
+	*bflowp = bflow;
+
+	return 0;
+}
+
 static int do_bootflow_scan(struct cmd_tbl *cmdtp, int flag, int argc,
 			    char *const argv[])
 {
@@ -455,18 +493,9 @@
 	if (ret)
 		return CMD_RET_FAILURE;
 
-	ret = bootflow_menu_run(std, text_mode, &bflow);
-	if (ret) {
-		if (ret == -EAGAIN)
-			printf("Nothing chosen\n");
-		else {
-			printf("Menu failed (err=%d)\n", ret);
-			return CMD_RET_FAILURE;
-		}
-	}
-
-	printf("Selected: %s\n", bflow->os_name ? bflow->os_name : bflow->name);
-	std->cur_bootflow = bflow;
+	ret = bootflow_handle_menu(std, text_mode, &bflow);
+	if (ret)
+		return CMD_RET_FAILURE;
 
 	return 0;
 }
diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst
index 9c5ea9c..2198ff6 100644
--- a/doc/usage/cmd/bootflow.rst
+++ b/doc/usage/cmd/bootflow.rst
@@ -15,6 +15,7 @@
     bootflow read
     bootflow boot
     bootflow cmdline [set|get|clear|delete|auto] <param> [<value>]
+    bootfloe menu [-t]
 
 Description
 -----------
@@ -24,6 +25,9 @@
 
 See :doc:`../../develop/bootstd` for more information.
 
+Note that `CONFIG_BOOTSTD_FULL` (which enables `CONFIG_CMD_BOOTFLOW_FULL) must
+be enabled to obtain full functionality with this command. Otherwise, it only
+supports `bootflow scan` which scans and boots the first available bootflow.
 
 bootflow scan
 ~~~~~~~~~~~~~
@@ -247,6 +251,16 @@
 output appears on the serial port. This is only supported by the 16550 serial
 driver so far.
 
+bootflow menu
+~~~~~~~~~~~~~
+
+This shows a menu with available bootflows. The user can select a particular
+bootflow, which then becomes the current one.
+
+The `-t` flag requests a text menu. Otherwise, if a display is available, a
+graphical menu is shown.
+
+
 Example
 -------
 
@@ -658,6 +672,56 @@
     77b7e4e0: 320fc000 08e8ba0f c031300f b8d0000f  ...2.....01.....
     77b7e4f0: 00000020 6ad8000f 00858d10 50000002   ......j.......P
 
+This shows using a text menu to boot an OS::
+
+    => bootflow scan
+    => bootfl list
+    => bootfl menu -t
+    U-Boot    :    Boot Menu
+
+    UP and DOWN to choose, ENTER to select
+
+      >    0  mmc1        mmc1.bootdev.whole
+           1  mmc1        Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+           2  mmc1        mmc1.bootdev.part_1
+           3  mmc4        mmc4.bootdev.whole
+           4  mmc4        Armbian
+           5  mmc4        mmc4.bootdev.part_1
+           6  mmc5        mmc5.bootdev.whole
+           7  mmc5        ChromeOS
+           8  mmc5        ChromeOS
+    U-Boot    :    Boot Menu
+
+    UP and DOWN to choose, ENTER to select
+
+           0  mmc1        mmc1.bootdev.whole
+      >    1  mmc1        Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+           2  mmc1        mmc1.bootdev.part_1
+           3  mmc4        mmc4.bootdev.whole
+           4  mmc4        Armbian
+           5  mmc4        mmc4.bootdev.part_1
+           6  mmc5        mmc5.bootdev.whole
+           7  mmc5        ChromeOS
+           8  mmc5        ChromeOS
+    U-Boot    :    Boot Menu
+
+    Selected: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+    => bootfl boot
+    ** Booting bootflow 'mmc1.bootdev.part_1' with extlinux
+    Ignoring unknown command: ui
+    Ignoring malformed menu command:  autoboot
+    Ignoring malformed menu command:  hidden
+    Ignoring unknown command: totaltimeout
+    Fedora-Workstation-armhfp-31-1.9 Boot Options.
+    1:	Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+    Enter choice: 1
+    1:	Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+    Retrieving file: /vmlinuz-5.3.7-301.fc31.armv7hl
+    Retrieving file: /initramfs-5.3.7-301.fc31.armv7hl.img
+    append: ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
+    Retrieving file: /dtb-5.3.7-301.fc31.armv7hl/sandbox.dtb
+    ...
+
 
 Return value
 ------------
@@ -667,6 +731,9 @@
 return value $? is 1. If the boot succeeds but for some reason the Operating
 System returns, then $? is 0, indicating success.
 
+For `bootflow menu` the return value is $? is 0 (true) if an option was choses,
+else 1.
+
 For other subcommands, the return value $? is always 0 (true).
 
 
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index 102b2b5..b97c566 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -604,8 +604,12 @@
 /* Check 'bootflow menu' to select a bootflow */
 static int bootflow_cmd_menu(struct unit_test_state *uts)
 {
+	struct bootstd_priv *std;
 	char prev[3];
 
+	/* get access to the current bootflow */
+	ut_assertok(bootstd_get_priv(&std));
+
 	ut_assertok(scan_mmc4_bootdev(uts));
 
 	/* Add keypresses to move to and select the second one in the list */
@@ -616,6 +620,17 @@
 
 	ut_assertok(run_command("bootflow menu", 0));
 	ut_assert_nextline("Selected: Armbian");
+	ut_assertnonnull(std->cur_bootflow);
+	ut_assert_console_end();
+
+	/* Check not selecting anything */
+	prev[0] = '\e';
+	prev[1] = '\0';
+	ut_asserteq(1, console_in_puts(prev));
+
+	ut_asserteq(1, run_command("bootflow menu", 0));
+	ut_assertnull(std->cur_bootflow);
+	ut_assert_nextline("Nothing chosen");
 	ut_assert_console_end();
 
 	return 0;