diff mbox

[U-Boot,13/14] New command bootmenu: ANSI terminal Boot Menu support

Message ID 1327415291-13260-14-git-send-email-pali.rohar@gmail.com
State Changes Requested
Headers show

Commit Message

Pali Rohár Jan. 24, 2012, 2:28 p.m. UTC
* Added some ANSI escape codes definitions in common.h
 * Configuration is done via env variables bootmenu_delay and bootmenu_<num>:

    bootmenu_delay=<delay>
    bootmenu_<num>="<title>=<commands>"

    (title and commands are separated by first char '=')

    <delay> is delay in seconds of autobooting first entry
    <num> is boot menu entry, starting from zero
    <title> is text shown in boot screen
    <commands> are commands which will be executed when menu entry is selected

 * First argument of bootmenu command override bootmenu_delay env
 * If env bootmenu_delay or bootmenu arg is not specified, delay is 10 seconds
 * If delay is 0, no entry will be shown on screen and first will be called
 * If delay is less then 0, no autoboot delay will be used
 * Boot Menu will stop finding next menu entry if last was not defined
 * Boot Menu always add menu entry "U-Boot console" at end of all entries

 * Example using:

    setenv bootmenu_0 Boot 1. kernel=bootm 0x82000000  # Set first menu entry
    setenv bootmenu_1 Boot 2. kernel=bootm 0x83000000  # Set second menu entry
    setenv bootmenu_2 Reset board=reset                # Set third menu entry
    setenv bootmenu_3 U-Boot boot order=boot           # Set fourth menu entry
    setenv bootmenu_4  # Empty string is end of all bootmenu entries
    bootmenu 20        # Run bootmenu with autoboot delay 20s

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
---
Changes since original version:
   - ANSI bootmenu command: use puts instead printf
   - Merged parts of patch "Add some ANSI escape codes definitions in common.h"

 common/Makefile          |    1 +
 common/cmd_bootmenu.c    |  366 ++++++++++++++++++++++++++++++++++++++++++++++
 include/common.h         |   13 ++
 include/config_cmd_all.h |    1 +
 4 files changed, 381 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_bootmenu.c

Comments

Marek Vasut Jan. 25, 2012, 6:18 p.m. UTC | #1
>  * Added some ANSI escape codes definitions in common.h
>  * Configuration is done via env variables bootmenu_delay and
> bootmenu_<num>:
> 
>     bootmenu_delay=<delay>
>     bootmenu_<num>="<title>=<commands>"
> 
>     (title and commands are separated by first char '=')
> 
>     <delay> is delay in seconds of autobooting first entry
>     <num> is boot menu entry, starting from zero
>     <title> is text shown in boot screen
>     <commands> are commands which will be executed when menu entry is
> selected

Do you need this command if you already have common/menu.c?

M

