"Das U-Boot" Source Tree
at master 293 lines 6.5 kB view raw
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 <sjg@chromium.org> 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 */ 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++) { 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 141int 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 178int 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}