cmd_bootmenu.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /*
  2. * (C) Copyright 2011-2013 Pali Rohár <pali.rohar@gmail.com>
  3. *
  4. * See file CREDITS for list of people who contributed to this
  5. * project.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20. * MA 02111-1307 USA
  21. */
  22. #include <common.h>
  23. #include <command.h>
  24. #include <ansi.h>
  25. #include <menu.h>
  26. #include <hush.h>
  27. #include <watchdog.h>
  28. #include <malloc.h>
  29. #include <linux/string.h>
  30. /* maximum bootmenu entries */
  31. #define MAX_COUNT 99
  32. /* maximal size of bootmenu env
  33. * 9 = strlen("bootmenu_")
  34. * 2 = strlen(MAX_COUNT)
  35. * 1 = NULL term
  36. */
  37. #define MAX_ENV_SIZE (9 + 2 + 1)
  38. struct bootmenu_entry {
  39. unsigned short int num; /* unique number 0 .. MAX_COUNT */
  40. char key[3]; /* key identifier of number */
  41. char *title; /* title of entry */
  42. char *command; /* hush command of entry */
  43. struct bootmenu_data *menu; /* this bootmenu */
  44. struct bootmenu_entry *next; /* next menu entry (num+1) */
  45. };
  46. struct bootmenu_data {
  47. int delay; /* delay for autoboot */
  48. int active; /* active menu entry */
  49. int count; /* total count of menu entries */
  50. struct bootmenu_entry *first; /* first menu entry */
  51. };
  52. enum bootmenu_key {
  53. KEY_NONE = 0,
  54. KEY_UP,
  55. KEY_DOWN,
  56. KEY_SELECT,
  57. };
  58. static char *bootmenu_getoption(unsigned short int n)
  59. {
  60. char name[MAX_ENV_SIZE];
  61. if (n > MAX_COUNT)
  62. return NULL;
  63. sprintf(name, "bootmenu_%d", n);
  64. return getenv(name);
  65. }
  66. static void bootmenu_print_entry(void *data)
  67. {
  68. struct bootmenu_entry *entry = data;
  69. int reverse = (entry->menu->active == entry->num);
  70. /*
  71. * Move cursor to line where the entry will be drown (entry->num)
  72. * First 3 lines contain bootmenu header + 1 empty line
  73. */
  74. printf(ANSI_CURSOR_POSITION, entry->num + 4, 1);
  75. puts(" ");
  76. if (reverse)
  77. puts(ANSI_COLOR_REVERSE);
  78. puts(entry->title);
  79. if (reverse)
  80. puts(ANSI_COLOR_RESET);
  81. }
  82. static void bootmenu_autoboot_loop(struct bootmenu_data *menu,
  83. enum bootmenu_key *key, int *esc)
  84. {
  85. int i, c;
  86. if (menu->delay > 0) {
  87. printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
  88. printf(" Hit any key to stop autoboot: %2d ", menu->delay);
  89. }
  90. while (menu->delay > 0) {
  91. for (i = 0; i < 100; ++i) {
  92. if (!tstc()) {
  93. WATCHDOG_RESET();
  94. mdelay(10);
  95. continue;
  96. }
  97. menu->delay = -1;
  98. c = getc();
  99. switch (c) {
  100. case '\e':
  101. *esc = 1;
  102. *key = KEY_NONE;
  103. break;
  104. case '\r':
  105. *key = KEY_SELECT;
  106. break;
  107. default:
  108. *key = KEY_NONE;
  109. break;
  110. }
  111. break;
  112. }
  113. if (menu->delay < 0)
  114. break;
  115. --menu->delay;
  116. printf("\b\b\b%2d ", menu->delay);
  117. }
  118. printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
  119. puts(ANSI_CLEAR_LINE);
  120. if (menu->delay == 0)
  121. *key = KEY_SELECT;
  122. }
  123. static void bootmenu_loop(struct bootmenu_data *menu,
  124. enum bootmenu_key *key, int *esc)
  125. {
  126. int c;
  127. while (!tstc()) {
  128. WATCHDOG_RESET();
  129. mdelay(10);
  130. }
  131. c = getc();
  132. switch (*esc) {
  133. case 0:
  134. /* First char of ANSI escape sequence '\e' */
  135. if (c == '\e') {
  136. *esc = 1;
  137. *key = KEY_NONE;
  138. }
  139. break;
  140. case 1:
  141. /* Second char of ANSI '[' */
  142. if (c == '[') {
  143. *esc = 2;
  144. *key = KEY_NONE;
  145. } else {
  146. *esc = 0;
  147. }
  148. break;
  149. case 2:
  150. case 3:
  151. /* Third char of ANSI (number '1') - optional */
  152. if (*esc == 2 && c == '1') {
  153. *esc = 3;
  154. *key = KEY_NONE;
  155. break;
  156. }
  157. *esc = 0;
  158. /* ANSI 'A' - key up was pressed */
  159. if (c == 'A')
  160. *key = KEY_UP;
  161. /* ANSI 'B' - key down was pressed */
  162. else if (c == 'B')
  163. *key = KEY_DOWN;
  164. /* other key was pressed */
  165. else
  166. *key = KEY_NONE;
  167. break;
  168. }
  169. /* enter key was pressed */
  170. if (c == '\r')
  171. *key = KEY_SELECT;
  172. }
  173. static char *bootmenu_choice_entry(void *data)
  174. {
  175. struct bootmenu_data *menu = data;
  176. struct bootmenu_entry *iter;
  177. enum bootmenu_key key = KEY_NONE;
  178. int esc = 0;
  179. int i;
  180. while (1) {
  181. if (menu->delay >= 0) {
  182. /* Autoboot was not stopped */
  183. bootmenu_autoboot_loop(menu, &key, &esc);
  184. } else {
  185. /* Some key was pressed, so autoboot was stopped */
  186. bootmenu_loop(menu, &key, &esc);
  187. }
  188. switch (key) {
  189. case KEY_UP:
  190. if (menu->active > 0)
  191. --menu->active;
  192. /* no menu key selected, regenerate menu */
  193. return NULL;
  194. case KEY_DOWN:
  195. if (menu->active < menu->count - 1)
  196. ++menu->active;
  197. /* no menu key selected, regenerate menu */
  198. return NULL;
  199. case KEY_SELECT:
  200. iter = menu->first;
  201. for (i = 0; i < menu->active; ++i)
  202. iter = iter->next;
  203. return iter->key;
  204. default:
  205. break;
  206. }
  207. }
  208. /* never happens */
  209. debug("bootmenu: this should not happen");
  210. return NULL;
  211. }
  212. static void bootmenu_destroy(struct bootmenu_data *menu)
  213. {
  214. struct bootmenu_entry *iter = menu->first;
  215. struct bootmenu_entry *next;
  216. while (iter) {
  217. next = iter->next;
  218. free(iter->title);
  219. free(iter->command);
  220. free(iter);
  221. iter = next;
  222. }
  223. free(menu);
  224. }
  225. static struct bootmenu_data *bootmenu_create(int delay)
  226. {
  227. unsigned short int i = 0;
  228. const char *option;
  229. struct bootmenu_data *menu;
  230. struct bootmenu_entry *iter = NULL;
  231. int len;
  232. char *sep;
  233. struct bootmenu_entry *entry;
  234. menu = malloc(sizeof(struct bootmenu_data));
  235. if (!menu)
  236. return NULL;
  237. menu->delay = delay;
  238. menu->active = 0;
  239. menu->first = NULL;
  240. while ((option = bootmenu_getoption(i))) {
  241. sep = strchr(option, '=');
  242. if (!sep) {
  243. printf("Invalid bootmenu entry: %s\n", option);
  244. break;
  245. }
  246. entry = malloc(sizeof(struct bootmenu_entry));
  247. if (!entry)
  248. goto cleanup;
  249. len = sep-option;
  250. entry->title = malloc(len + 1);
  251. if (!entry->title) {
  252. free(entry);
  253. goto cleanup;
  254. }
  255. memcpy(entry->title, option, len);
  256. entry->title[len] = 0;
  257. len = strlen(sep + 1);
  258. entry->command = malloc(len + 1);
  259. if (!entry->command) {
  260. free(entry->title);
  261. free(entry);
  262. goto cleanup;
  263. }
  264. memcpy(entry->command, sep + 1, len);
  265. entry->command[len] = 0;
  266. sprintf(entry->key, "%d", i);
  267. entry->num = i;
  268. entry->menu = menu;
  269. entry->next = NULL;
  270. if (!iter)
  271. menu->first = entry;
  272. else
  273. iter->next = entry;
  274. iter = entry;
  275. ++i;
  276. if (i == MAX_COUNT - 1)
  277. break;
  278. }
  279. /* Add U-Boot console entry at the end */
  280. if (i <= MAX_COUNT - 1) {
  281. entry = malloc(sizeof(struct bootmenu_entry));
  282. if (!entry)
  283. goto cleanup;
  284. entry->title = strdup("U-Boot console");
  285. if (!entry->title) {
  286. free(entry);
  287. goto cleanup;
  288. }
  289. entry->command = strdup("");
  290. if (!entry->command) {
  291. free(entry->title);
  292. free(entry);
  293. goto cleanup;
  294. }
  295. sprintf(entry->key, "%d", i);
  296. entry->num = i;
  297. entry->menu = menu;
  298. entry->next = NULL;
  299. if (!iter)
  300. menu->first = entry;
  301. else
  302. iter->next = entry;
  303. iter = entry;
  304. ++i;
  305. }
  306. menu->count = i;
  307. return menu;
  308. cleanup:
  309. bootmenu_destroy(menu);
  310. return NULL;
  311. }
  312. static void bootmenu_show(int delay)
  313. {
  314. int init = 0;
  315. void *choice = NULL;
  316. char *title = NULL;
  317. char *command = NULL;
  318. struct menu *menu;
  319. struct bootmenu_data *bootmenu;
  320. struct bootmenu_entry *iter;
  321. char *option, *sep;
  322. /* If delay is 0 do not create menu, just run first entry */
  323. if (delay == 0) {
  324. option = bootmenu_getoption(0);
  325. if (!option) {
  326. puts("bootmenu option 0 was not found\n");
  327. return;
  328. }
  329. sep = strchr(option, '=');
  330. if (!sep) {
  331. puts("bootmenu option 0 is invalid\n");
  332. return;
  333. }
  334. run_command(sep+1, 0);
  335. return;
  336. }
  337. bootmenu = bootmenu_create(delay);
  338. if (!bootmenu)
  339. return;
  340. menu = menu_create(NULL, bootmenu->delay, 1, bootmenu_print_entry,
  341. bootmenu_choice_entry, bootmenu);
  342. if (!menu) {
  343. bootmenu_destroy(bootmenu);
  344. return;
  345. }
  346. for (iter = bootmenu->first; iter; iter = iter->next) {
  347. if (!menu_item_add(menu, iter->key, iter))
  348. goto cleanup;
  349. }
  350. /* Default menu entry is always first */
  351. menu_default_set(menu, "0");
  352. puts(ANSI_CURSOR_HIDE);
  353. puts(ANSI_CLEAR_CONSOLE);
  354. printf(ANSI_CURSOR_POSITION, 1, 1);
  355. init = 1;
  356. if (menu_get_choice(menu, &choice)) {
  357. iter = choice;
  358. title = strdup(iter->title);
  359. command = strdup(iter->command);
  360. }
  361. cleanup:
  362. menu_destroy(menu);
  363. bootmenu_destroy(bootmenu);
  364. if (init) {
  365. puts(ANSI_CURSOR_SHOW);
  366. puts(ANSI_CLEAR_CONSOLE);
  367. printf(ANSI_CURSOR_POSITION, 1, 1);
  368. }
  369. if (title && command) {
  370. debug("Starting entry '%s'\n", title);
  371. free(title);
  372. run_command(command, 0);
  373. free(command);
  374. }
  375. #ifdef CONFIG_POSTBOOTMENU
  376. run_command(CONFIG_POSTBOOTMENU, 0);
  377. #endif
  378. }
  379. void menu_display_statusline(struct menu *m)
  380. {
  381. struct bootmenu_entry *entry;
  382. struct bootmenu_data *menu;
  383. if (menu_default_choice(m, (void *)&entry) < 0)
  384. return;
  385. menu = entry->menu;
  386. printf(ANSI_CURSOR_POSITION, 1, 1);
  387. puts(ANSI_CLEAR_LINE);
  388. printf(ANSI_CURSOR_POSITION, 2, 1);
  389. puts(" *** U-Boot Boot Menu ***");
  390. puts(ANSI_CLEAR_LINE_TO_END);
  391. printf(ANSI_CURSOR_POSITION, 3, 1);
  392. puts(ANSI_CLEAR_LINE);
  393. /* First 3 lines are bootmenu header + 2 empty lines between entries */
  394. printf(ANSI_CURSOR_POSITION, menu->count + 5, 1);
  395. puts(ANSI_CLEAR_LINE);
  396. printf(ANSI_CURSOR_POSITION, menu->count + 6, 1);
  397. puts(" Press UP/DOWN to move, ENTER to select");
  398. puts(ANSI_CLEAR_LINE_TO_END);
  399. printf(ANSI_CURSOR_POSITION, menu->count + 7, 1);
  400. puts(ANSI_CLEAR_LINE);
  401. }
  402. #ifdef CONFIG_MENU_SHOW
  403. int menu_show(int bootdelay)
  404. {
  405. bootmenu_show(bootdelay);
  406. return -1; /* -1 - abort boot and run monitor code */
  407. }
  408. #endif
  409. int do_bootmenu(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
  410. {
  411. char *delay_str = NULL;
  412. int delay = 10;
  413. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  414. delay = CONFIG_BOOTDELAY;
  415. #endif
  416. if (argc >= 2)
  417. delay_str = argv[1];
  418. if (!delay_str)
  419. delay_str = getenv("bootmenu_delay");
  420. if (delay_str)
  421. delay = (int)simple_strtol(delay_str, NULL, 10);
  422. bootmenu_show(delay);
  423. return 0;
  424. }
  425. U_BOOT_CMD(
  426. bootmenu, 2, 1, do_bootmenu,
  427. "ANSI terminal bootmenu",
  428. "[delay]\n"
  429. " - show ANSI terminal bootmenu with autoboot delay"
  430. );