pandora: add a boot menu and it's helpers
authorGrazvydas Ignotas <notasas@gmail.com>
Tue, 27 Dec 2011 15:08:02 +0000 (17:08 +0200)
committernotaz <notasas@gmail.com>
Sat, 2 Jul 2016 23:52:47 +0000 (02:52 +0300)
board/pandora/Makefile
board/pandora/menu.c [new file with mode: 0644]
board/pandora/pandora.c
include/configs/omap3_pandora.h

index 4b4870e..8ebad1f 100644 (file)
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
 
 LIB    = $(obj)lib$(BOARD).o
 
-COBJS  := pandora.o video.o
+COBJS  := pandora.o video.o menu.o
 
 SRCS   := $(COBJS:.o=.c)
 OBJS   := $(addprefix $(obj),$(COBJS))
diff --git a/board/pandora/menu.c b/board/pandora/menu.c
new file mode 100644 (file)
index 0000000..15f5306
--- /dev/null
@@ -0,0 +1,377 @@
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+#include <hush.h>
+#include <malloc.h>
+#include <lcd.h>
+#include <twl4030.h>
+
+/* game buttons as in GPIO bank 4 */
+#define BTN_R          GPIO9
+#define BTN_UP         GPIO14
+#define BTN_DOWN       GPIO7
+#define BTN_G2         GPIO15
+#define BTN_G3         GPIO10
+
+struct menu_item {
+       const char *name;
+       int (*handler)(struct menu_item *item);
+       char *cmd;
+};
+
+static struct menu_item *menu_items[24];
+static int menu_item_count;
+
+static int do_cmd(const char *fmt, ...)
+{
+       char cmdbuff[256];
+       va_list args;
+
+       va_start(args, fmt);
+       vsprintf(cmdbuff, fmt, args);
+       va_end(args);
+
+       return !parse_string_outer(cmdbuff,
+               FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
+}
+
+static u32 menu_wait_for_input(int down)
+{
+       struct gpio *gpio4_base = (struct gpio *)OMAP34XX_GPIO4_BASE;
+       u32 btns;
+
+       while (1) {
+               btns = ~readl(&gpio4_base->datain) &
+                       (BTN_UP|BTN_DOWN|BTN_G2|BTN_G3);
+               if (!btns == !down)
+                       break;
+               udelay(5000);
+       }
+
+       return btns;
+}
+
+static int menu_do_default(struct menu_item *item)
+{
+       return 1;
+}
+
+static int menu_do_poweroff(struct menu_item *item)
+{
+       u8 d;
+
+       printf("power off.\n");
+
+       twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, &d, TWL4030_PM_MASTER_P1_SW_EVENTS);
+       d |= TWL4030_PM_MASTER_SW_EVENTS_DEVOFF;
+       twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, d, TWL4030_PM_MASTER_P1_SW_EVENTS);
+
+       return 0;
+}
+
+static int menu_do_usb_serial(struct menu_item *item)
+{
+       do_cmd("dcache off"); /* doesn't work with dcache */
+       do_cmd("usbinit");
+       printf("Switched to USB serial.\n");
+
+       setenv("stdout", "usbtty");
+       setenv("stdin", "usbtty");
+       setenv("stderr", "usbtty");
+       setenv("bootcmd", "");
+       return 1;
+}
+
+static int menu_do_serial(struct menu_item *item)
+{
+       printf("Switched to serial.\n");
+
+       setenv("stdout", "serial");
+       setenv("bootcmd", "");
+       return 1;
+}
+
+static int menu_do_script_cmd(struct menu_item *item)
+{
+       int failed = 0;
+
+       if (item->cmd == NULL || !do_cmd(item->cmd))
+               failed = 1;
+
+       printf("script %s.\n", failed ? "failed" : "finished");
+       flush_dcache_all();
+       menu_wait_for_input(0);
+       menu_wait_for_input(1);
+       return 0;
+}
+
+static void add_menu_item(const char *name,
+       int (*handler)(struct menu_item *), const char *cmd)
+{
+       struct menu_item *mitem;
+
+       mitem = malloc(sizeof(*mitem));
+       if (mitem == NULL)
+               return;
+       mitem->name = strdup(name);
+       mitem->handler = handler;
+       mitem->cmd = strdup(cmd);
+
+       if (menu_item_count < ARRAY_SIZE(menu_items))
+               menu_items[menu_item_count++] = mitem;
+}
+
+static char *bootmenu_next_ctl(char *p)
+{
+       while (*p && *p != '|' && *p != '\r' && *p != '\n')
+               p++;
+
+       return p;
+}
+
+static char *bootmenu_skip_blanks(char *p)
+{
+       while (*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
+               p++;
+
+       return p;
+}
+
+static char *bootmenu_skip_line(char *p)
+{
+       while (*p && *p != '\r' && *p != '\n')
+               p++;
+
+       return p;
+}
+
+static void parse_bootmenu(char *buf)
+{
+       char *p = buf, *name, *cmd;
+       int i;
+
+       for (i = 1; ; i++)
+       {
+               p = bootmenu_skip_blanks(p);
+               if (*p == 0)
+                       break;
+               if (*p == '#') {
+                       p = bootmenu_skip_line(p);
+                       continue;
+               }
+
+               name = p;
+               p = bootmenu_next_ctl(p);
+               if (*p != '|') {
+                       printf("bootmenu.txt: invalid line ~%d\n", i);
+                       p = bootmenu_skip_line(p);
+                       continue;
+               }
+               *p++ = 0;
+
+               cmd = p;
+               p = bootmenu_skip_line(p);
+               if (*p != 0)
+                       *p++ = 0;
+
+               add_menu_item(name, menu_do_script_cmd, cmd);
+       }
+}
+
+static struct menu_item default_menu_items[] = {
+       { "default boot",       menu_do_default, },
+       { "power off",          menu_do_poweroff, },
+       { "USB serial prompt",  menu_do_usb_serial, },
+       { "serial prompt",      menu_do_serial, },
+};
+
+static void menu_init(void)
+{
+       const char *check_format1 = "%sload mmc1 0:%d ${loadaddr} boot.scr 4";
+       const char *check_format2 = "%sload mmc1 0:%d ${loadaddr} boot.txt 4";
+       const char *run_format1 = "%sload mmc1 0:%d ${loadaddr} boot.scr;source ${loadaddr}";
+       const char *run_format2 = "mw.l ${loadaddr} 0 1024;%sload mmc1 0:%d ${loadaddr} boot.txt;"
+                                       "ssource ${loadaddr}";
+       disk_partition_t part_info;
+       block_dev_desc_t *dev_desc;
+       char tmp_name[32], tmp_cmd[128];
+       int i;
+
+       for (i = 0; i < 2; i++)
+               menu_items[i] = &default_menu_items[i];
+       menu_item_count = i;
+
+       if (!do_cmd("mmc rescan"))
+               goto no_mmc;
+
+       dev_desc = get_dev("mmc1", 0);
+       if (dev_desc == NULL) {
+               printf("dev desc null\n");
+               goto no_mmc;
+       }
+
+       /* kill stdout while we search for bootfiles */
+       setenv("stdout", "nulldev");
+
+       for (i = 1; menu_item_count < ARRAY_SIZE(menu_items); i++) {
+               if (get_partition_info(dev_desc, i, &part_info))
+                       break;
+               if (do_cmd("fatls mmc1 0:%d", i)) {
+                       if (do_cmd(check_format1, "fat", i)) {
+                               sprintf(tmp_cmd, run_format1, "fat", i);
+                               goto found;
+                       }
+                       if (do_cmd(check_format2, "fat", i)) {
+                               sprintf(tmp_cmd, run_format2, "fat", i);
+                               goto found;
+                       }
+                       continue;
+               }
+               if (do_cmd("ext2ls mmc1 0:%d", i)) {
+                       if (do_cmd(check_format1, "ext2", i)) {
+                               sprintf(tmp_cmd, run_format1, "ext2", i);
+                               goto found;
+                       }
+                       if (do_cmd(check_format2, "ext2", i)) {
+                               sprintf(tmp_cmd, run_format2, "ext2", i);
+                               goto found;
+                       }
+                       continue;
+               }
+               continue;
+
+found:
+               sprintf(tmp_name, "boot from SD1:%d", i);
+               add_menu_item(tmp_name, menu_do_script_cmd, tmp_cmd);
+       }
+
+no_mmc:
+       setenv("stdout", "serial");
+
+       if (do_cmd("ubi part boot && ubifsmount boot")) {
+               ulong addr = getenv_ulong("loadaddr", 16, 0);
+               if ((int)addr < (int)0x90000000) {
+                       if (do_cmd("ubifsload ${loadaddr} bootmenu.txt")) {
+                               ulong size = getenv_ulong("filesize", 16, 0);
+                               *(char *)(addr + size) = 0;
+                               parse_bootmenu((char *)addr);
+                       }
+               }
+       }
+
+       for (i = 2; i < ARRAY_SIZE(default_menu_items); i++) {
+               if (menu_item_count >= ARRAY_SIZE(menu_items))
+                       break;
+               menu_items[menu_item_count++] = &default_menu_items[i];
+       }
+
+       setenv("stdout", "lcd");
+}
+
+static int boot_menu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       int i, sel = 0, max_sel;
+       int tl_row;
+       u32 btns;
+
+       menu_init();
+
+       tl_row = panel_info.vl_row / 16 / 2 - (menu_item_count + 2) / 2;
+       max_sel = menu_item_count - 1;
+
+       console_col = 3;
+       console_row = tl_row;
+       lcd_printf("Boot menu");
+
+       while (1)
+       {
+               for (i = 0; i < menu_item_count; i++) {
+                       console_col = 3;
+                       console_row = tl_row + 2 + i;
+                       lcd_printf(menu_items[i]->name);
+               }
+
+               for (i = 0; i < menu_item_count; i++) {
+                       console_col = 1;
+                       console_row = tl_row + 2 + i;
+                       lcd_printf(i == sel ? ">" : " ");
+               }
+
+               flush_dcache_all();
+               menu_wait_for_input(0);
+               btns = menu_wait_for_input(1);
+               if (btns & BTN_UP) {
+                       sel--;
+                       if (sel < 0)
+                               sel = max_sel;
+               }
+               else if (btns & BTN_DOWN) {
+                       sel++;
+                       if (sel > max_sel)
+                               sel = 0;
+               }
+               else {
+                       do_cmd("cls");
+                       if (menu_items[sel]->handler(menu_items[sel]))
+                               break;
+                       do_cmd("cls");
+               }
+       }
+
+       return 0;
+}
+
+U_BOOT_CMD(
+       pmenu, 1, 1, boot_menu,
+       "show pandora's boot menu",
+       ""
+);
+
+/* helpers */
+static int do_ssource(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       ulong addr;
+
+       if (argc < 2)
+               return 1;
+
+       addr = simple_strtoul(argv[1], NULL, 16);
+
+       printf("## Executing plain script at %08lx\n", addr);
+       return parse_string_outer((char *)addr, FLAG_PARSE_SEMICOLON);
+}
+
+U_BOOT_CMD(
+       ssource, 2, 0, do_ssource,
+       "run script from memory (no header)",
+       "<addr>"
+);
+
+static int do_usbinit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       extern int drv_usbtty_init(void);
+       static int usbinit_done;
+       if (!usbinit_done) {
+               usbinit_done = 1;
+               return !drv_usbtty_init();
+       }
+       return 0;
+}
+
+U_BOOT_CMD(
+       usbinit, 1, 0, do_usbinit,
+       "initialize USB",
+       ""
+);
+
+static int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       menu_do_poweroff(NULL);
+       return 0;
+}
+
+U_BOOT_CMD(
+       poweroff, 1, 0, do_poweroff,
+       "power off this device",
+       ""
+);
index 82b71a7..d942b09 100644 (file)
@@ -120,6 +120,10 @@ int misc_init_r(void)
 
        dieid_num_r();
 
