1 // SPDX-License-Identifier: GPL-2.0+
3 * Provide a menu of available bootflows and related options
5 * Copyright 2022 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
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 struct bootmeth_uc_plat
*ucp
;
81 char str
[2], *label
, *key
;
85 if (bflow
->state
!= BOOTFLOWST_READY
)
88 /* No media to show for BOOTMETHF_GLOBAL bootmeths */
89 ucp
= dev_get_uclass_plat(bflow
->method
);
90 if (ucp
->flags
& BOOTMETHF_GLOBAL
)
93 *str
= i
< 10 ? '0' + i
: 'A' + i
- 10;
97 return log_msg_ret("key", -ENOMEM
);
98 label
= strdup(dev_get_parent(bflow
->dev
)->name
);
101 return log_msg_ret("nam", -ENOMEM
);
104 add_gap
= last_bootdev
!= bflow
->dev
;
105 last_bootdev
= bflow
->dev
;
107 ret
= expo_str(exp
, "prompt", STR_POINTER
, ">");
108 ret
|= scene_txt_str(scn
, "label", ITEM_LABEL
+ i
,
109 STR_LABEL
+ i
, label
, NULL
);
110 ret
|= scene_txt_str(scn
, "desc", ITEM_DESC
+ i
, STR_DESC
+ i
,
111 bflow
->os_name
? bflow
->os_name
:
113 ret
|= scene_txt_str(scn
, "key", ITEM_KEY
+ i
, STR_KEY
+ i
, key
,
117 preview_id
= ITEM_PREVIEW
+ i
;
118 ret
|= scene_img(scn
, "preview", preview_id
,
121 ret
|= scene_menuitem(scn
, OBJ_MENU
, "item", ITEM
+ i
,
122 ITEM_KEY
+ i
, ITEM_LABEL
+ i
,
123 ITEM_DESC
+ i
, preview_id
,
124 add_gap
? SCENEMIF_GAP_BEFORE
: 0,
128 return log_msg_ret("itm", -EINVAL
);
129 priv
->num_bootflows
++;
132 ret
= scene_arrange(scn
);
134 return log_msg_ret("arr", ret
);
141 int bootflow_menu_apply_theme(struct expo
*exp
, ofnode node
)
143 struct menu_priv
*priv
= exp
->priv
;
148 log_debug("Applying theme %s\n", ofnode_get_name(node
));
149 scn
= expo_lookup_scene_id(exp
, MAIN
);
151 return log_msg_ret("scn", -ENOENT
);
153 /* Avoid error-checking optional items */
154 if (!ofnode_read_u32(node
, "font-size", &font_size
)) {
157 log_debug("font size %d\n", font_size
);
158 scene_txt_set_font(scn
, OBJ_PROMPT
, NULL
, font_size
);
159 scene_txt_set_font(scn
, OBJ_POINTER
, NULL
, font_size
);
160 for (i
= 0; i
< priv
->num_bootflows
; i
++) {
161 ret
= scene_txt_set_font(scn
, ITEM_DESC
+ i
, NULL
,
164 return log_msg_ret("des", ret
);
165 scene_txt_set_font(scn
, ITEM_KEY
+ i
, NULL
, font_size
);
166 scene_txt_set_font(scn
, ITEM_LABEL
+ i
, NULL
,
171 ret
= scene_arrange(scn
);
173 return log_msg_ret("arr", ret
);
178 int bootflow_menu_run(struct bootstd_priv
*std
, bool text_mode
,
179 struct bootflow
**bflowp
)
181 struct cli_ch_state s_cch
, *cch
= &s_cch
;
182 struct bootflow
*sel_bflow
;
194 ret
= bootflow_menu_new(&exp
);
196 return log_msg_ret("exp", ret
);
198 if (ofnode_valid(std
->theme
)) {
199 ret
= bootflow_menu_apply_theme(exp
, std
->theme
);
201 return log_msg_ret("thm", ret
);
204 /* For now we only support a video console */
205 ret
= uclass_first_device_err(UCLASS_VIDEO
, &dev
);
207 return log_msg_ret("vid", ret
);
208 ret
= expo_set_display(exp
, dev
);
210 return log_msg_ret("dis", ret
);
212 ret
= expo_set_scene_id(exp
, MAIN
);
214 return log_msg_ret("scn", ret
);
217 expo_set_text_mode(exp
, text_mode
);
221 struct expo_action act
;
224 ret
= expo_render(exp
);
228 ichar
= cli_ch_process(cch
, 0);
230 while (!ichar
&& !tstc()) {
233 ichar
= cli_ch_process(cch
, -ETIMEDOUT
);
237 ichar
= cli_ch_process(cch
, ichar
);
243 key
= bootmenu_conv_key(ichar
);
244 if (key
== BKEY_NONE
)
250 ret
= expo_send_key(exp
, key
);
254 ret
= expo_action_get(exp
, &act
);
258 sel_id
= act
.select
.id
;
271 return log_msg_ret("end", ret
);
274 struct bootflow
*bflow
;
277 for (ret
= bootflow_first_glob(&bflow
), i
= 0; !ret
&& i
< 36;
278 ret
= bootflow_next_glob(&bflow
), i
++) {
279 if (i
== sel_id
- ITEM
) {