Merge branch 'next' of git://git.denx.de/u-boot-sh
[oweals/u-boot.git] / common / menu.c
index 56439374f7464734440e040ba44214b917f1de87..7b66d199a9b13054706f5886e19829a85c322d12 100644 (file)
@@ -1,21 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * 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/>.
+ * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
  */
 
 #include <common.h>
+#include <cli.h>
 #include <malloc.h>
 #include <errno.h>
 #include <linux/list.h>
@@ -43,10 +33,14 @@ struct menu_item {
  */
 struct menu {
        struct menu_item *default_item;
+       int timeout;
        char *title;
        int prompt;
        void (*item_data_print)(void *);
+       char *(*item_choice)(void *);
+       void *item_choice_data;
        struct list_head items;
+       int item_cnt;
 };
 
 /*
@@ -86,10 +80,12 @@ 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
+       if (!m->item_data_print) {
+               puts(item->key);
+               putc('\n');
+       } else {
                m->item_data_print(item->data);
+       }
 
        return NULL;
 }
@@ -110,14 +106,21 @@ static inline void *menu_item_destroy(struct menu *m,
        return NULL;
 }
 
+__weak void menu_display_statusline(struct menu *m)
+{
+}
+
 /*
  * Display a menu so the user can make a choice of an item. First display its
  * title, if any, and then each item in the menu.
  */
 static inline void menu_display(struct menu *m)
 {
-       if (m->title)
-               printf("%s:\n", m->title);
+       if (m->title) {
+               puts(m->title);
+               putc('\n');
+       }
+       menu_display_statusline(m);
 
        menu_items_iter(m, menu_item_print, NULL);
 }
@@ -157,21 +160,11 @@ static inline struct menu_item *menu_item_by_key(struct menu *m,
        return menu_items_iter(m, menu_item_key_match, item_key);
 }
 
-/*
- * Checks whether or not the default menu item should be used without
- * prompting for a user choice.  If the menu is set to always prompt, return
- * 0. Otherwise, return 1 to indicate we should use the default menu item.
- */
-static inline int menu_use_default(struct menu *m)
-{
-       return !m->prompt;
-}
-
 /*
  * Set *choice to point to the default item's data, if any default item was
  * set, and returns 1. If no default item was set, returns -ENOENT.
  */
-static inline int menu_default_choice(struct menu *m, void **choice)
+int menu_default_choice(struct menu *m, void **choice)
 {
        if (m->default_item) {
                *choice = m->default_item->data;
@@ -201,17 +194,29 @@ static inline int menu_interactive_choice(struct menu *m, void **choice)
 
                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);
+               if (!m->item_choice) {
+                       readret = cli_readline_into_buffer("Enter choice: ",
+                                                          cbuf, m->timeout);
+
+                       if (readret >= 0) {
+                               choice_item = menu_item_by_key(m, cbuf);
+                               if (!choice_item)
+                                       printf("%s not found\n", cbuf);
+                       } else if (readret == -1)  {
+                               printf("<INTERRUPT>\n");
+                               return -EINTR;
+                       } else {
+                               return menu_default_choice(m, choice);
+                       }
                } else {
-                       printf("^C\n");
-                       return -EINTR;
+                       char *key = m->item_choice(m->item_choice_data);
+
+                       if (key)
+                               choice_item = menu_item_by_key(m, key);
                }
+
+               if (!choice_item)
+                       m->timeout = 0;
        }
 
        *choice = choice_item->data;
@@ -250,7 +255,8 @@ int menu_default_set(struct menu *m, char *item_key)
 
 /*
  * 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.
+ * if the menu is set to not prompt or the timeout expires. This is safe to
+ * call more than once.
  *
  * m - Points to a menu created by menu_create().
  *
@@ -259,15 +265,15 @@ int menu_default_set(struct menu *m, char *item_key)
  * 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 ^c.
+ * default has been set and the menu is set to not prompt or the timeout
+ * expires, or -EINTR if the user exits the menu via ^c.
  */
 int menu_get_choice(struct menu *m, void **choice)
 {
        if (!m || !choice)
                return -EINVAL;
 
-       if (menu_use_default(m))
+       if (!m->prompt || m->item_cnt == 1)
                return menu_default_choice(m, choice);
 
        return menu_interactive_choice(m, choice);
@@ -319,6 +325,7 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data)
        item->data = item_data;
 
        list_add_tail(&item->list, &m->items);
+       m->item_cnt++;
 
        return 1;
 }
@@ -330,7 +337,12 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data)
  * 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.
+ * timeout - A delay in seconds to wait for user input. If 0, timeout is
+ * disabled, and the default choice will be returned unless prompt is 1.
+ *
+ * prompt - If 0, don't ask for user input unless there is an interrupted
+ * timeout. If 1, the user will be prompted for input regardless of the value
+ * of timeout.
  *
  * 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.
@@ -338,11 +350,19 @@ int menu_item_add(struct menu *m, char *item_key, void *item_data)
  * what must be entered to select an item, the item_data_print function should
  * make it obvious what the key for each entry is.
  *
+ * item_choice - If not NULL, will be called when asking the user to choose an
+ * item. Returns a key string corresponding to the chosen item or NULL if
+ * no item has been selected.
+ *
+ * item_choice_data - Will be passed as the argument to the item_choice function
+ *
  * Returns a pointer to the menu if successful, or NULL if there is
  * insufficient memory available to create the menu.
  */
-struct menu *menu_create(char *title, int prompt,
-                               void (*item_data_print)(void *))
+struct menu *menu_create(char *title, int timeout, int prompt,
+                               void (*item_data_print)(void *),
+                               char *(*item_choice)(void *),
+                               void *item_choice_data)
 {
        struct menu *m;
 
@@ -353,7 +373,11 @@ struct menu *menu_create(char *title, int prompt,
 
        m->default_item = NULL;
        m->prompt = prompt;
+       m->timeout = timeout;
        m->item_data_print = item_data_print;
+       m->item_choice = item_choice;
+       m->item_choice_data = item_choice_data;
+       m->item_cnt = 0;
 
        if (title) {
                m->title = strdup(title);