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