[U-Boot] [PATCH 0/2] ANSI terminal Bootmenu

This patch series adds ANSI terminal Bootmenu command. It use generic menu code for creating menu structures, but use own functions for drawing menu on ANSI terminal. First patch modify generic menu code for using other functions for printing and choosing menu entry, second patch is bootmenu command itself.
Pali Rohár (2): menu: Added support to use user defined functions New command bootmenu: ANSI terminal Boot Menu support
board/ait/cam_enc_4xx/cam_enc_4xx.c | 5 +- common/Makefile | 1 + common/cmd_bootmenu.c | 446 +++++++++++++++++++++++++++++++++++ common/cmd_pxe.c | 3 +- common/menu.c | 43 ++-- doc/README.bootmenu | 61 +++++ include/common.h | 20 ++ include/config_cmd_all.h | 1 + include/menu.h | 6 +- 9 files changed, 566 insertions(+), 20 deletions(-) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu

* In menu_interactive_choice can be used user specified function item_data_choice (instead hardcoded function which read input from standard input)
* Added option to specify user data for menu
* menu_display_statusline will pass pointer to user data (instead pointer to menu)
* This patch is needed for creating ANSI bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com --- board/ait/cam_enc_4xx/cam_enc_4xx.c | 5 ++-- common/cmd_pxe.c | 3 ++- common/menu.c | 43 +++++++++++++++++++++++------------ include/menu.h | 6 +++-- 4 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/board/ait/cam_enc_4xx/cam_enc_4xx.c b/board/ait/cam_enc_4xx/cam_enc_4xx.c index 32b28f9..5078b01 100644 --- a/board/ait/cam_enc_4xx/cam_enc_4xx.c +++ b/board/ait/cam_enc_4xx/cam_enc_4xx.c @@ -561,7 +561,8 @@ static char *menu_handle(struct menu_display *display) char *s; char temp[6][200];
- m = menu_create(display->title, display->timeout, 1, ait_menu_print); + m = menu_create(display->title, display->timeout, 1, ait_menu_print, + NULL, NULL);
for (i = 0; display->menulist[i]; i++) { sprintf(key, "%d", i + 1); @@ -1097,7 +1098,7 @@ int menu_show(int bootdelay) return MENU_EXIT; }
-void menu_display_statusline(struct menu *m) +void menu_display_statusline(void *data) { char *s1, *s2;
diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index b3c1f67..346e275 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -1198,7 +1198,8 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) /* * Create a menu and add items for all the labels. */ - m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print); + m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, + NULL, NULL);
if (!m) return NULL; diff --git a/common/menu.c b/common/menu.c index aa16c9a..470eb9f 100644 --- a/common/menu.c +++ b/common/menu.c @@ -47,6 +47,8 @@ struct menu { char *title; int prompt; void (*item_data_print)(void *); + char *(*item_data_choice)(void *); + void *data; struct list_head items; };
@@ -113,11 +115,11 @@ static inline void *menu_item_destroy(struct menu *m, return NULL; }
-void __menu_display_statusline(struct menu *m) +void __menu_display_statusline(void *menu_data) { return; } -void menu_display_statusline(struct menu *m) +void menu_display_statusline(void *menu_data) __attribute__ ((weak, alias("__menu_display_statusline")));
/* @@ -130,7 +132,7 @@ static inline void menu_display(struct menu *m) puts(m->title); putc('\n'); } - menu_display_statusline(m); + menu_display_statusline(m->data);
menu_items_iter(m, menu_item_print, NULL); } @@ -230,20 +232,27 @@ static inline int menu_interactive_choice(struct menu *m, void **choice)
menu_display(m);
- readret = readline_into_buffer("Enter choice: ", cbuf, - m->timeout); - - if (readret >= 0) { - choice_item = menu_item_by_key(m, cbuf); - - if (!choice_item) { - printf("%s not found\n", cbuf); - m->timeout = 0; + if (!m->item_data_choice) { + readret = readline_into_buffer("Enter choice: ", cbuf, + m->timeout); + + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + if (!choice_item) + printf("%s not found\n", cbuf); + } else { + puts("^C\n"); + return -EINTR; } } else { - puts("^C\n"); - return -EINTR; + char *key = m->item_data_choice(m->data); + if (!key) + return -EINTR; + choice_item = menu_item_by_key(m, key); } + + if (!choice_item) + m->timeout = 0; }
*choice = choice_item->data; @@ -380,7 +389,9 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data) * insufficient memory available to create the menu. */ struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)) + void (*item_data_print)(void *), + char *(*item_data_choice)(void *), + void *menu_data) { struct menu *m;
@@ -393,6 +404,8 @@ struct menu *menu_create(char *title, int timeout, int prompt, m->prompt = prompt; m->timeout = timeout; m->item_data_print = item_data_print; + m->item_data_choice = item_data_choice; + m->data = menu_data;
if (title) { m->title = strdup(title); diff --git a/include/menu.h b/include/menu.h index 7af5fdb..00e8975 100644 --- a/include/menu.h +++ b/include/menu.h @@ -21,12 +21,14 @@ struct menu;
struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)); + void (*item_data_print)(void *), + char *(*item_data_choice)(void *), + void *menu_data); int menu_default_set(struct menu *m, char *item_key); int menu_get_choice(struct menu *m, void **choice); int menu_item_add(struct menu *m, char *item_key, void *item_data); int menu_destroy(struct menu *m); -void menu_display_statusline(struct menu *m); +void menu_display_statusline(void *menu_data);
#if defined(CONFIG_MENU_SHOW) int menu_show(int bootdelay);

Dear Pali Rohár,
Please CC proper custodians in order to get reviews next time.
[...]
--- a/include/menu.h +++ b/include/menu.h @@ -21,12 +21,14 @@ struct menu;
struct menu *menu_create(char *title, int timeout, int prompt,
void (*item_data_print)(void *));
void (*item_data_print)(void *),
char *(*item_data_choice)(void *),
Where is this item_data_choice() used?
void *menu_data);
int menu_default_set(struct menu *m, char *item_key); int menu_get_choice(struct menu *m, void **choice); int menu_item_add(struct menu *m, char *item_key, void *item_data); int menu_destroy(struct menu *m); -void menu_display_statusline(struct menu *m); +void menu_display_statusline(void *menu_data);
#if defined(CONFIG_MENU_SHOW) int menu_show(int bootdelay);
Best regards, Marek Vasut

On Sunday 03 June 2012 11:59:16 Marek Vasut wrote:
--- a/include/menu.h +++ b/include/menu.h @@ -21,12 +21,14 @@
struct menu;
struct menu *menu_create(char *title, int timeout, int prompt,
void (*item_data_print)(void *));
void (*item_data_print)(void *),
char *(*item_data_choice)(void *),
Where is this item_data_choice() used?
This is alternative function for menu entry choice. It is used in function menu_interactive_choice. If item_data_choice is NULL default code with readline_into_buffer is used.
ANSI bootmenu command (in next patch) is using its own function.

Dear Pali Rohár,
On Sunday 03 June 2012 11:59:16 Marek Vasut wrote:
--- a/include/menu.h +++ b/include/menu.h @@ -21,12 +21,14 @@
struct menu;
struct menu *menu_create(char *title, int timeout, int prompt,
void (*item_data_print)(void *));
void (*item_data_print)(void *),
char *(*item_data_choice)(void *),
Where is this item_data_choice() used?
This is alternative function for menu entry choice. It is used in function menu_interactive_choice. If item_data_choice is NULL default code with readline_into_buffer is used.
ANSI bootmenu command (in next patch) is using its own function.
Hm so why not make this ANSI stuff one plugin and the other bootmenu stuff another plugin (which will be default). Then you won't have to check if anything is null.
Best regards, Marek Vasut

