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

Jean-Christophe PLAGNIOL-VILLARD plagnioj at jcrosoft.com
Sat Jun 13 21:13:01 CEST 2009


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 at 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 at 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 at 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



More information about the U-Boot mailing list