> 
>  * First argument of bootmenu command override bootmenu_delay env
>  * If env bootmenu_delay or bootmenu arg is not specified, delay is 10
> seconds * If delay is 0, no entry will be shown on screen and first will
> be called * If delay is less then 0, no autoboot delay will be used
>  * Boot Menu will stop finding next menu entry if last was not defined
>  * Boot Menu always add menu entry "U-Boot console" at end of all entries
> 
>  * Example using:
> 
>     setenv bootmenu_0 Boot 1. kernel=bootm 0x82000000  # Set first menu
> entry setenv bootmenu_1 Boot 2. kernel=bootm 0x83000000  # Set second menu
> entry setenv bootmenu_2 Reset board=reset                # Set third menu
> entry setenv bootmenu_3 U-Boot boot order=boot           # Set fourth menu
> entry setenv bootmenu_4  # Empty string is end of all bootmenu entries
> bootmenu 20        # Run bootmenu with autoboot delay 20s
> 
> Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> ---
> Changes since original version:
>    - ANSI bootmenu command: use puts instead printf
>    - Merged parts of patch "Add some ANSI escape codes definitions in
> common.h"
> 
>  common/Makefile          |    1 +
>  common/cmd_bootmenu.c    |  366
> ++++++++++++++++++++++++++++++++++++++++++++++ include/common.h         | 
>  13 ++
>  include/config_cmd_all.h |    1 +
>  4 files changed, 381 insertions(+), 0 deletions(-)
>  create mode 100644 common/cmd_bootmenu.c
> 
> diff --git a/common/Makefile b/common/Makefile
> index e1efd45..7402bfb 100644
> --- a/common/Makefile
> +++ b/common/Makefile
> @@ -67,6 +67,7 @@ COBJS-$(CONFIG_CMD_SOURCE) += cmd_source.o
>  COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o
>  COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o
>  COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o
> +COBJS-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o
>  COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o
>  COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o
>  COBJS-$(CONFIG_CMD_CLEAR) += cmd_clear.o
> diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c
> new file mode 100644
> index 0000000..931ed18
> --- /dev/null
> +++ b/common/cmd_bootmenu.c
> @@ -0,0 +1,366 @@
> +/*
> + * (C) Copyright 2011 Pali Rohár <pali.rohar@gmail.com>
> + *
> + * See file CREDITS for list of people who contributed to this
> + * project.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of
> + * the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include <common.h>
> +#include <command.h>
> +#include <watchdog.h>
> +#include <linux/string.h>
> +
> +#ifdef CONFIG_SYS_HUSH_PARSER
> +#include <hush.h>
> +#endif
> +
> +static char *get_option(int n)
> +{
> +
> +	char name[] = "bootmenu_\0\0";
> +
> +	if (n < 0 || n > 99)
> +		return NULL;
> +
> +	sprintf(name+9, "%d", n);
> +
> +	return getenv(name);
> +
> +}
> +
> +static char *get_end_of_title(char *str)
> +{
> +
> +	if (!str)
> +		return NULL;
> +
> +	return strchr(str, '=');
> +
> +}
> +
> +static int print_title(char *begin, char *end)
> +{
> +
> +	if (!begin || !end || end < begin)
> +		return 1;
> +
> +	while (begin != end)
> +		putc(*(begin++));
> +
> +	return 0;
> +
> +}
> +
> +static int print_entry(int n, int reverse)
> +{
> +
> +	char *str = get_option(n);
> +	char *end = get_end_of_title(str);
> +
> +	if (!end)
> +		return 1;
> +
> +	printf(ANSI_CURSOR_POSITION, n+4, 1);
> +
> +	if (reverse)
> +		puts(ANSI_COLOR_REVERSE);
> +
> +	puts("     ");
> +	print_title(str, end);
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +
> +	if (reverse)
> +		puts(ANSI_COLOR_RESET);
> +
> +	return 0;
> +
> +}
> +
> +static int print_menu(int active)
> +{
> +
> +	int n = 0;
> +
> +	printf(ANSI_CURSOR_POSITION, 1, 1);
> +	puts(ANSI_CLEAR_LINE);
> +	printf(ANSI_CURSOR_POSITION, 2, 1);
> +	puts("  *** U-Boot BOOT MENU ***");
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +	printf(ANSI_CURSOR_POSITION, 3, 1);
> +	puts(ANSI_CLEAR_LINE);
> +
> +	while (1) {
> +
> +		int ret = print_entry(n, n == active ? 1 : 0);
> +
> +		if (ret == 1)
> +			break;
> +
> +		++n;
> +
> +	}
> +
> +	printf(ANSI_CURSOR_POSITION, n+4, 1);
> +
> +	if (n == active)
> +		puts(ANSI_COLOR_REVERSE);
> +
> +	puts("     U-Boot console");
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +
> +	if (n == active)
> +		puts(ANSI_COLOR_RESET);
> +
> +	printf(ANSI_CURSOR_POSITION, n+5, 1);
> +	puts(ANSI_CLEAR_LINE);
> +	printf(ANSI_CURSOR_POSITION, n+6, 1);
> +	puts("  Press UP/DOWN to move, ENTER to select");
> +	puts(ANSI_CLEAR_LINE_TO_END);
> +	printf(ANSI_CURSOR_POSITION, n+7, 1);
> +	puts(ANSI_CLEAR_LINE);
> +
> +	return n;
> +
> +}
> +
> +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
> +{
> +
> +	int active = 0;
> +	int abort = 0;
> +	int key = 0; /* 0 - NONE, 1 - UP, 2 - DOWN, 3 - SELECT */
> +	int esc = 0;
> +	int count = 0;
> +	int delay = 10;
> +	int instant = 0;
> +	char *delay_str = NULL;
> +
> +	if (argc >= 2)
> +		delay_str = argv[1];
> +
> +	if (!delay_str)
> +		delay_str = getenv("bootmenu_delay");
> +
> +	if (delay_str)
> +		delay = (int)simple_strtol(delay_str, NULL, 10);
> +
> +	if (delay == 0) {
> +
> +		/* prevent setting U-Boot console as first menu entry */
> +		if (get_end_of_title(get_option(0)))
> +			count = 1;
> +
> +		instant = 1;
> +
> +	}
> +
> +	if (delay < 0)
> +		abort = 1;
> +
> +	if (!instant) {
> +
> +		puts(ANSI_CURSOR_HIDE);
> +		puts(ANSI_CLEAR_CONSOLE);
> +		printf(ANSI_CURSOR_POSITION, 1, 1);
> +
> +	}
> +
> +	while (1) {
> +
> +		if (abort || delay > 0)
> +			count = print_menu(active);
> +
> +		if (!abort) {
> +
> +			if (delay > 0)
> +				printf("  Hit any key to stop autoboot: %2d ",
> +						delay);
> +
> +			while (delay > 0) {
> +
> +				int i;
> +
> +				for (i = 0; i < 100; ++i) {
> +
> +					if (tstc()) {
> +
> +						abort = 1;
> +						key = getc();
> +
> +						if (key == '\e') {
> +							esc = 1;
> +							key = 0;
> +						} else if (key == '\r')
> +							key = 3;
> +						else
> +							key = 0;
> +
> +						break;
> +
> +					}
> +
> +					WATCHDOG_RESET();
> +					udelay(10000);
> +
> +				}
> +
> +				if (abort)
> +					break;
> +
> +				--delay;
> +				printf("\b\b\b%2d ", delay);
> +
> +			}
> +
> +			if (delay <= 0)
> +				key = 3;
> +
> +		} else {
> +
> +			while (!tstc()) {
> +
> +				WATCHDOG_RESET();
> +				udelay(10000);
> +
> +			}
> +
> +			key = getc();
> +
> +			if (esc == 0) {
> +
> +				if (key == '\e') {
> +					esc = 1;
> +					key = 0;
> +				}
> +
> +			} else if (esc == 1) {
> +
> +				if (key == '[') {
> +					esc = 2;
> +					key = 0;
> +				} else
> +					esc = 0;
> +
> +			} else if (esc == 2 || esc == 3) {
> +
> +				if (esc == 2 && key == '1') {
> +					esc = 3;
> +					key = 0;
> +				} else
> +					esc = 0;
> +
> +				if (key == 'A')
> +					key = 1;
> +				else if (key == 'B')
> +					key = 2;
> +				else
> +					key = 0;
> +
> +			}
> +
> +			if (key == '\r')
> +				key = 3;
> +
> +		}
> +
> +		if (key == 1) {
> +
> +			if (active > 0)
> +				--active;
> +
> +		} else if (key == 2) {
> +
> +			if (active < count)
> +				++active;
> +
> +		} else if (key == 3) {
> +
> +			char *str;
> +			char *end;
> +
> +			putc('\n');
> +
> +			if (!instant) {
> +
> +				puts(ANSI_CURSOR_SHOW);
> +				puts(ANSI_CLEAR_CONSOLE);
> +				printf(ANSI_CURSOR_POSITION, 1, 1);
> +
> +			}
> +
> +			WATCHDOG_RESET();
> +
> +			/* last entry is always U-Boot console */
> +			if (active == count) {
> +
> +				puts("Starting U-Boot console\n\n");
> +				return 0;
> +
> +			}
> +
> +			str = get_option(active);
> +			end = get_end_of_title(str);
> +
> +			if (!end) {
> +
> +				printf("Invalid Boot Menu entry %d\n", active);
> +				puts("Starting U-Boot console\n\n");
> +				return 0;
> +
> +			}
> +
> +			if (!end[1]) {
> +
> +				printf("Invalid Boot Menu entry %d: ", active);
> +				print_title(str, end);
> +				puts("\nStarting U-Boot console\n\n");
> +				return 0;
> +
> +			}
> +
> +			printf("Booting Boot Menu entry %d: ", active);
> +			print_title(str, end);
> +			puts(" ...\n\n");
> +
> +#ifndef CONFIG_SYS_HUSH_PARSER
> +			run_command(end+1, 0);
> +#else
> +			parse_string_outer(end+1, FLAG_PARSE_SEMICOLON |
> +					FLAG_EXIT_FROM_LOOP);
> +#endif
> +
> +			printf("\nFailed booting Boot Menu entry %d: ", active);
> +			print_title(str, end);
> +			puts("\nStarting U-Boot console\n\n");
> +			return 0;
> +
> +		}
> +
> +	}
> +
> +	/* never happends */
> +	return 1;
> +
> +}
> +
> +U_BOOT_CMD(
> +	bootmenu, 2, 1, do_bootmenu,
> +	"ANSI terminal bootmenu",
> +	"[delay]\n"
> +	"    - show ANSI terminal bootmenu with autoboot delay (default 10s)"
> +);
> diff --git a/include/common.h b/include/common.h
> index 9c0449e..c6dd2ea 100644
> --- a/include/common.h
> +++ b/include/common.h
> @@ -754,8 +754,21 @@ int	disable_ctrlc (int);	/* 1 to disable, 0 to 
enable
> Control-C detect */ * ANSI terminal
>   */
> 
> +#define ANSI_CURSOR_UP			"\e[%dA"
> +#define ANSI_CURSOR_DOWN		"\e[%dB"
> +#define ANSI_CURSOR_FORWARD		"\e[%dC"
> +#define ANSI_CURSOR_BACK		"\e[%dD"
> +#define ANSI_CURSOR_NEXTLINE		"\e[%dE"
> +#define ANSI_CURSOR_PREVIOUSLINE	"\e[%dF"
> +#define ANSI_CURSOR_COLUMN		"\e[%dG"
>  #define ANSI_CURSOR_POSITION		"\e[%d;%dH"
> +#define ANSI_CURSOR_SHOW		"\e[?25h"
> +#define ANSI_CURSOR_HIDE		"\e[?25l"
>  #define ANSI_CLEAR_CONSOLE		"\e[2J"
> +#define ANSI_CLEAR_LINE_TO_END		"\e[0K"
> +#define ANSI_CLEAR_LINE			"\e[2K"
> +#define ANSI_COLOR_RESET		"\e[0m"
> +#define ANSI_COLOR_REVERSE		"\e[7m"
> 
>  /*
>   * STDIO based functions (can always be used)
> diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h
> index 3f25eba..8c0a648 100644
> --- a/include/config_cmd_all.h
> +++ b/include/config_cmd_all.h
> @@ -20,6 +20,7 @@
>  #define CONFIG_CMD_BEDBUG	/* Include BedBug Debugger	*/
>  #define CONFIG_CMD_BMP		/* BMP support			*/
>  #define CONFIG_CMD_BOOTD	/* bootd			*/
> +#define CONFIG_CMD_BOOTMENU	/* ANSI terminal Boot Menu	*/
>  #define CONFIG_CMD_BSP		/* Board Specific functions	*/
>  #define CONFIG_CMD_CACHE	/* icache, dcache		*/
>  #define CONFIG_CMD_CDP		/* Cisco Discovery Protocol	*/
Pali Rohár Jan. 25, 2012, 7:57 p.m. UTC | #2
On Wednesday 25 January 2012 19:18:50 Marek Vasut wrote:
> Do you need this command if you already have common/menu.c?
> 

