MINI2440: Add a command to re-init CFI NOR
[u-boot-openmoko/mini2440.git] / common / bootmenu.c
blob0c46a1c4ea1aa1065de9f2545046c30349308c69
1 /*
2 * bootmenu.c - Boot menu
4 * Copyright (C) 2006-2007 by OpenMoko, Inc.
5 * Written by Werner Almesberger <werner@openmoko.org>
6 * All Rights Reserved
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <common.h>
26 #ifdef CFG_BOOTMENU
28 #include <malloc.h>
29 #include <devices.h>
30 #include <console.h>
31 #include <bootmenu.h>
34 extern const char version_string[];
37 #define ANSI_CLEAR "\e[2J"
38 #define ANSI_REVERSE "\e[7m"
39 #define ANSI_NORMAL "\e[m"
40 #define ANSI_GOTOYX "\e[%d;%dH"
43 * MIN_BOOT_MENU_TIMEOUT ensures that users can't by accident set the timeout
44 * unusably short.
46 #define MIN_BOOT_MENU_TIMEOUT 10 /* 10 seconds */
47 #define BOOT_MENU_TIMEOUT 60 /* 60 seconds */
48 #define AFTER_COMMAND_WAIT 3 /* wait (2,3] after running commands */
49 #define MAX_MENU_ITEMS 10 /* cut off after that many */
51 #define TOP_ROW 2
52 #define MENU_0_ROW (TOP_ROW+5)
55 struct option {
56 const char *label;
57 void (*fn)(void *user); /* run_command if NULL */
58 void *user;
62 static const struct bootmenu_setup *setup;
63 static struct option options[MAX_MENU_ITEMS];
64 static int num_options = 0;
65 static int max_width = 0;
67 static device_t *bm_con;
70 static void bm_printf(const char *fmt, ...)
72 va_list args;
73 char printbuffer[CFG_PBSIZE];
75 va_start(args, fmt);
76 vsprintf(printbuffer, fmt, args);
77 va_end(args);
79 bm_con->puts(printbuffer);
83 static char *get_option(int n)
85 char name[] = "menu_XX";
87 sprintf(name+5, "%d", n);
88 return getenv(name);
92 static void print_option(const struct option *option, int reverse)
94 int n = option-options;
96 bm_printf(ANSI_GOTOYX, MENU_0_ROW+n, 1);
97 if (reverse)
98 bm_printf(ANSI_REVERSE);
99 bm_printf(" %-*s ", max_width, option->label);
100 if (reverse)
101 bm_printf(ANSI_NORMAL);
105 static int get_var_positive_int(char *var, int default_value)
107 const char *s;
108 char *end;
109 int n;
111 s = getenv(var);
112 if (!s)
113 return default_value;
114 n = simple_strtoul(s, &end, 0);
115 if (!*s || *end || n < 1)
116 return default_value;
117 return n;
121 static void show_bootmenu(void)
123 const struct option *option;
125 bm_printf(ANSI_CLEAR ANSI_GOTOYX "%s", TOP_ROW, 1, version_string);
126 if (setup->comment)
127 bm_printf(ANSI_GOTOYX "*** BOOT MENU (%s) ***",
128 TOP_ROW+3, 1, setup->comment);
129 else
130 bm_printf(ANSI_GOTOYX "*** BOOT MENU ***", TOP_ROW+3, 1);
131 bm_printf(ANSI_GOTOYX, MENU_0_ROW, 1);
133 for (option = options; option != options+num_options; option++)
134 print_option(option, option == options);
136 bm_printf("\n\n%s to select, %s to execute.\n",
137 setup->next_key_action, setup->enter_key_name);
141 static void redirect_console(int grab)
143 static device_t *orig_stdout, *orig_stderr;
145 if (grab) {
146 orig_stdout = stdio_devices[stdout];
147 orig_stderr = stdio_devices[stderr];
148 stdio_devices[stdout] = bm_con;
149 stdio_devices[stderr] = bm_con;
151 else {
153 * Make this conditional, because the command may also change
154 * the console.
156 if (stdio_devices[stdout] == bm_con)
157 stdio_devices[stdout] = orig_stdout;
158 if (stdio_devices[stderr] == bm_con)
159 stdio_devices[stderr] = orig_stderr;
164 static void do_option(const struct option *option)
166 int seconds, next;
168 bm_printf(ANSI_CLEAR ANSI_GOTOYX, 1, 1);
169 redirect_console(1);
171 if (option->fn)
172 option->fn(option->user);
173 else
174 run_command(option->user, 0);
176 redirect_console(0);
177 seconds = get_var_positive_int("after_command_wait",
178 AFTER_COMMAND_WAIT);
179 if (seconds)
180 bm_printf("\n%s to %s.", setup->next_key_action,
181 option ? "return to boot menu" : "power off");
182 next = 1; /* require up-down transition */
183 while (seconds) {
184 int tmp;
186 tmp = setup->next_key(setup->user);
187 if (tmp && !next)
188 break;
189 next = tmp;
190 if (setup->seconds(setup->user))
191 seconds--;
193 if (!option)
194 setup->idle_action(setup->idle_action);
195 show_bootmenu();
199 static void bootmenu_hook(int activity)
201 static int next = 1, on = 1;
202 static const struct option *option = options;
203 static int seconds = 0;
204 int tmp;
206 if (activity)
207 seconds = 0;
208 tmp = setup->next_key(setup->user);
209 if (tmp && !next) {
210 print_option(option, 0);
211 option++;
212 if (option == options+num_options)
213 option = options;
214 print_option(option, 1);
215 seconds = 0;
217 next = tmp;
218 tmp = setup->enter_key(setup->user);
219 if (tmp && !on) {
220 do_option(option);
221 option = options;
222 seconds = 0;
224 on = tmp;
225 if (setup->seconds(setup->user)) {
226 int timeout;
228 timeout = get_var_positive_int("boot_menu_timeout",
229 BOOT_MENU_TIMEOUT);
230 if (timeout < MIN_BOOT_MENU_TIMEOUT)
231 timeout = MIN_BOOT_MENU_TIMEOUT;
232 if (++seconds > timeout) {
233 setup->idle_action(setup->idle_action);
234 seconds = 0;
240 static device_t *find_console(const char *name)
242 int i;
244 for (i = 1; i != ListNumItems(devlist); i++) {
245 device_t *dev = ListGetPtrToItem(devlist, i);
247 if (!strcmp(name, dev->name))
248 if (dev->flags & DEV_FLAGS_OUTPUT)
249 return dev;
251 return NULL;
255 void bootmenu_add(const char *label, void (*fn)(void *user), void *user)
257 int len;
259 options[num_options].label = label;
260 options[num_options].fn = fn;
261 options[num_options].user = user;
262 num_options++;
264 len = strlen(label);
265 if (len > max_width)
266 max_width = len;
270 void bootmenu_init(struct bootmenu_setup *__setup)
272 int n;
274 setup = __setup;
275 for (n = 1; n != MAX_MENU_ITEMS+1; n++) {
276 const char *spec, *colon;
278 spec = get_option(n);
279 if (!spec)
280 continue;
281 colon = strchr(spec, ':');
282 if (!colon)
283 bootmenu_add(spec, NULL, (char *) spec);
284 else {
285 char *label;
286 int len = colon-spec;
288 label = malloc(len+1);
289 if (!label)
290 return;
291 memcpy(label, spec, len);
292 label[len] = 0;
293 bootmenu_add(label, NULL, (char *) colon+1);
299 void bootmenu(void)
301 bm_con = find_console("vga");
302 if (bm_con && bm_con->start && bm_con->start() < 0)
303 bm_con = NULL;
304 if (!bm_con)
305 bm_con = stdio_devices[stdout];
306 if (!bm_con)
307 return;
308 #if 0
309 console_assign(stdout, "vga");
310 console_assign(stderr, "vga");
311 #endif
312 show_bootmenu();
313 console_poll_hook = bootmenu_hook;
316 #endif /* CFG_BOOTMENU */