Signed-off-by: Pali Rohár pali.rohar@gmail.com --- common/Makefile | 1 + common/cmd_bootmenu.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 61 +++++++ include/common.h | 20 +++ include/config_cmd_all.h | 1 + 5 files changed, 529 insertions(+) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu
diff --git a/common/Makefile b/common/Makefile index 6e23baa..b9d4a4a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -69,6 +69,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_CONSOLE) += cmd_console.o diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c new file mode 100644 index 0000000..935b60a --- /dev/null +++ b/common/cmd_bootmenu.c @@ -0,0 +1,446 @@ +/* + * (C) Copyright 2011-2012 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 <menu.h> +#include <hush.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/string.h> + +struct bootmenu_entry { + int num; /* unique number 0..99 */ + char key[3]; /* key idetifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +static char *bootmenu_getoption(int n) +{ + char name[] = "bootmenu_\0\0"; + + if (n < 0 || n > 99) + return NULL; + + sprintf(name+9, "%d", n); + return getenv(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(" "); + puts(entry->title); + puts(ANSI_CLEAR_LINE_TO_END); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + + int key = 0; /* 0 - NONE, 1 - UP, 2 - DOWN, 3 - SELECT */ + int esc = 0; + + while (1) { + + if (menu->delay >= 0) { + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + printf(" Hit any key to stop autoboot: %2d ", + menu->delay); + } + + while (menu->delay > 0) { + + int i; + for (i = 0; i < 100; ++i) { + if (tstc()) { + menu->delay = -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 (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + + } + + if (menu->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 (menu->active > 0) + --menu->active; + return ""; /* invalid menu entry, regenerate menu */ + } else if (key == 2) { + if (menu->active < menu->count-1) + ++menu->active; + return ""; /* invalid menu entry, regenerate menu */ + } else if (key == 3) { + int i; + struct bootmenu_entry *iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + } + + } + + /* never happends */ + return NULL; +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + while ((option = bootmenu_getoption(i))) { + + int len; + char *sep; + struct bootmenu_entry *entry; + + sep = strchr(option, '='); + if (!sep) + break; + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len+1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep+1); + entry->command = malloc(len+1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep+1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i >= 100) + break; + + } + + /* Add U-Boot console entry at the end */ + if (i < 100) { + struct bootmenu_entry *entry = malloc( + sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + } + + menu->count = i; + return menu; + +cleanup: + iter = menu->first; + while (iter) { + struct bootmenu_entry *next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + while (iter) { + struct bootmenu_entry *next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + char *option, *sep; + option = bootmenu_getoption(0); + if (!option) + return; + sep = strchr(option, '='); + if (!sep) + return; + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, (bootmenu->delay >= 0), + bootmenu_print_entry, bootmenu_choice_entry, bootmenu); + if (!menu) + return; + + for (iter = bootmenu->first; iter; iter = iter->next) + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + printf("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } +} + +void menu_display_statusline(void *data) +{ + struct bootmenu_data *menu = data; + + 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); + + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} + +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + + 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); + + bootmenu_show(delay); + return 0; +} + +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/doc/README.bootmenu b/doc/README.bootmenu new file mode 100644 index 0000000..69ef93b --- /dev/null +++ b/doc/README.bootmenu @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2011-2012 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 + */ + +This is ANSI terminal BootMenu command. It is extension to generic menu, +which provide output for ANSI terminals. + +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 + + +To enable ANSI bootmenu comamnd add these definitions to board code: + + #define CONFIG_BOOTDELAY 30 + #define CONFIG_AUTOBOOT_KEYED + #define CONFIG_MENU + #define CONFIG_MENU_SHOW + #define CONFIG_CMD_BOOTMENU diff --git a/include/common.h b/include/common.h index 8564a65..59ecdb9 100644 --- a/include/common.h +++ b/include/common.h @@ -761,6 +761,26 @@ void clear_ctrlc (void); /* clear the Control-C condition */ 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) */ /* serial stuff */ diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index 55f4f7a..d47a860 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -19,6 +19,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_BOOTZ /* boot zImage */ #define CONFIG_CMD_BSP /* Board Specific functions */ #define CONFIG_CMD_CACHE /* icache, dcache */

Dear Pali Rohár,
Signed-off-by: Pali Rohár pali.rohar@gmail.com
Try keeping the subject line short and add a patch description instead. Btw. Try avoiding unicode characters in the patch. Also, is "Pali" your real name that you have on your IDs etc. ?
common/Makefile | 1 + common/cmd_bootmenu.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 61 +++++++ include/common.h | 20 +++ include/config_cmd_all.h | 1 + 5 files changed, 529 insertions(+) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu
diff --git a/common/Makefile b/common/Makefile index 6e23baa..b9d4a4a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -69,6 +69,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_CONSOLE) += cmd_console.o diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c new file mode 100644 index 0000000..935b60a --- /dev/null +++ b/common/cmd_bootmenu.c @@ -0,0 +1,446 @@ +/*
- (C) Copyright 2011-2012 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 <menu.h> +#include <hush.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/string.h>
+struct bootmenu_entry {
- int num; /* unique number 0..99 */
- char key[3]; /* key idetifier of number */
- char *title; /* title of entry */
- char *command; /* hush command of entry */
- struct bootmenu_data *menu; /* this bootmenu */
- struct bootmenu_entry *next; /* next menu entry (num+1) */
+};
+struct bootmenu_data {
- int delay; /* delay for autoboot */
- int active; /* active menu entry */
- int count; /* total count of menu entries */
- struct bootmenu_entry *first; /* first menu entry */
+};
+static char *bootmenu_getoption(int n) +{
- char name[] = "bootmenu_\0\0";
- if (n < 0 || n > 99)
Why add this artificial limit?
return NULL;
- sprintf(name+9, "%d", n);
- return getenv(name);
+}
+static void bootmenu_print_entry(void *data) +{
- struct bootmenu_entry *entry = data;
- int reverse = (entry->menu->active == entry->num);
Why not just write it below into the condition?
- printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
What are all these artificial numbers here?
- if (reverse)
puts(ANSI_COLOR_REVERSE);
- puts(" ");
- puts(entry->title);
- puts(ANSI_CLEAR_LINE_TO_END);
- if (reverse)
puts(ANSI_COLOR_RESET);
+}
+static char *bootmenu_choice_entry(void *data) +{
- struct bootmenu_data *menu = data;
- int key = 0; /* 0 - NONE, 1 - UP, 2 - DOWN, 3 - SELECT */
- int esc = 0;
Can't the spaghetti function below be split into multiple smaller ones? You know the rule of the thumb, if the code spans multiple screens, something's seriously wrong.
- while (1) {
if (menu->delay >= 0) {
if (menu->delay > 0) {
printf(ANSI_CURSOR_POSITION, menu->count+5, 1);
printf(" Hit any key to stop autoboot: %2d ",
menu->delay);
}
while (menu->delay > 0) {
int i;
for (i = 0; i < 100; ++i) {
if (tstc()) {
if (!tstc()) continue;
... do your job break;
You'll get one less level of indent and much more readable code.
menu->delay = -1;
key = getc();
if (key == '\e') {
esc = 1;
key = 0;
} else if (key == '\r')
key = 3;
else
key = 0;
break;
}
WATCHDOG_RESET();
udelay(10000);
mdelay() ?
}
if (menu->delay < 0)
break;
--menu->delay;
Why do you use predecrement in the above statement? It's pointless here, simply postdecrement it.
printf("\b\b\b%2d ", menu->delay);
}
if (menu->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 (menu->active > 0)
--menu->active;
return ""; /* invalid menu entry, regenerate menu */
} else if (key == 2) {
if (menu->active < menu->count-1)
++menu->active;
Let me guess ... they taught you at the compiler class that predecrement is faster than postdecrement, right? Not so much on anything but intel ... and even on intel, GCC does the decision about putting the instructions there properly to create the fastest possible code anyway.
return ""; /* invalid menu entry, regenerate menu */
} else if (key == 3) {
int i;
struct bootmenu_entry *iter = menu->first;
for (i = 0; i < menu->active; ++i)
iter = iter->next;
return iter->key;
}
- }
- /* never happends */
Add debug() clause maybe ?
- return NULL;
+}
+static struct bootmenu_data *bootmenu_create(int delay) +{
- int i = 0;
- const char *option;
- struct bootmenu_data *menu;
- struct bootmenu_entry *iter = NULL;
- menu = malloc(sizeof(struct bootmenu_data));
- if (!menu)
return NULL;
- menu->delay = delay;
- menu->active = 0;
- menu->first = NULL;
- while ((option = bootmenu_getoption(i))) {
int len;
char *sep;
struct bootmenu_entry *entry;
sep = strchr(option, '=');
if (!sep)
break;
entry = malloc(sizeof(struct bootmenu_entry));
if (!entry)
goto cleanup;
len = sep-option;
entry->title = malloc(len+1);
if (!entry->title) {
free(entry);
goto cleanup;
}
memcpy(entry->title, option, len);
entry->title[len] = 0;
len = strlen(sep+1);
entry->command = malloc(len+1);
if (!entry->command) {
free(entry->title);
free(entry);
goto cleanup;
}
memcpy(entry->command, sep+1, len);
entry->command[len] = 0;
sprintf(entry->key, "%d", i);
entry->num = i;
entry->menu = menu;
entry->next = NULL;
if (!iter)
menu->first = entry;
else
iter->next = entry;
iter = entry;
++i;
if (i >= 100)
break;
- }
- /* Add U-Boot console entry at the end */
- if (i < 100) {
struct bootmenu_entry *entry = malloc(
sizeof(struct bootmenu_entry));
if (!entry)
goto cleanup;
entry->title = strdup("U-Boot console");
if (!entry->title) {
free(entry);
goto cleanup;
}
entry->command = strdup("");
if (!entry->command) {
free(entry->title);
free(entry);
goto cleanup;
}
entry->num = i;
entry->menu = menu;
entry->next = NULL;
if (!iter)
menu->first = entry;
else
iter->next = entry;
iter = entry;
++i;
- }
- menu->count = i;
- return menu;
+cleanup:
- iter = menu->first;
- while (iter) {
struct bootmenu_entry *next = iter->next;
free(iter->title);
free(iter->command);
free(iter);
iter = next;
- }
- free(menu);
- return NULL;
+}
+static void bootmenu_destroy(struct bootmenu_data *menu) +{
- struct bootmenu_entry *iter = menu->first;
- while (iter) {
struct bootmenu_entry *next = iter->next;
I don't like how you declare variables in the middle of code. It has sideeffects and looking them up is really troublesome.
free(iter->title);
free(iter->command);
free(iter);
iter = next;
- }
- free(menu);
+}
+static void bootmenu_show(int delay) +{
- int init = 0;
- void *choice = NULL;
- char *title = NULL;
- char *command = NULL;
- struct menu *menu;
- struct bootmenu_data *bootmenu;
- struct bootmenu_entry *iter;
- /* If delay is 0 do not create menu, just run first entry */
- if (delay == 0) {
char *option, *sep;
option = bootmenu_getoption(0);
if (!option)
return;
sep = strchr(option, '=');
if (!sep)
return;
run_command(sep+1, 0);
return;
- }
- bootmenu = bootmenu_create(delay);
- if (!bootmenu)
return;
- menu = menu_create(NULL, bootmenu->delay, (bootmenu->delay >= 0),
bootmenu_print_entry, bootmenu_choice_entry, bootmenu);
- if (!menu)
return;
- for (iter = bootmenu->first; iter; iter = iter->next)
if (!menu_item_add(menu, iter->key, iter))
goto cleanup;
- /* Default menu entry is always first */
- menu_default_set(menu, "0");
- puts(ANSI_CURSOR_HIDE);
- puts(ANSI_CLEAR_CONSOLE);
- printf(ANSI_CURSOR_POSITION, 1, 1);
- init = 1;
- if (menu_get_choice(menu, &choice)) {
iter = choice;
title = strdup(iter->title);
command = strdup(iter->command);
- }
+cleanup:
- menu_destroy(menu);
- bootmenu_destroy(bootmenu);
- if (init) {
puts(ANSI_CURSOR_SHOW);
puts(ANSI_CLEAR_CONSOLE);
printf(ANSI_CURSOR_POSITION, 1, 1);
- }
- if (title && command) {
printf("Starting entry '%s'\n", title);
free(title);
run_command(command, 0);
free(command);
- }
+}
+void menu_display_statusline(void *data) +{
- struct bootmenu_data *menu = data;
- 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);
- printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
- puts(ANSI_CLEAR_LINE);
- printf(ANSI_CURSOR_POSITION, menu->count + 6, 1);
- puts(" Press UP/DOWN to move, ENTER to select");
- puts(ANSI_CLEAR_LINE_TO_END);
- printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
- puts(ANSI_CLEAR_LINE);
+}
+int menu_show(int bootdelay) +{
- bootmenu_show(bootdelay);
- return -1; /* -1 - abort boot and run monitor code */
+}
+int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{
- char *delay_str = NULL;
- int delay = 10;
- 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);
- bootmenu_show(delay);
- return 0;
+}
+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/doc/README.bootmenu b/doc/README.bootmenu new file mode 100644 index 0000000..69ef93b --- /dev/null +++ b/doc/README.bootmenu @@ -0,0 +1,61 @@ +/*
- (C) Copyright 2011-2012 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
- */
+This is ANSI terminal BootMenu command. It is extension to generic menu, +which provide output for ANSI terminals.
+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
+To enable ANSI bootmenu comamnd add these definitions to board code:
- #define CONFIG_BOOTDELAY 30
- #define CONFIG_AUTOBOOT_KEYED
- #define CONFIG_MENU
- #define CONFIG_MENU_SHOW
- #define CONFIG_CMD_BOOTMENU
diff --git a/include/common.h b/include/common.h index 8564a65..59ecdb9 100644 --- a/include/common.h +++ b/include/common.h @@ -761,6 +761,26 @@ void clear_ctrlc (void); /* clear the Control-C condition */ 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"
Isn't it possible to extend the normal bootmenu code with the ansi command sequences and be done with it?
+/*
- STDIO based functions (can always be used)
*/ /* serial stuff */ diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index 55f4f7a..d47a860 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -19,6 +19,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_BOOTZ /* boot zImage */ #define CONFIG_CMD_BSP /* Board Specific functions */ #define CONFIG_CMD_CACHE /* icache, dcache */

Hi Marek,
On Sun, Jun 03, 2012 at 12:06:55PM +0200, Marek Vasut wrote:
Dear Pali Rohár,
Signed-off-by: Pali Rohár pali.rohar@gmail.com
Try keeping the subject line short and add a patch description instead. Btw. Try avoiding unicode characters in the patch. Also, is "Pali" your real name that you have on your IDs etc. ?
What kind of question is that :) ?
Like you can do anything if it's real or not...
Regards, Luka

Dear Luka Perkov,
Hi Marek,
On Sun, Jun 03, 2012 at 12:06:55PM +0200, Marek Vasut wrote:
Dear Pali Rohár,
Signed-off-by: Pali Rohár pali.rohar@gmail.com
Try keeping the subject line short and add a patch description instead. Btw. Try avoiding unicode characters in the patch. Also, is "Pali" your real name that you have on your IDs etc. ?
What kind of question is that :) ?
Like you can do anything if it's real or not...
I can vote for the patch to not be merged obviously if it's of suspicious origin. But before you loose your temper completely and flame me to death, read on ...
Basically, I don't want to be the bitch that wards people off here, but we better obey some kind of rules. See http://lwn.net/Articles/195643/ why such possibly minor thing might be an issue. There was very long thread about it in the LKML back in the day. Though it's not exactly the case here, Pavel, please use the real name in your patches and especially in the SoB lines.
Regards, Luka
Best regards, Marek Vasut

Hi Marek,
On Sun, Jun 03, 2012 at 08:27:53PM +0200, Marek Vasut wrote:
Try keeping the subject line short and add a patch description instead. Btw. Try avoiding unicode characters in the patch. Also, is "Pali" your real name that you have on your IDs etc. ?
What kind of question is that :) ?
Like you can do anything if it's real or not...
I can vote for the patch to not be merged obviously if it's of suspicious origin.
Ah yes, I forgot that one.
But before you loose your temper completely and flame me to death, read on ...
I'm not loosing my temper nor going to flame you ;) I was just supprised with the question. And now I know why you have asked it.
Basically, I don't want to be the bitch that wards people off here, but we better obey some kind of rules. See http://lwn.net/Articles/195643/ why such possibly minor thing might be an issue. There was very long thread about it in the LKML back in the day.
It was a good read. Thanks for pointing out that one.
Regards, Luka

This patch series adds ANSI terminal Bootmenu command. It use generic menu code for creating menu structures, but use own functions for drawing menu on ANSI terminal. First patch modify generic menu code for using other functions for printing and choosing menu entry, second patch is bootmenu command itself. Third and fourth patches are new in v2. Third adding new command clear which clear ANSI terminal and fourth adding bootmenu support to Nokia RX-51 board.
Pali Rohár (4): menu: Added support to use user defined functions New command bootmenu: ANSI terminal Boot Menu support New command clear: Clear the ANSI terminal RX-51: Add support for bootmenu
board/ait/cam_enc_4xx/cam_enc_4xx.c | 5 +- common/Makefile | 2 + common/cmd_bootmenu.c | 471 +++++++++++++++++++++++++++++++++++ common/cmd_clear.c | 43 ++++ common/cmd_pxe.c | 3 +- common/menu.c | 43 ++-- doc/README.bootmenu | 61 +++++ include/ansi.h | 42 ++++ include/configs/nokia_rx51.h | 25 +- include/menu.h | 6 +- 10 files changed, 678 insertions(+), 23 deletions(-) create mode 100644 common/cmd_bootmenu.c create mode 100644 common/cmd_clear.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h