I need menu which can work with ANSI terminal and framebuffer on device 
screen. Something which can easy choose booting system...

I can look at menu.c if it can replace my bootmenu (or change bootmenu code to 
use menu.c)...

Anyway this patch [13] and next [14] can be reviewed separatly. Nokia RX-51 
support is in patches 1-12. Patch 14 is additional for rx51, which only add 
this bootmenu support. U-Boot rx51 port working fine without 13 and 14.
Sergey Lapin Jan. 27, 2012, 10:51 a.m. UTC | #3
On Wed, Jan 25, 2012 at 08:57:19PM +0100, Pali Rohár wrote:
> On Wednesday 25 January 2012 19:18:50 Marek Vasut wrote:
> > Do you need this command if you already have common/menu.c?
> > 
> 
> I need menu which can work with ANSI terminal and framebuffer on device 
> screen. Something which can easy choose booting system...
> 
> I can look at menu.c if it can replace my bootmenu (or change bootmenu code to 
> use menu.c)...
> 
> Anyway this patch [13] and next [14] can be reviewed separatly. Nokia RX-51 
> support is in patches 1-12. Patch 14 is additional for rx51, which only add 
> this bootmenu support. U-Boot rx51 port working fine without 13 and 14.
Regardless of whatever when you add new commands it might be useful to add
some documentation in doc directory.

