vbe: Move OS implementation into a separate file
[platform/kernel/u-boot.git] / common / menu.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2010-2011 Calxeda, Inc.
4  * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
5  */
6
7 #include <ansi.h>
8 #include <common.h>
9 #include <cli.h>
10 #include <malloc.h>
11 #include <errno.h>
12 #include <linux/delay.h>
13 #include <linux/list.h>
14 #include <watchdog.h>
15
16 #include "menu.h"
17
18 /*
19  * Internally, each item in a menu is represented by a struct menu_item.
20  *
21  * These items will be alloc'd and initialized by menu_item_add and destroyed
22  * by menu_item_destroy, and the consumer of the interface never sees that
23  * this struct is used at all.
24  */
25 struct menu_item {
26         char *key;
27         void *data;
28         struct list_head list;
29 };
30
31 /*
32  * The menu is composed of a list of items along with settings and callbacks
33  * provided by the user. An incomplete definition of this struct is available
34  * in menu.h, but the full definition is here to prevent consumers from
35  * relying on its contents.
36  */
37 struct menu {
38         struct menu_item *default_item;
39         int timeout;
40         char *title;
41         int prompt;
42         void (*display_statusline)(struct menu *);
43         void (*item_data_print)(void *);
44         char *(*item_choice)(void *);
45         void *item_choice_data;
46         struct list_head items;
47         int item_cnt;
48 };
49
50 /*
51  * An iterator function for menu items. callback will be called for each item
52  * in m, with m, a pointer to the item, and extra being passed to callback. If
53  * callback returns a value other than NULL, iteration stops and the value
54  * return by callback is returned from menu_items_iter.  This allows it to be
55  * used for search type operations. It is also safe for callback to remove the
56  * item from the list of items.
57  */
58 static inline void *menu_items_iter(struct menu *m,
59                 void *(*callback)(struct menu *, struct menu_item *, void *),
60                 void *extra)
61 {
62         struct list_head *pos, *n;
63         struct menu_item *item;
64         void *ret;
65
66         list_for_each_safe(pos, n, &m->items) {
67                 item = list_entry(pos, struct menu_item, list);
68
69                 ret = callback(m, item, extra);
70
71                 if (ret)
72                         return ret;
73         }
74
75         return NULL;
76 }
77
78 /*
79  * Print a menu_item. If the consumer provided an item_data_print function
80  * when creating the menu, call it with a pointer to the item's private data.
81  * Otherwise, print the key of the item.
82  */
83 static inline void *menu_item_print(struct menu *m,
84                                 struct menu_item *item,
85                                 void *extra)
86 {
87         if (!m->item_data_print) {
88                 puts(item->key);
89                 putc('\n');
90         } else {
91                 m->item_data_print(item->data);
92         }
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                 puts(m->title);
121                 putc('\n');
122         }
123         if (m->display_statusline)
124                 m->display_statusline(m);
125
126         menu_items_iter(m, menu_item_print, NULL);
127 }
128
129 /*
130  * Check if an item's key matches a provided string, pointed to by extra. If
131  * extra is NULL, an item with a NULL key will match. Otherwise, the item's
132  * key has to match according to strcmp.
133  *
134  * This is called via menu_items_iter, so it returns a pointer to the item if
135  * the key matches, and returns NULL otherwise.
136  */
137 static inline void *menu_item_key_match(struct menu *m,
138                         struct menu_item *item, void *extra)
139 {
140         char *item_key = extra;
141
142         if (!item_key || !item->key) {
143                 if (item_key == item->key)
144                         return item;
145
146                 return NULL;
147         }
148
149         if (strcmp(item->key, item_key) == 0)
150                 return item;
151
152         return NULL;
153 }
154
155 /*
156  * Find the first item with a key matching item_key, if any exists.
157  */
158 static inline struct menu_item *menu_item_by_key(struct menu *m,
159                                                         char *item_key)
160 {
161         return menu_items_iter(m, menu_item_key_match, item_key);
162 }
163
164 /*
165  * Set *choice to point to the default item's data, if any default item was
166  * set, and returns 1. If no default item was set, returns -ENOENT.
167  */
168 int menu_default_choice(struct menu *m, void **choice)
169 {
170         if (m->default_item) {
171                 *choice = m->default_item->data;
172                 return 1;
173         }
174
175         return -ENOENT;
176 }
177
178 /*
179  * Displays the menu and asks the user to choose an item. *choice will point
180  * to the private data of the item the user chooses. The user makes a choice
181  * by inputting a string matching the key of an item. Invalid choices will
182  * cause the user to be prompted again, repeatedly, until the user makes a
183  * valid choice. The user can exit the menu without making a choice via ^c.
184  *
185  * Returns 1 if the user made a choice, or -EINTR if they bail via ^c.
186  */
187 static inline int menu_interactive_choice(struct menu *m, void **choice)
188 {
189         char cbuf[CONFIG_SYS_CBSIZE];
190         struct menu_item *choice_item = NULL;
191         int readret;
192
193         while (!choice_item) {
194                 cbuf[0] = '\0';
195
196                 menu_display(m);
197
198                 if (!m->item_choice) {
199                         readret = cli_readline_into_buffer("Enter choice: ",
200                                                            cbuf, m->timeout);
201
202                         if (readret >= 0) {
203                                 choice_item = menu_item_by_key(m, cbuf);
204                                 if (!choice_item)
205                                         printf("%s not found\n", cbuf);
206                         } else if (readret == -1)  {
207                                 printf("<INTERRUPT>\n");
208                                 return -EINTR;
209                         } else {
210                                 return menu_default_choice(m, choice);
211                         }
212                 } else {
213                         char *key = m->item_choice(m->item_choice_data);
214
215                         if (key)
216                                 choice_item = menu_item_by_key(m, key);
217                 }
218
219                 if (!choice_item)
220                         m->timeout = 0;
221         }
222
223         *choice = choice_item->data;
224
225         return 1;
226 }
227
228 /*
229  * menu_default_set() - Sets the default choice for the menu. This is safe to
230  * call more than once on a menu.
231  *
232  * m - Points to a menu created by menu_create().
233  *
234  * item_key - Points to a string that, when compared using strcmp, matches the
235  * key for an existing item in the menu.
236  *
237  * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a
238  * key matching item_key is found.
239  */
240 int menu_default_set(struct menu *m, char *item_key)
241 {
242         struct menu_item *item;
243
244         if (!m)
245                 return -EINVAL;
246
247         item = menu_item_by_key(m, item_key);
248
249         if (!item)
250                 return -ENOENT;
251
252         m->default_item = item;
253
254         return 1;
255 }
256
257 /*
258  * menu_get_choice() - Returns the user's selected menu entry, or the default
259  * if the menu is set to not prompt or the timeout expires. This is safe to
260  * call more than once.
261  *
262  * m - Points to a menu created by menu_create().
263  *
264  * choice - Points to a location that will store a pointer to the selected
265  * menu item. If no item is selected or there is an error, no value will be
266  * written at the location it points to.
267  *
268  * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no
269  * default has been set and the menu is set to not prompt or the timeout
270  * expires, or -EINTR if the user exits the menu via ^c.
271  */
272 int menu_get_choice(struct menu *m, void **choice)
273 {
274         if (!m || !choice)
275                 return -EINVAL;
276
277         if (!m->item_cnt)
278                 return -ENOENT;
279
280         if (!m->prompt)
281                 return menu_default_choice(m, choice);
282
283         return menu_interactive_choice(m, choice);
284 }
285
286 /*
287  * menu_item_add() - Adds or replaces a menu item. Note that this replaces the
288  * data of an item if it already exists, but doesn't change the order of the
289  * item.
290  *
291  * m - Points to a menu created by menu_create().
292  *
293  * item_key - Points to a string that will uniquely identify the item.  The
294  * string will be copied to internal storage, and is safe to discard after
295  * passing to menu_item_add.
296  *
297  * item_data - An opaque pointer associated with an item. It is never
298  * dereferenced internally, but will be passed to the item_data_print, and
299  * will be returned from menu_get_choice if the menu item is selected.
300  *
301  * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is
302  * insufficient memory to add the menu item.
303  */
304 int menu_item_add(struct menu *m, char *item_key, void *item_data)
305 {
306         struct menu_item *item;
307
308         if (!m)
309                 return -EINVAL;
310
311         item = menu_item_by_key(m, item_key);
312
313         if (item) {
314                 item->data = item_data;
315                 return 1;
316         }
317
318         item = malloc(sizeof *item);
319         if (!item)
320                 return -ENOMEM;
321
322         item->key = strdup(item_key);
323
324         if (!item->key) {
325                 free(item);
326                 return -ENOMEM;
327         }
328
329         item->data = item_data;
330
331         list_add_tail(&item->list, &m->items);
332         m->item_cnt++;
333
334         return 1;
335 }
336
337 /*
338  * menu_create() - Creates a menu handle with default settings
339  *
340  * title - If not NULL, points to a string that will be displayed before the
341  * list of menu items. It will be copied to internal storage, and is safe to
342  * discard after passing to menu_create().
343  *
344  * timeout - A delay in seconds to wait for user input. If 0, timeout is
345  * disabled, and the default choice will be returned unless prompt is 1.
346  *
347  * prompt - If 0, don't ask for user input unless there is an interrupted
348  * timeout. If 1, the user will be prompted for input regardless of the value
349  * of timeout.
350  *
351  * display_statusline - If not NULL, will be called to show a statusline when
352  * the menu is displayed.
353  *
354  * item_data_print - If not NULL, will be called for each item when the menu
355  * is displayed, with the pointer to the item's data passed as the argument.
356  * If NULL, each item's key will be printed instead.  Since an item's key is
357  * what must be entered to select an item, the item_data_print function should
358  * make it obvious what the key for each entry is.
359  *
360  * item_choice - If not NULL, will be called when asking the user to choose an
361  * item. Returns a key string corresponding to the chosen item or NULL if
362  * no item has been selected.
363  *
364  * item_choice_data - Will be passed as the argument to the item_choice function
365  *
366  * Returns a pointer to the menu if successful, or NULL if there is
367  * insufficient memory available to create the menu.
368  */
369 struct menu *menu_create(char *title, int timeout, int prompt,
370                                 void (*display_statusline)(struct menu *),
371                                 void (*item_data_print)(void *),
372                                 char *(*item_choice)(void *),
373                                 void *item_choice_data)
374 {
375         struct menu *m;
376
377         m = malloc(sizeof *m);
378
379         if (!m)
380                 return NULL;
381
382         m->default_item = NULL;
383         m->prompt = prompt;
384         m->timeout = timeout;
385         m->display_statusline = display_statusline;
386         m->item_data_print = item_data_print;
387         m->item_choice = item_choice;
388         m->item_choice_data = item_choice_data;
389         m->item_cnt = 0;
390
391         if (title) {
392                 m->title = strdup(title);
393                 if (!m->title) {
394                         free(m);
395                         return NULL;
396                 }
397         } else
398                 m->title = NULL;
399
400
401         INIT_LIST_HEAD(&m->items);
402
403         return m;
404 }
405
406 /*
407  * menu_destroy() - frees the memory used by a menu and its items.
408  *
409  * m - Points to a menu created by menu_create().
410  *
411  * Returns 1 if successful, or -EINVAL if m is NULL.
412  */
413 int menu_destroy(struct menu *m)
414 {
415         if (!m)
416                 return -EINVAL;
417
418         menu_items_iter(m, menu_item_destroy, NULL);
419
420         if (m->title)
421                 free(m->title);
422
423         free(m);
424
425         return 1;
426 }
427
428 void bootmenu_autoboot_loop(struct bootmenu_data *menu,
429                             enum bootmenu_key *key, int *esc)
430 {
431         int i, c;
432
433         while (menu->delay > 0) {
434                 printf(ANSI_CURSOR_POSITION, menu->count + 5, 3);
435                 printf("Hit any key to stop autoboot: %d ", menu->delay);
436                 for (i = 0; i < 100; ++i) {
437                         if (!tstc()) {
438                                 schedule();
439                                 mdelay(10);
440                                 continue;
441                         }
442
443                         menu->delay = -1;
444                         c = getchar();
445
446                         switch (c) {
447                         case '\e':
448                                 *esc = 1;
449                                 *key = KEY_NONE;
450                                 break;
451                         case '\r':
452                                 *key = KEY_SELECT;
453                                 break;
454                         case 0x3: /* ^C */
455                                 *key = KEY_QUIT;
456                                 break;
457                         default:
458                                 *key = KEY_NONE;
459                                 break;
460                         }
461
462                         break;
463                 }
464
465                 if (menu->delay < 0)
466                         break;
467
468                 --menu->delay;
469         }
470
471         printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE, menu->count + 5, 1);
472
473         if (menu->delay == 0)
474                 *key = KEY_SELECT;
475 }
476
477 void bootmenu_loop(struct bootmenu_data *menu,
478                    enum bootmenu_key *key, int *esc)
479 {
480         int c;
481
482         if (*esc == 1) {
483                 if (tstc()) {
484                         c = getchar();
485                 } else {
486                         schedule();
487                         mdelay(10);
488                         if (tstc())
489                                 c = getchar();
490                         else
491                                 c = '\e';
492                 }
493         } else {
494                 while (!tstc()) {
495                         schedule();
496                         mdelay(10);
497                 }
498                 c = getchar();
499         }
500
501         switch (*esc) {
502         case 0:
503                 /* First char of ANSI escape sequence '\e' */
504                 if (c == '\e') {
505                         *esc = 1;
506                         *key = KEY_NONE;
507                 }
508                 break;
509         case 1:
510                 /* Second char of ANSI '[' */
511                 if (c == '[') {
512                         *esc = 2;
513                         *key = KEY_NONE;
514                 } else {
515                 /* Alone ESC key was pressed */
516                         *key = KEY_QUIT;
517                         *esc = (c == '\e') ? 1 : 0;
518                 }
519                 break;
520         case 2:
521         case 3:
522                 /* Third char of ANSI (number '1') - optional */
523                 if (*esc == 2 && c == '1') {
524                         *esc = 3;
525                         *key = KEY_NONE;
526                         break;
527                 }
528
529                 *esc = 0;
530
531                 /* ANSI 'A' - key up was pressed */
532                 if (c == 'A')
533                         *key = KEY_UP;
534                 /* ANSI 'B' - key down was pressed */
535                 else if (c == 'B')
536                         *key = KEY_DOWN;
537                 /* other key was pressed */
538                 else
539                         *key = KEY_NONE;
540
541                 break;
542         }
543
544         /* enter key was pressed */
545         if (c == '\r')
546                 *key = KEY_SELECT;
547
548         /* ^C was pressed */
549         if (c == 0x3)
550                 *key = KEY_QUIT;
551
552         if (c == '+')
553                 *key = KEY_PLUS;
554
555         if (c == '-')
556                 *key = KEY_MINUS;
557
558         if (c == ' ')
559                 *key = KEY_SPACE;
560 }