bootflow_menu.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Provide a menu of available bootflows and related options
  4. *
  5. * Copyright 2022 Google LLC
  6. * Written by Simon Glass <sjg@chromium.org>
  7. */
  8. #define LOG_CATEGORY UCLASS_BOOTSTD
  9. #include <common.h>
  10. #include <bootflow.h>
  11. #include <bootstd.h>
  12. #include <cli.h>
  13. #include <dm.h>
  14. #include <expo.h>
  15. #include <malloc.h>
  16. #include <menu.h>
  17. #include <video_console.h>
  18. #include <watchdog.h>
  19. #include <linux/delay.h>
  20. #include "bootflow_internal.h"
  21. /**
  22. * struct menu_priv - information about the menu
  23. *
  24. * @num_bootflows: Number of bootflows in the menu
  25. */
  26. struct menu_priv {
  27. int num_bootflows;
  28. };
  29. int bootflow_menu_new(struct expo **expp)
  30. {
  31. struct udevice *last_bootdev;
  32. struct scene_obj_menu *menu;
  33. struct menu_priv *priv;
  34. struct bootflow *bflow;
  35. struct scene *scn;
  36. struct expo *exp;
  37. void *logo;
  38. int ret, i;
  39. priv = calloc(1, sizeof(*priv));
  40. if (!priv)
  41. return log_msg_ret("prv", -ENOMEM);
  42. ret = expo_new("bootflows", priv, &exp);
  43. if (ret)
  44. return log_msg_ret("exp", ret);
  45. ret = scene_new(exp, "main", MAIN, &scn);
  46. if (ret < 0)
  47. return log_msg_ret("scn", ret);
  48. ret |= scene_txt_str(scn, "prompt", OBJ_PROMPT, STR_PROMPT,
  49. "UP and DOWN to choose, ENTER to select", NULL);
  50. ret = scene_menu(scn, "main", OBJ_MENU, &menu);
  51. ret |= scene_obj_set_pos(scn, OBJ_MENU, MARGIN_LEFT, 100);
  52. ret |= scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
  53. "U-Boot - Boot Menu", NULL);
  54. ret |= scene_menu_set_title(scn, OBJ_MENU, OBJ_PROMPT);
  55. logo = video_get_u_boot_logo();
  56. if (logo) {
  57. ret |= scene_img(scn, "ulogo", OBJ_U_BOOT_LOGO, logo, NULL);
  58. ret |= scene_obj_set_pos(scn, OBJ_U_BOOT_LOGO, -4, 4);
  59. }
  60. ret |= scene_txt_str(scn, "cur_item", OBJ_POINTER, STR_POINTER, ">",
  61. NULL);
  62. ret |= scene_menu_set_pointer(scn, OBJ_MENU, OBJ_POINTER);
  63. if (ret < 0)
  64. return log_msg_ret("new", -EINVAL);
  65. last_bootdev = NULL;
  66. for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36;
  67. ret = bootflow_next_glob(&bflow), i++) {
  68. char str[2], *label, *key;
  69. uint preview_id;
  70. bool add_gap;
  71. if (bflow->state != BOOTFLOWST_READY)
  72. continue;
  73. *str = i < 10 ? '0' + i : 'A' + i - 10;
  74. str[1] = '\0';
  75. key = strdup(str);
  76. if (!key)
  77. return log_msg_ret("key", -ENOMEM);
  78. label = strdup(dev_get_parent(bflow->dev)->name);
  79. if (!label) {
  80. free(key);
  81. return log_msg_ret("nam", -ENOMEM);
  82. }
  83. add_gap = last_bootdev != bflow->dev;
  84. last_bootdev = bflow->dev;
  85. ret = expo_str(exp, "prompt", STR_POINTER, ">");
  86. ret |= scene_txt_str(scn, "label", ITEM_LABEL + i,
  87. STR_LABEL + i, label, NULL);
  88. ret |= scene_txt_str(scn, "desc", ITEM_DESC + i, STR_DESC + i,
  89. bflow->os_name ? bflow->os_name :
  90. bflow->name, NULL);
  91. ret |= scene_txt_str(scn, "key", ITEM_KEY + i, STR_KEY + i, key,
  92. NULL);
  93. preview_id = 0;
  94. if (bflow->logo) {
  95. preview_id = ITEM_PREVIEW + i;
  96. ret |= scene_img(scn, "preview", preview_id,
  97. bflow->logo, NULL);
  98. }
  99. ret |= scene_menuitem(scn, OBJ_MENU, "item", ITEM + i,
  100. ITEM_KEY + i, ITEM_LABEL + i,
  101. ITEM_DESC + i, preview_id,
  102. add_gap ? SCENEMIF_GAP_BEFORE : 0,
  103. NULL);
  104. if (ret < 0)
  105. return log_msg_ret("itm", -EINVAL);
  106. ret = 0;
  107. priv->num_bootflows++;
  108. }
  109. ret = scene_arrange(scn);
  110. if (ret)
  111. return log_msg_ret("arr", ret);
  112. *expp = exp;
  113. return 0;
  114. }
  115. int bootflow_menu_apply_theme(struct expo *exp, ofnode node)
  116. {
  117. struct menu_priv *priv = exp->priv;
  118. struct scene *scn;
  119. u32 font_size;
  120. int ret;
  121. log_debug("Applying theme %s\n", ofnode_get_name(node));
  122. scn = expo_lookup_scene_id(exp, MAIN);
  123. if (!scn)
  124. return log_msg_ret("scn", -ENOENT);
  125. /* Avoid error-checking optional items */
  126. if (!ofnode_read_u32(node, "font-size", &font_size)) {
  127. int i;
  128. log_debug("font size %d\n", font_size);
  129. scene_txt_set_font(scn, OBJ_PROMPT, NULL, font_size);
  130. scene_txt_set_font(scn, OBJ_POINTER, NULL, font_size);
  131. for (i = 0; i < priv->num_bootflows; i++) {
  132. ret = scene_txt_set_font(scn, ITEM_DESC + i, NULL,
  133. font_size);
  134. if (ret)
  135. return log_msg_ret("des", ret);
  136. scene_txt_set_font(scn, ITEM_KEY + i, NULL, font_size);
  137. scene_txt_set_font(scn, ITEM_LABEL + i, NULL,
  138. font_size);
  139. }
  140. }
  141. ret = scene_arrange(scn);
  142. if (ret)
  143. return log_msg_ret("arr", ret);
  144. return 0;
  145. }
  146. int bootflow_menu_run(struct bootstd_priv *std, bool text_mode,
  147. struct bootflow **bflowp)
  148. {
  149. struct cli_ch_state s_cch, *cch = &s_cch;
  150. struct bootflow *sel_bflow;
  151. struct udevice *dev;
  152. struct expo *exp;
  153. uint sel_id;
  154. bool done;
  155. int ret;
  156. cli_ch_init(cch);
  157. sel_bflow = NULL;
  158. *bflowp = NULL;
  159. ret = bootflow_menu_new(&exp);
  160. if (ret)
  161. return log_msg_ret("exp", ret);
  162. if (ofnode_valid(std->theme)) {
  163. ret = bootflow_menu_apply_theme(exp, std->theme);
  164. if (ret)
  165. return log_msg_ret("thm", ret);
  166. }
  167. /* For now we only support a video console */
  168. ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
  169. if (ret)
  170. return log_msg_ret("vid", ret);
  171. ret = expo_set_display(exp, dev);
  172. if (ret)
  173. return log_msg_ret("dis", ret);
  174. ret = expo_set_scene_id(exp, MAIN);
  175. if (ret)
  176. return log_msg_ret("scn", ret);
  177. if (text_mode)
  178. expo_set_text_mode(exp, text_mode);
  179. done = false;
  180. do {
  181. struct expo_action act;
  182. int ichar, key;
  183. ret = expo_render(exp);
  184. if (ret)
  185. break;
  186. ichar = cli_ch_process(cch, 0);
  187. if (!ichar) {
  188. while (!ichar && !tstc()) {
  189. schedule();
  190. mdelay(2);
  191. ichar = cli_ch_process(cch, -ETIMEDOUT);
  192. }
  193. if (!ichar) {
  194. ichar = getchar();
  195. ichar = cli_ch_process(cch, ichar);
  196. }
  197. }
  198. key = 0;
  199. if (ichar) {
  200. key = bootmenu_conv_key(ichar);
  201. if (key == BKEY_NONE)
  202. key = ichar;
  203. }
  204. if (!key)
  205. continue;
  206. ret = expo_send_key(exp, key);
  207. if (ret)
  208. break;
  209. ret = expo_action_get(exp, &act);
  210. if (!ret) {
  211. switch (act.type) {
  212. case EXPOACT_SELECT:
  213. sel_id = act.select.id;
  214. done = true;
  215. break;
  216. case EXPOACT_QUIT:
  217. done = true;
  218. break;
  219. default:
  220. break;
  221. }
  222. }
  223. } while (!done);
  224. if (ret)
  225. return log_msg_ret("end", ret);
  226. if (sel_id) {
  227. struct bootflow *bflow;
  228. int i;
  229. for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36;
  230. ret = bootflow_next_glob(&bflow), i++) {
  231. if (i == sel_id - ITEM) {
  232. sel_bflow = bflow;
  233. break;
  234. }
  235. }
  236. }
  237. expo_destroy(exp);
  238. if (!sel_bflow)
  239. return -EAGAIN;
  240. *bflowp = sel_bflow;
  241. return 0;
  242. }