
Introduce a menu framework that allow us to create list menu to simplify u-boot and make it more convivial for the end-user.
This kind of menu is very usefull when you do not have a keyboard or a serial console attached to your board to allow you to interract with u-boot
For the develloper part, The framework introduce two API
1) C that allow you to create menu, submenu, entry and complex menu action
2) Command that allow you as the C API to create menu, submenu, entry and complex menu action but this time the actions will be store in a env var and then be evaluated and excecuted.
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.com --- common/Makefile | 1 + common/cmd_menu.c | 527 ++++++++++++++++++++++++++++++++++++++++++++++ include/config_cmd_all.h | 1 + include/console.h | 5 + include/menu.h | 85 ++++++++ 5 files changed, 619 insertions(+), 0 deletions(-) create mode 100644 common/cmd_menu.c create mode 100644 include/menu.h
diff --git a/common/Makefile b/common/Makefile index ee0cb33..ba9cfbd 100644 --- a/common/Makefile +++ b/common/Makefile @@ -108,6 +108,7 @@ COBJS-y += cmd_load.o COBJS-$(CONFIG_LOGBUFFER) += cmd_log.o COBJS-$(CONFIG_ID_EEPROM) += cmd_mac.o COBJS-$(CONFIG_CMD_MEMORY) += cmd_mem.o +COBJS-$(CONFIG_CMD_MENU) += cmd_menu.o COBJS-$(CONFIG_CMD_MFSL) += cmd_mfsl.o COBJS-$(CONFIG_CMD_MG_DISK) += cmd_mgdisk.o COBJS-$(CONFIG_MII) += miiphyutil.o diff --git a/common/cmd_menu.c b/common/cmd_menu.c new file mode 100644 index 0000000..cd7a0c0 --- /dev/null +++ b/common/cmd_menu.c @@ -0,0 +1,527 @@ +/* + * (C) Copyright 2009 Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.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 <console.h> +#include <menu.h> +#ifdef CONFIG_SYS_HUSH_PARSER +#include <hush.h> +#endif + +static struct menu menus; + +int menu_init(void) +{ + INIT_LIST_HEAD(&(menus.list)); + + return 0; +} + +void menu_free(struct menu *m) +{ + struct list_head *pos; + struct menu_entry *me; + + if (!m) + return; + if (m->name) + free(m->name); + + if (m->display) + free(m->display); + + list_for_each(pos, &(m->entries.list)) { + me = list_entry(pos, struct menu_entry, list); + menu_entry_free(me); + list_del(pos); + } + + + free(m); +} + +int menu_add(struct menu *m) +{ + if (!m || !m->name) + return -1; + + list_add_tail(&(m->list), &(menus.list)); + m->nb_entries = 1; + + INIT_LIST_HEAD(&(m->entries.list)); + + return 0; +} + +int menu_add_entry(struct menu *m, struct menu_entry *me) +{ + if (!m || !me || !me->display) + return -1; + + me->num = m->nb_entries; + m->nb_entries++; + list_add_tail(&(me->list), &(m->entries.list)); + + return 0; +} + +struct menu* menu_get_by_name(char *name) +{ + struct list_head *pos; + struct menu* m; + + if (!name) + return NULL; + + list_for_each(pos, &(menus.list)) { + m = list_entry(pos, struct menu, list); + if(strcmp(m->name, name) == 0) + return m; + } + + return NULL; +} + +struct menu_entry* menu_entry_get_by_num(struct menu* m, int num) +{ + struct list_head *pos; + struct menu_entry* me; + + if (!m || num < 1 || m->nb_entries > num) + return NULL; + + list_for_each(pos, &(m->entries.list)) { + me = list_entry(pos, struct menu_entry, list); + if(me->num == num) + return me; + } + + return NULL; +} + +void menu_entry_free(struct menu_entry *me) +{ + if (!me) + return; + + if (me->display) + free(me->display); + + free(me); +} + +static void print_menu_entry(struct menu_entry *me, int reverse) +{ + gotoXY(me->num + 1, 3); + if (reverse) + printf_reverse("%d: %s", me->num, me->display); + else + printf("%d: %s", me->num, me->display); +} + +int menu_set_selected_entry(struct menu *m, struct menu_entry* me) +{ + struct list_head *pos; + struct menu_entry* tmp; + + if (!m || !me) + return -1; + + list_for_each(pos, &(m->entries.list)) { + tmp = list_entry(pos, struct menu_entry, list); + if(me == tmp) { + m->selected = me; + return 0; + } + } + + return -1; +} + +int menu_set_selected(struct menu *m, int num) +{ + struct menu_entry *me; + + me = menu_entry_get_by_num(m, num); + + if (!me) + return -1; + + m->selected = me; + + return 0; +} + +int menu_show(struct menu *m) +{ + struct list_head *pos; + struct menu_entry *me; + int ch; + int escape = 0; + + if(!m) + return -1; + + clear(); + gotoXY(1, 2); + if(m->display) { + puts(m->display); + } else { + puts("Menu : "); + puts(m->name); + } + + list_for_each(pos, &(m->entries.list)) { + me = list_entry(pos, struct menu_entry, list); + if(m->selected != me) + print_menu_entry(me, 0); + } + + if (!m->selected) { + m->selected = list_first_entry(&(m->entries.list), + struct menu_entry, list); + } + + print_menu_entry(m->selected, 1); + + do { + ch = getc(); + switch(ch) { + case 0x1b: + escape = 1; + break; + case '[': + if (escape) + break; + case 'A': /* up */ + escape = 0; + print_menu_entry(m->selected, 0); + m->selected = list_entry(m->selected->list.prev, struct menu_entry, + list); + if (&(m->selected->list) == &(m->entries.list)) { + m->selected = list_entry(m->selected->list.prev, struct menu_entry, + list); + } + print_menu_entry(m->selected, 1); + break; + case 'B': /* down */ + escape = 0; + print_menu_entry(m->selected, 0); + m->selected = list_entry(m->selected->list.next, struct menu_entry, + list); + if (&(m->selected->list) == &(m->entries.list)) { + m->selected = list_entry(m->selected->list.next, struct menu_entry, + list); + } + print_menu_entry(m->selected, 1); + break; + case '\n': + case '\r': + if (!m->selected->fn(m, m->selected)) + return m->selected->num; + default: + break; + } + } while(1); + + return 0; +} + +int menu_action_exit(struct menu *m, struct menu_entry *me) +{ + return 0; +} + +int menu_action_run(struct menu *m, struct menu_entry *me) +{ + char *s = getenv(me->priv); + + if (!s) + return -1; + +#if !defined(CONFIG_SYS_HUSH_PARSER) + return run_command (s, 0); +#else + return parse_string_outer(s, FLAG_PARSE_SEMICOLON + | FLAG_EXIT_FROM_LOOP); +#endif +} + +/* + * Commands + */ + +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +/* + * menu entry add <menu> <command> <selected> <description> + */ +int do_menu_entry_add(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + struct menu_entry *me; + struct menu *m; + int len; + int i; + char *src, *dst; + + if (argc < 5) + return -1; + + m = menu_get_by_name(argv[1]); + + if (!m) + return -1; + + me = menu_entry_alloc(); + + if (!me) + return -1; + + me->fn = menu_action_run; + + len = strlen(argv[2]) + 1; + + me->priv = calloc(len, sizeof(char)); + if (!me->priv) + goto free; + + strncpy(me->priv, argv[2], len); + + len = 0; + + for (i = 4; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + + me->display = calloc(len, sizeof(char)); + + if (!me->display) + goto free; + + dst = me->display; + + for (i = 4; i < argc; i++) { + src = argv[i]; + + while ((*dst = *src) != '\0') { + dst++; + src++; + } + *dst = (i == (argc - 1)) ? '\0' : ' '; + dst++; + } + + if (menu_add_entry(m, me)) + goto free; + + if (simple_strtoul(argv[3], NULL, 16)) + m->selected = me; + + return 0; +free: + if (me->priv) + free(me->priv); + menu_entry_free(me); + return -1; +} + +static void print_entries(struct menu *m) +{ + struct list_head *pos; + struct menu_entry *me; + + list_for_each(pos, &(m->entries.list)) { + me = list_entry(pos, struct menu_entry, list); + printf("%d: %s\n", me->num, me->display); + } +} + +/* + * menu entry list [menu] + */ +int do_menu_entry_list(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + struct list_head *pos; + struct menu *m = NULL; + + if (argc > 2) + m = menu_get_by_name(argv[1]); + + if (m) { + print_entries(m); + return 0; + } + + list_for_each(pos, &(menus.list)) { + m = list_entry(pos, struct menu, list); + printf("%s: %s\n", m->name, m->display); + print_entries(m); + } + + return 0; +} + +/* + * menu add <name> <description> + */ +int do_menu_add(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + struct menu *m; + int len = 0; + int i; + char *src, *dst; + + if (argc < 2) + return -1; + + m = menu_alloc(); + + if (!m) + return -1; + + len = strlen(argv[1]) + 1; + + m->name = calloc(len, sizeof(char));; + if (!m->name) + goto free; + + strncpy(m->name, argv[1], len); + + len = 0; + + for (i = 2; i < argc; i++) { + len += strlen(argv[i]) + 1; + } + + m->display = calloc(len, sizeof(char)); + + if (!m->display) + goto free; + + dst = m->display; + + for (i = 2; i < argc; i++) { + src = argv[i]; + + while ((*dst = *src) != '\0') { + dst++; + src++; + } + *dst = (i == (argc - 1)) ? '\0' : ' '; + dst++; + } + + if (menu_add(m)) { + fprintf(stderr, "Menu %s add fail\n", m->name); + goto free; + } + + return 0; +free: + menu_free(m); + return -1; +} +#endif /* CONFIG_CMD_MENU_MANAGEMENT */ + +/* + * menu show [menu] + */ +int do_menu_show(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + struct menu *m; + + if (argc > 1) + m = menu_get_by_name(argv[1]); + else + m = menu_get_by_name("boot"); + + if (!m) + return -1; + + return menu_show(m); +} + +/* + * menu list + */ +int do_menu_list(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + struct list_head *pos; + struct menu* m; + + list_for_each(pos, &(menus.list)) { + m = list_entry(pos, struct menu, list); + printf("%s: %s\n", m->name, m->display); + } + + return 0; +} + +#if defined(CONFIG_CMD_MENU_MANAGEMENT) +int do_menu_entry(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + /* Strip off leading 'entry' command argument */ + argc--; + argv++; + + if (!strncmp(argv[0], "a", 1)) + return do_menu_entry_add(cmdtp, flag, argc, argv); + if (!strncmp(argv[0], "l", 1)) + return do_menu_entry_list(cmdtp, flag, argc, argv); + else + cmd_usage(cmdtp); + return 0; +} +#endif /* CONFIG_CMD_MENU_MANAGEMENT */ + +int do_menu(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + /* Strip off leading 'menu' command argument */ + argc--; + argv++; + +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + if (!strncmp(argv[0], "a", 1)) + return do_menu_add(cmdtp, flag, argc, argv); + if (!strncmp(argv[0], "e", 1)) + return do_menu_entry(cmdtp, flag, argc, argv); +#endif + if (!strncmp(argv[0], "l", 1)) + return do_menu_list(cmdtp, flag, argc, argv); + if (!strncmp(argv[0], "s", 1)) + return do_menu_show(cmdtp, flag, argc, argv); + else + cmd_usage(cmdtp); + return 0; +} + +U_BOOT_CMD( + menu, CONFIG_SYS_MAXARGS, 1, do_menu, + "menu", + "list\n" + "menu show [menu]\n" +#if defined(CONFIG_CMD_MENU_MANAGEMENT) + "menu add <name> <description>\n" + "menu entry list [menu]\n" + "menu entry add <menu> <command> <selected> <description>\n" +#endif +); diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index c747b4b..b99a13f 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -53,6 +53,7 @@ #define CONFIG_CMD_LOADB /* loadb */ #define CONFIG_CMD_LOADS /* loads */ #define CONFIG_CMD_MEMORY /* md mm nm mw cp cmp crc base loop mtest */ +#define CONFIG_CMD_MEMU /* menu */ #define CONFIG_CMD_MFSL /* FSL support for Microblaze */ #define CONFIG_CMD_MII /* MII support */ #define CONFIG_CMD_MISC /* Misc functions like sleep etc*/ diff --git a/include/console.h b/include/console.h index bc8b139..23c1b0c 100644 --- a/include/console.h +++ b/include/console.h @@ -33,4 +33,9 @@ extern device_t *stdio_devices[] ; extern char *stdio_names[MAX_FILES] ;
+#define printf_reverse(fmt,args...) printf("\e[7m" fmt "\e[m",##args) +#define puts_reverse(fmt) puts("\e[7m" fmt "\e[m") +#define gotoXY(row, col) printf("\e[%d;%dH", row, col) +#define clear() puts("\e[2J") + #endif diff --git a/include/menu.h b/include/menu.h new file mode 100644 index 0000000..33b88e0 --- /dev/null +++ b/include/menu.h @@ -0,0 +1,85 @@ +/* + * (C) Copyright 2009 Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.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 + */ + +#ifndef __MENU_H__ +#define __MENU_H__ + +#include <linux/list.h> +#include <malloc.h> + +struct menu; + +struct menu_entry { + int num; + char *display; + int (*fn)(struct menu *m, struct menu_entry *me); + + struct list_head list; + void *priv; +}; + +struct menu { + char *name; + char *display; + + struct list_head list; + struct menu_entry entries; + int nb_entries; + struct menu_entry *selected; + void *priv; +}; + +/* + * menu functions + */ +static inline struct menu* menu_alloc(void) +{ + return calloc(1, sizeof(struct menu)); +} + + +int menu_init(void); +void menu_free(struct menu *m); +int menu_add(struct menu* m); +struct menu* menu_get_by_name(char *name); +int menu_show(struct menu *m); +int menu_set_selected_entry(struct menu *m, struct menu_entry* me); +int menu_set_selected(struct menu *m, int num); + +/* + * menu entry functions + */ +static inline struct menu_entry* menu_entry_alloc(void) +{ + return calloc(1, sizeof(struct menu_entry)); +} +void menu_entry_free(struct menu_entry *me); +int menu_add_entry(struct menu *m, struct menu_entry* me); +struct menu_entry* menu_entry_get_by_num(struct menu* m, int num); + +/* + * menu entry action functions + */ +int menu_action_run(struct menu *m, struct menu_entry *me); +int menu_action_exit(struct menu *m, struct menu_entry *me); + +#endif /* __MENU_H__ */