* In menu_interactive_choice can be used user specified function item_data_choice (instead hardcoded function which read input from standard input)
* Added option to specify user data for menu
* menu_display_statusline will pass pointer to user data (instead pointer to menu)
* This patch is needed for creating ANSI bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com --- Changes in v2: - Rebased on next
board/ait/cam_enc_4xx/cam_enc_4xx.c | 5 ++-- common/cmd_pxe.c | 3 ++- common/menu.c | 43 +++++++++++++++++++++++------------ include/menu.h | 6 +++-- 4 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/board/ait/cam_enc_4xx/cam_enc_4xx.c b/board/ait/cam_enc_4xx/cam_enc_4xx.c index 32b28f9..5078b01 100644 --- a/board/ait/cam_enc_4xx/cam_enc_4xx.c +++ b/board/ait/cam_enc_4xx/cam_enc_4xx.c @@ -561,7 +561,8 @@ static char *menu_handle(struct menu_display *display) char *s; char temp[6][200];
- m = menu_create(display->title, display->timeout, 1, ait_menu_print); + m = menu_create(display->title, display->timeout, 1, ait_menu_print, + NULL, NULL);
for (i = 0; display->menulist[i]; i++) { sprintf(key, "%d", i + 1); @@ -1097,7 +1098,7 @@ int menu_show(int bootdelay) return MENU_EXIT; }
-void menu_display_statusline(struct menu *m) +void menu_display_statusline(void *data) { char *s1, *s2;
diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index ee75db9..2dbd49c 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -1280,7 +1280,8 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) /* * Create a menu and add items for all the labels. */ - m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print); + m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, + NULL, NULL);
if (!m) return NULL; diff --git a/common/menu.c b/common/menu.c index 6b2a2db..8b27c10 100644 --- a/common/menu.c +++ b/common/menu.c @@ -47,6 +47,8 @@ struct menu { char *title; int prompt; void (*item_data_print)(void *); + char *(*item_data_choice)(void *); + void *data; struct list_head items; };
@@ -113,11 +115,11 @@ static inline void *menu_item_destroy(struct menu *m, return NULL; }
-void __menu_display_statusline(struct menu *m) +void __menu_display_statusline(void *menu_data) { return; } -void menu_display_statusline(struct menu *m) +void menu_display_statusline(void *menu_data) __attribute__ ((weak, alias("__menu_display_statusline")));
/* @@ -130,7 +132,7 @@ static inline void menu_display(struct menu *m) puts(m->title); putc('\n'); } - menu_display_statusline(m); + menu_display_statusline(m->data);
menu_items_iter(m, menu_item_print, NULL); } @@ -204,18 +206,25 @@ static inline int menu_interactive_choice(struct menu *m, void **choice)
menu_display(m);
- readret = readline_into_buffer("Enter choice: ", cbuf, - m->timeout / 10); - - if (readret >= 0) { - choice_item = menu_item_by_key(m, cbuf); + if (!m->item_data_choice) { + readret = readline_into_buffer("Enter choice: ", cbuf, + m->timeout / 10); + + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + if (!choice_item) + printf("%s not found\n", cbuf); + } else + return menu_default_choice(m, choice); + } else { + char *key = m->item_data_choice(m->data); + if (!key) + return menu_default_choice(m, choice); + choice_item = menu_item_by_key(m, key); + }
- if (!choice_item) { - printf("%s not found\n", cbuf); - m->timeout = 0; - } - } else - return menu_default_choice(m, choice); + if (!choice_item) + m->timeout = 0; }
*choice = choice_item->data; @@ -352,7 +361,9 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data) * insufficient memory available to create the menu. */ struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)) + void (*item_data_print)(void *), + char *(*item_data_choice)(void *), + void *menu_data) { struct menu *m;
@@ -365,6 +376,8 @@ struct menu *menu_create(char *title, int timeout, int prompt, m->prompt = prompt; m->timeout = timeout; m->item_data_print = item_data_print; + m->item_data_choice = item_data_choice; + m->data = menu_data;
if (title) { m->title = strdup(title); diff --git a/include/menu.h b/include/menu.h index 7af5fdb..00e8975 100644 --- a/include/menu.h +++ b/include/menu.h @@ -21,12 +21,14 @@ struct menu;
struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)); + void (*item_data_print)(void *), + char *(*item_data_choice)(void *), + void *menu_data); int menu_default_set(struct menu *m, char *item_key); int menu_get_choice(struct menu *m, void **choice); int menu_item_add(struct menu *m, char *item_key, void *item_data); int menu_destroy(struct menu *m); -void menu_display_statusline(struct menu *m); +void menu_display_statusline(void *menu_data);
#if defined(CONFIG_MENU_SHOW) int menu_show(int bootdelay);

