1 // SPDX-License-Identifier: GPL-2.0+
3 * Provide a menu of available bootflows and related options
5 * Copyright 2022 Google LLC
9 #define LOG_CATEGORY UCLASS_BOOTSTD
19 #include <video_console.h>
21 #include <linux/delay.h>
22 #include "bootflow_internal.h"
25 * struct menu_priv - information about the menu
27 * @num_bootflows: Number of bootflows in the menu
33 int bootflow_menu_new(struct expo **expp)
35 struct udevice *last_bootdev;
36 struct scene_obj_menu *menu;
37 struct menu_priv *priv;
38 struct bootflow *bflow;
44 priv = calloc(1, sizeof(*priv));
46 return log_msg_ret("prv", -ENOMEM);
48 ret = expo_new("bootflows", priv, &exp);
50 return log_msg_ret("exp", ret);
52 ret = scene_new(exp, "main", MAIN, &scn);
54 return log_msg_ret("scn", ret);
56 ret |= scene_txt_str(scn, "prompt", OBJ_PROMPT, STR_PROMPT,
57 "UP and DOWN to choose, ENTER to select", NULL);
59 ret = scene_menu(scn, "main", OBJ_MENU, &menu);
60 ret |= scene_obj_set_pos(scn, OBJ_MENU, MARGIN_LEFT, 100);
61 ret |= scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
62 "U-Boot - Boot Menu", NULL);
63 ret |= scene_menu_set_title(scn, OBJ_MENU, OBJ_PROMPT);
65 logo = video_get_u_boot_logo();
67 ret |= scene_img(scn, "ulogo", OBJ_U_BOOT_LOGO, logo, NULL);
68 ret |= scene_obj_set_pos(scn, OBJ_U_BOOT_LOGO, -4, 4);
71 ret |= scene_txt_str(scn, "cur_item", OBJ_POINTER, STR_POINTER, ">",
73 ret |= scene_menu_set_pointer(scn, OBJ_MENU, OBJ_POINTER);
75 return log_msg_ret("new", -EINVAL);
78 for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36;
79 ret = bootflow_next_glob(&bflow), i++) {
80 char str[2], *label, *key;
84 if (bflow->state != BOOTFLOWST_READY)
87 *str = i < 10 ? '0' + i : 'A' + i - 10;
91 return log_msg_ret("key", -ENOMEM);
92 label = strdup(dev_get_parent(bflow->dev)->name);
95 return log_msg_ret("nam", -ENOMEM);
98 add_gap = last_bootdev != bflow->dev;
99 last_bootdev = bflow->dev;
101 ret = expo_str(exp, "prompt", STR_POINTER, ">");
102 ret |= scene_txt_str(scn, "label", ITEM_LABEL + i,
103 STR_LABEL + i, label, NULL);
104 ret |= scene_txt_str(scn, "desc", ITEM_DESC + i, STR_DESC + i,
105 bflow->os_name ? bflow->os_name :
107 ret |= scene_txt_str(scn, "key", ITEM_KEY + i, STR_KEY + i, key,
111 preview_id = ITEM_PREVIEW + i;
112 ret |= scene_img(scn, "preview", preview_id,
115 ret |= scene_menuitem(scn, OBJ_MENU, "item", ITEM + i,
116 ITEM_KEY + i, ITEM_LABEL + i,
117 ITEM_DESC + i, preview_id,
118 add_gap ? SCENEMIF_GAP_BEFORE : 0,
122 return log_msg_ret("itm", -EINVAL);
123 priv->num_bootflows++;
126 ret = scene_arrange(scn);
128 return log_msg_ret("arr", ret);
135 int bootflow_menu_apply_theme(struct expo *exp, ofnode node)
137 struct menu_priv *priv = exp->priv;
142 log_debug("Applying theme %s\n", ofnode_get_name(node));
143 scn = expo_lookup_scene_id(exp, MAIN);
145 return log_msg_ret("scn", -ENOENT);
147 /* Avoid error-checking optional items */
148 if (!ofnode_read_u32(node, "font-size", &font_size)) {
151 log_debug("font size %d\n", font_size);
152 scene_txt_set_font(scn, OBJ_PROMPT, NULL, font_size);
153 scene_txt_set_font(scn, OBJ_POINTER, NULL, font_size);
154 for (i = 0; i < priv->num_bootflows; i++) {
155 ret = scene_txt_set_font(scn, ITEM_DESC + i, NULL,
158 return log_msg_ret("des", ret);
159 scene_txt_set_font(scn, ITEM_KEY + i, NULL, font_size);
160 scene_txt_set_font(scn, ITEM_LABEL + i, NULL,
165 ret = scene_arrange(scn);
167 return log_msg_ret("arr", ret);
172 int bootflow_menu_run(struct bootstd_priv *std, bool text_mode,
173 struct bootflow **bflowp)
175 struct cli_ch_state s_cch, *cch = &s_cch;
176 struct bootflow *sel_bflow;
188 ret = bootflow_menu_new(&exp);
190 return log_msg_ret("exp", ret);
192 if (ofnode_valid(std->theme)) {
193 ret = bootflow_menu_apply_theme(exp, std->theme);
195 return log_msg_ret("thm", ret);
198 /* For now we only support a video console */
199 ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
201 return log_msg_ret("vid", ret);
202 ret = expo_set_display(exp, dev);
204 return log_msg_ret("dis", ret);
206 ret = expo_set_scene_id(exp, MAIN);
208 return log_msg_ret("scn", ret);
211 expo_set_text_mode(exp, text_mode);
215 struct expo_action act;
218 ret = expo_render(exp);
222 ichar = cli_ch_process(cch, 0);
224 while (!ichar && !tstc()) {
227 ichar = cli_ch_process(cch, -ETIMEDOUT);
231 ichar = cli_ch_process(cch, ichar);
237 key = bootmenu_conv_key(ichar);
238 if (key == BKEY_NONE)
244 ret = expo_send_key(exp, key);
248 ret = expo_action_get(exp, &act);
252 sel_id = act.select.id;
265 return log_msg_ret("end", ret);
268 struct bootflow *bflow;
271 for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36;
272 ret = bootflow_next_glob(&bflow), i++) {
273 if (i == sel_id - ITEM) {