blob: e48424995b649561b9d346299dea3c9e97f3342f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Jason Hobbs0685d822011-08-23 11:06:49 +00002/*
3 * Copyright 2010-2011 Calxeda, Inc.
Leon Yu9686a932019-06-21 12:12:39 +08004 * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
Jason Hobbs0685d822011-08-23 11:06:49 +00005 */
6
Masahisa Kojima0e1b9962022-04-28 17:09:45 +09007#include <ansi.h>
Simon Glassdec3c012014-04-10 20:01:25 -06008#include <cli.h>
Jason Hobbs0685d822011-08-23 11:06:49 +00009#include <malloc.h>
10#include <errno.h>
Masahisa Kojima0e1b9962022-04-28 17:09:45 +090011#include <linux/delay.h>
Jason Hobbs0685d822011-08-23 11:06:49 +000012#include <linux/list.h>
Masahisa Kojima0e1b9962022-04-28 17:09:45 +090013#include <watchdog.h>
Jason Hobbs0685d822011-08-23 11:06:49 +000014
15#include "menu.h"
16
Simon Glass1ee7ab82023-06-17 11:49:48 +010017#define ansi 1
Simon Glass9d8d3872023-01-06 08:52:26 -060018
Jason Hobbs0685d822011-08-23 11:06:49 +000019/*
20 * Internally, each item in a menu is represented by a struct menu_item.
21 *
22 * These items will be alloc'd and initialized by menu_item_add and destroyed
23 * by menu_item_destroy, and the consumer of the interface never sees that
24 * this struct is used at all.
25 */
26struct menu_item {
27 char *key;
28 void *data;
29 struct list_head list;
30};
31
32/*
33 * The menu is composed of a list of items along with settings and callbacks
34 * provided by the user. An incomplete definition of this struct is available
35 * in menu.h, but the full definition is here to prevent consumers from
36 * relying on its contents.
37 */
38struct menu {
39 struct menu_item *default_item;
Jason Hobbs65febe62011-08-23 11:06:50 +000040 int timeout;
Jason Hobbs0685d822011-08-23 11:06:49 +000041 char *title;
42 int prompt;
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -070043 void (*display_statusline)(struct menu *);
Jason Hobbs0685d822011-08-23 11:06:49 +000044 void (*item_data_print)(void *);
Pali Rohárd0d8d3b2013-03-23 14:50:40 +000045 char *(*item_choice)(void *);
46 void *item_choice_data;
Jason Hobbs0685d822011-08-23 11:06:49 +000047 struct list_head items;
Leon Yu9686a932019-06-21 12:12:39 +080048 int item_cnt;
Jason Hobbs0685d822011-08-23 11:06:49 +000049};
50
51/*
52 * An iterator function for menu items. callback will be called for each item
53 * in m, with m, a pointer to the item, and extra being passed to callback. If
54 * callback returns a value other than NULL, iteration stops and the value
55 * return by callback is returned from menu_items_iter. This allows it to be
56 * used for search type operations. It is also safe for callback to remove the
57 * item from the list of items.
58 */
59static inline void *menu_items_iter(struct menu *m,
60 void *(*callback)(struct menu *, struct menu_item *, void *),
61 void *extra)
62{
63 struct list_head *pos, *n;
64 struct menu_item *item;
65 void *ret;
66
67 list_for_each_safe(pos, n, &m->items) {
68 item = list_entry(pos, struct menu_item, list);
69
70 ret = callback(m, item, extra);
71
72 if (ret)
73 return ret;
74 }
75
76 return NULL;
77}
78
79/*
80 * Print a menu_item. If the consumer provided an item_data_print function
81 * when creating the menu, call it with a pointer to the item's private data.
82 * Otherwise, print the key of the item.
83 */
84static inline void *menu_item_print(struct menu *m,
85 struct menu_item *item,
86 void *extra)
87{
Wolfgang Denk4f5c100b2011-11-28 20:19:41 +010088 if (!m->item_data_print) {
Anatolij Gustschinb50e1e02011-12-03 06:46:07 +000089 puts(item->key);
Wolfgang Denk4f5c100b2011-11-28 20:19:41 +010090 putc('\n');
91 } else {
Jason Hobbs0685d822011-08-23 11:06:49 +000092 m->item_data_print(item->data);
Wolfgang Denk4f5c100b2011-11-28 20:19:41 +010093 }
Jason Hobbs0685d822011-08-23 11:06:49 +000094
95 return NULL;
96}
97
98/*
99 * Free the memory used by a menu item. This includes the memory used by its
100 * key.
101 */
102static inline void *menu_item_destroy(struct menu *m,
103 struct menu_item *item,
104 void *extra)
105{
106 if (item->key)
107 free(item->key);
108
109 free(item);
110
111 return NULL;
112}
113
114/*
115 * Display a menu so the user can make a choice of an item. First display its
116 * title, if any, and then each item in the menu.
117 */
118static inline void menu_display(struct menu *m)
119{
Wolfgang Denk4f5c100b2011-11-28 20:19:41 +0100120 if (m->title) {
121 puts(m->title);
122 putc('\n');
123 }
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700124 if (m->display_statusline)
125 m->display_statusline(m);
Jason Hobbs0685d822011-08-23 11:06:49 +0000126
127 menu_items_iter(m, menu_item_print, NULL);
128}
129
130/*
131 * Check if an item's key matches a provided string, pointed to by extra. If
132 * extra is NULL, an item with a NULL key will match. Otherwise, the item's
133 * key has to match according to strcmp.
134 *
135 * This is called via menu_items_iter, so it returns a pointer to the item if
136 * the key matches, and returns NULL otherwise.
137 */
138static inline void *menu_item_key_match(struct menu *m,
139 struct menu_item *item, void *extra)
140{
141 char *item_key = extra;
142
143 if (!item_key || !item->key) {
144 if (item_key == item->key)
145 return item;
146
147 return NULL;
148 }
149
150 if (strcmp(item->key, item_key) == 0)
151 return item;
152
153 return NULL;
154}
155
156/*
157 * Find the first item with a key matching item_key, if any exists.
158 */
159static inline struct menu_item *menu_item_by_key(struct menu *m,
160 char *item_key)
161{
162 return menu_items_iter(m, menu_item_key_match, item_key);
163}
164
165/*
Jason Hobbs0685d822011-08-23 11:06:49 +0000166 * Set *choice to point to the default item's data, if any default item was
167 * set, and returns 1. If no default item was set, returns -ENOENT.
168 */
Anatolij Gustschinfb6068a2013-03-23 14:52:04 +0000169int menu_default_choice(struct menu *m, void **choice)
Jason Hobbs0685d822011-08-23 11:06:49 +0000170{
171 if (m->default_item) {
172 *choice = m->default_item->data;
173 return 1;
174 }
175
176 return -ENOENT;
177}
178
179/*
180 * Displays the menu and asks the user to choose an item. *choice will point
181 * to the private data of the item the user chooses. The user makes a choice
182 * by inputting a string matching the key of an item. Invalid choices will
183 * cause the user to be prompted again, repeatedly, until the user makes a
184 * valid choice. The user can exit the menu without making a choice via ^c.
185 *
186 * Returns 1 if the user made a choice, or -EINTR if they bail via ^c.
187 */
188static inline int menu_interactive_choice(struct menu *m, void **choice)
189{
190 char cbuf[CONFIG_SYS_CBSIZE];
191 struct menu_item *choice_item = NULL;
192 int readret;
193
194 while (!choice_item) {
195 cbuf[0] = '\0';
196
197 menu_display(m);
198
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000199 if (!m->item_choice) {
Simon Glassbe6aafc2014-04-10 20:01:27 -0600200 readret = cli_readline_into_buffer("Enter choice: ",
Masahiro Yamadac46d0fe2018-05-24 17:04:57 +0900201 cbuf, m->timeout);
Jason Hobbs0685d822011-08-23 11:06:49 +0000202
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000203 if (readret >= 0) {
204 choice_item = menu_item_by_key(m, cbuf);
205 if (!choice_item)
206 printf("%s not found\n", cbuf);
Tuomas Tynkkynen60c47af2015-05-07 21:29:19 +0300207 } else if (readret == -1) {
208 printf("<INTERRUPT>\n");
209 return -EINTR;
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000210 } else {
211 return menu_default_choice(m, choice);
Heiko Schochere795b2e2012-01-16 22:24:29 +0000212 }
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000213 } else {
214 char *key = m->item_choice(m->item_choice_data);
215
216 if (key)
217 choice_item = menu_item_by_key(m, key);
218 }
219
220 if (!choice_item)
221 m->timeout = 0;
Jason Hobbs0685d822011-08-23 11:06:49 +0000222 }
223
224 *choice = choice_item->data;
225
226 return 1;
227}
228
229/*
230 * menu_default_set() - Sets the default choice for the menu. This is safe to
231 * call more than once on a menu.
232 *
233 * m - Points to a menu created by menu_create().
234 *
235 * item_key - Points to a string that, when compared using strcmp, matches the
236 * key for an existing item in the menu.
237 *
238 * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a
239 * key matching item_key is found.
240 */
241int menu_default_set(struct menu *m, char *item_key)
242{
243 struct menu_item *item;
244
245 if (!m)
246 return -EINVAL;
247
248 item = menu_item_by_key(m, item_key);
249
250 if (!item)
251 return -ENOENT;
252
253 m->default_item = item;
254
255 return 1;
256}
257
258/*
259 * menu_get_choice() - Returns the user's selected menu entry, or the default
Jason Hobbs65febe62011-08-23 11:06:50 +0000260 * if the menu is set to not prompt or the timeout expires. This is safe to
261 * call more than once.
Jason Hobbs0685d822011-08-23 11:06:49 +0000262 *
263 * m - Points to a menu created by menu_create().
264 *
265 * choice - Points to a location that will store a pointer to the selected
266 * menu item. If no item is selected or there is an error, no value will be
267 * written at the location it points to.
268 *
269 * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no
Jason Hobbs65febe62011-08-23 11:06:50 +0000270 * default has been set and the menu is set to not prompt or the timeout
271 * expires, or -EINTR if the user exits the menu via ^c.
Jason Hobbs0685d822011-08-23 11:06:49 +0000272 */
273int menu_get_choice(struct menu *m, void **choice)
274{
275 if (!m || !choice)
276 return -EINVAL;
277
Masahisa Kojima72f823f2022-04-28 17:09:37 +0900278 if (!m->item_cnt)
279 return -ENOENT;
280
Masahisa Kojima6f59d192022-04-28 17:09:36 +0900281 if (!m->prompt)
Jason Hobbs0685d822011-08-23 11:06:49 +0000282 return menu_default_choice(m, choice);
283
284 return menu_interactive_choice(m, choice);
285}
286
287/*
288 * menu_item_add() - Adds or replaces a menu item. Note that this replaces the
289 * data of an item if it already exists, but doesn't change the order of the
290 * item.
291 *
292 * m - Points to a menu created by menu_create().
293 *
294 * item_key - Points to a string that will uniquely identify the item. The
295 * string will be copied to internal storage, and is safe to discard after
296 * passing to menu_item_add.
297 *
298 * item_data - An opaque pointer associated with an item. It is never
299 * dereferenced internally, but will be passed to the item_data_print, and
300 * will be returned from menu_get_choice if the menu item is selected.
301 *
302 * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is
303 * insufficient memory to add the menu item.
304 */
305int menu_item_add(struct menu *m, char *item_key, void *item_data)
306{
307 struct menu_item *item;
308
309 if (!m)
310 return -EINVAL;
311
312 item = menu_item_by_key(m, item_key);
313
314 if (item) {
315 item->data = item_data;
316 return 1;
317 }
318
319 item = malloc(sizeof *item);
320 if (!item)
321 return -ENOMEM;
322
323 item->key = strdup(item_key);
324
325 if (!item->key) {
326 free(item);
327 return -ENOMEM;
328 }
329
330 item->data = item_data;
331
332 list_add_tail(&item->list, &m->items);
Leon Yu9686a932019-06-21 12:12:39 +0800333 m->item_cnt++;
Jason Hobbs0685d822011-08-23 11:06:49 +0000334
335 return 1;
336}
337
338/*
339 * menu_create() - Creates a menu handle with default settings
340 *
341 * title - If not NULL, points to a string that will be displayed before the
342 * list of menu items. It will be copied to internal storage, and is safe to
343 * discard after passing to menu_create().
344 *
Jason Hobbs65febe62011-08-23 11:06:50 +0000345 * timeout - A delay in seconds to wait for user input. If 0, timeout is
346 * disabled, and the default choice will be returned unless prompt is 1.
347 *
348 * prompt - If 0, don't ask for user input unless there is an interrupted
349 * timeout. If 1, the user will be prompted for input regardless of the value
350 * of timeout.
Jason Hobbs0685d822011-08-23 11:06:49 +0000351 *
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700352 * display_statusline - If not NULL, will be called to show a statusline when
353 * the menu is displayed.
354 *
Jason Hobbs0685d822011-08-23 11:06:49 +0000355 * item_data_print - If not NULL, will be called for each item when the menu
356 * is displayed, with the pointer to the item's data passed as the argument.
357 * If NULL, each item's key will be printed instead. Since an item's key is
358 * what must be entered to select an item, the item_data_print function should
359 * make it obvious what the key for each entry is.
360 *
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000361 * item_choice - If not NULL, will be called when asking the user to choose an
Alexander Merkle0137e602016-03-17 15:44:47 +0100362 * item. Returns a key string corresponding to the chosen item or NULL if
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000363 * no item has been selected.
364 *
365 * item_choice_data - Will be passed as the argument to the item_choice function
366 *
Jason Hobbs0685d822011-08-23 11:06:49 +0000367 * Returns a pointer to the menu if successful, or NULL if there is
368 * insufficient memory available to create the menu.
369 */
Jason Hobbs65febe62011-08-23 11:06:50 +0000370struct menu *menu_create(char *title, int timeout, int prompt,
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700371 void (*display_statusline)(struct menu *),
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000372 void (*item_data_print)(void *),
373 char *(*item_choice)(void *),
374 void *item_choice_data)
Jason Hobbs0685d822011-08-23 11:06:49 +0000375{
376 struct menu *m;
377
378 m = malloc(sizeof *m);
379
380 if (!m)
381 return NULL;
382
383 m->default_item = NULL;
384 m->prompt = prompt;
Jason Hobbs65febe62011-08-23 11:06:50 +0000385 m->timeout = timeout;
Thirupathaiah Annapureddyd6b9f6b2020-03-18 11:38:42 -0700386 m->display_statusline = display_statusline;
Jason Hobbs0685d822011-08-23 11:06:49 +0000387 m->item_data_print = item_data_print;
Pali Rohárd0d8d3b2013-03-23 14:50:40 +0000388 m->item_choice = item_choice;
389 m->item_choice_data = item_choice_data;
Leon Yu9686a932019-06-21 12:12:39 +0800390 m->item_cnt = 0;
Jason Hobbs0685d822011-08-23 11:06:49 +0000391
392 if (title) {
393 m->title = strdup(title);
394 if (!m->title) {
395 free(m);
396 return NULL;
397 }
398 } else
399 m->title = NULL;
400
401
402 INIT_LIST_HEAD(&m->items);
403
404 return m;
405}
406
407/*
408 * menu_destroy() - frees the memory used by a menu and its items.
409 *
410 * m - Points to a menu created by menu_create().
411 *
412 * Returns 1 if successful, or -EINVAL if m is NULL.
413 */
414int menu_destroy(struct menu *m)
415{
416 if (!m)
417 return -EINVAL;
418
419 menu_items_iter(m, menu_item_destroy, NULL);
420
421 if (m->title)
422 free(m->title);
423
424 free(m);
425
426 return 1;
427}
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900428
Simon Glass9d8d3872023-01-06 08:52:26 -0600429enum bootmenu_key bootmenu_autoboot_loop(struct bootmenu_data *menu,
430 struct cli_ch_state *cch)
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900431{
Simon Glass81544c42023-01-06 08:52:23 -0600432 enum bootmenu_key key = BKEY_NONE;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900433 int i, c;
434
435 while (menu->delay > 0) {
Simon Glass9d8d3872023-01-06 08:52:26 -0600436 if (ansi)
437 printf(ANSI_CURSOR_POSITION, menu->count + 5, 3);
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900438 printf("Hit any key to stop autoboot: %d ", menu->delay);
439 for (i = 0; i < 100; ++i) {
Simon Glass9d8d3872023-01-06 08:52:26 -0600440 int ichar;
441
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900442 if (!tstc()) {
Stefan Roese80877fa2022-09-02 14:10:46 +0200443 schedule();
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900444 mdelay(10);
445 continue;
446 }
447
448 menu->delay = -1;
449 c = getchar();
450
Simon Glass9d8d3872023-01-06 08:52:26 -0600451 ichar = cli_ch_process(cch, c);
452
453 switch (ichar) {
454 case '\0':
Simon Glass81544c42023-01-06 08:52:23 -0600455 key = BKEY_NONE;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900456 break;
Simon Glass9d8d3872023-01-06 08:52:26 -0600457 case '\n':
Simon Glass81544c42023-01-06 08:52:23 -0600458 key = BKEY_SELECT;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900459 break;
460 case 0x3: /* ^C */
Simon Glass81544c42023-01-06 08:52:23 -0600461 key = BKEY_QUIT;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900462 break;
463 default:
Simon Glass81544c42023-01-06 08:52:23 -0600464 key = BKEY_NONE;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900465 break;
466 }
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900467 break;
468 }
469
470 if (menu->delay < 0)
471 break;
472
473 --menu->delay;
474 }
475
Simon Glass9d8d3872023-01-06 08:52:26 -0600476 if (ansi)
477 printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE, menu->count + 5, 1);
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900478
479 if (menu->delay == 0)
Simon Glass81544c42023-01-06 08:52:23 -0600480 key = BKEY_SELECT;
481
482 return key;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900483}
484
Simon Glass1b56fe12023-01-06 08:52:35 -0600485enum bootmenu_key bootmenu_conv_key(int ichar)
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900486{
Simon Glass1b56fe12023-01-06 08:52:35 -0600487 enum bootmenu_key key;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900488
Simon Glass1b56fe12023-01-06 08:52:35 -0600489 switch (ichar) {
Simon Glass9d8d3872023-01-06 08:52:26 -0600490 case '\n':
Simon Glassd52ce652023-01-06 08:52:25 -0600491 /* enter key was pressed */
Simon Glassf1d3c8e2023-01-06 08:52:24 -0600492 key = BKEY_SELECT;
Simon Glassd52ce652023-01-06 08:52:25 -0600493 break;
494 case CTL_CH('c'):
Simon Glass9d8d3872023-01-06 08:52:26 -0600495 case '\e':
Simon Glassd52ce652023-01-06 08:52:25 -0600496 /* ^C was pressed */
Simon Glassf1d3c8e2023-01-06 08:52:24 -0600497 key = BKEY_QUIT;
Simon Glassd52ce652023-01-06 08:52:25 -0600498 break;
499 case CTL_CH('p'):
500 key = BKEY_UP;
501 break;
502 case CTL_CH('n'):
503 key = BKEY_DOWN;
504 break;
Masahisa Kojimad38ecb72023-02-02 18:24:44 +0900505 case CTL_CH('s'):
506 key = BKEY_SAVE;
507 break;
Simon Glassd52ce652023-01-06 08:52:25 -0600508 case '+':
Simon Glassf1d3c8e2023-01-06 08:52:24 -0600509 key = BKEY_PLUS;
Simon Glassd52ce652023-01-06 08:52:25 -0600510 break;
511 case '-':
Simon Glassf1d3c8e2023-01-06 08:52:24 -0600512 key = BKEY_MINUS;
Simon Glassd52ce652023-01-06 08:52:25 -0600513 break;
514 case ' ':
Simon Glassf1d3c8e2023-01-06 08:52:24 -0600515 key = BKEY_SPACE;
Simon Glassd52ce652023-01-06 08:52:25 -0600516 break;
Simon Glass1b56fe12023-01-06 08:52:35 -0600517 default:
518 key = BKEY_NONE;
519 break;
Simon Glassd52ce652023-01-06 08:52:25 -0600520 }
Simon Glassf1d3c8e2023-01-06 08:52:24 -0600521
522 return key;
Masahisa Kojima0e1b9962022-04-28 17:09:45 +0900523}
Simon Glass1b56fe12023-01-06 08:52:35 -0600524
525enum bootmenu_key bootmenu_loop(struct bootmenu_data *menu,
526 struct cli_ch_state *cch)
527{
528 enum bootmenu_key key;
529 int c;
530
531 c = cli_ch_process(cch, 0);
532 if (!c) {
533 while (!c && !tstc()) {
534 schedule();
535 mdelay(10);
536 c = cli_ch_process(cch, -ETIMEDOUT);
537 }
538 if (!c) {
539 c = getchar();
540 c = cli_ch_process(cch, c);
541 }
542 }
543
544 key = bootmenu_conv_key(c);
545
546 return key;
547}