blob: ed7c0768c7b57a411ed1786d5f13b2f1d34cfaaf [file] [log] [blame]
Simon Glass0a2f6a32023-01-06 08:52:40 -06001// 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
9#define LOG_CATEGORY UCLASS_BOOTSTD
10
Simon Glass0a2f6a32023-01-06 08:52:40 -060011#include <bootflow.h>
Quentin Schulz21a6aec2024-06-12 16:58:49 +020012#include <bootmeth.h>
Simon Glass0a2f6a32023-01-06 08:52:40 -060013#include <bootstd.h>
14#include <cli.h>
15#include <dm.h>
16#include <expo.h>
17#include <malloc.h>
18#include <menu.h>
19#include <video_console.h>
20#include <watchdog.h>
21#include <linux/delay.h>
22#include "bootflow_internal.h"
23
24/**
25 * struct menu_priv - information about the menu
26 *
27 * @num_bootflows: Number of bootflows in the menu
28 */
29struct menu_priv {
30 int num_bootflows;
31};
32
33int bootflow_menu_new(struct expo **expp)
34{
35 struct udevice *last_bootdev;
36 struct scene_obj_menu *menu;
37 struct menu_priv *priv;
38 struct bootflow *bflow;
39 struct scene *scn;
40 struct expo *exp;
41 void *logo;
42 int ret, i;
43
44 priv = calloc(1, sizeof(*priv));
45 if (!priv)
46 return log_msg_ret("prv", -ENOMEM);
47
48 ret = expo_new("bootflows", priv, &exp);
49 if (ret)
50 return log_msg_ret("exp", ret);
51
52 ret = scene_new(exp, "main", MAIN, &scn);
53 if (ret < 0)
54 return log_msg_ret("scn", ret);
55
56 ret |= scene_txt_str(scn, "prompt", OBJ_PROMPT, STR_PROMPT,
57 "UP and DOWN to choose, ENTER to select", NULL);
58
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);
64
65 logo = video_get_u_boot_logo();
66 if (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);
69 }
70
71 ret |= scene_txt_str(scn, "cur_item", OBJ_POINTER, STR_POINTER, ">",
72 NULL);
73 ret |= scene_menu_set_pointer(scn, OBJ_MENU, OBJ_POINTER);
74 if (ret < 0)
75 return log_msg_ret("new", -EINVAL);
76
77 last_bootdev = NULL;
78 for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36;
79 ret = bootflow_next_glob(&bflow), i++) {
Quentin Schulz21a6aec2024-06-12 16:58:49 +020080 struct bootmeth_uc_plat *ucp;
Simon Glass0a2f6a32023-01-06 08:52:40 -060081 char str[2], *label, *key;
82 uint preview_id;
83 bool add_gap;
84
85 if (bflow->state != BOOTFLOWST_READY)
86 continue;
87
Quentin Schulz21a6aec2024-06-12 16:58:49 +020088 /* No media to show for BOOTMETHF_GLOBAL bootmeths */
89 ucp = dev_get_uclass_plat(bflow->method);
90 if (ucp->flags & BOOTMETHF_GLOBAL)
91 continue;
92
Simon Glass0a2f6a32023-01-06 08:52:40 -060093 *str = i < 10 ? '0' + i : 'A' + i - 10;
94 str[1] = '\0';
95 key = strdup(str);
96 if (!key)
97 return log_msg_ret("key", -ENOMEM);
98 label = strdup(dev_get_parent(bflow->dev)->name);
99 if (!label) {
100 free(key);
101 return log_msg_ret("nam", -ENOMEM);
102 }
103
104 add_gap = last_bootdev != bflow->dev;
105 last_bootdev = bflow->dev;
106
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 :
112 bflow->name, NULL);
113 ret |= scene_txt_str(scn, "key", ITEM_KEY + i, STR_KEY + i, key,
114 NULL);
115 preview_id = 0;
116 if (bflow->logo) {
117 preview_id = ITEM_PREVIEW + i;
118 ret |= scene_img(scn, "preview", preview_id,
119 bflow->logo, NULL);
120 }
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,
125 NULL);
126
127 if (ret < 0)
128 return log_msg_ret("itm", -EINVAL);
Simon Glass0a2f6a32023-01-06 08:52:40 -0600129 priv->num_bootflows++;
130 }
131
Simon Glassd7e32a82023-06-01 10:22:35 -0600132 ret = scene_arrange(scn);
133 if (ret)
134 return log_msg_ret("arr", ret);
135
Simon Glass0a2f6a32023-01-06 08:52:40 -0600136 *expp = exp;
137
138 return 0;
139}
140
Simon Glassd92bcc42023-01-06 08:52:42 -0600141int bootflow_menu_apply_theme(struct expo *exp, ofnode node)
142{
143 struct menu_priv *priv = exp->priv;
144 struct scene *scn;
145 u32 font_size;
146 int ret;
147
148 log_debug("Applying theme %s\n", ofnode_get_name(node));
149 scn = expo_lookup_scene_id(exp, MAIN);
150 if (!scn)
151 return log_msg_ret("scn", -ENOENT);
152
153 /* Avoid error-checking optional items */
154 if (!ofnode_read_u32(node, "font-size", &font_size)) {
155 int i;
156
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,
162 font_size);
163 if (ret)
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,
167 font_size);
168 }
169 }
170
171 ret = scene_arrange(scn);
172 if (ret)
173 return log_msg_ret("arr", ret);
174
175 return 0;
176}
177
Simon Glassd7cef832025-05-02 08:46:27 -0600178int bootflow_menu_start(struct bootstd_priv *std, bool text_mode,
179 struct expo **expp)
Simon Glass0a2f6a32023-01-06 08:52:40 -0600180{
Simon Glass0a2f6a32023-01-06 08:52:40 -0600181 struct udevice *dev;
182 struct expo *exp;
Simon Glass0a2f6a32023-01-06 08:52:40 -0600183 int ret;
184
Simon Glass0a2f6a32023-01-06 08:52:40 -0600185 ret = bootflow_menu_new(&exp);
186 if (ret)
187 return log_msg_ret("exp", ret);
188
Simon Glassd92bcc42023-01-06 08:52:42 -0600189 if (ofnode_valid(std->theme)) {
190 ret = bootflow_menu_apply_theme(exp, std->theme);
191 if (ret)
192 return log_msg_ret("thm", ret);
193 }
194
Simon Glass0a2f6a32023-01-06 08:52:40 -0600195 /* For now we only support a video console */
196 ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
197 if (ret)
198 return log_msg_ret("vid", ret);
199 ret = expo_set_display(exp, dev);
200 if (ret)
201 return log_msg_ret("dis", ret);
202
203 ret = expo_set_scene_id(exp, MAIN);
204 if (ret)
205 return log_msg_ret("scn", ret);
206
207 if (text_mode)
Simon Glassb2c40342023-06-01 10:22:37 -0600208 expo_set_text_mode(exp, text_mode);
Simon Glass0a2f6a32023-01-06 08:52:40 -0600209
Simon Glassd7cef832025-05-02 08:46:27 -0600210 *expp = exp;
Simon Glass0a2f6a32023-01-06 08:52:40 -0600211
Simon Glassd7cef832025-05-02 08:46:27 -0600212 return 0;
213}
214
215int bootflow_menu_poll(struct expo *exp, struct bootflow **bflowp)
216{
217 struct bootflow *sel_bflow;
218 struct expo_action act;
219 int ret;
Simon Glass0a2f6a32023-01-06 08:52:40 -0600220
Simon Glassd7cef832025-05-02 08:46:27 -0600221 sel_bflow = NULL;
222 *bflowp = NULL;
223
224 ret = expo_poll(exp, &act);
225 if (ret)
226 return log_msg_ret("bmp", ret);
227
228 switch (act.type) {
229 case EXPOACT_SELECT: {
Simon Glass0a2f6a32023-01-06 08:52:40 -0600230 struct bootflow *bflow;
231 int i;
232
233 for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36;
234 ret = bootflow_next_glob(&bflow), i++) {
Simon Glassd7cef832025-05-02 08:46:27 -0600235 if (i == act.select.id - ITEM) {
236 *bflowp = bflow;
237 // printf("found %p\n", bflow);
238 return 0;
Simon Glass0a2f6a32023-01-06 08:52:40 -0600239 }
240 }
Simon Glassd7cef832025-05-02 08:46:27 -0600241 break;
Simon Glass0a2f6a32023-01-06 08:52:40 -0600242 }
Simon Glassd7cef832025-05-02 08:46:27 -0600243 case EXPOACT_POINT_ITEM: {
244 struct scene *scn = expo_lookup_scene_id(exp, MAIN);
Simon Glass0a2f6a32023-01-06 08:52:40 -0600245
Simon Glassd7cef832025-05-02 08:46:27 -0600246 if (!scn)
247 return log_msg_ret("bms", -ENOENT);
248 ret = scene_menu_select_item(scn, OBJ_MENU, act.select.id);
249 if (ret)
250 return log_msg_ret("bmp", ret);
251 break;
252 }
253 case EXPOACT_QUIT:
254 return -EPIPE;
255 default:
256 break;
257 }
Simon Glass0a2f6a32023-01-06 08:52:40 -0600258
Simon Glassd7cef832025-05-02 08:46:27 -0600259 return -EAGAIN;
Simon Glass0a2f6a32023-01-06 08:52:40 -0600260}