S.
Mike Frysinger Feb. 14, 2012, 7:07 a.m. UTC | #4
On Wednesday 25 January 2012 14:57:19 Pali Rohár wrote:
> On Wednesday 25 January 2012 19:18:50 Marek Vasut wrote:
> > Do you need this command if you already have common/menu.c?
> 
> I need menu which can work with ANSI terminal and framebuffer on device
> screen. Something which can easy choose booting system...
> 
> I can look at menu.c if it can replace my bootmenu (or change bootmenu code
> to use menu.c)...

yes, as i suggested before, you should be merging with the existing menu code 
and not proposing parallel implementations

so NAK this for now
-mike
diff mbox

Patch

diff --git a/common/Makefile b/common/Makefile
index e1efd45..7402bfb 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -67,6 +67,7 @@  COBJS-$(CONFIG_CMD_SOURCE) += cmd_source.o
 COBJS-$(CONFIG_CMD_BDI) += cmd_bdinfo.o
 COBJS-$(CONFIG_CMD_BEDBUG) += bedbug.o cmd_bedbug.o
 COBJS-$(CONFIG_CMD_BMP) += cmd_bmp.o
+COBJS-$(CONFIG_CMD_BOOTMENU) += cmd_bootmenu.o
 COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o
 COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o
 COBJS-$(CONFIG_CMD_CLEAR) += cmd_clear.o
diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c
new file mode 100644
index 0000000..931ed18
--- /dev/null
+++ b/common/cmd_bootmenu.c
@@ -0,0 +1,366 @@ 
+/*
+ * (C) Copyright 2011 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+#include <watchdog.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_SYS_HUSH_PARSER
+#include <hush.h>
+#endif
+
+static char *get_option(int n)
+{
+
+	char name[] = "bootmenu_\0\0";
+
+	if (n < 0 || n > 99)
+		return NULL;
+
+	sprintf(name+9, "%d", n);
+
+	return getenv(name);
+
+}
+
+static char *get_end_of_title(char *str)
+{
+
+	if (!str)
+		return NULL;
+
+	return strchr(str, '=');
+
+}
+
+static int print_title(char *begin, char *end)
+{
+
+	if (!begin || !end || end < begin)
+		return 1;
+
+	while (begin != end)
+		putc(*(begin++));
+
+	return 0;
+
+}
+
+static int print_entry(int n, int reverse)
+{
+
+	char *str = get_option(n);
+	char *end = get_end_of_title(str);
+
+	if (!end)
+		return 1;
+
+	printf(ANSI_CURSOR_POSITION, n+4, 1);
+
+	if (reverse)
+		puts(ANSI_COLOR_REVERSE);
+
+	puts("     ");
+	print_title(str, end);
+	puts(ANSI_CLEAR_LINE_TO_END);
+
+	if (reverse)
+		puts(ANSI_COLOR_RESET);
+
+	return 0;
+
+}
+
+static int print_menu(int active)
+{
+
+	int n = 0;
+
+	printf(ANSI_CURSOR_POSITION, 1, 1);
+	puts(ANSI_CLEAR_LINE);
+	printf(ANSI_CURSOR_POSITION, 2, 1);
+	puts("  *** U-Boot BOOT MENU ***");
+	puts(ANSI_CLEAR_LINE_TO_END);
+	printf(ANSI_CURSOR_POSITION, 3, 1);
+	puts(ANSI_CLEAR_LINE);
+
+	while (1) {
+
+		int ret = print_entry(n, n == active ? 1 : 0);
+
+		if (ret == 1)
+			break;
+
+		++n;
+
+	}
+
+	printf(ANSI_CURSOR_POSITION, n+4, 1);
+
+	if (n == active)
+		puts(ANSI_COLOR_REVERSE);
+
+	puts("     U-Boot console");
+	puts(ANSI_CLEAR_LINE_TO_END);
+
+	if (n == active)
+		puts(ANSI_COLOR_RESET);
+
+	printf(ANSI_CURSOR_POSITION, n+5, 1);
+	puts(ANSI_CLEAR_LINE);
+	printf(ANSI_CURSOR_POSITION, n+6, 1);
+	puts("  Press UP/DOWN to move, ENTER to select");
+	puts(ANSI_CLEAR_LINE_TO_END);
+	printf(ANSI_CURSOR_POSITION, n+7, 1);
+	puts(ANSI_CLEAR_LINE);
+
+	return n;
+
+}
+
+int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+
+	int active = 0;
+	int abort = 0;
+	int key = 0; /* 0 - NONE, 1 - UP, 2 - DOWN, 3 - SELECT */
+	int esc = 0;
+	int count = 0;
+	int delay = 10;
+	int instant = 0;
+	char *delay_str = NULL;
+
+	if (argc >= 2)
+		delay_str = argv[1];
+
+	if (!delay_str)
+		delay_str = getenv("bootmenu_delay");
+
+	if (delay_str)
+		delay = (int)simple_strtol(delay_str, NULL, 10);
+
+	if (delay == 0) {
+
+		/* prevent setting U-Boot console as first menu entry */
+		if (get_end_of_title(get_option(0)))
+			count = 1;
+
+		instant = 1;
+
+	}
+
+	if (delay < 0)
+		abort = 1;
+
+	if (!instant) {
+
+		puts(ANSI_CURSOR_HIDE);
+		puts(ANSI_CLEAR_CONSOLE);
+		printf(ANSI_CURSOR_POSITION, 1, 1);
+
+	}
+
+	while (1) {
+
+		if (abort || delay > 0)
+			count = print_menu(active);
+
+		if (!abort) {
+
+			if (delay > 0)
+				printf("  Hit any key to stop autoboot: %2d ",
+						delay);
+
+			while (delay > 0) {
+
+				int i;
+
+				for (i = 0; i < 100; ++i) {
+
+					if (tstc()) {
+
+						abort = 1;
+						key = getc();
+
+						if (key == '\e') {
+							esc = 1;
+							key = 0;
+						} else if (key == '\r')
+							key = 3;
+						else
+							key = 0;
+
+						break;
+
+					}
+
+					WATCHDOG_RESET();
+					udelay(10000);
+
+				}
+
+				if (abort)
+					break;
+
+				--delay;
+				printf("\b\b\b%2d ", delay);
+
+			}
+
+			if (delay <= 0)
+				key = 3;
+
+		} else {
+
+			while (!tstc()) {
+
+				WATCHDOG_RESET();
+				udelay(10000);
+
+			}
+
+			key = getc();
+
+			if (esc == 0) {
+
+				if (key == '\e') {
+					esc = 1;
+					key = 0;
+				}
+
+			} else if (esc == 1) {
+
+				if (key == '[') {
+					esc = 2;
+					key = 0;
+				} else
+					esc = 0;
+
+			} else if (esc == 2 || esc == 3) {
+
+				if (esc == 2 && key == '1') {
+					esc = 3;
+					key = 0;
+				} else
+					esc = 0;
+
+				if (key == 'A')
+					key = 1;
+				else if (key == 'B')
+					key = 2;
+				else
+					key = 0;
+
+			}
+
+			if (key == '\r')
+				key = 3;
+
+		}
+
+		if (key == 1) {
+
+			if (active > 0)
+				--active;
+
+		} else if (key == 2) {
+
+			if (active < count)
+				++active;
+
+		} else if (key == 3) {
+
+			char *str;
+			char *end;
+
+			putc('\n');
+
+			if (!instant) {
+
+				puts(ANSI_CURSOR_SHOW);
+				puts(ANSI_CLEAR_CONSOLE);
+				printf(ANSI_CURSOR_POSITION, 1, 1);
+
+			}
+
+			WATCHDOG_RESET();
+
+			/* last entry is always U-Boot console */
+			if (active == count) {
+
+				puts("Starting U-Boot console\n\n");
+				return 0;
+
+			}
+
+			str = get_option(active);
+			end = get_end_of_title(str);
+
+			if (!end) {
+
+				printf("Invalid Boot Menu entry %d\n", active);
+				puts("Starting U-Boot console\n\n");
+				return 0;
+
+			}
+
+			if (!end[1]) {
+
+				printf("Invalid Boot Menu entry %d: ", active);
+				print_title(str, end);
+				puts("\nStarting U-Boot console\n\n");
+				return 0;
+
+			}
+
+			printf("Booting Boot Menu entry %d: ", active);
+			print_title(str, end);
+			puts(" ...\n\n");
+
+#ifndef CONFIG_SYS_HUSH_PARSER
+			run_command(end+1, 0);
+#else
+			parse_string_outer(end+1, FLAG_PARSE_SEMICOLON |
+					FLAG_EXIT_FROM_LOOP);
+#endif
+
+			printf("\nFailed booting Boot Menu entry %d: ", active);
+			print_title(str, end);
+			puts("\nStarting U-Boot console\n\n");
+			return 0;
+
+		}
+
+	}
+
+	/* never happends */
+	return 1;
+
+}
+
+U_BOOT_CMD(
+	bootmenu, 2, 1, do_bootmenu,
+	"ANSI terminal bootmenu",
+	"[delay]\n"
+	"    - show ANSI terminal bootmenu with autoboot delay (default 10s)"
+);
diff --git a/include/common.h b/include/common.h
index 9c0449e..c6dd2ea 100644
--- a/include/common.h
+++ b/include/common.h
@@ -754,8 +754,21 @@  int	disable_ctrlc (int);	/* 1 to disable, 0 to enable Control-C detect */
  * ANSI terminal
  */
 
