]> Git Repo - J-u-boot.git/blob - boot/bootflow_menu.c
upl: fix parsing of DT property
[J-u-boot.git] / boot / bootflow_menu.c
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 <[email protected]>
7  */
8
9 #define LOG_CATEGORY UCLASS_BOOTSTD
10
11 #include <bootflow.h>
12 #include <bootmeth.h>
13 #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  */
29 struct menu_priv {
30         int num_bootflows;
31 };
32
33 int 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++) {
80                 struct bootmeth_uc_plat *ucp;
81                 char str[2], *label, *key;
82                 uint preview_id;
83                 bool add_gap;
84
85                 if (bflow->state != BOOTFLOWST_READY)
86                         continue;
87
88                 /* 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
93                 *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);
129                 priv->num_bootflows++;
130         }
131
132         ret = scene_arrange(scn);
133         if (ret)
134                 return log_msg_ret("arr", ret);
135
136         *expp = exp;
137
138         return 0;
139 }
140
141 int 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
178 int bootflow_menu_run(struct bootstd_priv *std, bool text_mode,
179                       struct bootflow **bflowp)
180 {
181         struct cli_ch_state s_cch, *cch = &s_cch;
182         struct bootflow *sel_bflow;
183         struct udevice *dev;
184         struct expo *exp;
185         uint sel_id;
186         bool done;
187         int ret;
188
189         cli_ch_init(cch);
190
191         sel_bflow = NULL;
192         *bflowp = NULL;
193
194         ret = bootflow_menu_new(&exp);
195         if (ret)
196                 return log_msg_ret("exp", ret);
197
198         if (ofnode_valid(std->theme)) {
199                 ret = bootflow_menu_apply_theme(exp, std->theme);
200                 if (ret)
201                         return log_msg_ret("thm", ret);
202         }
203
204         /* For now we only support a video console */
205         ret = uclass_first_device_err(UCLASS_VIDEO, &dev);
206         if (ret)
207                 return log_msg_ret("vid", ret);
208         ret = expo_set_display(exp, dev);
209         if (ret)
210                 return log_msg_ret("dis", ret);
211
212         ret = expo_set_scene_id(exp, MAIN);
213         if (ret)
214                 return log_msg_ret("scn", ret);
215
216         if (text_mode)
217                 expo_set_text_mode(exp, text_mode);
218
219         done = false;
220         do {
221                 struct expo_action act;
222                 int ichar, key;
223
224                 ret = expo_render(exp);
225                 if (ret)
226                         break;
227
228                 ichar = cli_ch_process(cch, 0);
229                 if (!ichar) {
230                         while (!ichar && !tstc()) {
231                                 schedule();
232                                 mdelay(2);
233                                 ichar = cli_ch_process(cch, -ETIMEDOUT);
234                         }
235                         if (!ichar) {
236                                 ichar = getchar();
237                                 ichar = cli_ch_process(cch, ichar);
238                         }
239                 }
240
241                 key = 0;
242                 if (ichar) {
243                         key = bootmenu_conv_key(ichar);
244                         if (key == BKEY_NONE)
245                                 key = ichar;
246                 }
247                 if (!key)
248                         continue;
249
250                 ret = expo_send_key(exp, key);
251                 if (ret)
252                         break;
253
254                 ret = expo_action_get(exp, &act);
255                 if (!ret) {
256                         switch (act.type) {
257                         case EXPOACT_SELECT:
258                                 sel_id = act.select.id;
259                                 done = true;
260                                 break;
261                         case EXPOACT_QUIT:
262                                 done = true;
263                                 break;
264                         default:
265                                 break;
266                         }
267                 }
268         } while (!done);
269
270         if (ret)
271                 return log_msg_ret("end", ret);
272
273         if (sel_id) {
274                 struct bootflow *bflow;
275                 int i;
276
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) {
280                                 sel_bflow = bflow;
281                                 break;
282                         }
283                 }
284         }
285
286         expo_destroy(exp);
287
288         if (!sel_bflow)
289                 return -EAGAIN;
290         *bflowp = sel_bflow;
291
292         return 0;
293 }
This page took 0.042473 seconds and 4 git commands to generate.