This patch adding ANSI terminal bootmenu command. It is extension to generic menu which provide output for ANSI terminals.
Signed-off-by: Pali Rohár pali.rohar@gmail.com --- Changes in v2: - Added commit message - Removed bootmenu from include/config_cmd_all.h - Moved ANSI escape codes from include/common.h to include/ansi.h - Fixed style and indentation problems - Use mdelay instead udelay - Removed autoboot delay message when some key is pressed
common/Makefile | 1 + common/cmd_bootmenu.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 61 +++++++ include/ansi.h | 42 +++++ 4 files changed, 575 insertions(+) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h
diff --git a/common/Makefile b/common/Makefile index a4eb477..1435992 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_CONSOLE) += cmd_console.o diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c new file mode 100644 index 0000000..908e13f --- /dev/null +++ b/common/cmd_bootmenu.c @@ -0,0 +1,471 @@ +/* + * (C) Copyright 2011-2012 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 <ansi.h> +#include <menu.h> +#include <hush.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/string.h> + +struct bootmenu_entry { + int num; /* unique number 0..99 */ + char key[3]; /* key idetifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +static char *bootmenu_getoption(int n) +{ + char name[] = "bootmenu_\0\0"; + + if (n < 0 || n > 99) + return NULL; + + sprintf(name+9, "%d", n); + return getenv(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + /* + * Move cursor to line where entry will be drown (entry->num) + * First 3 lines contains Bootmenu header + 1 empty line + */ + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(" "); + puts(entry->title); + puts(ANSI_CLEAR_LINE_TO_END); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + int i; + + int key = 0; /* 0 - NONE, 1 - UP, 2 - DOWN, 3 - SELECT */ + int esc = 0; + + while (1) { + + /* Autoboot was not stopped */ + if (menu->delay >= 0) { + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + printf(" Hit any key to stop autoboot: %2d ", + menu->delay); + } + + while (menu->delay > 0) { + + for (i = 0; i < 100; ++i) { + + if (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + continue; + } + + menu->delay = -1; + key = getc(); + + if (key == '\e') { + esc = 1; + key = 0; + } else if (key == '\r') + key = 3; + else + key = 0; + + break; + + } + + if (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + + } + + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + puts(ANSI_CLEAR_LINE); + + if (menu->delay == 0) + key = 3; + + /* Some key was pressed, so autoboot was stopped */ + } else { + + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + + key = getc(); + + if (esc == 0) { + + /* Start of ANSI escape sequence */ + 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; + + /* key up was pressed */ + if (key == 'A') + key = 1; + /* key down was pressed */ + else if (key == 'B') + key = 2; + /* other key was pressed */ + else + key = 0; + + } + + /* enter key was pressed */ + if (key == '\r') + key = 3; + + } + + /* key up */ + if (key == 1) { + if (menu->active > 0) + --menu->active; + return ""; /* invalid menu entry, regenerate menu */ + /* key down */ + } else if (key == 2) { + if (menu->active < menu->count-1) + ++menu->active; + return ""; /* invalid menu entry, regenerate menu */ + /* enter */ + } else if (key == 3) { + int i; + struct bootmenu_entry *iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + } + + } + + /* never happends */ + debug("bootmenu: this should not happen"); + return NULL; +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + int len; + char *sep; + struct bootmenu_entry *entry; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + while ((option = bootmenu_getoption(i))) { + + sep = strchr(option, '='); + if (!sep) + break; + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len+1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep+1); + entry->command = malloc(len+1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep+1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i >= 100) + break; + + } + + /* Add U-Boot console entry at the end */ + if (i < 100) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + } + + menu->count = i; + return menu; + +cleanup: + iter = menu->first; + while (iter) { + entry = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = entry; + } + free(menu); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + struct bootmenu_entry *next; + while (iter) { + next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + char *option, *sep; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + option = bootmenu_getoption(0); + if (!option) + return; + sep = strchr(option, '='); + if (!sep) + return; + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, (bootmenu->delay >= 0), + bootmenu_print_entry, bootmenu_choice_entry, bootmenu); + if (!menu) + return; + + for (iter = bootmenu->first; iter; iter = iter->next) + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + printf("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } + +#ifdef CONFIG_POSTBOOTMENU + run_command(CONFIG_POSTBOOTMENU, 0); +#endif +} + +void menu_display_statusline(void *data) +{ + struct bootmenu_data *menu = data; + + 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); + + /* First 3 lines are bootmenu header + 2 empty lines between entries */ + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} + +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + + 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); + + bootmenu_show(delay); + return 0; +} + +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/doc/README.bootmenu b/doc/README.bootmenu new file mode 100644 index 0000000..69ef93b --- /dev/null +++ b/doc/README.bootmenu @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2011-2012 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 + */ + +This is ANSI terminal BootMenu command. It is extension to generic menu, +which provide output for ANSI terminals. + +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 + + +To enable ANSI bootmenu comamnd add these definitions to board code: + + #define CONFIG_BOOTDELAY 30 + #define CONFIG_AUTOBOOT_KEYED + #define CONFIG_MENU + #define CONFIG_MENU_SHOW + #define CONFIG_CMD_BOOTMENU diff --git a/include/ansi.h b/include/ansi.h new file mode 100644 index 0000000..0e40b1d --- /dev/null +++ b/include/ansi.h @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2012 + * 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 + */ + +/* + * 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"

Dear Pali Rohár,
In message 1351769953-13560-3-git-send-email-pali.rohar@gmail.com you wrote:
This patch adding ANSI terminal bootmenu command. It is extension to generic menu which provide output for ANSI terminals.
...
if (key == '\e') {
esc = 1;
key = 0;
} else if (key == '\r')
key = 3;
else
key = 0;
use switch() ?
if (esc == 0) {
/* Start of ANSI escape sequence */
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;
/* key up was pressed */
if (key == 'A')
key = 1;
/* key down was pressed */
else if (key == 'B')
key = 2;
/* other key was pressed */
else
key = 0;
}
use switch() ?
Can we please avoid using hard-coded magic constants like 'A', 'B' etc. here?
/* key up */
if (key == 1) {
if (menu->active > 0)
--menu->active;
return ""; /* invalid menu entry, regenerate menu */
/* key down */
} else if (key == 2) {
if (menu->active < menu->count-1)
++menu->active;
return ""; /* invalid menu entry, regenerate menu */
/* enter */
} else if (key == 3) {
int i;
struct bootmenu_entry *iter = menu->first;
for (i = 0; i < menu->active; ++i)
iter = iter->next;
return iter->key;
}
use switch() ?
- /* never happends */
Typo.
- while ((option = bootmenu_getoption(i))) {
sep = strchr(option, '=');
if (!sep)
break;
Is there any specific reason for inventing yet another data format here? Can we not for example re-use what we already have in the hwconfig command, and use common code for parsing the format?
Using a '=' as separator here is not a good idea, IMO.
- /* Add U-Boot console entry at the end */
- if (i < 100) {
Magic constant 100 ?
+cleanup:
- iter = menu->first;
- while (iter) {
entry = iter->next;
free(iter->title);
free(iter->command);
free(iter);
iter = entry;
- }
- free(menu);
Make this a separate function?
+static void bootmenu_destroy(struct bootmenu_data *menu) +{
- struct bootmenu_entry *iter = menu->first;
- struct bootmenu_entry *next;
- while (iter) {
next = iter->next;
free(iter->title);
free(iter->command);
free(iter);
iter = next;
- }
- free(menu);
and use it here again?
- /* If delay is 0 do not create menu, just run first entry */
- if (delay == 0) {
option = bootmenu_getoption(0);
if (!option)
return;
sep = strchr(option, '=');
if (!sep)
return;
That would be an error condition, would it not? Should this not raise some error message, then?
- for (iter = bootmenu->first; iter; iter = iter->next)
if (!menu_item_add(menu, iter->key, iter))
goto cleanup;
Need braces for multi-line "for".
+int menu_show(int bootdelay) +{
- bootmenu_show(bootdelay);
- return -1; /* -1 - abort boot and run monitor code */
+}
If this function never returns anything else, why do we need the return value at all?
+int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{
- char *delay_str = NULL;
- int delay = 10;
- 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);
- bootmenu_show(delay);
- return 0;
+}
Umm... don't we handle any errors at all?
+This is ANSI terminal BootMenu command. It is extension to generic menu,
Please no camel-caps.
+First argument of bootmenu command override bootmenu_delay env
I cannot parse this.
+If env bootmenu_delay or bootmenu arg is not specified, delay is 10 seconds
Why not using CONFIG_BOOTDELAY as default instead?
+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
What will happen then? How is this different from delay==0 ?
+Boot Menu will stop finding next menu entry if last was not defined
I cannot parse this.
+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
You might give an example here what the resulting screen will look like.
+To enable ANSI bootmenu comamnd add these definitions to board code:
Typo.
- #define CONFIG_BOOTDELAY 30
- #define CONFIG_AUTOBOOT_KEYED
Do we really need CONFIG_AUTOBOOT_KEYED ?
Best regards,
Wolfgang Denk

Hi, I will fix all mentioned style problems.
On Tuesday 13 November 2012 09:27:41 Wolfgang Denk wrote:
/* key up was pressed */
if (key == 'A')
key = 1;
/* key down was pressed */
else if (key == 'B')
key = 2;
/* other key was pressed */
else
key = 0;
}
use switch() ?
Can we please avoid using hard-coded magic constants like 'A', 'B' etc. here?
And what to use? ANSI sequence for key up is \e[1A (down is B).
- while ((option = bootmenu_getoption(i))) {
sep = strchr(option, '=');
if (!sep)
break;
Is there any specific reason for inventing yet another data format here? Can we not for example re-use what we already have in the hwconfig command, and use common code for parsing the format?
Using a '=' as separator here is not a good idea, IMO.
So which char is better for separator? Bootmenu line has config "name=command" and I think '=' is the best char for separating key and value pair.
- /* Add U-Boot console entry at the end */
- if (i < 100) {
Magic constant 100 ?
<i>-th menu entry is stored in env bootmenu_<i> (you can look to README for API). So I need to create string "bootmenu_<i>" and call getenv. I think it is very bad idea to use malloc for very allocating string of size: strlen("bootmenu_") + log10(i) + 2. I'm using array of size strlen("bootmenu_") + 3 allocated at stack which is enught for number less then 100. And I think 100 menu entries in uboot is really a lot of. If you do not think I can increase number to 1000 or 10000 (which increase size of string +2)...
+cleanup:
- iter = menu->first;
- while (iter) {
entry = iter->next;
free(iter->title);
free(iter->command);
free(iter);
iter = entry;
- }
- free(menu);
Make this a separate function?
Same as function bootmenu_destroy, so I changed code to call bootmenu_destroy.
- /* If delay is 0 do not create menu, just run first entry
*/
- if (delay == 0) {
option = bootmenu_getoption(0);
if (!option)
return;
sep = strchr(option, '=');
if (!sep)
return;
That would be an error condition, would it not? Should this not raise some error message, then?
Ok, I added error message.
+int menu_show(int bootdelay) +{
- bootmenu_show(bootdelay);
- return -1; /* -1 - abort boot and run monitor code */
+}
If this function never returns anything else, why do we need the return value at all?
You can see that this function is not static. Reason is that menu_show is uboot API function which must return int. -1 means abort booting and run monitor code.
+int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{
- char *delay_str = NULL;
- int delay = 10;
- 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);
- bootmenu_show(delay);
- return 0;
+}
Umm... don't we handle any errors at all?
I do not understand what you mean.
+First argument of bootmenu command override bootmenu_delay env
I cannot parse this.
First argument of bootmenu command is delay and override env bootmenu_delay.
+If env bootmenu_delay or bootmenu arg is not specified, delay is 10 seconds
Why not using CONFIG_BOOTDELAY as default instead?
Ok.
+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
What will happen then? How is this different from delay==0 ?
If delay is less then 0, bootmenu will be shown and autoboot will be disabled. So there will not be any timer which automatically boot first entry in time $delay.
+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
You might give an example here what the resulting screen will look like.
Ok, I can here text output. If you want to see screenshot from bootmenu rendered to framebuffer (in qemu) here is: http://atrey.karlin.mff.cuni.cz/~pali/u-boot-bootmenu.png
- #define CONFIG_BOOTDELAY 30
- #define CONFIG_AUTOBOOT_KEYED
Do we really need CONFIG_AUTOBOOT_KEYED ?
Yes, because menu_show() call in common/main.c is called only of CONFIG_AUTOBOOT_KEYED is defined. You can look at this condition.

This patch adding new simple command clear which clear ANSI terminal.
Signed-off-by: Pali Rohár pali.rohar@gmail.com Cc: Marcel Mol marcel@mesa.nl --- This patch was in Nokia RX-51 patch series (v2).
Changes since RX-51 patch v2: - Removed from include/config_cmd_all.h - Removed ANSI escape codes (now in BootMenu command patch)
Changes since original version: - Renamed command clr to clear - Use puts instead printf - Move cursor to pos1,1
common/Makefile | 1 + common/cmd_clear.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 common/cmd_clear.c
diff --git a/common/Makefile b/common/Makefile index 1435992..328ca49 100644 --- a/common/Makefile +++ b/common/Makefile @@ -70,6 +70,7 @@ 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 COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o COBJS-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o diff --git a/common/cmd_clear.c b/common/cmd_clear.c new file mode 100644 index 0000000..29b4718 --- /dev/null +++ b/common/cmd_clear.c @@ -0,0 +1,43 @@ +/* + * Copyright 2011 + * Marcel Mol, MESA Consulting, marcel@mesa.nl + * + * 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 <ansi.h> + +static int do_clear(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + return 0; +} + +U_BOOT_CMD( + clear, CONFIG_SYS_MAXARGS, 1, do_clear, + "clear", + "\n" + " - clear screen and move cursor to top of screen" +);

Dear Pali Rohár,
In message 1351769953-13560-4-git-send-email-pali.rohar@gmail.com you wrote:
This patch adding new simple command clear which clear ANSI terminal.
Please note that this duplicates or affects existing functionality; for example, common/lcd.c implements the "cls" = Clear Screen command.
If we add such more globnal support now, this should be unified (in a backward compatible) way with the existing code.
Thanks.
Best regards,
Wolfgang Denk

* default bootmenu entries: attached kernel, internal eMMC memory, external SD card, u-boot boot order
* in CONFIG_PREBOOT try load bootmenu.scr from first FAT partition of internal eMMC memory (also known as MyDocs) which (should) overwrite default bootmenu entries
* when keyboard slide is closed boot first menu entry
* when keyborad slide is open in show bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com Acked-by: Tom Rini trini@ti.com --- This patch was in Nokia RX-51 patch series.
Changes since RX-51 patch: - Rebased on last Nokia RX-51 patch v5
Changes since original version: - Fixed name of env variables
include/configs/nokia_rx51.h | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/include/configs/nokia_rx51.h b/include/configs/nokia_rx51.h index 8506604..cd31d21 100644 --- a/include/configs/nokia_rx51.h +++ b/include/configs/nokia_rx51.h @@ -148,6 +148,7 @@ #define CONFIG_CMDLINE_EDITING /* add command line history */ #define CONFIG_AUTO_COMPLETE /* add autocompletion support */
+#define CONFIG_CMD_BOOTMENU /* ANSI terminal Boot Menu */ #define CONFIG_CMD_CLEAR /* ANSI terminal clear screen command */
#ifdef ONENAND_SUPPORT @@ -287,8 +288,6 @@ int rx51_kp_getc(void); #endif
/* Environment information */ -#define CONFIG_BOOTDELAY 3 - #define CONFIG_EXTRA_ENV_SETTINGS \ "mtdparts=" MTDPARTS_DEFAULT "\0" \ "usbtty=cdc_acm\0" \ @@ -360,10 +359,25 @@ int rx51_kp_getc(void); "fi\0" \ "emmcboot=setenv mmcnum 1; run trymmcboot\0" \ "sdboot=setenv mmcnum 0; run trymmcboot\0" \ + "menucmd=bootmenu\0" \ + "bootmenu_0=Attached kernel=run attachboot\0" \ + "bootmenu_1=Internal eMMC=run emmcboot\0" \ + "bootmenu_2=External SD card=run sdboot\0" \ + "bootmenu_3=U-Boot boot order=boot\0" \ + "bootmenu_delay=30\0" \ ""
#define CONFIG_PREBOOT \ - "if run slide; then true; else run attachboot; fi;" \ + "setenv mmcnum 1; setenv mmcpart 1; setenv mmctype fat;" \ + "setenv mmcscriptfile bootmenu.scr;" \ + "run trymmcscriptboot;" \ + "if run slide; then true; else " \ + "setenv bootmenu_delay 0;" \ + "setenv bootdelay 0;" \ + "fi" + +#define CONFIG_POSTBOOTMENU \ + "echo;" \ "echo Extra commands:;" \ "echo run sercon - Use serial port for control.;" \ "echo run usbcon - Use usbtty for control.;" \ @@ -379,6 +393,11 @@ int rx51_kp_getc(void); "run attachboot;" \ "echo"
+#define CONFIG_BOOTDELAY 30 +#define CONFIG_AUTOBOOT_KEYED +#define CONFIG_MENU +#define CONFIG_MENU_SHOW + /* * Miscellaneous configurable options */

This patch series adds ANSI terminal Bootmenu command. It use generic menu code for creating menu structures, but use own functions for drawing menu on ANSI terminal. First patch modify generic menu code for using other functions for printing and choosing menu entry, second patch is bootmenu command itself. Third and fourth patches were new in v2. Third adding new command clear which clear ANSI terminal and fourth adding bootmenu support to Nokia RX-51 board.
Pali Rohár (4): menu: Added support to use user defined functions New command bootmenu: ANSI terminal Boot Menu support New command clear: Clear the ANSI terminal RX-51: Add support for bootmenu
board/ait/cam_enc_4xx/cam_enc_4xx.c | 5 +- common/Makefile | 2 + common/cmd_bootmenu.c | 515 +++++++++++++++++++++++++++++++++++ common/cmd_clear.c | 43 +++ common/cmd_pxe.c | 3 +- common/menu.c | 43 ++- doc/README.bootmenu | 86 ++++++ include/ansi.h | 42 +++ include/configs/nokia_rx51.h | 25 +- include/menu.h | 6 +- 10 files changed, 747 insertions(+), 23 deletions(-) create mode 100644 common/cmd_bootmenu.c create mode 100644 common/cmd_clear.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h

* In menu_interactive_choice can be used user specified function item_data_choice (instead hardcoded function which read input from standard input)
* Added option to specify user data for menu
* menu_display_statusline will pass pointer to user data (instead pointer to menu)
* This patch is needed for creating ANSI bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com --- No changes in v3
Changes in v2: - Rebased on next
board/ait/cam_enc_4xx/cam_enc_4xx.c | 5 ++-- common/cmd_pxe.c | 3 ++- common/menu.c | 43 +++++++++++++++++++++++------------ include/menu.h | 6 +++-- 4 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/board/ait/cam_enc_4xx/cam_enc_4xx.c b/board/ait/cam_enc_4xx/cam_enc_4xx.c index 32b28f9..5078b01 100644 --- a/board/ait/cam_enc_4xx/cam_enc_4xx.c +++ b/board/ait/cam_enc_4xx/cam_enc_4xx.c @@ -561,7 +561,8 @@ static char *menu_handle(struct menu_display *display) char *s; char temp[6][200];
- m = menu_create(display->title, display->timeout, 1, ait_menu_print); + m = menu_create(display->title, display->timeout, 1, ait_menu_print, + NULL, NULL);
for (i = 0; display->menulist[i]; i++) { sprintf(key, "%d", i + 1); @@ -1097,7 +1098,7 @@ int menu_show(int bootdelay) return MENU_EXIT; }
-void menu_display_statusline(struct menu *m) +void menu_display_statusline(void *data) { char *s1, *s2;
diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index ee75db9..2dbd49c 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -1280,7 +1280,8 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) /* * Create a menu and add items for all the labels. */ - m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print); + m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, + NULL, NULL);
if (!m) return NULL; diff --git a/common/menu.c b/common/menu.c index 6b2a2db..8b27c10 100644 --- a/common/menu.c +++ b/common/menu.c @@ -47,6 +47,8 @@ struct menu { char *title; int prompt; void (*item_data_print)(void *); + char *(*item_data_choice)(void *); + void *data; struct list_head items; };
@@ -113,11 +115,11 @@ static inline void *menu_item_destroy(struct menu *m, return NULL; }
-void __menu_display_statusline(struct menu *m) +void __menu_display_statusline(void *menu_data) { return; } -void menu_display_statusline(struct menu *m) +void menu_display_statusline(void *menu_data) __attribute__ ((weak, alias("__menu_display_statusline")));
/* @@ -130,7 +132,7 @@ static inline void menu_display(struct menu *m) puts(m->title); putc('\n'); } - menu_display_statusline(m); + menu_display_statusline(m->data);
menu_items_iter(m, menu_item_print, NULL); } @@ -204,18 +206,25 @@ static inline int menu_interactive_choice(struct menu *m, void **choice)
menu_display(m);
- readret = readline_into_buffer("Enter choice: ", cbuf, - m->timeout / 10); - - if (readret >= 0) { - choice_item = menu_item_by_key(m, cbuf); + if (!m->item_data_choice) { + readret = readline_into_buffer("Enter choice: ", cbuf, + m->timeout / 10); + + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + if (!choice_item) + printf("%s not found\n", cbuf); + } else + return menu_default_choice(m, choice); + } else { + char *key = m->item_data_choice(m->data); + if (!key) + return menu_default_choice(m, choice); + choice_item = menu_item_by_key(m, key); + }
- if (!choice_item) { - printf("%s not found\n", cbuf); - m->timeout = 0; - } - } else - return menu_default_choice(m, choice); + if (!choice_item) + m->timeout = 0; }
*choice = choice_item->data; @@ -352,7 +361,9 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data) * insufficient memory available to create the menu. */ struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)) + void (*item_data_print)(void *), + char *(*item_data_choice)(void *), + void *menu_data) { struct menu *m;
@@ -365,6 +376,8 @@ struct menu *menu_create(char *title, int timeout, int prompt, m->prompt = prompt; m->timeout = timeout; m->item_data_print = item_data_print; + m->item_data_choice = item_data_choice; + m->data = menu_data;
if (title) { m->title = strdup(title); diff --git a/include/menu.h b/include/menu.h index 7af5fdb..00e8975 100644 --- a/include/menu.h +++ b/include/menu.h @@ -21,12 +21,14 @@ struct menu;
struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)); + void (*item_data_print)(void *), + char *(*item_data_choice)(void *), + void *menu_data); int menu_default_set(struct menu *m, char *item_key); int menu_get_choice(struct menu *m, void **choice); int menu_item_add(struct menu *m, char *item_key, void *item_data); int menu_destroy(struct menu *m); -void menu_display_statusline(struct menu *m); +void menu_display_statusline(void *menu_data);
#if defined(CONFIG_MENU_SHOW) int menu_show(int bootdelay);

From: Pali Rohár pali.rohar@gmail.com
Selecting menu items is currently done in menu_interactive_choice() by reading the user input strings from standard input.
Extend menu_interactive_choice() to support user defined function for selecting menu items. This function and its argument can be specified when creating the menu.
Signed-off-by: Pali Rohár pali.rohar@gmail.com Signed-off-by: Anatolij Gustschin agust@denx.de --- Changes in v4: - slightly rename the new item choice callback and its argument - add documentation for added item choice callback - search for menu item key only if the choice callback returned a key (not NULL) - do not change menu_display_statusline() function argument (previous patch changed this menu API function but didn't update documentation for this change. This change is actually not needed, menu item data can be obtained by exported menu_default_choice()) - revise commit log
No changes in v3
Changes in v2: - Rebased on next
board/ait/cam_enc_4xx/cam_enc_4xx.c | 3 +- common/cmd_pxe.c | 3 +- common/menu.c | 42 +++++++++++++++++++++++++--------- doc/README.menu | 4 ++- include/menu.h | 4 ++- 5 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/board/ait/cam_enc_4xx/cam_enc_4xx.c b/board/ait/cam_enc_4xx/cam_enc_4xx.c index 32b28f9..644c445 100644 --- a/board/ait/cam_enc_4xx/cam_enc_4xx.c +++ b/board/ait/cam_enc_4xx/cam_enc_4xx.c @@ -561,7 +561,8 @@ static char *menu_handle(struct menu_display *display) char *s; char temp[6][200];
- m = menu_create(display->title, display->timeout, 1, ait_menu_print); + m = menu_create(display->title, display->timeout, 1, ait_menu_print, + NULL, NULL);
for (i = 0; display->menulist[i]; i++) { sprintf(key, "%d", i + 1); diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index ee75db9..2dbd49c 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -1280,7 +1280,8 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) /* * Create a menu and add items for all the labels. */ - m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print); + m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, + NULL, NULL);
if (!m) return NULL; diff --git a/common/menu.c b/common/menu.c index 6b2a2db..322b75e 100644 --- a/common/menu.c +++ b/common/menu.c @@ -47,6 +47,8 @@ struct menu { char *title; int prompt; void (*item_data_print)(void *); + char *(*item_choice)(void *); + void *item_choice_data; struct list_head items; };
@@ -204,18 +206,26 @@ static inline int menu_interactive_choice(struct menu *m, void **choice)
menu_display(m);
- readret = readline_into_buffer("Enter choice: ", cbuf, - m->timeout / 10); + if (!m->item_choice) { + readret = readline_into_buffer("Enter choice: ", cbuf, + m->timeout / 10);
- if (readret >= 0) { - choice_item = menu_item_by_key(m, cbuf); - - if (!choice_item) { - printf("%s not found\n", cbuf); - m->timeout = 0; + if (readret >= 0) { + choice_item = menu_item_by_key(m, cbuf); + if (!choice_item) + printf("%s not found\n", cbuf); + } else { + return menu_default_choice(m, choice); } - } else - return menu_default_choice(m, choice); + } else { + char *key = m->item_choice(m->item_choice_data); + + if (key) + choice_item = menu_item_by_key(m, key); + } + + if (!choice_item) + m->timeout = 0; }
*choice = choice_item->data; @@ -348,11 +358,19 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data) * what must be entered to select an item, the item_data_print function should * make it obvious what the key for each entry is. * + * item_choice - If not NULL, will be called when asking the user to choose an + * item. Returns a key string corresponding to the choosen item or NULL if + * no item has been selected. + * + * item_choice_data - Will be passed as the argument to the item_choice function + * * Returns a pointer to the menu if successful, or NULL if there is * insufficient memory available to create the menu. */ struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)) + void (*item_data_print)(void *), + char *(*item_choice)(void *), + void *item_choice_data) { struct menu *m;
@@ -365,6 +383,8 @@ struct menu *menu_create(char *title, int timeout, int prompt, m->prompt = prompt; m->timeout = timeout; m->item_data_print = item_data_print; + m->item_choice = item_choice; + m->item_choice_data = item_choice_data;
if (title) { m->title = strdup(title); diff --git a/doc/README.menu b/doc/README.menu index 6ce6bba..c949398 100644 --- a/doc/README.menu +++ b/doc/README.menu @@ -51,7 +51,9 @@ struct menu; * menu_create() - Creates a menu handle with default settings */ struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)); + void (*item_data_print)(void *), + char *(*item_choice)(void *), + void *item_choice_data);
/* * menu_item_add() - Adds or replaces a menu item diff --git a/include/menu.h b/include/menu.h index 7af5fdb..f4dd5af 100644 --- a/include/menu.h +++ b/include/menu.h @@ -21,7 +21,9 @@ struct menu;
struct menu *menu_create(char *title, int timeout, int prompt, - void (*item_data_print)(void *)); + void (*item_data_print)(void *), + char *(*item_choice)(void *), + void *item_choice_data); int menu_default_set(struct menu *m, char *item_key); int menu_get_choice(struct menu *m, void **choice); int menu_item_add(struct menu *m, char *item_key, void *item_data);

Checking the default menu item and obtaining its data can be useful in custom menu code. Export menu_default_choice() function which serves this purpose.
Signed-off-by: Anatolij Gustschin agust@denx.de --- common/menu.c | 2 +- doc/README.menu | 5 +++++ include/menu.h | 1 + 3 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/common/menu.c b/common/menu.c index 322b75e..64b461a 100644 --- a/common/menu.c +++ b/common/menu.c @@ -176,7 +176,7 @@ static inline struct menu_item *menu_item_by_key(struct menu *m, * Set *choice to point to the default item's data, if any default item was * set, and returns 1. If no default item was set, returns -ENOENT. */ -static inline int menu_default_choice(struct menu *m, void **choice) +int menu_default_choice(struct menu *m, void **choice) { if (m->default_item) { *choice = m->default_item->data; diff --git a/doc/README.menu b/doc/README.menu index c949398..a8999ca 100644 --- a/doc/README.menu +++ b/doc/README.menu @@ -66,6 +66,11 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data); int menu_default_set(struct menu *m, char *item_key);
/* + * menu_default_choice() - Set *choice to point to the default item's data + */ +int menu_default_choice(struct menu *m, void **choice); + +/* * menu_get_choice() - Returns the user's selected menu entry, or the * default if the menu is set to not prompt or the timeout expires. */ diff --git a/include/menu.h b/include/menu.h index f4dd5af..d8200ee 100644 --- a/include/menu.h +++ b/include/menu.h @@ -29,6 +29,7 @@ int menu_get_choice(struct menu *m, void **choice); int menu_item_add(struct menu *m, char *item_key, void *item_data); int menu_destroy(struct menu *m); void menu_display_statusline(struct menu *m); +int menu_default_choice(struct menu *m, void **choice);
#if defined(CONFIG_MENU_SHOW) int menu_show(int bootdelay);

On Sun, 24 Mar 2013 01:52:04 +0100 Anatolij Gustschin agust@denx.de wrote:
Checking the default menu item and obtaining its data can be useful in custom menu code. Export menu_default_choice() function which serves this purpose.
Signed-off-by: Anatolij Gustschin agust@denx.de
common/menu.c | 2 +- doc/README.menu | 5 +++++ include/menu.h | 1 + 3 files changed, 7 insertions(+), 1 deletions(-)
applied to staging/agust@denx.de. Thanks.
Anatolij

On Sun, 24 Mar 2013 01:50:40 +0100 Anatolij Gustschin agust@denx.de wrote:
From: Pali Rohár pali.rohar@gmail.com
Selecting menu items is currently done in menu_interactive_choice() by reading the user input strings from standard input.
Extend menu_interactive_choice() to support user defined function for selecting menu items. This function and its argument can be specified when creating the menu.
Signed-off-by: Pali Rohár pali.rohar@gmail.com Signed-off-by: Anatolij Gustschin agust@denx.de
Changes in v4:
- slightly rename the new item choice callback and its argument
- add documentation for added item choice callback
- search for menu item key only if the choice callback returned a key (not NULL)
- do not change menu_display_statusline() function argument (previous patch changed this menu API function but didn't update documentation for this change. This change is actually not needed, menu item data can be obtained by exported menu_default_choice())
- revise commit log
No changes in v3
Changes in v2:
- Rebased on next
board/ait/cam_enc_4xx/cam_enc_4xx.c | 3 +- common/cmd_pxe.c | 3 +- common/menu.c | 42 +++++++++++++++++++++++++--------- doc/README.menu | 4 ++- include/menu.h | 4 ++- 5 files changed, 41 insertions(+), 15 deletions(-)
applied to staging/agust@denx.de. Thanks.
Anatolij

This patch adding ANSI terminal bootmenu command. It is extension to generic menu which provide output for ANSI terminals.
Signed-off-by: Pali Rohár pali.rohar@gmail.com --- Changes in v3: - Do not use hardcoded numbers, added MAX_COUNT and MAX_ENV_SIZE - Use unsigned short int for menu number - Use enum bootmenu_key for key selection - Separate loop code from function bootmenu_choice_entry to bootmenu_loop and bootmenu_autoboot_loop - Updated README, added example - Use switches, added braces, fix style problems
Changes in v2: - Added commit message - Removed bootmenu from include/config_cmd_all.h - Moved ANSI escape codes from include/common.h to include/ansi.h - Fixed style and indentation problems - Use mdelay instead udelay - Removed autoboot delay message when some key is pressed
common/Makefile | 1 + common/cmd_bootmenu.c | 515 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 86 +++++++++ include/ansi.h | 42 ++++ 4 files changed, 644 insertions(+) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h
diff --git a/common/Makefile b/common/Makefile index 54fcc81..0bd82a9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -71,6 +71,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_BOOTSTAGE) += cmd_bootstage.o COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c new file mode 100644 index 0000000..b4787ee --- /dev/null +++ b/common/cmd_bootmenu.c @@ -0,0 +1,515 @@ +/* + * (C) Copyright 2011-2013 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 <ansi.h> +#include <menu.h> +#include <hush.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/string.h> + +/* maximum bootmenu entries */ +#define MAX_COUNT 99 + +/* maximal size of bootmenu env + * 9 = strlen("bootmenu_") + * 2 = strlen(MAX_COUNT) + * 1 = NULL term + */ +#define MAX_ENV_SIZE (9+2+1) + +struct bootmenu_entry { + unsigned short int num; /* unique number 0 .. MAX_COUNT */ + char key[3]; /* key identifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +enum bootmenu_key { + KEY_NONE = 0, + KEY_UP, + KEY_DOWN, + KEY_SELECT, +}; + +static char *bootmenu_getoption(unsigned short int n) +{ + char name[MAX_ENV_SIZE] = "bootmenu_"; + + if (n > MAX_COUNT) + return NULL; + + sprintf(name+9, "%d", n); + return getenv(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + /* + * Move cursor to line where entry will be drown (entry->num) + * First 3 lines contains Bootmenu header + 1 empty line + */ + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(" "); + puts(entry->title); + puts(ANSI_CLEAR_LINE_TO_END); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static void bootmenu_autoboot_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int i, c; + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + printf(" Hit any key to stop autoboot: %2d ", + menu->delay); + } + + while (menu->delay > 0) { + + for (i = 0; i < 100; ++i) { + + if (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + continue; + } + + menu->delay = -1; + c = getc(); + + switch (c) { + case '\e': + *esc = 1; + *key = KEY_NONE; + break; + case '\r': + *key = KEY_SELECT; + break; + default: + *key = KEY_NONE; + break; + } + + break; + + } + + if (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + + } + + printf(ANSI_CURSOR_POSITION, menu->count+5, 1); + puts(ANSI_CLEAR_LINE); + + if (menu->delay == 0) + *key = KEY_SELECT; +} + +static void bootmenu_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int c; + + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + + c = getc(); + + switch (*esc) { + + case 0: + /* First char of ANSI escape sequence '\e' */ + if (c == '\e') { + *esc = 1; + *key = KEY_NONE; + } + break; + + case 1: + /* Second char of ANSI '[' */ + if (c == '[') { + *esc = 2; + *key = KEY_NONE; + } else + *esc = 0; + break; + + case 2: + case 3: + /* Third char of ANSI (number '1') - optional */ + if (*esc == 2 && c == '1') { + *esc = 3; + *key = KEY_NONE; + break; + } + + *esc = 0; + + /* ANSI 'A' - key up was pressed */ + if (c == 'A') + *key = KEY_UP; + /* ANSI 'B' - key down was pressed */ + else if (c == 'B') + *key = KEY_DOWN; + /* other key was pressed */ + else + *key = KEY_NONE; + + break; + + } + + /* enter key was pressed */ + if (c == '\r') + *key = KEY_SELECT; +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + struct bootmenu_entry *iter; + enum bootmenu_key key = KEY_NONE; + int esc = 0; + int i; + + while (1) { + + /* Autoboot was not stopped */ + if (menu->delay >= 0) { + bootmenu_autoboot_loop(menu, &key, &esc); + /* Some key was pressed, so autoboot was stopped */ + } else { + bootmenu_loop(menu, &key, &esc); + } + + switch (key) { + case KEY_UP: + if (menu->active > 0) + --menu->active; + return ""; /* invalid menu entry, regenerate menu */ + case KEY_DOWN: + if (menu->active < menu->count-1) + ++menu->active; + return ""; /* invalid menu entry, regenerate menu */ + case KEY_SELECT: + iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + default: + break; + } + + } + + /* never happends */ + debug("bootmenu: this should not happen"); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + struct bootmenu_entry *next; + while (iter) { + next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + unsigned short int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + int len; + char *sep; + struct bootmenu_entry *entry; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + while ((option = bootmenu_getoption(i))) { + + sep = strchr(option, '='); + if (!sep) + break; + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len+1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep+1); + entry->command = malloc(len+1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep+1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i > MAX_COUNT) + break; + + } + + /* Add U-Boot console entry at the end */ + if (i <= MAX_COUNT) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + } + + menu->count = i; + return menu; + +cleanup: + bootmenu_destroy(menu); + return NULL; +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + char *option, *sep; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + option = bootmenu_getoption(0); + if (!option) { + printf("bootmenu option 0 was not found\n"); + return; + } + sep = strchr(option, '='); + if (!sep) { + printf("bootmenu option 0 is invalid\n"); + return; + } + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, (bootmenu->delay >= 0), + bootmenu_print_entry, bootmenu_choice_entry, bootmenu); + if (!menu) + return; + + for (iter = bootmenu->first; iter; iter = iter->next) { + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + } + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + printf("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } + +#ifdef CONFIG_POSTBOOTMENU + run_command(CONFIG_POSTBOOTMENU, 0); +#endif +} + +void menu_display_statusline(void *data) +{ + struct bootmenu_data *menu = data; + + 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); + + /* First 3 lines are bootmenu header + 2 empty lines between entries */ + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +#ifdef CONFIG_MENU_SHOW +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} +#endif + +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + delay = CONFIG_BOOTDELAY; +#endif + + 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); + + bootmenu_show(delay); + return 0; +} + +U_BOOT_CMD( + bootmenu, 2, 1, do_bootmenu, + "ANSI terminal bootmenu", + "[delay]\n" + " - show ANSI terminal bootmenu with autoboot delay" +); diff --git a/doc/README.bootmenu b/doc/README.bootmenu new file mode 100644 index 0000000..71a9aa6 --- /dev/null +++ b/doc/README.bootmenu @@ -0,0 +1,86 @@ +/* + * (C) Copyright 2011-2012 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 + */ + +This is ANSI terminal bootmenu command. It is extension to generic menu, +which provide output for ANSI terminals. + +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 is delay and override env bootmenu_delay +If env bootmenu_delay or bootmenu arg is not specified, default delay is +CONFIG_BOOTDELAY. If delay is 0, no entry will be shown on screen and first +will be called. If delay is less then 0, bootmenu will be shown and autoboot +will be disabled. Bootmenu 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 + + +This example will be rendered as: + +-------------------------------------------- +| | +| *** U-Boot BOOT MENU *** | +| | +|#####Boot 1. kernel#######################| +| Boot 2. kernel | +| Reset board | +| U-Boot boot order | +| U-Boot console | +| | +| Hit any key to stop autoboot: 20 | +| Press UP/DOWN to move, ENTER to select | +| | +-------------------------------------------- + +(selected line is highlighted - has inverted background and text colors) + + +To enable ANSI bootmenu command add these definitions to board code: + + #define CONFIG_CMD_BOOTMENU + #define CONFIG_MENU + + +To run ANSI bootmenu at startup add these additional definitions: + + #define CONFIG_AUTOBOOT_KEYED + #define CONFIG_BOOTDELAY 30 + #define CONFIG_MENU_SHOW diff --git a/include/ansi.h b/include/ansi.h new file mode 100644 index 0000000..0e40b1d --- /dev/null +++ b/include/ansi.h @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2012 + * 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 + */ + +/* + * 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"

From: Pali Rohár pali.rohar@gmail.com
The "bootmenu" command uses U-Boot menu interfaces and provides a simple mechanism for creating menus with several boot items. When running this command the menu will be assembled as defined by a set of environment variables which contain a title and command key-value pairs. The "Up" and "Down" keys are used for navigation through the items. Current active menu item is highlighted and can be selected using the "Enter" key.
The command interprets and generates various ANSI escape sequencies, so for proper menu rendering and item selection the used terminal should support them.
Signed-off-by: Pali Rohár pali.rohar@gmail.com [agust: various fixes and documentation updates] Signed-off-by: Anatolij Gustschin agust@denx.de --- Changes in v4: - coding style fixes - highlight only the menu entry title, not the whole line - don't return empty strings in bootmenu_choice_entry() to avoid useless searching for menu keys in the menu item list. Empty key strings won't be found anyway, so for down and up keys just return NULL in this function to indicate that no item selection happened yet - print error message if invalid bootmenu environment variable without title/command separator found - if number of menu items is equal to MAX_COUNT, the U-Boot console selection entry won't be generated. Fix it so that documented and real behaviour match - include entry key initialisation fix for proper menu behaviour when running in sandbox (without it the item selection by up/down keys didn't work in sandbox) - use puts() instead of printf() where appropriate - always use 1 for prompt argument for menu_create() so that the bootmenu command works as documented when using a negative delay value - call bootmenu_destroy() in the case if menu_create() fails (avoid memory leaks) - don't display the title of selected item before running the commands (but do it only if debugging is enabled) - don't change the argument of menu_display_statusline(), use exported menu_default_choice() instead and obtain the needed bootmenu data pointer in the custom menu_display_statusline() function - use lower case in menu header - update documentation in readme file for the command
Changes in v3: - Do not use hardcoded numbers, added MAX_COUNT and MAX_ENV_SIZE - Use unsigned short int for menu number - Use enum bootmenu_key for key selection - Separate loop code from function bootmenu_choice_entry to bootmenu_loop and bootmenu_autoboot_loop - Updated README, added example - Use switches, added braces, fix style problems
Changes in v2: - Added commit message - Removed bootmenu from include/config_cmd_all.h - Moved ANSI escape codes from include/common.h to include/ansi.h - Fixed style and indentation problems - Use mdelay instead udelay - Removed autoboot delay message when some key is pressed
common/Makefile | 1 + common/cmd_bootmenu.c | 517 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 115 +++++++++++ include/ansi.h | 42 ++++ 4 files changed, 675 insertions(+), 0 deletions(-) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h
diff --git a/common/Makefile b/common/Makefile index 1abf4ea..f631311 100644 --- a/common/Makefile +++ b/common/Makefile @@ -75,6 +75,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_BOOTSTAGE) += cmd_bootstage.o COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o diff --git a/common/cmd_bootmenu.c b/common/cmd_bootmenu.c new file mode 100644 index 0000000..a3cbffa --- /dev/null +++ b/common/cmd_bootmenu.c @@ -0,0 +1,517 @@ +/* + * (C) Copyright 2011-2013 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 <ansi.h> +#include <menu.h> +#include <hush.h> +#include <watchdog.h> +#include <malloc.h> +#include <linux/string.h> + +/* maximum bootmenu entries */ +#define MAX_COUNT 99 + +/* maximal size of bootmenu env + * 9 = strlen("bootmenu_") + * 2 = strlen(MAX_COUNT) + * 1 = NULL term + */ +#define MAX_ENV_SIZE (9 + 2 + 1) + +struct bootmenu_entry { + unsigned short int num; /* unique number 0 .. MAX_COUNT */ + char key[3]; /* key identifier of number */ + char *title; /* title of entry */ + char *command; /* hush command of entry */ + struct bootmenu_data *menu; /* this bootmenu */ + struct bootmenu_entry *next; /* next menu entry (num+1) */ +}; + +struct bootmenu_data { + int delay; /* delay for autoboot */ + int active; /* active menu entry */ + int count; /* total count of menu entries */ + struct bootmenu_entry *first; /* first menu entry */ +}; + +enum bootmenu_key { + KEY_NONE = 0, + KEY_UP, + KEY_DOWN, + KEY_SELECT, +}; + +static char *bootmenu_getoption(unsigned short int n) +{ + char name[MAX_ENV_SIZE] = "bootmenu_"; + + if (n > MAX_COUNT) + return NULL; + + sprintf(name + 9, "%d", n); + return getenv(name); +} + +static void bootmenu_print_entry(void *data) +{ + struct bootmenu_entry *entry = data; + int reverse = (entry->menu->active == entry->num); + + /* + * Move cursor to line where the entry will be drown (entry->num) + * First 3 lines contain bootmenu header + 1 empty line + */ + printf(ANSI_CURSOR_POSITION, entry->num + 4, 1); + + puts(" "); + + if (reverse) + puts(ANSI_COLOR_REVERSE); + + puts(entry->title); + + if (reverse) + puts(ANSI_COLOR_RESET); +} + +static void bootmenu_autoboot_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int i, c; + + if (menu->delay > 0) { + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + printf(" Hit any key to stop autoboot: %2d ", menu->delay); + } + + while (menu->delay > 0) { + for (i = 0; i < 100; ++i) { + if (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + continue; + } + + menu->delay = -1; + c = getc(); + + switch (c) { + case '\e': + *esc = 1; + *key = KEY_NONE; + break; + case '\r': + *key = KEY_SELECT; + break; + default: + *key = KEY_NONE; + break; + } + + break; + } + + if (menu->delay < 0) + break; + + --menu->delay; + printf("\b\b\b%2d ", menu->delay); + } + + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + + if (menu->delay == 0) + *key = KEY_SELECT; +} + +static void bootmenu_loop(struct bootmenu_data *menu, + enum bootmenu_key *key, int *esc) +{ + int c; + + while (!tstc()) { + WATCHDOG_RESET(); + mdelay(10); + } + + c = getc(); + + switch (*esc) { + case 0: + /* First char of ANSI escape sequence '\e' */ + if (c == '\e') { + *esc = 1; + *key = KEY_NONE; + } + break; + case 1: + /* Second char of ANSI '[' */ + if (c == '[') { + *esc = 2; + *key = KEY_NONE; + } else { + *esc = 0; + } + break; + case 2: + case 3: + /* Third char of ANSI (number '1') - optional */ + if (*esc == 2 && c == '1') { + *esc = 3; + *key = KEY_NONE; + break; + } + + *esc = 0; + + /* ANSI 'A' - key up was pressed */ + if (c == 'A') + *key = KEY_UP; + /* ANSI 'B' - key down was pressed */ + else if (c == 'B') + *key = KEY_DOWN; + /* other key was pressed */ + else + *key = KEY_NONE; + + break; + } + + /* enter key was pressed */ + if (c == '\r') + *key = KEY_SELECT; +} + +static char *bootmenu_choice_entry(void *data) +{ + struct bootmenu_data *menu = data; + struct bootmenu_entry *iter; + enum bootmenu_key key = KEY_NONE; + int esc = 0; + int i; + + while (1) { + if (menu->delay >= 0) { + /* Autoboot was not stopped */ + bootmenu_autoboot_loop(menu, &key, &esc); + } else { + /* Some key was pressed, so autoboot was stopped */ + bootmenu_loop(menu, &key, &esc); + } + + switch (key) { + case KEY_UP: + if (menu->active > 0) + --menu->active; + /* no menu key selected, regenerate menu */ + return NULL; + case KEY_DOWN: + if (menu->active < menu->count - 1) + ++menu->active; + /* no menu key selected, regenerate menu */ + return NULL; + case KEY_SELECT: + iter = menu->first; + for (i = 0; i < menu->active; ++i) + iter = iter->next; + return iter->key; + default: + break; + } + } + + /* never happens */ + debug("bootmenu: this should not happen"); + return NULL; +} + +static void bootmenu_destroy(struct bootmenu_data *menu) +{ + struct bootmenu_entry *iter = menu->first; + struct bootmenu_entry *next; + + while (iter) { + next = iter->next; + free(iter->title); + free(iter->command); + free(iter); + iter = next; + } + free(menu); +} + +static struct bootmenu_data *bootmenu_create(int delay) +{ + unsigned short int i = 0; + const char *option; + struct bootmenu_data *menu; + struct bootmenu_entry *iter = NULL; + + int len; + char *sep; + struct bootmenu_entry *entry; + + menu = malloc(sizeof(struct bootmenu_data)); + if (!menu) + return NULL; + + menu->delay = delay; + menu->active = 0; + menu->first = NULL; + + while ((option = bootmenu_getoption(i))) { + sep = strchr(option, '='); + if (!sep) { + printf("Invalid bootmenu entry: %s\n", option); + break; + } + + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + len = sep-option; + entry->title = malloc(len + 1); + if (!entry->title) { + free(entry); + goto cleanup; + } + memcpy(entry->title, option, len); + entry->title[len] = 0; + + len = strlen(sep + 1); + entry->command = malloc(len + 1); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + memcpy(entry->command, sep + 1, len); + entry->command[len] = 0; + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + + if (i == MAX_COUNT - 1) + break; + } + + /* Add U-Boot console entry at the end */ + if (i <= MAX_COUNT - 1) { + entry = malloc(sizeof(struct bootmenu_entry)); + if (!entry) + goto cleanup; + + entry->title = strdup("U-Boot console"); + if (!entry->title) { + free(entry); + goto cleanup; + } + + entry->command = strdup(""); + if (!entry->command) { + free(entry->title); + free(entry); + goto cleanup; + } + + sprintf(entry->key, "%d", i); + + entry->num = i; + entry->menu = menu; + entry->next = NULL; + + if (!iter) + menu->first = entry; + else + iter->next = entry; + + iter = entry; + ++i; + } + + menu->count = i; + return menu; + +cleanup: + bootmenu_destroy(menu); + return NULL; +} + +static void bootmenu_show(int delay) +{ + int init = 0; + void *choice = NULL; + char *title = NULL; + char *command = NULL; + struct menu *menu; + struct bootmenu_data *bootmenu; + struct bootmenu_entry *iter; + char *option, *sep; + + /* If delay is 0 do not create menu, just run first entry */ + if (delay == 0) { + option = bootmenu_getoption(0); + if (!option) { + puts("bootmenu option 0 was not found\n"); + return; + } + sep = strchr(option, '='); + if (!sep) { + puts("bootmenu option 0 is invalid\n"); + return; + } + run_command(sep+1, 0); + return; + } + + bootmenu = bootmenu_create(delay); + if (!bootmenu) + return; + + menu = menu_create(NULL, bootmenu->delay, 1, bootmenu_print_entry, + bootmenu_choice_entry, bootmenu); + if (!menu) { + bootmenu_destroy(bootmenu); + return; + } + + for (iter = bootmenu->first; iter; iter = iter->next) { + if (!menu_item_add(menu, iter->key, iter)) + goto cleanup; + } + + /* Default menu entry is always first */ + menu_default_set(menu, "0"); + + puts(ANSI_CURSOR_HIDE); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + + init = 1; + + if (menu_get_choice(menu, &choice)) { + iter = choice; + title = strdup(iter->title); + command = strdup(iter->command); + } + +cleanup: + menu_destroy(menu); + bootmenu_destroy(bootmenu); + + if (init) { + puts(ANSI_CURSOR_SHOW); + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + } + + if (title && command) { + debug("Starting entry '%s'\n", title); + free(title); + run_command(command, 0); + free(command); + } + +#ifdef CONFIG_POSTBOOTMENU + run_command(CONFIG_POSTBOOTMENU, 0); +#endif +} + +void menu_display_statusline(struct menu *m) +{ + struct bootmenu_entry *entry; + struct bootmenu_data *menu; + + if (menu_default_choice(m, (void *)&entry) < 0) + return; + + menu = entry->menu; + + 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); + + /* First 3 lines are bootmenu header + 2 empty lines between entries */ + printf(ANSI_CURSOR_POSITION, menu->count + 5, 1); + puts(ANSI_CLEAR_LINE); + printf(ANSI_CURSOR_POSITION, menu->count + 6, 1); + puts(" Press UP/DOWN to move, ENTER to select"); + puts(ANSI_CLEAR_LINE_TO_END); + printf(ANSI_CURSOR_POSITION, menu->count + 7, 1); + puts(ANSI_CLEAR_LINE); +} + +#ifdef CONFIG_MENU_SHOW +int menu_show(int bootdelay) +{ + bootmenu_show(bootdelay); + return -1; /* -1 - abort boot and run monitor code */ +} +#endif + +int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + char *delay_str = NULL; + int delay = 10; + +#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) + delay = CONFIG_BOOTDELAY; +#endif + + 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); + + bootmenu_show(delay); + return 0; +} + +U_BOOT_CMD( + bootmenu, 2, 1, do_bootmenu, + "ANSI terminal bootmenu", + "[delay]\n" + " - show ANSI terminal bootmenu with autoboot delay" +); diff --git a/doc/README.bootmenu b/doc/README.bootmenu new file mode 100644 index 0000000..9e85b40 --- /dev/null +++ b/doc/README.bootmenu @@ -0,0 +1,115 @@ +/* + * (C) Copyright 2011-2012 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 + */ + +ANSI terminal bootmenu command + +The "bootmenu" command uses U-Boot menu interfaces and provides +a simple mechanism for creating menus with different boot items. +The cursor keys "Up" and "Down" are used for navigation through +the items. Current active menu item is highlighted and can be +selected using the "Enter" key. The selection of the highlighted +menu entry invokes an U-Boot command (or a list of commands) +associated with this menu entry. + +The "bootmenu" command interprets ANSI escape sequencies, so +an ANSI terminal is required for proper menu rendering and item +selection. + +The assembling of the menu is done via a set of environment variables +"bootmenu_<num>" and "bootmenu_delay", i.e.: + + bootmenu_delay=<delay> + bootmenu_<num>="<title>=<commands>" + + <delay> is the autoboot delay in seconds, after which the first + menu entry will be selected automatically + + <num> is the boot menu entry number, starting from zero + + <title> is the text of the menu entry shown on the console + or on the boot screen + + <commands> are commands which will be executed when a menu + entry is selected + + (title and commands are separated by first appearance of '=' + character in the environment variable) + +First (optional) argument of the "bootmenu" command is a delay specifier +and it overrides the delay value defined by "bootmenu_delay" environment +variable. If the environment variable "bootmenu_delay" is not set or if +the argument of the "bootmenu" command is not specified, the default delay +will be CONFIG_BOOTDELAY. If delay is 0, no menu entries will be shown on +the console (or on the screen) and the command of the first menu entry will +be called immediately. If delay is less then 0, bootmenu will be shown and +autoboot will be disabled. + +Bootmenu always adds menu entry "U-Boot console" at the end of all menu +entries specified by environment variables. When selecting this entry +the bootmenu terminates and the usual U-Boot command prompt is presented +to the user. + +Example environment: + + 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 + bootmenu 20 # Run bootmenu with autoboot delay 20s + + +The above example will be rendered as below +(without decorating rectangle): + +┌──────────────────────────────────────────┐ +│ │ +│ *** U-Boot Boot Menu *** │ +│ │ +│ Boot 1. kernel │ +│ Boot 2. kernel │ +│ Reset board │ +│ U-Boot boot order │ +│ U-Boot console │ +│ │ +│ Hit any key to stop autoboot: 20 │ +│ Press UP/DOWN to move, ENTER to select │ +│ │ +└──────────────────────────────────────────┘ + +Selected menu entry will be highlighted - it will have inverted +background and text colors. + +To enable the "bootmenu" command add following definitions to the +board config file: + + #define CONFIG_CMD_BOOTMENU + #define CONFIG_MENU + +To run the bootmenu at startup add these additional definitions: + + #define CONFIG_AUTOBOOT_KEYED + #define CONFIG_BOOTDELAY 30 + #define CONFIG_MENU_SHOW + +When you intend to use the bootmenu on color frame buffer console, +make sure to additionally define CONFIG_CFB_CONSOLE_ANSI in the +board config file. diff --git a/include/ansi.h b/include/ansi.h new file mode 100644 index 0000000..0e40b1d --- /dev/null +++ b/include/ansi.h @@ -0,0 +1,42 @@ +/* + * (C) Copyright 2012 + * 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 + */ + +/* + * 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"

