Commit b69bf52dfe34b9d7b2a20845c8a7e7e5978c2d2f

Authored by Jason Hobbs
Committed by Wolfgang Denk
1 parent 5c1d082b14

Add generic, reusable menu code

This will be used first by the pxe code, but is intended to be
generic and reusable for other jobs in U-boot.

Signed-off-by: Jason Hobbs <jason.hobbs@calxeda.com>

Showing 4 changed files with 543 additions and 0 deletions Side-by-side Diff

... ... @@ -177,6 +177,7 @@
177 177 COBJS-$(CONFIG_KALLSYMS) += kallsyms.o
178 178 COBJS-$(CONFIG_LCD) += lcd.o
179 179 COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o
  180 +COBJS-$(CONFIG_MENU) += menu.o
180 181 COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o
181 182 COBJS-$(CONFIG_UPDATE_TFTP) += update.o
182 183 COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
  1 +/*
  2 + * Copyright 2010-2011 Calxeda, Inc.
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify it
  5 + * under the terms of the GNU General Public License as published by the Free
  6 + * Software Foundation; either version 2 of the License, or (at your option)
  7 + * any later version.
  8 + *
  9 + * This program is distributed in the hope it will be useful, but WITHOUT
  10 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12 + * more details.
  13 + *
  14 + * You should have received a copy of the GNU General Public License along with
  15 + * this program. If not, see <http://www.gnu.org/licenses/>.
  16 + */
  17 +
  18 +#include <common.h>
  19 +#include <malloc.h>
  20 +#include <errno.h>
  21 +#include <linux/list.h>
  22 +
  23 +#include "menu.h"
  24 +
  25 +/*
  26 + * Internally, each item in a menu is represented by a struct menu_item.
  27 + *
  28 + * These items will be alloc'd and initialized by menu_item_add and destroyed
  29 + * by menu_item_destroy, and the consumer of the interface never sees that
  30 + * this struct is used at all.
  31 + */
  32 +struct menu_item {
  33 + char *key;
  34 + void *data;
  35 + struct list_head list;
  36 +};
  37 +
  38 +/*
  39 + * The menu is composed of a list of items along with settings and callbacks
  40 + * provided by the user. An incomplete definition of this struct is available
  41 + * in menu.h, but the full definition is here to prevent consumers from
  42 + * relying on its contents.
  43 + */
  44 +struct menu {
  45 + struct menu_item *default_item;
  46 + char *title;
  47 + int prompt;
  48 + void (*item_data_print)(void *);
  49 + struct list_head items;
  50 +};
  51 +
  52 +/*
  53 + * An iterator function for menu items. callback will be called for each item
  54 + * in m, with m, a pointer to the item, and extra being passed to callback. If
  55 + * callback returns a value other than NULL, iteration stops and the value
  56 + * return by callback is returned from menu_items_iter. This allows it to be
  57 + * used for search type operations. It is also safe for callback to remove the
  58 + * item from the list of items.
  59 + */
  60 +static inline void *menu_items_iter(struct menu *m,
  61 + void *(*callback)(struct menu *, struct menu_item *, void *),
  62 + void *extra)
  63 +{
  64 + struct list_head *pos, *n;
  65 + struct menu_item *item;
  66 + void *ret;
  67 +
  68 + list_for_each_safe(pos, n, &m->items) {
  69 + item = list_entry(pos, struct menu_item, list);
  70 +
  71 + ret = callback(m, item, extra);
  72 +
  73 + if (ret)
  74 + return ret;
  75 + }
  76 +
  77 + return NULL;
  78 +}
  79 +
  80 +/*
  81 + * Print a menu_item. If the consumer provided an item_data_print function
  82 + * when creating the menu, call it with a pointer to the item's private data.
  83 + * Otherwise, print the key of the item.
  84 + */
  85 +static inline void *menu_item_print(struct menu *m,
  86 + struct menu_item *item,
  87 + void *extra)
  88 +{
  89 + if (!m->item_data_print)
  90 + printf("%s\n", item->key);
  91 + else
  92 + m->item_data_print(item->data);
  93 +
  94 + return NULL;
  95 +}
  96 +
  97 +/*
  98 + * Free the memory used by a menu item. This includes the memory used by its
  99 + * key.
  100 + */
  101 +static inline void *menu_item_destroy(struct menu *m,
  102 + struct menu_item *item,
  103 + void *extra)
  104 +{
  105 + if (item->key)
  106 + free(item->key);
  107 +
  108 + free(item);
  109 +
  110 + return NULL;
  111 +}
  112 +
  113 +/*
  114 + * Display a menu so the user can make a choice of an item. First display its
  115 + * title, if any, and then each item in the menu.
  116 + */
  117 +static inline void menu_display(struct menu *m)
  118 +{
  119 + if (m->title)
  120 + printf("%s:\n", m->title);
  121 +
  122 + menu_items_iter(m, menu_item_print, NULL);
  123 +}
  124 +
  125 +/*
  126 + * Check if an item's key matches a provided string, pointed to by extra. If
  127 + * extra is NULL, an item with a NULL key will match. Otherwise, the item's
  128 + * key has to match according to strcmp.
  129 + *
  130 + * This is called via menu_items_iter, so it returns a pointer to the item if
  131 + * the key matches, and returns NULL otherwise.
  132 + */
  133 +static inline void *menu_item_key_match(struct menu *m,
  134 + struct menu_item *item, void *extra)
  135 +{
  136 + char *item_key = extra;
  137 +
  138 + if (!item_key || !item->key) {
  139 + if (item_key == item->key)
  140 + return item;
  141 +
  142 + return NULL;
  143 + }
  144 +
  145 + if (strcmp(item->key, item_key) == 0)
  146 + return item;
  147 +
  148 + return NULL;
  149 +}
  150 +
  151 +/*
  152 + * Find the first item with a key matching item_key, if any exists.
  153 + */
  154 +static inline struct menu_item *menu_item_by_key(struct menu *m,
  155 + char *item_key)
  156 +{
  157 + return menu_items_iter(m, menu_item_key_match, item_key);
  158 +}
  159 +
  160 +/*
  161 + * Checks whether or not the default menu item should be used without
  162 + * prompting for a user choice. If the menu is set to always prompt, return
  163 + * 0. Otherwise, return 1 to indicate we should use the default menu item.
  164 + */
  165 +static inline int menu_use_default(struct menu *m)
  166 +{
  167 + return !m->prompt;
  168 +}
  169 +
  170 +/*
  171 + * Set *choice to point to the default item's data, if any default item was
  172 + * set, and returns 1. If no default item was set, returns -ENOENT.
  173 + */
  174 +static inline int menu_default_choice(struct menu *m, void **choice)
  175 +{
  176 + if (m->default_item) {
  177 + *choice = m->default_item->data;
  178 + return 1;
  179 + }
  180 +
  181 + return -ENOENT;
  182 +}
  183 +
  184 +/*
  185 + * Displays the menu and asks the user to choose an item. *choice will point
  186 + * to the private data of the item the user chooses. The user makes a choice
  187 + * by inputting a string matching the key of an item. Invalid choices will
  188 + * cause the user to be prompted again, repeatedly, until the user makes a
  189 + * valid choice. The user can exit the menu without making a choice via ^c.
  190 + *
  191 + * Returns 1 if the user made a choice, or -EINTR if they bail via ^c.
  192 + */
  193 +static inline int menu_interactive_choice(struct menu *m, void **choice)
  194 +{
  195 + char cbuf[CONFIG_SYS_CBSIZE];
  196 + struct menu_item *choice_item = NULL;
  197 + int readret;
  198 +
  199 + while (!choice_item) {
  200 + cbuf[0] = '\0';
  201 +
  202 + menu_display(m);
  203 +
  204 + readret = readline_into_buffer("Enter choice: ", cbuf);
  205 +
  206 + if (readret >= 0) {
  207 + choice_item = menu_item_by_key(m, cbuf);
  208 +
  209 + if (!choice_item)
  210 + printf("%s not found\n", cbuf);
  211 + } else {
  212 + printf("^C\n");
  213 + return -EINTR;
  214 + }
  215 + }
  216 +
  217 + *choice = choice_item->data;
  218 +
  219 + return 1;
  220 +}
  221 +
  222 +/*
  223 + * menu_default_set() - Sets the default choice for the menu. This is safe to
  224 + * call more than once on a menu.
  225 + *
  226 + * m - Points to a menu created by menu_create().
  227 + *
  228 + * item_key - Points to a string that, when compared using strcmp, matches the
  229 + * key for an existing item in the menu.
  230 + *
  231 + * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a
  232 + * key matching item_key is found.
  233 + */
  234 +int menu_default_set(struct menu *m, char *item_key)
  235 +{
  236 + struct menu_item *item;
  237 +
  238 + if (!m)
  239 + return -EINVAL;
  240 +
  241 + item = menu_item_by_key(m, item_key);
  242 +
  243 + if (!item)
  244 + return -ENOENT;
  245 +
  246 + m->default_item = item;
  247 +
  248 + return 1;
  249 +}
  250 +
  251 +/*
  252 + * menu_get_choice() - Returns the user's selected menu entry, or the default
  253 + * if the menu is set to not prompt. This is safe to call more than once.
  254 + *
  255 + * m - Points to a menu created by menu_create().
  256 + *
  257 + * choice - Points to a location that will store a pointer to the selected
  258 + * menu item. If no item is selected or there is an error, no value will be
  259 + * written at the location it points to.
  260 + *
  261 + * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no
  262 + * default has been set and the menu is set to not prompt, or -EINTR if the
  263 + * user exits the menu via ^c.
  264 + */
  265 +int menu_get_choice(struct menu *m, void **choice)
  266 +{
  267 + if (!m || !choice)
  268 + return -EINVAL;
  269 +
  270 + if (menu_use_default(m))
  271 + return menu_default_choice(m, choice);
  272 +
  273 + return menu_interactive_choice(m, choice);
  274 +}
  275 +
  276 +/*
  277 + * menu_item_add() - Adds or replaces a menu item. Note that this replaces the
  278 + * data of an item if it already exists, but doesn't change the order of the
  279 + * item.
  280 + *
  281 + * m - Points to a menu created by menu_create().
  282 + *
  283 + * item_key - Points to a string that will uniquely identify the item. The
  284 + * string will be copied to internal storage, and is safe to discard after
  285 + * passing to menu_item_add.
  286 + *
  287 + * item_data - An opaque pointer associated with an item. It is never
  288 + * dereferenced internally, but will be passed to the item_data_print, and
  289 + * will be returned from menu_get_choice if the menu item is selected.
  290 + *
  291 + * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is
  292 + * insufficient memory to add the menu item.
  293 + */
  294 +int menu_item_add(struct menu *m, char *item_key, void *item_data)
  295 +{
  296 + struct menu_item *item;
  297 +
  298 + if (!m)
  299 + return -EINVAL;
  300 +
  301 + item = menu_item_by_key(m, item_key);
  302 +
  303 + if (item) {
  304 + item->data = item_data;
  305 + return 1;
  306 + }
  307 +
  308 + item = malloc(sizeof *item);
  309 + if (!item)
  310 + return -ENOMEM;
  311 +
  312 + item->key = strdup(item_key);
  313 +
  314 + if (!item->key) {
  315 + free(item);
  316 + return -ENOMEM;
  317 + }
  318 +
  319 + item->data = item_data;
  320 +
  321 + list_add_tail(&item->list, &m->items);
  322 +
  323 + return 1;
  324 +}
  325 +
  326 +/*
  327 + * menu_create() - Creates a menu handle with default settings
  328 + *
  329 + * title - If not NULL, points to a string that will be displayed before the
  330 + * list of menu items. It will be copied to internal storage, and is safe to
  331 + * discard after passing to menu_create().
  332 + *
  333 + * prompt - If 0, don't ask for user input.
  334 + *
  335 + * item_data_print - If not NULL, will be called for each item when the menu
  336 + * is displayed, with the pointer to the item's data passed as the argument.
  337 + * If NULL, each item's key will be printed instead. Since an item's key is
  338 + * what must be entered to select an item, the item_data_print function should
  339 + * make it obvious what the key for each entry is.
  340 + *
  341 + * Returns a pointer to the menu if successful, or NULL if there is
  342 + * insufficient memory available to create the menu.
  343 + */
  344 +struct menu *menu_create(char *title, int prompt,
  345 + void (*item_data_print)(void *))
  346 +{
  347 + struct menu *m;
  348 +
  349 + m = malloc(sizeof *m);
  350 +
  351 + if (!m)
  352 + return NULL;
  353 +
  354 + m->default_item = NULL;
  355 + m->prompt = prompt;
  356 + m->item_data_print = item_data_print;
  357 +
  358 + if (title) {
  359 + m->title = strdup(title);
  360 + if (!m->title) {
  361 + free(m);
  362 + return NULL;
  363 + }
  364 + } else
  365 + m->title = NULL;
  366 +
  367 +
  368 + INIT_LIST_HEAD(&m->items);
  369 +
  370 + return m;
  371 +}
  372 +
  373 +/*
  374 + * menu_destroy() - frees the memory used by a menu and its items.
  375 + *
  376 + * m - Points to a menu created by menu_create().
  377 + *
  378 + * Returns 1 if successful, or -EINVAL if m is NULL.
  379 + */
  380 +int menu_destroy(struct menu *m)
  381 +{
  382 + if (!m)
  383 + return -EINVAL;
  384 +
  385 + menu_items_iter(m, menu_item_destroy, NULL);
  386 +
  387 + if (m->title)
  388 + free(m->title);
  389 +
  390 + free(m);
  391 +
  392 + return 1;
  393 +}
  1 +/*
  2 + * Copyright 2010-2011 Calxeda, Inc.
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify it
  5 + * under the terms of the GNU General Public License as published by the Free
  6 + * Software Foundation; either version 2 of the License, or (at your option)
  7 + * any later version.
  8 + *
  9 + * This program is distributed in the hope it will be useful, but WITHOUT
  10 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12 + * more details.
  13 + *
  14 + * You should have received a copy of the GNU General Public License along with
  15 + * this program. If not, see <http://www.gnu.org/licenses/>.
  16 + */
  17 +
  18 +U-boot provides a set of interfaces for creating and using simple, text
  19 +based menus. Menus are displayed as lists of labeled entries on the
  20 +console, and an entry can be selected by entering its label.
  21 +
  22 +To use the menu code, enable CONFIG_MENU, and include "menu.h" where
  23 +the interfaces should be available.
  24 +
  25 +Menus are composed of items. Each item has a key used to identify it in
  26 +the menu, and an opaque pointer to data controlled by the consumer.
  27 +
  28 +Interfaces
  29 +----------
  30 +#include "menu.h"
  31 +
  32 +/*
  33 + * Consumers of the menu interfaces will use a struct menu * as the
  34 + * handle for a menu. struct menu is only fully defined in menu.c,
  35 + * preventing consumers of the menu interfaces from accessing its
  36 + * contents directly.
  37 + */
  38 +struct menu;
  39 +
  40 +/*
  41 + * NOTE: See comments in common/menu.c for more detailed documentation on
  42 + * these interfaces.
  43 + */
  44 +
  45 +/*
  46 + * menu_create() - Creates a menu handle with default settings
  47 + */
  48 +struct menu *menu_create(char *title, int prompt,
  49 + void (*item_data_print)(void *));
  50 +
  51 +/*
  52 + * menu_item_add() - Adds or replaces a menu item
  53 + */
  54 +int menu_item_add(struct menu *m, char *item_key, void *item_data);
  55 +
  56 +/*
  57 + * menu_default_set() - Sets the default choice for the menu
  58 + */
  59 +int menu_default_set(struct menu *m, char *item_key);
  60 +
  61 +/*
  62 + * menu_get_choice() - Returns the user's selected menu entry, or the
  63 + * default if the menu is set to not prompt.
  64 + */
  65 +int menu_get_choice(struct menu *m, void **choice);
  66 +
  67 +/*
  68 + * menu_destroy() - frees the memory used by a menu and its items.
  69 + */
  70 +int menu_destroy(struct menu *m);
  71 +
  72 +
  73 +Example Code
  74 +------------
  75 +This example creates a menu that always prompts, and allows the user
  76 +to pick from a list of tools. The item key and data are the same.
  77 +
  78 +#include "menu.h"
  79 +
  80 +char *tools[] = {
  81 + "Hammer",
  82 + "Screwdriver",
  83 + "Nail gun",
  84 + NULL
  85 +};
  86 +
  87 +char *pick_a_tool(void)
  88 +{
  89 + struct menu *m;
  90 + int i;
  91 + char *tool = NULL;
  92 +
  93 + m = menu_create("Tools", 1, NULL);
  94 +
  95 + for(i = 0; tools[i]; i++) {
  96 + if (menu_item_add(m, tools[i], tools[i]) != 1) {
  97 + printf("failed to add item!");
  98 + menu_destroy(m);
  99 + return NULL;
  100 + }
  101 + }
  102 +
  103 + if (menu_get_choice(m, (void **)&tool) != 1)
  104 + printf("Problem picking tool!\n");
  105 +
  106 + menu_destroy(m);
  107 +
  108 + return tool;
  109 +}
  110 +
  111 +void caller(void)
  112 +{
  113 + char *tool = pick_a_tool();
  114 +
  115 + if (tool) {
  116 + printf("picked a tool: %s\n", tool);
  117 + use_tool(tool);
  118 + }
  119 +}
  1 +/*
  2 + * Copyright 2010-2011 Calxeda, Inc.
  3 + *
  4 + * This program is free software; you can redistribute it and/or modify it
  5 + * under the terms of the GNU General Public License as published by the Free
  6 + * Software Foundation; either version 2 of the License, or (at your option)
  7 + * any later version.
  8 + *
  9 + * This program is distributed in the hope it will be useful, but WITHOUT
  10 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12 + * more details.
  13 + *
  14 + * You should have received a copy of the GNU General Public License along with
  15 + * this program. If not, see <http://www.gnu.org/licenses/>.
  16 + */
  17 +
  18 +#ifndef __MENU_H__
  19 +#define __MENU_H__
  20 +
  21 +struct menu;
  22 +
  23 +struct menu *menu_create(char *title, int prompt,
  24 + void (*item_data_print)(void *));
  25 +int menu_default_set(struct menu *m, char *item_key);
  26 +int menu_get_choice(struct menu *m, void **choice);
  27 +int menu_item_add(struct menu *m, char *item_key, void *item_data);
  28 +int menu_destroy(struct menu *m);
  29 +
  30 +#endif /* __MENU_H__ */