aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Hobbs <jason.hobbs@calxeda.com>2011-06-29 11:25:13 -0500
committerJohn Rigby <john.rigby@linaro.org>2011-08-15 15:10:47 -0600
commitcd82395dfc34b6b06df9b93bdf5522e5d0a774dc (patch)
tree79d31cf46b0ce071abfdc432252205708e4a6113
parent8906724f20c1a29fa44541777396179e1cefe91d (diff)
Add generic, reusable menu code
This will be used first by the pxecfg code, but is intended to be generic and reusable for other jobs in U-boot. Signed-off-by: Jason Hobbs <jason.hobbs@calxeda.com>
-rw-r--r--common/Makefile1
-rw-r--r--common/menu.c266
-rw-r--r--doc/README.menu158
-rw-r--r--include/menu.h30
4 files changed, 455 insertions, 0 deletions
diff --git a/common/Makefile b/common/Makefile
index d66246823..ebe45b825 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -169,6 +169,7 @@ COBJS-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o
COBJS-$(CONFIG_KALLSYMS) += kallsyms.o
COBJS-$(CONFIG_LCD) += lcd.o
COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o
+COBJS-$(CONFIG_MENU) += menu.o
COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o
COBJS-$(CONFIG_UPDATE_TFTP) += update.o
COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
diff --git a/common/menu.c b/common/menu.c
new file mode 100644
index 000000000..9bcd90609
--- /dev/null
+++ b/common/menu.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <linux/list.h>
+
+#include "menu.h"
+
+struct menu_item {
+ char *key;
+ void *data;
+ struct list_head list;
+};
+
+struct menu {
+ struct menu_item *default_item;
+ char *title;
+ int prompt;
+ void (*item_data_print)(void *);
+ struct list_head items;
+};
+
+static inline void *menu_items_iter(struct menu *m,
+ void *(*callback)(struct menu *, struct menu_item *, void *),
+ void *extra)
+{
+ struct list_head *pos, *n;
+ struct menu_item *item;
+ void *ret;
+
+ list_for_each_safe(pos, n, &m->items) {
+ item = list_entry(pos, struct menu_item, list);
+
+ ret = callback(m, item, extra);
+
+ if (ret)
+ return ret;
+ }
+
+ return NULL;
+}
+
+static inline void *menu_item_print(struct menu *m,
+ struct menu_item *item,
+ void *extra)
+{
+ if (!m->item_data_print)
+ printf("%s\n", item->key);
+ else
+ m->item_data_print(item->data);
+
+ return NULL;
+}
+
+static inline void *menu_item_destroy(struct menu *m,
+ struct menu_item *item,
+ void *extra)
+{
+ if (item->key)
+ free(item->key);
+
+ free(item);
+
+ return NULL;
+}
+
+static inline void menu_display(struct menu *m)
+{
+ if (m->title)
+ printf("%s:\n", m->title);
+
+ menu_items_iter(m, menu_item_print, NULL);
+}
+
+static inline void *menu_item_key_match(struct menu *m,
+ struct menu_item *item,
+ void *extra)
+{
+ char *item_key = extra;
+
+ if (!item_key || !item->key) {
+ if (item_key == item->key)
+ return item;
+
+ return NULL;
+ }
+
+ if (strcmp(item->key, item_key) == 0)
+ return item;
+
+ return NULL;
+}
+
+static inline struct menu_item *menu_item_by_key(struct menu *m,
+ char *item_key)
+{
+ return menu_items_iter(m, menu_item_key_match, item_key);
+}
+
+static inline int menu_use_default(struct menu *m)
+{
+ return !m->prompt;
+}
+
+static inline int menu_default_choice(struct menu *m, void **choice)
+{
+ if (m->default_item) {
+ *choice = m->default_item->data;
+ return 1;
+ }
+
+ return -ENOENT;
+}
+
+static inline int menu_interactive_choice(struct menu *m, void **choice)
+{
+ char cbuf[CONFIG_SYS_CBSIZE];
+ struct menu_item *choice_item = NULL;
+ int readret;
+
+ while (!choice_item) {
+ cbuf[0] = '\0';
+
+ menu_display(m);
+
+ readret = readline_into_buffer("Enter choice: ", cbuf);
+
+ if (readret >= 0) {
+ choice_item = menu_item_by_key(m, cbuf);
+
+ if (!choice_item)
+ printf("%s not found\n", cbuf);
+ } else {
+ printf("^C\n");
+ return -EINTR;
+ }
+ }
+
+ *choice = choice_item->data;
+
+ return 1;
+}
+
+int menu_default_set(struct menu *m, char *item_key)
+{
+ struct menu_item *item;
+
+ if (!m)
+ return -EINVAL;
+
+ item = menu_item_by_key(m, item_key);
+
+ if (!item)
+ return -ENOENT;
+
+ m->default_item = item;
+
+ return 1;
+}
+
+int menu_get_choice(struct menu *m, void **choice)
+{
+ if (!m || !choice)
+ return -EINVAL;
+
+ if (menu_use_default(m))
+ return menu_default_choice(m, choice);
+
+ return menu_interactive_choice(m, choice);
+}
+
+/*
+ * note that this replaces the data of an item if it already exists, but
+ * doesn't change the order of the item.
+ */
+int menu_item_add(struct menu *m, char *item_key, void *item_data)
+{
+ struct menu_item *item;
+
+ if (!m)
+ return -EINVAL;
+
+ item = menu_item_by_key(m, item_key);
+
+ if (item) {
+ item->data = item_data;
+ return 1;
+ }
+
+ item = malloc(sizeof *item);
+ if (!item)
+ return -ENOMEM;
+
+ item->key = strdup(item_key);
+
+ if (!item->key) {
+ free(item);
+ return -ENOMEM;
+ }
+
+ item->data = item_data;
+
+ list_add_tail(&item->list, &m->items);
+
+ return 1;
+}
+
+struct menu *menu_create(char *title, int prompt,
+ void (*item_data_print)(void *))
+{
+ struct menu *m;
+
+ m = malloc(sizeof *m);
+
+ if (!m)
+ return NULL;
+
+ m->default_item = NULL;
+ m->prompt = prompt;
+ m->item_data_print = item_data_print;
+
+ if (title) {
+ m->title = strdup(title);
+ if (!m->title) {
+ free(m);
+ return NULL;
+ }
+ } else
+ m->title = NULL;
+
+
+ INIT_LIST_HEAD(&m->items);
+
+ return m;
+}
+
+int menu_destroy(struct menu *m)
+{
+ if (!m)
+ return -EINVAL;
+
+ menu_items_iter(m, menu_item_destroy, NULL);
+
+ if (m->title)
+ free(m->title);
+
+ free(m);
+
+ return 1;
+}
diff --git a/doc/README.menu b/doc/README.menu
new file mode 100644
index 000000000..bca44be3f
--- /dev/null
+++ b/doc/README.menu
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+U-boot provides a set of interfaces for creating and using simple, text
+based menus. Menus are displayed as lists of labeled entries on the
+console, and an entry can be selected by entering its label.
+
+To use the menu code, enable CONFIG_MENU, and include "menu.h" where
+the interfaces should be available.
+
+Menus are composed of items. Each item has a key used to identify it in
+the menu, and an opaque pointer to data controlled by the consumer.
+
+Interfaces
+----------
+#include "menu.h"
+
+struct menu;
+
+struct menu *menu_create(char *title, int prompt,
+ void (*item_data_print)(void *));
+int menu_item_add(struct menu *m, char *item_key, void *item_data);
+int menu_default_set(struct menu *m, char *item_key);
+int menu_get_choice(struct menu *m, void **choice);
+int menu_destroy(struct menu *m);
+
+menu_create() - Creates a menu handle with default settings
+
+ title - If not NULL, points to a string that will be displayed before
+ the list of menu items. It will be copied to internal storage, and is
+ safe to discard after passing to menu_create().
+
+ prompt - If 0, don't ask for user input. If 1, the user will be ed for
+ promptinput.
+
+ item_data_print - If not NULL, will be called for each item when
+ the menu is displayed, with the pointer to the item's data passed
+ as the argument. If NULL, each item's key will be printed instead.
+ Since an item's key is what must be entered to select an item, the
+ item_data_print function should make it obvious what the key for each
+ entry is.
+
+ Returns a pointer to the menu if successful, or NULL if there is
+ insufficient memory available to create the menu.
+
+
+menu_item_add() - Adds or replaces a menu item
+
+ m - Points to a menu created by menu_create().
+
+ item_key - Points to a string that will uniquely identify the item.
+ The string will be copied to internal storage, and is safe to discard
+ after passing to menu_item_add.
+
+ item_data - An opaque pointer associated with an item. It is never
+ dereferenced internally, but will be passed to the item_data_print,
+ and will be returned from menu_get_choice if the menu item is
+ selected.
+
+ Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is
+ insufficient memory to add the menu item.
+
+
+menu_default_set() - Sets the default choice for the menu. This is safe
+to call more than once.
+
+ m - Points to a menu created by menu_create().
+
+ item_key - Points to a string that, when compared using strcmp,
+ matches the key for an existing item in the menu.
+
+ Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item
+ with a key matching item_key is found.
+
+
+menu_get_choice() - Returns the user's selected menu entry, or the
+default if the menu is set to not prompt. This is safe to call more than
+once.
+
+ m - Points to a menu created by menu_create().
+
+ choice - Points to a location that will store a pointer to the
+ selected menu item. If no item is selected or there is an error, no
+ value will be written at the location it points to.
+
+ Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no
+ default has been set and the menu is set to not prompt, or -EINTR if
+ the user exits the menu via ctrl+c.
+
+
+menu_destroy() - frees the memory used by a menu and its items.
+
+ m - Points to a menu created by menu_create().
+
+ Returns 1 if successful, or -EINVAL if m is NULL.
+
+
+Example
+-------
+This example creates a menu that always prompts, and allows the user
+to pick from a list of tools. The item key and data are the same.
+
+#include "menu.h"
+
+char *tools[] = {
+ "Hammer",
+ "Screwdriver",
+ "Nail gun",
+ NULL
+};
+
+char *pick_a_tool(void)
+{
+ struct menu *m;
+ int i;
+ char *tool = NULL;
+
+ m = menu_create("Tools", 1, NULL);
+
+ for(i = 0; tools[i]; i++) {
+ if (menu_item_add(m, tools[i], tools[i]) != 1) {
+ printf("failed to add item!");
+ menu_destroy(m);
+ return NULL;
+ }
+ }
+
+ if (menu_get_choice(m, (void **)&tool) != 1)
+ printf("Problem picking tool!\n");
+
+ menu_destroy(m);
+
+ return tool;
+}
+
+void caller(void)
+{
+ char *tool = pick_a_tool();
+
+ if (tool) {
+ printf("picked a tool: %s\n", tool);
+ use_tool(tool);
+ }
+}
diff --git a/include/menu.h b/include/menu.h
new file mode 100644
index 000000000..d47e1a029
--- /dev/null
+++ b/include/menu.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ *
+ * 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 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MENU_H__
+#define __MENU_H__
+
+struct menu;
+
+struct menu *menu_create(char *title, int prompt,
+ void (*item_data_print)(void *));
+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);
+
+#endif /* __MENU_H__ */