board/neo1973/bootmenu.c: simple configurable boot menu board/neo1973/neo1973.c (neo1973_new_second): return 1 if a new second has started since the last call board/neo1973/neo1973.c (neo1973_on_key_pressed): return 1 if the $POWER key is pressed board/neo1973/neo1973.c (board_late_init): make use of neo1973_new_second and neo1973_on_key_pressed board/neo1973/neo1973.h: added function prototypes u-boot/board/neo1973/neo1973.c (board_late_init): enter the boot menu when "AUX" was pressed at least half the time u-boot/board/neo1973/neo1973.c (board_late_init): minor code cleanup u-boot/common/console.c, include/console.h: added "console_poll_hook" to be called when waiting for console in put in "getc" and "tstc" board/neo1973/neo1973.c (board_late_init): poll for the boot menu also on RAM boot, reset, or unknown cause board/neo1973/neo1973.c (board_late_init): don't look for the power key if woken up by the charger board/neo1973/neo1973.h, board/neo1973/neo1973.c, board/neo1973/bootmenu.c: renamed neo1973_911_key_pressed to neo1973_aux_key_pressed - Werner Almesberger Index: u-boot/board/neo1973/common/bootmenu.c =================================================================== --- /dev/null +++ u-boot/board/neo1973/common/bootmenu.c @@ -0,0 +1,122 @@ +/* + * bootmenu.c - Boot menu + * + * Copyright (C) 2006-2007 by OpenMoko, Inc. + * Written by Werner Almesberger + * All Rights Reserved + * + * 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 +#include +#include +#include + +#ifdef CONFIG_USBD_DFU +#include "usbdcore.h" +#include "usb_dfu.h" +#endif + +#include "neo1973.h" + + +#define DEBOUNCE_LOOPS 1000 /* wild guess */ + + +static int debounce(int (*fn)(void), int *last) +{ + int on, i; + +again: + on = fn(); + if (on != *last) + for (i = DEBOUNCE_LOOPS; i; i--) + if (on != fn()) + goto again; + *last = on; + return on; +} + + +static int aux_key(void *user) +{ + static int last_aux = -1; + + return debounce(neo1973_aux_key_pressed, &last_aux); +} + + +static int on_key(void *user) +{ + static int last_on = -1; + + return debounce(neo1973_on_key_pressed, &last_on); +} + + +static void factory_reset(void *user) +{ + default_env(); + run_command("dynpart", 0); + run_command("bootd", 0); +} + + +static int seconds(void *user) +{ + return neo1973_new_second(); +} + + +static int system_idle(void) +{ +#ifdef CONFIG_USBD_DFU + if (system_dfu_state) + return *system_dfu_state == DFU_STATE_appIDLE; +#endif + return 1; +} + + +static void poweroff_if_idle(void *user) +{ + unsigned long flags; + + local_irq_save(flags); + if (system_idle()) + neo1973_poweroff(); + local_irq_restore(flags); +} + + +static struct bootmenu_setup bootmenu_setup = { + .next_key = aux_key, + .enter_key = on_key, + .seconds = seconds, + .idle_action = poweroff_if_idle, + .next_key_action = "Press [AUX]", + .enter_key_name = "[POWER]", +}; + + +void neo1973_bootmenu(void) +{ + bootmenu_add("Boot", NULL, "bootd"); + bootmenu_init(&bootmenu_setup); + bootmenu_add("Factory reset", factory_reset, NULL); + bootmenu(); +} Index: u-boot/board/neo1973/gta01/gta01.c =================================================================== --- u-boot.orig/board/neo1973/gta01/gta01.c +++ u-boot/board/neo1973/gta01/gta01.c @@ -229,10 +229,15 @@ int board_late_init(void) extern unsigned char booted_from_nand; unsigned char tmp; char buf[32]; + int menu_vote = 0; /* <= 0: no, > 0: yes */ + int seconds = 0; /* Initialize the Power Management Unit with a safe register set */ pcf50606_init(); + /* if there's no other reason, must be regular reset */ + neo1973_wakeup_cause = NEO1973_WAKEUP_RESET; + if (!booted_from_nand) goto woken_by_reset; @@ -242,45 +247,41 @@ int board_late_init(void) setenv("pcf50606_int1", buf); if (tmp & PCF50606_INT1_ALARM) { - /* we've been woken up by RTC alarm or charger insert, boot */ + /* we've been woken up by RTC alarm, boot */ neo1973_wakeup_cause = NEO1973_WAKEUP_ALARM; goto continue_boot; } if (tmp & PCF50606_INT1_EXTONR) { + /* we've been woken up by charger insert */ neo1973_wakeup_cause = NEO1973_WAKEUP_CHARGER; } if (tmp & PCF50606_INT1_ONKEYF) { - int seconds = 0; - neo1973_wakeup_cause = NEO1973_WAKEUP_POWER_KEY; /* we've been woken up by a falling edge of the onkey */ + neo1973_wakeup_cause = NEO1973_WAKEUP_POWER_KEY; + } - /* we can't just setenv(bootdelay,-1) because that would - * accidentially become permanent if the user does saveenv */ - if (neo1973_911_key_pressed()) - nobootdelay = 1; - - while (1) { - u_int8_t int1, oocs; - - oocs = pcf50606_reg_read(PCF50606_REG_OOCS); - if (oocs & PFC50606_OOCS_ONKEY) - break; - - int1 = pcf50606_reg_read(PCF50606_REG_INT1); - if (int1 & PCF50606_INT1_SECOND) - seconds++; - - if (seconds >= POWER_KEY_SECONDS) - goto continue_boot; - } - /* Power off if minimum number of seconds not reached */ - neo1973_poweroff(); + if (neo1973_wakeup_cause == NEO1973_WAKEUP_CHARGER) { + /* if we still think it was only a charger insert, boot */ + goto continue_boot; } woken_by_reset: - /* if there's no other reason, must be regular reset */ - neo1973_wakeup_cause = NEO1973_WAKEUP_RESET; + + while (neo1973_wakeup_cause == NEO1973_WAKEUP_RESET || + neo1973_on_key_pressed()) { + if (neo1973_aux_key_pressed()) + menu_vote++; + else + menu_vote--; + + if (neo1973_new_second()) + seconds++; + if (seconds >= POWER_KEY_SECONDS) + goto continue_boot; + } + /* Power off if minimum number of seconds not reached */ + neo1973_poweroff(); continue_boot: jbt6k74_init(); @@ -304,6 +305,11 @@ continue_boot: } #endif + if (menu_vote > 0) { + neo1973_bootmenu(); + nobootdelay = 1; + } + return 0; } @@ -369,7 +375,17 @@ void neo1973_vibrator(int on) #endif } -int neo1973_911_key_pressed(void) +int neo1973_new_second(void) +{ + return pcf50606_reg_read(PCF50606_REG_INT1) & PCF50606_INT1_SECOND; +} + +int neo1973_on_key_pressed(void) +{ + return !(pcf50606_reg_read(PCF50606_REG_OOCS) & PFC50606_OOCS_ONKEY); +} + +int neo1973_aux_key_pressed(void) { S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO(); if (gpio->GPFDAT & (1 << 6)) Index: u-boot/board/neo1973/gta01/Makefile =================================================================== --- u-boot.orig/board/neo1973/gta01/Makefile +++ u-boot/board/neo1973/gta01/Makefile @@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk LIB = lib$(BOARD).a -OBJS := gta01.o pcf50606.o ../common/cmd_neo1973.o ../common/jbt6k74.o ../common/udc.o +OBJS := gta01.o pcf50606.o ../common/cmd_neo1973.o ../common/jbt6k74.o ../common/udc.o ../common/bootmenu.o SOBJS := ../common/lowlevel_init.o .PHONY: all Index: u-boot/board/neo1973/common/neo1973.h =================================================================== --- u-boot.orig/board/neo1973/common/neo1973.h +++ u-boot/board/neo1973/common/neo1973.h @@ -29,4 +29,10 @@ int neo1973_911_key_pressed(void); const char *neo1973_get_charge_status(void); int neo1973_set_charge_mode(enum neo1973_charger_cmd cmd); +int neo1973_new_second(void); +int neo1973_on_key_pressed(void); +int neo1973_aux_key_pressed(void); + +void neo1973_bootmenu(void); + #endif Index: u-boot/common/console.c =================================================================== --- u-boot.orig/common/console.c +++ u-boot/common/console.c @@ -160,8 +160,12 @@ void fprintf (int file, const char *fmt, /** U-Boot INITIAL CONSOLE-COMPATIBLE FUNCTION *****************************/ +void (*console_poll_hook)(int activity); + int getc (void) { + while (console_poll_hook && !tstc()); + if (gd->flags & GD_FLG_DEVINIT) { /* Get from the standard input */ return fgetc (stdin); @@ -171,7 +175,7 @@ int getc (void) return serial_getc (); } -int tstc (void) +static int do_tstc (void) { if (gd->flags & GD_FLG_DEVINIT) { /* Test the standard input */ @@ -182,6 +186,16 @@ int tstc (void) return serial_tstc (); } +int tstc (void) +{ + int ret; + + ret = do_tstc(); + if (console_poll_hook) + console_poll_hook(ret); + return ret; +} + void putc (const char c) { #ifdef CONFIG_SILENT_CONSOLE Index: u-boot/include/console.h =================================================================== --- u-boot.orig/include/console.h +++ u-boot/include/console.h @@ -33,6 +33,8 @@ extern device_t *stdio_devices[] ; extern char *stdio_names[MAX_FILES] ; +extern void (*console_poll_hook)(int activity); + int console_realloc(int top); #endif Index: u-boot/common/Makefile =================================================================== --- u-boot.orig/common/Makefile +++ u-boot/common/Makefile @@ -50,7 +50,8 @@ COBJS = main.o ACEX1K.o altera.o bedbug. memsize.o miiphybb.o miiphyutil.o \ s_record.o serial.o soft_i2c.o soft_spi.o spartan2.o spartan3.o \ usb.o usb_kbd.o usb_storage.o \ - virtex2.o xilinx.o crc16.o xyzModem.o cmd_mac.o cmd_mfsl.o + virtex2.o xilinx.o crc16.o xyzModem.o cmd_mac.o cmd_mfsl.o \ + bootmenu.o SRCS := $(AOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(AOBJS) $(COBJS)) Index: u-boot/common/bootmenu.c =================================================================== --- /dev/null +++ u-boot/common/bootmenu.c @@ -0,0 +1,312 @@ +/* + * bootmenu.c - Boot menu + * + * Copyright (C) 2006-2007 by OpenMoko, Inc. + * Written by Werner Almesberger + * All Rights Reserved + * + * 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 + +#ifdef CFG_BOOTMENU + +#include +#include +#include +#include + + +extern const char version_string[]; + + +#define ANSI_CLEAR "\e[2J" +#define ANSI_REVERSE "\e[7m" +#define ANSI_NORMAL "\e[m" +#define ANSI_GOTOYX "\e[%d;%dH" + +/* + * MIN_BOOT_MENU_TIMEOUT ensures that users can't by accident set the timeout + * unusably short. + */ +#define MIN_BOOT_MENU_TIMEOUT 10 /* 10 seconds */ +#define BOOT_MENU_TIMEOUT 60 /* 60 seconds */ +#define AFTER_COMMAND_WAIT 3 /* wait (2,3] after running commands */ +#define MAX_MENU_ITEMS 10 /* cut off after that many */ + +#define TOP_ROW 2 +#define MENU_0_ROW (TOP_ROW+5) + + +struct option { + const char *label; + void (*fn)(void *user); /* run_command if NULL */ + void *user; +}; + + +static const struct bootmenu_setup *setup; +static struct option options[MAX_MENU_ITEMS]; +static int num_options = 0; +static int max_width = 0; + +static device_t *bm_con; + + +static void bm_printf(const char *fmt, ...) +{ + va_list args; + char printbuffer[CFG_PBSIZE]; + + va_start(args, fmt); + vsprintf(printbuffer, fmt, args); + va_end(args); + + bm_con->puts(printbuffer); +} + + +static char *get_option(int n) +{ + char name[] = "menu_XX"; + + sprintf(name+5, "%d", n); + return getenv(name); +} + + +static void print_option(const struct option *option, int reverse) +{ + int n = option-options; + + bm_printf(ANSI_GOTOYX, MENU_0_ROW+n, 1); + if (reverse) + bm_printf(ANSI_REVERSE); + bm_printf(" %-*s ", max_width, option->label); + if (reverse) + bm_printf(ANSI_NORMAL); +} + + +static int get_var_positive_int(char *var, int default_value) +{ + const char *s; + char *end; + int n; + + s = getenv(var); + if (!s) + return default_value; + n = simple_strtoul(s, &end, 0); + if (!*s || *end || n < 1) + return default_value; + return n; +} + + +static void show_bootmenu(void) +{ + const struct option *option; + + bm_printf(ANSI_CLEAR ANSI_GOTOYX "%s", TOP_ROW, 1, version_string); + bm_printf(ANSI_GOTOYX "*** BOOT MENU ***", TOP_ROW+3, 1); + bm_printf(ANSI_GOTOYX, MENU_0_ROW, 1); + + for (option = options; option != options+num_options; option++) + print_option(option, option == options); + + bm_printf("\n\n%s to select, %s to execute.\n", + setup->next_key_action, setup->enter_key_name); +} + + +static void redirect_console(int grab) +{ + static device_t *orig_stdout, *orig_stderr; + + if (grab) { + orig_stdout = stdio_devices[stdout]; + orig_stderr = stdio_devices[stderr]; + stdio_devices[stdout] = bm_con; + stdio_devices[stderr] = bm_con; + } + else { + /* + * Make this conditional, because the command may also change + * the console. + */ + if (stdio_devices[stdout] == bm_con) + stdio_devices[stdout] = orig_stdout; + if (stdio_devices[stderr] == bm_con) + stdio_devices[stderr] = orig_stderr; + } +} + + +static void do_option(const struct option *option) +{ + int seconds, next; + + bm_printf(ANSI_CLEAR ANSI_GOTOYX, 1, 1); + redirect_console(1); + + if (option->fn) + option->fn(option->user); + else + run_command(option->user, 0); + + redirect_console(0); + seconds = get_var_positive_int("after_command_wait", + AFTER_COMMAND_WAIT); + if (seconds) + bm_printf("\n%s to %s.", setup->next_key_action, + option ? "return to boot menu" : "power off"); + next = 1; /* require up-down transition */ + while (seconds) { + int tmp; + + tmp = setup->next_key(setup->user); + if (tmp && !next) + break; + next = tmp; + if (setup->seconds(setup->user)) + seconds--; + } + if (!option) + setup->idle_action(setup->idle_action); + show_bootmenu(); +} + + +static void bootmenu_hook(int activity) +{ + static int next = 1, on = 1; + static const struct option *option = options; + static int seconds = 0; + int tmp; + + if (activity) + seconds = 0; + tmp = setup->next_key(setup->user); + if (tmp && !next) { + print_option(option, 0); + option++; + if (option == options+num_options) + option = options; + print_option(option, 1); + seconds = 0; + } + next = tmp; + tmp = setup->enter_key(setup->user); + if (tmp && !on) { + do_option(option); + option = options; + seconds = 0; + } + on = tmp; + if (setup->seconds(setup->user)) { + int timeout; + + timeout = get_var_positive_int("boot_menu_timeout", + BOOT_MENU_TIMEOUT); + if (timeout < MIN_BOOT_MENU_TIMEOUT) + timeout = MIN_BOOT_MENU_TIMEOUT; + if (++seconds > timeout) { + setup->idle_action(setup->idle_action); + seconds = 0; + } + } +} + + +static device_t *find_console(const char *name) +{ + int i; + + for (i = 1; i != ListNumItems(devlist); i++) { + device_t *dev = ListGetPtrToItem(devlist, i); + + if (!strcmp(name, dev->name)) + if (dev->flags & DEV_FLAGS_OUTPUT) + return dev; + } + return NULL; +} + + +void bootmenu_add(const char *label, void (*fn)(void *user), void *user) +{ + int len; + + options[num_options].label = label; + options[num_options].fn = fn; + options[num_options].user = user; + num_options++; + + len = strlen(label); + if (len > max_width) + max_width = len; +} + + +void bootmenu_init(struct bootmenu_setup *__setup) +{ + int n; + + setup = __setup; + for (n = 1; n != MAX_MENU_ITEMS+1; n++) { + const char *spec, *colon; + + spec = get_option(n); + if (!spec) + continue; + colon = strchr(spec, ':'); + if (!colon) + bootmenu_add(spec, NULL, (char *) spec); + else { + char *label; + int len = colon-spec; + + label = malloc(len+1); + if (!label) + return; + memcpy(label, spec, len); + label[len] = 0; + bootmenu_add(label, NULL, (char *) colon+1); + } + } +} + + +void bootmenu(void) +{ + bm_con = find_console("vga"); + if (bm_con && bm_con->start && bm_con->start() < 0) + bm_con = NULL; + if (!bm_con) + bm_con = stdio_devices[stdout]; + if (!bm_con) + return; +#if 0 + console_assign(stdout, "vga"); + console_assign(stderr, "vga"); +#endif + show_bootmenu(); + console_poll_hook = bootmenu_hook; +} + +#endif /* CFG_BOOTMENU */ Index: u-boot/include/bootmenu.h =================================================================== --- /dev/null +++ u-boot/include/bootmenu.h @@ -0,0 +1,79 @@ +/* + * bootmenu.h - Boot menu + * + * Copyright (C) 2006-2007 by OpenMoko, Inc. + * Written by Werner Almesberger + * All Rights Reserved + * + * 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. + */ + +#ifndef BOOTMENU_H +#define BOOTMENU_H + +#define MIN_BOOT_MENU_TIMEOUT 10 /* 10 seconds */ +#define BOOT_MENU_TIMEOUT 60 /* 60 seconds */ +#define AFTER_COMMAND_WAIT 3 /* wait (2,3] after running commands */ +#define MAX_MENU_ITEMS 10 /* cut off after that many */ + + +struct bootmenu_setup { + /* non-zero while the "next" key is being pressed */ + int (*next_key)(void *user); + + /* non-zero while the "enter" key is being pressed */ + int (*enter_key)(void *user); + + /* return the number of seconds that have passed since the last call + to "seconds". It's okay to limit the range to [0, 1]. */ + int (*seconds)(void *user); + + /* action to take if the boot menu times out */ + void (*idle_action)(void *user); + + /* user-specific data, passed "as is" to the functions above */ + void *user; + + /* Action to invoke the "next" function. Begins in upper case. E.g., + "Press [AUX]". */ + const char *next_key_action; + + /* Name of the "enter" key, optionally with an action (in lower case). + E.g., "[POWER]" or "tap the screen". */ + const char *enter_key_name; +}; + + +/* + * Initialize the menu from the environment. + */ + +void bootmenu_init(struct bootmenu_setup *setup); + +/* + * To add entries on top of the boot menu, call bootmenu_add before + * bootmenu_init. To add entries at the end, call it after bootmenu_init. + * If "fn" is NULL, the command specified in "user" is executed. + */ + +void bootmenu_add(const char *label, void (*fn)(void *user), void *user); + +/* + * Run the boot menu. + */ + +void bootmenu(void); + +#endif /* !BOOTMENU_H */ Index: u-boot/include/configs/neo1973_gta01.h =================================================================== --- u-boot.orig/include/configs/neo1973_gta01.h +++ u-boot/include/configs/neo1973_gta01.h @@ -155,6 +155,8 @@ /* valid baudrates */ #define CFG_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } +#define CFG_BOOTMENU + /*----------------------------------------------------------------------- * Stack sizes *