menu.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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. #include <ansi.h>
  7. #include <common.h>
  8. #include <cli.h>
  9. #include <malloc.h>
  10. #include <errno.h>
  11. #include <linux/delay.h>
  12. #include <linux/list.h>
  13. #include <watchdog.h>
  14. #include "menu.h"
  15. #define ansi 1
  16. /*
  17. * Internally, each item in a menu is represented by a struct menu_item.
  18. *
  19. * These items will be alloc'd and initialized by menu_item_add and destroyed
  20. * by menu_item_destroy, and the consumer of the interface never sees that
  21. * this struct is used at all.
  22. */
  23. struct menu_item {
  24. char *key;
  25. void *data;
  26. struct list_head list;
  27. };
  28. /*
  29. * The menu is composed of a list of items along with settings and callbacks
  30. * provided by the user. An incomplete definition of this struct is available
  31. * in menu.h, but the full definition is here to prevent consumers from
  32. * relying on its contents.
  33. */
  34. struct menu {
  35. struct menu_item *default_item;
  36. int timeout;
  37. char *title;
  38. int prompt;
  39. void (*display_statusline)(struct menu *);
  40. void (*item_data_print)(void *);
  41. char *(*item_choice)(void *);
  42. void *item_choice_data;
  43. struct list_head items;
  44. int item_cnt;
  45. };
  46. /*
  47. * An iterator function for menu items. callback will be called for each item
  48. * in m, with m, a pointer to the item, and extra being passed to callback. If
  49. * callback returns a value other than NULL, iteration stops and the value
  50. * return by callback is returned from menu_items_iter. This allows it to be
  51. * used for search type operations. It is also safe for callback to remove the
  52. * item from the list of items.
  53. */
  54. static inline void *menu_items_iter(struct menu *m,
  55. void *(*callback)(struct menu *, struct menu_item *, void *),
  56. void *extra)
  57. {
  58. struct list_head *pos, *n;
  59. struct menu_item *item;
  60. void *ret;
  61. list_for_each_safe(pos, n, &m->items) {
  62. item = list_entry(pos, struct menu_item, list);
  63. ret = callback(m, item, extra);
  64. if (ret)
  65. return ret;
  66. }
  67. return NULL;
  68. }
  69. /*
  70. * Print a menu_item. If the consumer provided an item_data_print function
  71. * when creating the menu, call it with a pointer to the item's private data.
  72. * Otherwise, print the key of the item.
  73. */
  74. static inline void *menu_item_print(struct menu *m,
  75. struct menu_item *item,
  76. void *extra)
  77. {
  78. if (!m->item_data_print) {
  79. puts(item->key);
  80. putc('\n');
  81. } else {
  82. m->item_data_print(item->data);
  83. }
  84. return NULL;
  85. }
  86. /*
  87. * Free the memory used by a menu item. This includes the memory used by its
  88. * key.
  89. */
  90. static inline void *menu_item_destroy(struct menu *m,
  91. struct menu_item *item,
  92. void *extra)
  93. {
  94. if (item->key)
  95. free(item->key);
  96. free(item);
  97. return NULL;
  98. }
  99. /*
  100. * Display a menu so the user can make a choice of an item. First display its
  101. * title, if any, and then each item in the menu.
  102. */
  103. static inline void menu_display(struct menu *m)
  104. {
  105. if (m->title) {
  106. puts(m->title);
  107. putc('\n');
  108. }
  109. if (m->display_statusline)
  110. m->display_statusline(m);
  111. menu_items_iter(m, menu_item_print, NULL);
  112. }
  113. /*
  114. * Check if an item's key matches a provided string, pointed to by extra. If
  115. * extra is NULL, an item with a NULL key will match. Otherwise, the item's
  116. * key has to match according to strcmp.
  117. *
  118. * This is called via menu_items_iter, so it returns a pointer to the item if
  119. * the key matches, and returns NULL otherwise.
  120. */
  121. static inline void *menu_item_key_match(struct menu *m,
  122. struct menu_item *item, void *extra)
  123. {
  124. char *item_key = extra;
  125. if (!item_key || !item->key) {
  126. if (item_key == item->key)
  127. return item;
  128. return NULL;
  129. }
  130. if (strcmp(item->key, item_key) == 0)
  131. return item;
  132. return NULL;
  133. }
  134. /*
  135. * Find the first item with a key matching item_key, if any exists.
  136. */
  137. static inline struct menu_item *menu_item_by_key(struct menu *m,
  138. char *item_key)
  139. {
  140. return menu_items_iter(m, menu_item_key_match, item_key);
  141. }
  142. /*
  143. * Set *choice to point to the default item's data, if any default item was
  144. * set, and returns 1. If no default item was set, returns -ENOENT.
  145. */
  146. int menu_default_choice(struct menu *m, void **choice)
  147. {
  148. if (m->default_item) {
  149. *choice = m->default_item->data;
  150. return 1;
  151. }
  152. return -ENOENT;
  153. }
  154. /*
  155. * Displays the menu and asks the user to choose an item. *choice will point
  156. * to the private data of the item the user chooses. The user makes a choice
  157. * by inputting a string matching the key of an item. Invalid choices will
  158. * cause the user to be prompted again, repeatedly, until the user makes a
  159. * valid choice. The user can exit the menu without making a choice via ^c.
  160. *
  161. * Returns 1 if the user made a choice, or -EINTR if they bail via ^c.
  162. */
  163. static inline int menu_interactive_choice(struct menu *m, void **choice)
  164. {
  165. char cbuf[CONFIG_SYS_CBSIZE];
  166. struct menu_item *choice_item = NULL;
  167. int readret;
  168. while (!choice_item) {
  169. cbuf[0] = '\0';
  170. menu_display(m);
  171. if (!m->item_choice) {
  172. readret = cli_readline_into_buffer("Enter choice: ",
  173. cbuf, m->timeout);
  174. if (readret >= 0) {
  175. choice_item = menu_item_by_key(m, cbuf);
  176. if (!choice_item)
  177. printf("%s not found\n", cbuf);
  178. } else if (readret == -1) {
  179. printf("<INTERRUPT>\n");
  180. return -EINTR;
  181. } else {
  182. return menu_default_choice(m, choice);
  183. }
  184. } else {
  185. char *key = m->item_choice(m->item_choice_data);
  186. if (key)
  187. choice_item = menu_item_by_key(m, key);
  188. }
  189. if (!choice_item)
  190. m->timeout = 0;
  191. }
  192. *choice = choice_item->data;
  193. return 1;
  194. }
  195. /*
  196. * menu_default_set() - Sets the default choice for the menu. This is safe to
  197. * call more than once on a menu.
  198. *
  199. * m - Points to a menu created by menu_create().
  200. *
  201. * item_key - Points to a string that, when compared using strcmp, matches the
  202. * key for an existing item in the menu.
  203. *
  204. * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a
  205. * key matching item_key is found.
  206. */
  207. int menu_default_set(struct menu *m, char *item_key)
  208. {
  209. struct menu_item *item;
  210. if (!m)
  211. return -EINVAL;
  212. item = menu_item_by_key(m, item_key);
  213. if (!item)
  214. return -ENOENT;
  215. m->default_item = item;
  216. return 1;
  217. }
  218. /*
  219. * menu_get_choice() - Returns the user's selected menu entry, or the default
  220. * if the menu is set to not prompt or the timeout expires. This is safe to
  221. * call more than once.
  222. *
  223. * m - Points to a menu created by menu_create().
  224. *
  225. * choice - Points to a location that will store a pointer to the selected
  226. * menu item. If no item is selected or there is an error, no value will be
  227. * written at the location it points to.
  228. *
  229. * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no
  230. * default has been set and the menu is set to not prompt or the timeout
  231. * expires, or -EINTR if the user exits the menu via ^c.
  232. */
  233. int menu_get_choice(struct menu *m, void **choice)
  234. {
  235. if (!m || !choice)
  236. return -EINVAL;
  237. if (!m->item_cnt)
  238. return -ENOENT;
  239. if (!m->prompt)
  240. return menu_default_choice(m, choice);
  241. return menu_interactive_choice(m, choice);
  242. }
  243. /*
  244. * menu_item_add() - Adds or replaces a menu item. Note that this replaces the
  245. * data of an item if it already exists, but doesn't change the order of the
  246. * item.
  247. *
  248. * m - Points to a menu created by menu_create().
  249. *
  250. * item_key - Points to a string that will uniquely identify the item. The
  251. * string will be copied to internal storage, and is safe to discard after
  252. * passing to menu_item_add.
  253. *
  254. * item_data - An opaque pointer associated with an item. It is never
  255. * dereferenced internally, but will be passed to the item_data_print, and
  256. * will be returned from menu_get_choice if the menu item is selected.
  257. *
  258. * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is
  259. * insufficient memory to add the menu item.
  260. */
  261. int menu_item_add(struct menu *m, char *item_key, void *item_data)
  262. {
  263. struct menu_item *item;
  264. if (!m)
  265. return -EINVAL;
  266. item = menu_item_by_key(m, item_key);
  267. if (item) {
  268. item->data = item_data;
  269. return 1;
  270. }
  271. item = malloc(sizeof *item);
  272. if (!item)
  273. return -ENOMEM;
  274. item->key = strdup(item_key);
  275. if (!item->key) {
  276. free(item);
  277. return -ENOMEM;
  278. }
  279. item->data = item_data;
  280. list_add_tail(&item->list, &m->items);
  281. m->item_cnt++;
  282. return 1;
  283. }
  284. /*
  285. * menu_create() - Creates a menu handle with default settings
  286. *
  287. * title - If not NULL, points to a string that will be displayed before the
  288. * list of menu items. It will be copied to internal storage, and is safe to
  289. * discard after passing to menu_create().
  290. *
  291. * timeout - A delay in seconds to wait for user input. If 0, timeout is
  292. * disabled, and the default choice will be returned unless prompt is 1.
  293. *
  294. * prompt - If 0, don't ask for user input unless there is an interrupted
  295. * timeout. If 1, the user will be prompted for input regardless of the value
  296. * of timeout.
  297. *
  298. * display_statusline - If not NULL, will be called to show a statusline when
  299. * the menu is displayed.
  300. *
  301. * item_data_print - If not NULL, will be called for each item when the menu
  302. * is displayed, with the pointer to the item's data passed as the argument.
  303. * If NULL, each item's key will be printed instead. Since an item's key is
  304. * what must be entered to select an item, the item_data_print function should
  305. * make it obvious what the key for each entry is.
  306. *
  307. * item_choice - If not NULL, will be called when asking the user to choose an
  308. * item. Returns a key string corresponding to the chosen item or NULL if
  309. * no item has been selected.
  310. *
  311. * item_choice_data - Will be passed as the argument to the item_choice function
  312. *
  313. * Returns a pointer to the menu if successful, or NULL if there is
  314. * insufficient memory available to create the menu.
  315. */
  316. struct menu *menu_create(char *title, int timeout, int prompt,
  317. void (*display_statusline)(struct menu *),
  318. void (*item_data_print)(void *),
  319. char *(*item_choice)(void *),
  320. void *item_choice_data)
  321. {
  322. struct menu *m;
  323. m = malloc(sizeof *m);
  324. if (!m)
  325. return NULL;
  326. m->default_item = NULL;
  327. m->prompt = prompt;
  328. m->timeout = timeout;
  329. m->display_statusline = display_statusline;
  330. m->item_data_print = item_data_print;
  331. m->item_choice = item_choice;
  332. m->item_choice_data = item_choice_data;
  333. m->item_cnt = 0;
  334. if (title) {
  335. m->title = strdup(title);
  336. if (!m->title) {
  337. free(m);
  338. return NULL;
  339. }
  340. } else
  341. m->title = NULL;
  342. INIT_LIST_HEAD(&m->items);
  343. return m;
  344. }
  345. /*
  346. * menu_destroy() - frees the memory used by a menu and its items.
  347. *
  348. * m - Points to a menu created by menu_create().
  349. *
  350. * Returns 1 if successful, or -EINVAL if m is NULL.
  351. */
  352. int menu_destroy(struct menu *m)
  353. {
  354. if (!m)
  355. return -EINVAL;
  356. menu_items_iter(m, menu_item_destroy, NULL);
  357. if (m->title)
  358. free(m->title);
  359. free(m);
  360. return 1;
  361. }
  362. enum bootmenu_key bootmenu_autoboot_loop(struct bootmenu_data *menu,
  363. struct cli_ch_state *cch)
  364. {
  365. enum bootmenu_key key = BKEY_NONE;
  366. int i, c;
  367. while (menu->delay > 0) {
  368. if (ansi)
  369. printf(ANSI_CURSOR_POSITION, menu->count + 5, 3);
  370. printf("Hit any key to stop autoboot: %d ", menu->delay);
  371. for (i = 0; i < 100; ++i) {
  372. int ichar;
  373. if (!tstc()) {
  374. schedule();
  375. mdelay(10);
  376. continue;
  377. }
  378. menu->delay = -1;
  379. c = getchar();
  380. ichar = cli_ch_process(cch, c);
  381. switch (ichar) {
  382. case '\0':
  383. key = BKEY_NONE;
  384. break;
  385. case '\n':
  386. key = BKEY_SELECT;
  387. break;
  388. case 0x3: /* ^C */
  389. key = BKEY_QUIT;
  390. break;
  391. default:
  392. key = BKEY_NONE;
  393. break;
  394. }
  395. break;
  396. }
  397. if (menu->delay < 0)
  398. break;
  399. --menu->delay;
  400. }
  401. if (ansi)
  402. printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE, menu->count + 5, 1);
  403. if (menu->delay == 0)
  404. key = BKEY_SELECT;
  405. return key;
  406. }
  407. enum bootmenu_key bootmenu_conv_key(int ichar)
  408. {
  409. enum bootmenu_key key;
  410. switch (ichar) {
  411. case '\n':
  412. /* enter key was pressed */
  413. key = BKEY_SELECT;
  414. break;
  415. case CTL_CH('c'):
  416. case '\e':
  417. /* ^C was pressed */
  418. key = BKEY_QUIT;
  419. break;
  420. case CTL_CH('p'):
  421. key = BKEY_UP;
  422. break;
  423. case CTL_CH('n'):
  424. key = BKEY_DOWN;
  425. break;
  426. case CTL_CH('s'):
  427. key = BKEY_SAVE;
  428. break;
  429. case '+':
  430. key = BKEY_PLUS;
  431. break;
  432. case '-':
  433. key = BKEY_MINUS;
  434. break;
  435. case ' ':
  436. key = BKEY_SPACE;
  437. break;
  438. default:
  439. key = BKEY_NONE;
  440. break;
  441. }
  442. return key;
  443. }
  444. enum bootmenu_key bootmenu_loop(struct bootmenu_data *menu,
  445. struct cli_ch_state *cch)
  446. {
  447. enum bootmenu_key key;
  448. int c;
  449. c = cli_ch_process(cch, 0);
  450. if (!c) {
  451. while (!c && !tstc()) {
  452. schedule();
  453. mdelay(10);
  454. c = cli_ch_process(cch, -ETIMEDOUT);
  455. }
  456. if (!c) {
  457. c = getchar();
  458. c = cli_ch_process(cch, c);
  459. }
  460. }
  461. key = bootmenu_conv_key(c);
  462. return key;
  463. }