
Give the user a choosable menu of probable bootables to boot from.
Signed-off-by: Suriyan Ramasami suriyan.r@gmail.com --- board/Seagate/goflexhome/goflexhomemenu.c | 381 +++++++++++++++++++++++++++++ 1 files changed, 381 insertions(+), 0 deletions(-) create mode 100644 board/Seagate/goflexhome/goflexhomemenu.c
diff --git a/board/Seagate/goflexhome/goflexhomemenu.c b/board/Seagate/goflexhome/goflexhomemenu.c new file mode 100644 index 0000000..8866da6 --- /dev/null +++ b/board/Seagate/goflexhome/goflexhomemenu.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2013 Suriyan Ramasami suriyan.r@gmail.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> + +#if defined(CONFIG_MENU) +/* Menu related code begins here */ + +/* Added to use the various usb/fat/ext4fs interfaces */ +#include <usb.h> +#include <ext4fs.h> +#include <menu.h> + +#define MENU_MAX_DEVICES 10 +#define MENU_MAX_PARTITIONS 10 +#define MENU_MAX_BOOTABLES 10 + +#define MENU_EXIT 1 +#define MENU_SHOW 2 + +typedef struct menu_bootables { + char interface[5]; + char drive; + int device; + int partition; + char filename[64]; + char fstype; /* f => fat, e => ext2/4 0 => invalid */ +} menu_bootables_t; + +static void goflexhome_menuprint(void *print_buffer) +{ + printf("%s\n", (char *) print_buffer); +} + +/* + * We shall use menu_<> variables to capture the state of past menu + * choices. + * menu_bootargs corresponds to bootargs + * menu_bootcmd corresponds to bootcmd + * menu_choice corresponds to the last choice that was picked + * menu_choice will be NULL the first time and also + * if a choice was never made. In that case we should pick + * to boot from the 1st bootable option if present. +*/ + +static int goflexhome_evaluate_env(void) +{ +char *s; + + run_command("run menu_bootargs", 0); + s = getenv("bootargs"); + printf("bootargs is %s\n", s); + run_command("run menu_bootcmd", 0); + s = getenv("bootcmd"); + printf("bootcmd is %s\n", s); + if (run_command("run bootcmd", 0) != 0) { + /* We failed to boot, present the menu */ + return MENU_SHOW; + } + if (strncmp(s, "echo", 4) == 0) { + /* User wants the u-boot prmpt */ + return MENU_EXIT; + } + run_command("bootm", 0); + + /* We are here, we failed to boot */ + return MENU_SHOW; +} + +static int goflexhome_handle_choice(menu_bootables_t menu_bootlist[], char *choice) +{ +char *s, *last_menu_choice; +char menu_command[128]; +char load_command[16]; +int index; +int call_saveenv = 0; + + if (choice == NULL) { + /* Exit menu and let it do its auto boot */ + return MENU_EXIT; + } + printf("\nYou chose: %s\n", choice); + + last_menu_choice = getenv("menu_choice"); + if (last_menu_choice == NULL) { + /* User has not yet chosen before */ + /* Lets default to boot from nand */ + setenv("menu_bootargs", "setenv bootargs ${console} ubi.mtd=2,2048 root=ubi0:root rootfstype=ubifs debug"); + setenv("menu_bootcmd", "setenv bootcmd nand read.e 0x800000 0x100000 0x600000"); + call_saveenv = 1; + } + if (choice[0] == '*') { + /* User wants same thing that was chosen the last time */ + return MENU_EXIT; + } + if (last_menu_choice && strcmp(choice, last_menu_choice) != 0) { + /* Save the choice chosen */ + setenv("menu_choice", choice); + call_saveenv = 1; + } + if (choice[0] == '+') { + /* User wants u-boot prompt */ + s = getenv("menu_bootcmd"); + if (strcmp(s, "setenv bootcmd echo Dropping you to u-boot prompt") != 0) { + setenv("menu_bootcmd", "setenv bootcmd echo Dropping you to u-boot prompt"); + saveenv(); + } + return MENU_EXIT; + } + + /* Steps to set the env variables to the chosen values */ + index = simple_strtoul(choice, NULL, 10); + sprintf(menu_command, "/dev/sd%c%d", menu_bootlist[index].drive, menu_bootlist[index].partition); + s = getenv("menu_root"); + if (strcmp(s, menu_command) != 0) { + setenv("menu_root", menu_command); + call_saveenv = 1; + } + s = getenv("menu_bootargs"); + if (strcmp(s, "setenv bootargs $console rootdelay=10 root=${menu_root} debug") != 0) { + setenv("menu_bootargs", "setenv bootargs ${console} rootdelay=10 root=${menu_root} debug"); + call_saveenv = 1; + } + switch (menu_bootlist[index].fstype) { + case 'e': + strcpy(load_command, "ext4load"); + break; + case 'f': + strcpy(load_command, "fatload"); + break; + default: + return MENU_EXIT; + } + + /* Lets try to load and check the image */ + sprintf(menu_command, "%s %s %d:%d %x %s", + load_command, + menu_bootlist[index].interface, + menu_bootlist[index].device, + menu_bootlist[index].partition, + CONFIG_SYS_LOAD_ADDR, + menu_bootlist[index].filename); + if (run_command(menu_command, 0) != 0) { + /* Could not load image */ + printf("Selected image could not be loaded ...\n"); + return MENU_SHOW; + } + sprintf(menu_command, "iminfo %x", CONFIG_SYS_LOAD_ADDR); + if (run_command(menu_command, 0) != 0) { + /* The image is not a valid image */ + printf("Selected image is not valid ...\n"); + return MENU_SHOW; + } + + sprintf(menu_command, "setenv bootcmd %s %s %d:%d %x %s", + load_command, + menu_bootlist[index].interface, + menu_bootlist[index].device, + menu_bootlist[index].partition, + CONFIG_SYS_LOAD_ADDR, + menu_bootlist[index].filename); + s = getenv("menu_bootcmd"); + if (strcmp(s, menu_command) != 0) { + setenv("menu_bootcmd", menu_command); + call_saveenv = 1; + } + if (call_saveenv) saveenv(); + return MENU_EXIT; +} + +static int goflexhome_menu(menu_bootables_t menu_bootlist[], int bootdelay) +{ +int index; +struct menu *m; +char menu_key[MENU_MAX_BOOTABLES][5]; +char menu_entry[MENU_MAX_BOOTABLES][64]; +char *menu_choice; +char *last_menu_choice; +char choice_menu_entry[64]; +char choice_menu[3]; + + m = menu_create("Bootables:\nChoice\tIntface\tDrive\tDevice\tPart\tFS\tFileName\n---------------------------------------------------------------", 60, 1, goflexhome_menuprint, NULL, NULL); + for (index = 0; index < MENU_MAX_BOOTABLES; index++) { + if (menu_bootlist[index].fstype == '0') break; + snprintf(menu_key[index], sizeof(menu_key[index]), "%d", index); + snprintf(menu_entry[index], sizeof(menu_entry[index]), "%d\t%s\t%c\t%d\t%d\t%c\t%s", index, menu_bootlist[index].interface, menu_bootlist[index].drive, menu_bootlist[index].device, menu_bootlist[index].partition, menu_bootlist[index].fstype, menu_bootlist[index].filename); + if (menu_item_add(m, menu_key[index], menu_entry[index]) != 1) { + menu_destroy(m); + return MENU_EXIT; + } + } + + /* Prep for what should be the default menu choice */ + /* If chosen before, choose the last boot options */ + /* If nothing chosen yet, then choose the first bootable option */ + /* If nothing chosen yet, and no first bootable option, then boot */ + /* from nand */ + + last_menu_choice = getenv("menu_choice"); + sprintf(choice_menu, "*"); + if (last_menu_choice) { + sprintf(choice_menu_entry, "* Last boot options (%s)", last_menu_choice); + } + else { + /* There was no last boot option */ + /* If there is at least 1 boot entry, make that the default */ + if (menu_bootlist[0].fstype != '0') { + setenv("menu_choice", menu_entry[0]); + sprintf(choice_menu_entry, menu_entry[0]); + } + else { + sprintf(choice_menu_entry, "* Last boot options (None, and no bootables found!)"); + } + } + if (menu_item_add(m, choice_menu, choice_menu_entry) != 1) { + menu_destroy(m); + return MENU_EXIT; + } + /* Mark this as the default choice. */ + menu_default_set(m, "*"); + if (menu_item_add(m, "+", "+ UBoot prompt") != 1) { + menu_destroy(m); + return MENU_EXIT; + } + + menu_get_choice(m, (void **) &menu_choice); + return(goflexhome_handle_choice(menu_bootlist, menu_choice)); +} + +static void goflexhome_filesearch(menu_bootables_t menu_bootlist[], int *bootindex) { +char *filenames[] = { "/uImage", "/boot/uImage", "" }; +int index = 0; + + while (filenames[index][0] != '\0') { + switch (menu_bootlist[*bootindex].fstype) { + case 'e': + if (ext4fs_open(filenames[index]) == -1) { + index++; + continue; + } + break; + + default: + break; + } + + /* Got a hit, record it */ + strcpy(menu_bootlist[*bootindex].filename, filenames[index]); + index++; + (*bootindex)++; + if (*bootindex >= MENU_MAX_BOOTABLES) break; + /* Prep next bootlist structure */ + memcpy(&menu_bootlist[*bootindex], &menu_bootlist[*bootindex - 1], sizeof(menu_bootables_t)); + } +} + +static void goflexhome_populate_partitions(menu_bootables_t menu_bootlist[], block_dev_desc_t *dev_desc, int *bootindex) +{ +int part; +disk_partition_t disk_part; + + part = menu_bootlist[*bootindex].partition; + + /* Get the partition structure */ + if (get_partition_info(dev_desc, part, &disk_part)) { + return; + } + + /* Try to check if its extX */ + if (ext4fs_probe(dev_desc, &disk_part) == 0) { + menu_bootlist[*bootindex].fstype = 'e'; + goflexhome_filesearch(menu_bootlist, bootindex); + ext4fs_close(); + return; + } +} + +static void goflexhome_populate_devices(menu_bootables_t menu_bootlist[], int *bootindex) +{ +block_dev_desc_t *dev_desc; +int device; +int part; + + /* Populate bootlist from each device and the partitions within */ + for (device = 0; device < MENU_MAX_DEVICES; device++) { + dev_desc = get_dev(menu_bootlist[*bootindex].interface, device); + if (dev_desc == NULL) continue; + menu_bootlist[*bootindex].device = device; + for (part = 0; part < MENU_MAX_PARTITIONS; part++) { + menu_bootlist[*bootindex].partition = part; + goflexhome_populate_partitions(menu_bootlist, dev_desc, bootindex); + } + } +} + +/* menu_bootlist[] can hold a max of MENU_MAX_BOOTABLES entries */ +static void goflexhome_populate_bootlist(menu_bootables_t menu_bootlist[]) +{ +/* ide is always first */ +char *interfaces[] = { "ide", "usb", "" }; +int bootindex = 0; +int i = 0; + + /* Lets initialize the usb sub system */ + usb_init(); + usb_stor_scan(0); + + /* This scans the partitions in the IDE storage */ + ide_init(); + + /* Populate bootlist from each interface */ + while ((interfaces[i][0] != '\0') && + (bootindex < MENU_MAX_BOOTABLES)) { + strcpy(menu_bootlist[bootindex].interface, interfaces[i]); + goflexhome_populate_devices(menu_bootlist, &bootindex); + i++; + } + if (bootindex < MENU_MAX_BOOTABLES) { + /* End marker of list */ + menu_bootlist[bootindex].fstype = '0'; + } + + /* Lets set the drive letter */ + menu_bootlist[0].drive = 'a'; + for (i = 1; i < bootindex; i++) { + if (menu_bootlist[i].fstype == '0') break; + /* Increase drive letter when interface changes */ + /* Or when device numbers change for same interface */ + menu_bootlist[i].drive = menu_bootlist[i - 1].drive; + if (strcmp(menu_bootlist[i].interface, menu_bootlist[i - 1].interface) != 0) { + menu_bootlist[i].drive++; + } + else { + if (menu_bootlist[i].device > menu_bootlist[i - 1].device) { + menu_bootlist[i].drive++; + } + } + } +} + +int menu_show(int bootdelay) +{ +menu_bootables_t menu_bootlist[MENU_MAX_BOOTABLES]; +int retval; + +#if 0 + set_default_env(); + saveenv(); +#endif + + goflexhome_populate_bootlist(menu_bootlist); + do { + retval = goflexhome_menu(menu_bootlist, bootdelay); + if (retval == MENU_EXIT) { + retval = goflexhome_evaluate_env(); + } + } while (retval == MENU_SHOW); + + return 0; +} + +#endif /* CONFIG_MENU */