+#define ANSI_CURSOR_UP			"\e[%dA"
+#define ANSI_CURSOR_DOWN		"\e[%dB"
+#define ANSI_CURSOR_FORWARD		"\e[%dC"
+#define ANSI_CURSOR_BACK		"\e[%dD"
+#define ANSI_CURSOR_NEXTLINE		"\e[%dE"
+#define ANSI_CURSOR_PREVIOUSLINE	"\e[%dF"
+#define ANSI_CURSOR_COLUMN		"\e[%dG"
 #define ANSI_CURSOR_POSITION		"\e[%d;%dH"
+#define ANSI_CURSOR_SHOW		"\e[?25h"
+#define ANSI_CURSOR_HIDE		"\e[?25l"
 #define ANSI_CLEAR_CONSOLE		"\e[2J"
+#define ANSI_CLEAR_LINE_TO_END		"\e[0K"
+#define ANSI_CLEAR_LINE			"\e[2K"
+#define ANSI_COLOR_RESET		"\e[0m"
+#define ANSI_COLOR_REVERSE		"\e[7m"
 
 /*
  * STDIO based functions (can always be used)
diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h
index 3f25eba..8c0a648 100644
--- a/include/config_cmd_all.h
+++ b/include/config_cmd_all.h
@@ -20,6 +20,7 @@ 
 #define CONFIG_CMD_BEDBUG	/* Include BedBug Debugger	*/
 #define CONFIG_CMD_BMP		/* BMP support			*/
 #define CONFIG_CMD_BOOTD	/* bootd			*/
+#define CONFIG_CMD_BOOTMENU	/* ANSI terminal Boot Menu	*/
 #define CONFIG_CMD_BSP		/* Board Specific functions	*/
 #define CONFIG_CMD_CACHE	/* icache, dcache		*/
 #define CONFIG_CMD_CDP		/* Cisco Discovery Protocol	*/