Hi,
On Sun, 24 Mar 2013 01:53:08 +0100 Anatolij Gustschin agust@denx.de wrote:
From: Pali Rohár pali.rohar@gmail.com
The "bootmenu" command uses U-Boot menu interfaces and provides a simple mechanism for creating menus with several boot items.
Could you please test v4 patches. I've tested them in sandbox and on a frame buffer device (cfb_console) and didn't see any issues. I'm going to apply these patches in a few days.
Thanks,
Anatolij

On Monday 25 March 2013 20:29:23 Anatolij Gustschin wrote:
Hi,
On Sun, 24 Mar 2013 01:53:08 +0100
Anatolij Gustschin agust@denx.de wrote:
From: Pali Rohár pali.rohar@gmail.com
The "bootmenu" command uses U-Boot menu interfaces and provides a simple mechanism for creating menus with several boot items.
Could you please test v4 patches. I've tested them in sandbox and on a frame buffer device (cfb_console) and didn't see any issues. I'm going to apply these patches in a few days.
Thanks,
Anatolij
Hi,
I tested them on Nokia N900 and in qemu (hw n900). Working fine.

Hi,
On Tue, 26 Mar 2013 16:19:35 +0100 Pali Rohár pali.rohar@gmail.com wrote: ...
I tested them on Nokia N900 and in qemu (hw n900). Working fine.
Thanks for testing!
Anatolij

