[U-Boot] [RFC/PATCH 1/2] Add menu Framework

Jean-Christophe PLAGNIOL-VILLARD
2009-06-13T19:13:01Z

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 <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 <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__ */
--
1.6.3.1