"Das U-Boot" Source Tree
at master 558 lines 13 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2010-2011 Calxeda, Inc. 4 * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. 5 */ 6 7#include <ansi.h> 8#include <cli.h> 9#include <malloc.h> 10#include <errno.h> 11#include <linux/delay.h> 12#include <linux/list.h> 13#include <watchdog.h> 14 15#include "menu.h" 16 17#define ansi 1 18 19/* 20 * Internally, each item in a menu is represented by a struct menu_item. 21 * 22 * These items will be alloc'd and initialized by menu_item_add and destroyed 23 * by menu_item_destroy, and the consumer of the interface never sees that 24 * this struct is used at all. 25 */ 26struct menu_item { 27 char *key; 28 void *data; 29 struct list_head list; 30}; 31 32/* 33 * The menu is composed of a list of items along with settings and callbacks 34 * provided by the user. An incomplete definition of this struct is available 35 * in menu.h, but the full definition is here to prevent consumers from 36 * relying on its contents. 37 */ 38struct menu { 39 struct menu_item *default_item; 40 int timeout; 41 char *title; 42 int prompt; 43 void (*display_statusline)(struct menu *); 44 void (*item_data_print)(void *); 45 char *(*item_choice)(void *); 46 bool (*need_reprint)(void *); 47 void *item_choice_data; 48 struct list_head items; 49 int item_cnt; 50}; 51 52/* 53 * An iterator function for menu items. callback will be called for each item 54 * in m, with m, a pointer to the item, and extra being passed to callback. If 55 * callback returns a value other than NULL, iteration stops and the value 56 * return by callback is returned from menu_items_iter. This allows it to be 57 * used for search type operations. It is also safe for callback to remove the 58 * item from the list of items. 59 */ 60static inline void *menu_items_iter(struct menu *m, 61 void *(*callback)(struct menu *, struct menu_item *, void *), 62 void *extra) 63{ 64 struct list_head *pos, *n; 65 struct menu_item *item; 66 void *ret; 67 68 list_for_each_safe(pos, n, &m->items) { 69 item = list_entry(pos, struct menu_item, list); 70 71 ret = callback(m, item, extra); 72 73 if (ret) 74 return ret; 75 } 76 77 return NULL; 78} 79 80/* 81 * Print a menu_item. If the consumer provided an item_data_print function 82 * when creating the menu, call it with a pointer to the item's private data. 83 * Otherwise, print the key of the item. 84 */ 85static inline void *menu_item_print(struct menu *m, 86 struct menu_item *item, 87 void *extra) 88{ 89 if (!m->item_data_print) { 90 puts(item->key); 91 putc('\n'); 92 } else { 93 m->item_data_print(item->data); 94 } 95 96 return NULL; 97} 98 99/* 100 * Free the memory used by a menu item. This includes the memory used by its 101 * key. 102 */ 103static inline void *menu_item_destroy(struct menu *m, 104 struct menu_item *item, 105 void *extra) 106{ 107 if (item->key) 108 free(item->key); 109 110 free(item); 111 112 return NULL; 113} 114 115/* 116 * Display a menu so the user can make a choice of an item. First display its 117 * title, if any, and then each item in the menu. 118 */ 119static inline void menu_display(struct menu *m) 120{ 121 if (m->need_reprint) { 122 if (!m->need_reprint(m->item_choice_data)) 123 return; 124 } 125 126 if (m->title) { 127 puts(m->title); 128 putc('\n'); 129 } 130 if (m->display_statusline) 131 m->display_statusline(m); 132 133 menu_items_iter(m, menu_item_print, NULL); 134} 135 136/* 137 * Check if an item's key matches a provided string, pointed to by extra. If 138 * extra is NULL, an item with a NULL key will match. Otherwise, the item's 139 * key has to match according to strcmp. 140 * 141 * This is called via menu_items_iter, so it returns a pointer to the item if 142 * the key matches, and returns NULL otherwise. 143 */ 144static inline void *menu_item_key_match(struct menu *m, 145 struct menu_item *item, void *extra) 146{ 147 char *item_key = extra; 148 149 if (!item_key || !item->key) { 150 if (item_key == item->key) 151 return item; 152 153 return NULL; 154 } 155 156 if (strcmp(item->key, item_key) == 0) 157 return item; 158 159 return NULL; 160} 161 162/* 163 * Find the first item with a key matching item_key, if any exists. 164 */ 165static inline struct menu_item *menu_item_by_key(struct menu *m, 166 char *item_key) 167{ 168 return menu_items_iter(m, menu_item_key_match, item_key); 169} 170 171/* 172 * Set *choice to point to the default item's data, if any default item was 173 * set, and returns 1. If no default item was set, returns -ENOENT. 174 */ 175int menu_default_choice(struct menu *m, void **choice) 176{ 177 if (m->default_item) { 178 *choice = m->default_item->data; 179 return 1; 180 } 181 182 return -ENOENT; 183} 184 185/* 186 * Displays the menu and asks the user to choose an item. *choice will point 187 * to the private data of the item the user chooses. The user makes a choice 188 * by inputting a string matching the key of an item. Invalid choices will 189 * cause the user to be prompted again, repeatedly, until the user makes a 190 * valid choice. The user can exit the menu without making a choice via ^c. 191 * 192 * Returns 1 if the user made a choice, or -EINTR if they bail via ^c. 193 */ 194static inline int menu_interactive_choice(struct menu *m, void **choice) 195{ 196 char cbuf[CONFIG_SYS_CBSIZE]; 197 struct menu_item *choice_item = NULL; 198 int readret; 199 200 while (!choice_item) { 201 cbuf[0] = '\0'; 202 203 menu_display(m); 204 205 if (!m->item_choice) { 206 readret = cli_readline_into_buffer("Enter choice: ", 207 cbuf, m->timeout); 208 209 if (readret >= 0) { 210 choice_item = menu_item_by_key(m, cbuf); 211 if (!choice_item) 212 printf("%s not found\n", cbuf); 213 } else if (readret == -1) { 214 printf("<INTERRUPT>\n"); 215 return -EINTR; 216 } else { 217 return menu_default_choice(m, choice); 218 } 219 } else { 220 char *key = m->item_choice(m->item_choice_data); 221 222 if (key) 223 choice_item = menu_item_by_key(m, key); 224 } 225 226 if (!choice_item) 227 m->timeout = 0; 228 } 229 230 *choice = choice_item->data; 231 232 return 1; 233} 234 235/* 236 * menu_default_set() - Sets the default choice for the menu. This is safe to 237 * call more than once on a menu. 238 * 239 * m - Points to a menu created by menu_create(). 240 * 241 * item_key - Points to a string that, when compared using strcmp, matches the 242 * key for an existing item in the menu. 243 * 244 * Returns 1 if successful, -EINVAL if m is NULL, or -ENOENT if no item with a 245 * key matching item_key is found. 246 */ 247int menu_default_set(struct menu *m, char *item_key) 248{ 249 struct menu_item *item; 250 251 if (!m) 252 return -EINVAL; 253 254 item = menu_item_by_key(m, item_key); 255 256 if (!item) 257 return -ENOENT; 258 259 m->default_item = item; 260 261 return 1; 262} 263 264/* 265 * menu_get_choice() - Returns the user's selected menu entry, or the default 266 * if the menu is set to not prompt or the timeout expires. This is safe to 267 * call more than once. 268 * 269 * m - Points to a menu created by menu_create(). 270 * 271 * choice - Points to a location that will store a pointer to the selected 272 * menu item. If no item is selected or there is an error, no value will be 273 * written at the location it points to. 274 * 275 * Returns 1 if successful, -EINVAL if m or choice is NULL, -ENOENT if no 276 * default has been set and the menu is set to not prompt or the timeout 277 * expires, or -EINTR if the user exits the menu via ^c. 278 */ 279int menu_get_choice(struct menu *m, void **choice) 280{ 281 if (!m || !choice) 282 return -EINVAL; 283 284 if (!m->item_cnt) 285 return -ENOENT; 286 287 if (!m->prompt) 288 return menu_default_choice(m, choice); 289 290 return menu_interactive_choice(m, choice); 291} 292 293/* 294 * menu_item_add() - Adds or replaces a menu item. Note that this replaces the 295 * data of an item if it already exists, but doesn't change the order of the 296 * item. 297 * 298 * m - Points to a menu created by menu_create(). 299 * 300 * item_key - Points to a string that will uniquely identify the item. The 301 * string will be copied to internal storage, and is safe to discard after 302 * passing to menu_item_add. 303 * 304 * item_data - An opaque pointer associated with an item. It is never 305 * dereferenced internally, but will be passed to the item_data_print, and 306 * will be returned from menu_get_choice if the menu item is selected. 307 * 308 * Returns 1 if successful, -EINVAL if m is NULL, or -ENOMEM if there is 309 * insufficient memory to add the menu item. 310 */ 311int menu_item_add(struct menu *m, char *item_key, void *item_data) 312{ 313 struct menu_item *item; 314 315 if (!m) 316 return -EINVAL; 317 318 item = menu_item_by_key(m, item_key); 319 320 if (item) { 321 item->data = item_data; 322 return 1; 323 } 324 325 item = malloc(sizeof *item); 326 if (!item) 327 return -ENOMEM; 328 329 item->key = strdup(item_key); 330 331 if (!item->key) { 332 free(item); 333 return -ENOMEM; 334 } 335 336 item->data = item_data; 337 338 list_add_tail(&item->list, &m->items); 339 m->item_cnt++; 340 341 return 1; 342} 343 344/* 345 * menu_create() - Creates a menu handle with default settings 346 * 347 * title - If not NULL, points to a string that will be displayed before the 348 * list of menu items. It will be copied to internal storage, and is safe to 349 * discard after passing to menu_create(). 350 * 351 * timeout - A delay in seconds to wait for user input. If 0, timeout is 352 * disabled, and the default choice will be returned unless prompt is 1. 353 * 354 * prompt - If 0, don't ask for user input unless there is an interrupted 355 * timeout. If 1, the user will be prompted for input regardless of the value 356 * of timeout. 357 * 358 * display_statusline - If not NULL, will be called to show a statusline when 359 * the menu is displayed. 360 * 361 * item_data_print - If not NULL, will be called for each item when the menu 362 * is displayed, with the pointer to the item's data passed as the argument. 363 * If NULL, each item's key will be printed instead. Since an item's key is 364 * what must be entered to select an item, the item_data_print function should 365 * make it obvious what the key for each entry is. 366 * 367 * item_choice - If not NULL, will be called when asking the user to choose an 368 * item. Returns a key string corresponding to the chosen item or NULL if 369 * no item has been selected. 370 * 371 * need_reprint - If not NULL, will be called before printing the menu. 372 * Returning FALSE means the menu does not need reprint. 373 * 374 * item_choice_data - Will be passed as the argument to the item_choice function 375 * 376 * Returns a pointer to the menu if successful, or NULL if there is 377 * insufficient memory available to create the menu. 378 */ 379struct menu *menu_create(char *title, int timeout, int prompt, 380 void (*display_statusline)(struct menu *), 381 void (*item_data_print)(void *), 382 char *(*item_choice)(void *), 383 bool (*need_reprint)(void *), 384 void *item_choice_data) 385{ 386 struct menu *m; 387 388 m = malloc(sizeof *m); 389 390 if (!m) 391 return NULL; 392 393 m->default_item = NULL; 394 m->prompt = prompt; 395 m->timeout = timeout; 396 m->display_statusline = display_statusline; 397 m->item_data_print = item_data_print; 398 m->item_choice = item_choice; 399 m->need_reprint = need_reprint; 400 m->item_choice_data = item_choice_data; 401 m->item_cnt = 0; 402 403 if (title) { 404 m->title = strdup(title); 405 if (!m->title) { 406 free(m); 407 return NULL; 408 } 409 } else 410 m->title = NULL; 411 412 INIT_LIST_HEAD(&m->items); 413 414 return m; 415} 416 417/* 418 * menu_destroy() - frees the memory used by a menu and its items. 419 * 420 * m - Points to a menu created by menu_create(). 421 * 422 * Returns 1 if successful, or -EINVAL if m is NULL. 423 */ 424int menu_destroy(struct menu *m) 425{ 426 if (!m) 427 return -EINVAL; 428 429 menu_items_iter(m, menu_item_destroy, NULL); 430 431 if (m->title) 432 free(m->title); 433 434 free(m); 435 436 return 1; 437} 438 439enum bootmenu_key bootmenu_autoboot_loop(struct bootmenu_data *menu, 440 struct cli_ch_state *cch) 441{ 442 enum bootmenu_key key = BKEY_NONE; 443 int i, c; 444 445 while (menu->delay > 0) { 446 if (ansi) 447 printf(ANSI_CURSOR_POSITION, menu->count + 5, 3); 448 printf("Hit any key to stop autoboot: %d ", menu->delay); 449 for (i = 0; i < 100; ++i) { 450 int ichar; 451 452 if (!tstc()) { 453 schedule(); 454 mdelay(10); 455 continue; 456 } 457 458 menu->delay = -1; 459 c = getchar(); 460 461 ichar = cli_ch_process(cch, c); 462 463 switch (ichar) { 464 case '\0': 465 key = BKEY_NONE; 466 break; 467 case '\n': 468 key = BKEY_SELECT; 469 break; 470 case 0x3: /* ^C */ 471 key = BKEY_QUIT; 472 break; 473 default: 474 key = BKEY_NONE; 475 break; 476 } 477 break; 478 } 479 480 if (menu->delay < 0) 481 break; 482 483 --menu->delay; 484 } 485 486 if (ansi) 487 printf(ANSI_CURSOR_POSITION ANSI_CLEAR_LINE, menu->count + 5, 1); 488 489 if (menu->delay == 0) 490 key = BKEY_SELECT; 491 492 return key; 493} 494 495enum bootmenu_key bootmenu_conv_key(int ichar) 496{ 497 enum bootmenu_key key; 498 499 switch (ichar) { 500 case '\n': 501 /* enter key was pressed */ 502 key = BKEY_SELECT; 503 break; 504 case CTL_CH('c'): 505 case '\e': 506 /* ^C was pressed */ 507 key = BKEY_QUIT; 508 break; 509 case CTL_CH('p'): 510 key = BKEY_UP; 511 break; 512 case CTL_CH('n'): 513 key = BKEY_DOWN; 514 break; 515 case CTL_CH('s'): 516 key = BKEY_SAVE; 517 break; 518 case '+': 519 key = BKEY_PLUS; 520 break; 521 case '-': 522 key = BKEY_MINUS; 523 break; 524 case ' ': 525 key = BKEY_SPACE; 526 break; 527 default: 528 key = BKEY_NONE; 529 break; 530 } 531 532 return key; 533} 534 535enum bootmenu_key bootmenu_loop(struct bootmenu_data *menu, 536 struct cli_ch_state *cch) 537{ 538 enum bootmenu_key key; 539 int c, errchar = 0; 540 541 c = cli_ch_process(cch, 0); 542 if (!c) { 543 while (!c && !tstc()) { 544 schedule(); 545 mdelay(10); 546 c = cli_ch_process(cch, errchar); 547 errchar = -ETIMEDOUT; 548 } 549 if (!c) { 550 c = getchar(); 551 c = cli_ch_process(cch, c); 552 } 553 } 554 555 key = bootmenu_conv_key(c); 556 557 return key; 558}