On Sun, 24 Mar 2013 01:53:08 +0100 Anatolij Gustschin agust@denx.de wrote:
From: Pali Rohár pali.rohar@gmail.com
The "bootmenu" command uses U-Boot menu interfaces and provides a simple mechanism for creating menus with several boot items. When running this command the menu will be assembled as defined by a set of environment variables which contain a title and command key-value pairs. The "Up" and "Down" keys are used for navigation through the items. Current active menu item is highlighted and can be selected using the "Enter" key.
The command interprets and generates various ANSI escape sequencies, so for proper menu rendering and item selection the used terminal should support them.
Signed-off-by: Pali Rohár pali.rohar@gmail.com [agust: various fixes and documentation updates] Signed-off-by: Anatolij Gustschin agust@denx.de
Changes in v4:
- coding style fixes
- highlight only the menu entry title, not the whole line
- don't return empty strings in bootmenu_choice_entry() to avoid useless searching for menu keys in the menu item list. Empty key strings won't be found anyway, so for down and up keys just return NULL in this function to indicate that no item selection happened yet
- print error message if invalid bootmenu environment variable without title/command separator found
- if number of menu items is equal to MAX_COUNT, the U-Boot console selection entry won't be generated. Fix it so that documented and real behaviour match
- include entry key initialisation fix for proper menu behaviour when running in sandbox (without it the item selection by up/down keys didn't work in sandbox)
- use puts() instead of printf() where appropriate
- always use 1 for prompt argument for menu_create() so that the bootmenu command works as documented when using a negative delay value
- call bootmenu_destroy() in the case if menu_create() fails (avoid memory leaks)
- don't display the title of selected item before running the commands (but do it only if debugging is enabled)
- don't change the argument of menu_display_statusline(), use exported menu_default_choice() instead and obtain the needed bootmenu data pointer in the custom menu_display_statusline() function
- use lower case in menu header
- update documentation in readme file for the command
Changes in v3:
- Do not use hardcoded numbers, added MAX_COUNT and MAX_ENV_SIZE
- Use unsigned short int for menu number
- Use enum bootmenu_key for key selection
- Separate loop code from function bootmenu_choice_entry to bootmenu_loop and bootmenu_autoboot_loop
- Updated README, added example
- Use switches, added braces, fix style problems
Changes in v2:
- Added commit message
- Removed bootmenu from include/config_cmd_all.h
- Moved ANSI escape codes from include/common.h to include/ansi.h
- Fixed style and indentation problems
- Use mdelay instead udelay
- Removed autoboot delay message when some key is pressed
common/Makefile | 1 + common/cmd_bootmenu.c | 517 +++++++++++++++++++++++++++++++++++++++++++++++++ doc/README.bootmenu | 115 +++++++++++ include/ansi.h | 42 ++++ 4 files changed, 675 insertions(+), 0 deletions(-) create mode 100644 common/cmd_bootmenu.c create mode 100644 doc/README.bootmenu create mode 100644 include/ansi.h
applied to staging/agust@denx.de. Thanks.
Anatolij

On Thursday 28 March 2013 16:32:47 Anatolij Gustschin wrote:
On Sun, 24 Mar 2013 01:53:08 +0100
Anatolij Gustschin agust@denx.de wrote:
From: Pali Rohár pali.rohar@gmail.com
The "bootmenu" command uses U-Boot menu interfaces and provides a simple mechanism for creating menus with several boot items. When running this command the menu will be assembled as defined by a set of environment variables which contain a title and command key-value pairs. The "Up" and "Down" keys are used for navigation through the items. Current active menu item is highlighted and can be selected using the "Enter" key.
The command interprets and generates various ANSI escape sequencies, so for proper menu rendering and item selection the used terminal should support them.
Signed-off-by: Pali Rohár pali.rohar@gmail.com [agust: various fixes and documentation updates] Signed-off-by: Anatolij Gustschin agust@denx.de
applied to staging/agust@denx.de. Thanks.
Anatolij
Hi, can you include also two next patches from this series?
http://patchwork.ozlabs.org/patch/217492/ http://patchwork.ozlabs.org/patch/225868/

Hi,
On Thu, 28 Mar 2013 20:18:48 +0100 Pali Rohár pali.rohar@gmail.com wrote: ...
Hi, can you include also two next patches from this series?
http://patchwork.ozlabs.org/patch/217492/ http://patchwork.ozlabs.org/patch/225868/
These patches are in my queue, I didn't forgot about them :-)
Regarding the "clear" command Wolfgang had some reservations since we already have "cls" command for LCDs. Wolfgang suggested to unify with the existing command in a backward compatible way. I'm going to submit a patch which tries to do this.
The v3 4/4 patch can be applied as is, I think.
Thanks,
Anatolij

On Friday 29 March 2013 00:13:30 Anatolij Gustschin wrote:
Hi,
On Thu, 28 Mar 2013 20:18:48 +0100 Pali Rohár pali.rohar@gmail.com wrote: ...
Hi, can you include also two next patches from this series?
http://patchwork.ozlabs.org/patch/217492/ http://patchwork.ozlabs.org/patch/225868/
These patches are in my queue, I didn't forgot about them :-)
Regarding the "clear" command Wolfgang had some reservations since we already have "cls" command for LCDs. Wolfgang suggested to unify with the existing command in a backward compatible way. I'm going to submit a patch which tries to do this.
The v3 4/4 patch can be applied as is, I think.
Thanks,
Anatolij
Ok, thanks!

This patch adding new simple command clear which clear ANSI terminal.
Signed-off-by: Pali Rohár pali.rohar@gmail.com Cc: Marcel Mol marcel@mesa.nl --- No changes in v3
This patch was in Nokia RX-51 patch series (v2).
Changes since RX-51 patch v2: - Removed from include/config_cmd_all.h - Removed ANSI escape codes (now in BootMenu command patch)
Changes since original version: - Renamed command clr to clear - Use puts instead printf - Move cursor to pos1,1
common/Makefile | 1 + common/cmd_clear.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 common/cmd_clear.c
diff --git a/common/Makefile b/common/Makefile index 0bd82a9..4a0519b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -76,6 +76,7 @@ COBJS-$(CONFIG_CMD_BOOTLDR) += cmd_bootldr.o COBJS-$(CONFIG_CMD_BOOTSTAGE) += cmd_bootstage.o COBJS-$(CONFIG_CMD_CACHE) += cmd_cache.o COBJS-$(CONFIG_CMD_CBFS) += cmd_cbfs.o +COBJS-$(CONFIG_CMD_CLEAR) += cmd_clear.o COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o COBJS-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o diff --git a/common/cmd_clear.c b/common/cmd_clear.c new file mode 100644 index 0000000..29b4718 --- /dev/null +++ b/common/cmd_clear.c @@ -0,0 +1,43 @@ +/* + * Copyright 2011 + * Marcel Mol, MESA Consulting, marcel@mesa.nl + * + * 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 <ansi.h> + +static int do_clear(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + puts(ANSI_CLEAR_CONSOLE); + printf(ANSI_CURSOR_POSITION, 1, 1); + return 0; +} + +U_BOOT_CMD( + clear, CONFIG_SYS_MAXARGS, 1, do_clear, + "clear", + "\n" + " - clear screen and move cursor to top of screen" +);

* default bootmenu entries: attached kernel, internal eMMC memory, external SD card, u-boot boot order
* in CONFIG_PREBOOT try load bootmenu.scr from first FAT partition of internal eMMC memory (also known as MyDocs) which (should) overwrite default bootmenu entries
* when keyboard slide is closed boot first menu entry
* when keyborad slide is open in show bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com Acked-by: Tom Rini trini@ti.com --- No changes in v3
This patch was in Nokia RX-51 patch series.
Changes since RX-51 patch: - Rebased on last Nokia RX-51 patch v5
Changes since original version: - Fixed name of env variables
include/configs/nokia_rx51.h | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/include/configs/nokia_rx51.h b/include/configs/nokia_rx51.h index 8506604..cd31d21 100644 --- a/include/configs/nokia_rx51.h +++ b/include/configs/nokia_rx51.h @@ -148,6 +148,7 @@ #define CONFIG_CMDLINE_EDITING /* add command line history */ #define CONFIG_AUTO_COMPLETE /* add autocompletion support */
+#define CONFIG_CMD_BOOTMENU /* ANSI terminal Boot Menu */ #define CONFIG_CMD_CLEAR /* ANSI terminal clear screen command */
#ifdef ONENAND_SUPPORT @@ -287,8 +288,6 @@ int rx51_kp_getc(void); #endif
/* Environment information */ -#define CONFIG_BOOTDELAY 3 - #define CONFIG_EXTRA_ENV_SETTINGS \ "mtdparts=" MTDPARTS_DEFAULT "\0" \ "usbtty=cdc_acm\0" \ @@ -360,10 +359,25 @@ int rx51_kp_getc(void); "fi\0" \ "emmcboot=setenv mmcnum 1; run trymmcboot\0" \ "sdboot=setenv mmcnum 0; run trymmcboot\0" \ + "menucmd=bootmenu\0" \ + "bootmenu_0=Attached kernel=run attachboot\0" \ + "bootmenu_1=Internal eMMC=run emmcboot\0" \ + "bootmenu_2=External SD card=run sdboot\0" \ + "bootmenu_3=U-Boot boot order=boot\0" \ + "bootmenu_delay=30\0" \ ""
#define CONFIG_PREBOOT \ - "if run slide; then true; else run attachboot; fi;" \ + "setenv mmcnum 1; setenv mmcpart 1; setenv mmctype fat;" \ + "setenv mmcscriptfile bootmenu.scr;" \ + "run trymmcscriptboot;" \ + "if run slide; then true; else " \ + "setenv bootmenu_delay 0;" \ + "setenv bootdelay 0;" \ + "fi" + +#define CONFIG_POSTBOOTMENU \ + "echo;" \ "echo Extra commands:;" \ "echo run sercon - Use serial port for control.;" \ "echo run usbcon - Use usbtty for control.;" \ @@ -379,6 +393,11 @@ int rx51_kp_getc(void); "run attachboot;" \ "echo"
+#define CONFIG_BOOTDELAY 30 +#define CONFIG_AUTOBOOT_KEYED +#define CONFIG_MENU +#define CONFIG_MENU_SHOW + /* * Miscellaneous configurable options */

Hi, I'm sending new version of patch which try to use also ext filesystem (not only fat)
From 4798e0c932577012ccde38f6b23413109f4b2fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= pali.rohar@gmail.com Date: Sat, 14 Jan 2012 18:57:31 +0100 Subject: [PATCH] RX-51: Add support for bootmenu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit
* default bootmenu entries: attached kernel, internal eMMC memory, external SD card, u-boot boot order
* in CONFIG_PREBOOT try load bootmenu.scr from first partition of internal eMMC memory (also known as MyDocs) which (should) overwrite default bootmenu entries
* when keyboard slide is closed boot first menu entry
* when keyborad slide is open show bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com --- include/configs/nokia_rx51.h | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-)
diff --git a/include/configs/nokia_rx51.h b/include/configs/nokia_rx51.h index 8506604..965330a 100644 --- a/include/configs/nokia_rx51.h +++ b/include/configs/nokia_rx51.h @@ -148,6 +148,7 @@ #define CONFIG_CMDLINE_EDITING /* add command line history */ #define CONFIG_AUTO_COMPLETE /* add autocompletion support */
+#define CONFIG_CMD_BOOTMENU /* ANSI terminal Boot Menu */ #define CONFIG_CMD_CLEAR /* ANSI terminal clear screen command */
#ifdef ONENAND_SUPPORT @@ -287,8 +288,6 @@ int rx51_kp_getc(void); #endif
/* Environment information */ -#define CONFIG_BOOTDELAY 3 - #define CONFIG_EXTRA_ENV_SETTINGS \ "mtdparts=" MTDPARTS_DEFAULT "\0" \ "usbtty=cdc_acm\0" \ @@ -360,10 +359,40 @@ int rx51_kp_getc(void); "fi\0" \ "emmcboot=setenv mmcnum 1; run trymmcboot\0" \ "sdboot=setenv mmcnum 0; run trymmcboot\0" \ + "menucmd=bootmenu\0" \ + "bootmenu_0=Attached kernel=run attachboot\0" \ + "bootmenu_1=Internal eMMC=run emmcboot\0" \ + "bootmenu_2=External SD card=run sdboot\0" \ + "bootmenu_3=U-Boot boot order=boot\0" \ + "bootmenu_delay=30\0" \ ""
#define CONFIG_PREBOOT \ - "if run slide; then true; else run attachboot; fi;" \ + "setenv mmcnum 1; setenv mmcpart 1;" \ + "setenv mmcscriptfile bootmenu.scr;" \ + "if run switchmmc; then " \ + "setenv mmcdone true;" \ + "setenv mmctype fat;" \ + "if run scriptload; then true; else " \ + "setenv mmctype ext2;" \ + "if run scriptload; then true; else " \ + "setenv mmctype ext4;" \ + "if run scriptload; then true; else " \ + "setenv mmcdone false;" \ + "fi;" \ + "fi;" \ + "fi;" \ + "if ${mmcdone}; then " \ + "run scriptboot;" \ + "fi;" \ + "fi;" \ + "if run slide; then true; else " \ + "setenv bootmenu_delay 0;" \ + "setenv bootdelay 0;" \ + "fi" + +#define CONFIG_POSTBOOTMENU \ + "echo;" \ "echo Extra commands:;" \ "echo run sercon - Use serial port for control.;" \ "echo run usbcon - Use usbtty for control.;" \ @@ -379,6 +408,11 @@ int rx51_kp_getc(void); "run attachboot;" \ "echo"
+#define CONFIG_BOOTDELAY 30 +#define CONFIG_AUTOBOOT_KEYED +#define CONFIG_MENU +#define CONFIG_MENU_SHOW + /* * Miscellaneous configurable options */

Hi,
On Thu, 7 Mar 2013 16:15:19 +0100 Pali Rohár pali.rohar@gmail.com wrote: ...
attached kernel, internal eMMC memory, external SD card, u-boot boot order
in CONFIG_PREBOOT try load bootmenu.scr from first partition of internal eMMC memory (also known as MyDocs) which (should) overwrite default bootmenu entries
when keyboard slide is closed boot first menu entry
when keyborad slide is open show bootmenu
Signed-off-by: Pali Rohár pali.rohar@gmail.com
include/configs/nokia_rx51.h | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-)
with slightly revised commit log, applied to staging/agust@denx.de.
Thanks,
Anatolij
participants (5)
-
Anatolij Gustschin
-
Luka Perkov
-
Marek Vasut
-
Pali Rohár
-
Wolfgang Denk