+       /* show boot menu? */
+       if (!gpio_get_value(105))
+               setenv("preboot", "pmenu");
+
        return 0;
 }
 
index f2b3996..de69291 100644 (file)
 #define CONFIG_LCD                     1
 #define LCD_BPP                                LCD_COLOR16
 #define CONFIG_SYS_WHITE_ON_BLACK      1
+
+/* used by menu code */
+#define CONFIG_PREBOOT
 #define CONFIG_SYS_CONSOLE_IS_IN_ENV   1
+#define CONFIG_SYS_DEVICE_NULLDEV      1
 
 /*
  * Hardware drivers
 #endif
 
 /* Environment information */
-#define CONFIG_BOOTDELAY               1
+#define CONFIG_BOOTDELAY               0
 
 #define CONFIG_EXTRA_ENV_SETTINGS \
        "usbtty=cdc_acm\0" \
        "mtdparts=" MTDPARTS_DEFAULT "\0" \
 
 #define CONFIG_BOOTCOMMAND \
-       "if mmc rescan && fatload mmc1 0 ${loadaddr} autoboot.scr || " \
-                       "ext2load mmc1 0 ${loadaddr} autoboot.scr; then " \
-               "source ${loadaddr}; " \
+       "if mmc rescan && fatload mmc1 0 ${loadaddr} autoboot.txt || " \
+                       "ext2load mmc1 0 ${loadaddr} autoboot.txt; then " \
+               "ssource ${loadaddr}; " \
        "fi; " \
-       "ubi part boot && ubifsmount boot && ubifsload ${loadaddr} uImage && bootm ${loadaddr}"
+       "ubi part boot && ubifsmount boot && ubifsload ${loadaddr} uImage && bootm ${loadaddr}; " \
+       "setenv stdout lcd; echo Failed to load kernel, you may need to reflash the firmware.; " \
+       "pmenu"
 
 #define CONFIG_AUTO_COMPLETE   1
 /*