pandora: fix card selection for bootfile scan
[pandora-u-boot.git] / board / pandora / menu.c
1 #include <common.h>
2 #include <asm/io.h>
3 #include <asm/arch/gpio.h>
4 #include <hush.h>
5 #include <malloc.h>
6 #include <lcd.h>
7 #include <twl4030.h>
8
9 #include "pandora-buttons.h"
10
11 struct menu_item {
12         const char *name;
13         int (*handler)(struct menu_item *item);
14         char *cmd;
15 };
16
17 static struct menu_item *menu_items[24];
18 static int menu_item_count;
19
20 static int do_cmd(const char *fmt, ...)
21 {
22         char cmdbuff[256];
23         va_list args;
24
25         va_start(args, fmt);
26         vsprintf(cmdbuff, fmt, args);
27         va_end(args);
28
29         return !parse_string_outer(cmdbuff,
30                 FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP);
31 }
32
33 static u32 menu_wait_for_input(int down)
34 {
35         struct gpio *gpio4_base = (struct gpio *)OMAP34XX_GPIO4_BASE;
36         u32 btns;
37
38         while (1) {
39                 btns = ~readl(&gpio4_base->datain) &
40                         (BTN_UP|BTN_DOWN|BTN_G2|BTN_G3);
41                 if (!btns == !down)
42                         break;
43                 udelay(5000);
44         }
45
46         return btns;
47 }
48
49 static int menu_do_default(struct menu_item *item)
50 {
51         return 1;
52 }
53
54 static int menu_do_poweroff(struct menu_item *item)
55 {
56         u8 d;
57
58         printf("power off.\n");
59
60         twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, &d, TWL4030_PM_MASTER_P1_SW_EVENTS);
61         d |= TWL4030_PM_MASTER_SW_EVENTS_DEVOFF;
62         twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, d, TWL4030_PM_MASTER_P1_SW_EVENTS);
63
64         return 0;
65 }
66
67 static int menu_do_usb_serial(struct menu_item *item)
68 {
69         do_cmd("dcache off"); /* doesn't work with dcache */
70         do_cmd("usbinit");
71         printf("Switched to USB serial.\n");
72
73         setenv("stdout", "usbtty");
74         setenv("stdin", "usbtty");
75         setenv("stderr", "usbtty");
76         setenv("bootcmd", "");
77         return 1;
78 }
79
80 static int menu_do_serial(struct menu_item *item)
81 {
82         printf("Switched to serial.\n");
83
84         setenv("stdout", "serial");
85         setenv("bootcmd", "");
86         return 1;
87 }
88
89 #ifdef CONFIG_KEYBOARD
90 static int menu_do_console(struct menu_item *item)
91 {
92         printf("Switched to console.\n");
93         setenv("stdout", "lcd");
94         setenv("stderr", "lcd");
95         setenv("stdin",  "kbd");
96         setenv("bootcmd", "");
97         return 1;
98 }
99 #endif
100
101 static int menu_do_script_cmd(struct menu_item *item)
102 {
103         int failed = 0;
104
105         if (item->cmd == NULL || !do_cmd(item->cmd))
106                 failed = 1;
107
108         printf("script %s.\n", failed ? "failed" : "finished");
109         flush_dcache_all();
110         menu_wait_for_input(0);
111         menu_wait_for_input(1);
112         return 0;
113 }
114
115 static void add_menu_item(const char *name,
116         int (*handler)(struct menu_item *), const char *cmd)
117 {
118         struct menu_item *mitem;
119
120         mitem = malloc(sizeof(*mitem));
121         if (mitem == NULL)
122                 return;
123         mitem->name = strdup(name);
124         mitem->handler = handler;
125         mitem->cmd = strdup(cmd);
126
127         if (menu_item_count < ARRAY_SIZE(menu_items))
128                 menu_items[menu_item_count++] = mitem;
129 }
130
131 static char *bootmenu_next_ctl(char *p)
132 {
133         while (*p && *p != '|' && *p != '\r' && *p != '\n')
134                 p++;
135
136         return p;
137 }
138
139 static char *bootmenu_skip_blanks(char *p)
140 {
141         while (*p && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
142                 p++;
143
144         return p;
145 }
146
147 static char *bootmenu_skip_line(char *p)
148 {
149         while (*p && *p != '\r' && *p != '\n')
150                 p++;
151
152         return p;
153 }
154
155 static void parse_bootmenu(char *buf)
156 {
157         char *p = buf, *name, *cmd;
158         int i;
159
160         for (i = 1; ; i++)
161         {
162                 p = bootmenu_skip_blanks(p);
163                 if (*p == 0)
164                         break;
165                 if (*p == '#') {
166                         p = bootmenu_skip_line(p);
167                         continue;
168                 }
169
170                 name = p;
171                 p = bootmenu_next_ctl(p);
172                 if (*p != '|') {
173                         printf("bootmenu.txt: invalid line ~%d\n", i);
174                         p = bootmenu_skip_line(p);
175                         continue;
176                 }
177                 *p++ = 0;
178
179                 cmd = p;
180                 p = bootmenu_skip_line(p);
181                 if (*p != 0)
182                         *p++ = 0;
183
184                 add_menu_item(name, menu_do_script_cmd, cmd);
185         }
186 }
187
188 static struct menu_item default_menu_items[] = {
189         { "default boot",       menu_do_default, },
190         { "power off",          menu_do_poweroff, },
191         { "USB serial prompt",  menu_do_usb_serial, },
192         { "serial prompt",      menu_do_serial, },
193 #ifdef CONFIG_KEYBOARD
194         { "console prompt",     menu_do_console, },
195 #endif
196 };
197
198 static void do_mmc(int dev_id)
199 {
200         static const char *check_format1 = "%sload mmc %d:%d ${loadaddr} boot.scr 4";
201         static const char *check_format2 = "%sload mmc %d:%d ${loadaddr} boot.txt 4";
202         static const char *run_format1 = "%sload mmc %d:%d ${loadaddr} boot.scr;source ${loadaddr}";
203         static const char *run_format2 = "%sload mmc %d:%d ${loadaddr} boot.txt;ssource ${loadaddr} ${filesize}";
204         disk_partition_t part_info;
205         block_dev_desc_t *dev_desc;
206         char tmp_name[32], tmp_cmd[128];
207         int i;
208
209         dev_desc = get_dev("mmc", dev_id);
210         if (dev_desc == NULL) {
211                 printf("dev desc null\n");
212                 return;
213         }
214
215         for (i = 1; menu_item_count < ARRAY_SIZE(menu_items); i++) {
216                 if (get_partition_info(dev_desc, i, &part_info))
217                         break;
218                 if (do_cmd("fatls mmc %d:%d", dev_id, i)) {
219                         if (do_cmd(check_format1, "fat", dev_id, i)) {
220                                 sprintf(tmp_cmd, run_format1, "fat", dev_id, i);
221                                 goto found;
222                         }
223                         if (do_cmd(check_format2, "fat", dev_id, i)) {
224                                 sprintf(tmp_cmd, run_format2, "fat", dev_id, i);
225                                 goto found;
226                         }
227                         continue;
228                 }
229                 if (do_cmd("ext2ls mmc %d:%d", dev_id, i)) {
230                         if (do_cmd(check_format1, "ext2", dev_id, i)) {
231                                 sprintf(tmp_cmd, run_format1, "ext2", dev_id, i);
232                                 goto found;
233                         }
234                         if (do_cmd(check_format2, "ext2", dev_id, i)) {
235                                 sprintf(tmp_cmd, run_format2, "ext2", dev_id, i);
236                                 goto found;
237                         }
238                         continue;
239                 }
240                 continue;
241
242 found:
243                 sprintf(tmp_name, "boot from SD%d:%d", dev_id + 1, i);
244                 add_menu_item(tmp_name, menu_do_script_cmd, tmp_cmd);
245         }
246 }
247
248 static void menu_init(void)
249 {
250         int i;
251
252         for (i = 0; i < 2; i++)
253                 menu_items[i] = &default_menu_items[i];
254         menu_item_count = i;
255
256         if (do_cmd("mmc dev 0 && mmc rescan"))
257         {
258                 /* kill stdout while we search for bootfiles */
259                 setenv("stdout", "nulldev");
260                 do_mmc(0);
261                 setenv("stdout", "serial");
262         }
263
264         if (do_cmd("mmc dev 1 && mmc rescan"))
265         {
266                 setenv("stdout", "nulldev");
267                 do_mmc(1);
268                 setenv("stdout", "serial");
269         }
270
271         do_cmd("mmc dev 0");
272
273         if (do_cmd("ubi part boot && ubifsmount boot")) {
274                 ulong addr = getenv_ulong("loadaddr", 16, 0);
275                 if ((int)addr < (int)0x90000000) {
276                         if (do_cmd("ubifsload ${loadaddr} bootmenu.txt")) {
277                                 ulong size = getenv_ulong("filesize", 16, 0);
278                                 *(char *)(addr + size) = 0;
279                                 parse_bootmenu((char *)addr);
280                         }
281                 }
282         }
283
284         for (i = 2; i < ARRAY_SIZE(default_menu_items); i++) {
285                 if (menu_item_count >= ARRAY_SIZE(menu_items))
286                         break;
287                 menu_items[menu_item_count++] = &default_menu_items[i];
288         }
289
290         setenv("stdout", "lcd");
291 }
292
293 static int boot_menu(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
294 {
295         int i, sel = 0, max_sel;
296         int tl_row;
297         u32 btns;
298
299         menu_init();
300
301         tl_row = panel_info.vl_row / 16 / 2 - (menu_item_count + 2) / 2;
302         max_sel = menu_item_count - 1;
303
304         console_col = 3;
305         console_row = tl_row;
306         lcd_printf("Boot menu");
307
308         while (1)
309         {
310                 for (i = 0; i < menu_item_count; i++) {
311                         console_col = 3;
312                         console_row = tl_row + 2 + i;
313                         lcd_printf(menu_items[i]->name);
314                 }
315
316                 for (i = 0; i < menu_item_count; i++) {
317                         console_col = 1;
318                         console_row = tl_row + 2 + i;
319                         lcd_printf(i == sel ? ">" : " ");
320                 }
321
322                 flush_dcache_all();
323                 menu_wait_for_input(0);
324                 btns = menu_wait_for_input(1);
325                 if (btns & BTN_UP) {
326                         sel--;
327                         if (sel < 0)
328                                 sel = max_sel;
329                 }
330                 else if (btns & BTN_DOWN) {
331                         sel++;
332                         if (sel > max_sel)
333                                 sel = 0;
334                 }
335                 else {
336                         do_cmd("cls");
337                         if (menu_items[sel]->handler(menu_items[sel]))
338                                 break;
339                         do_cmd("cls");
340                 }
341         }
342
343         return 0;
344 }
345
346 U_BOOT_CMD(
347         pmenu, 1, 1, boot_menu,
348         "show pandora's boot menu",
349         ""
350 );
351
352 /* helpers */
353 static int do_ssource(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
354 {
355         ulong addr, size = 0;
356
357         if (argc < 2)
358                 return 1;
359
360         addr = simple_strtoul(argv[1], NULL, 16);
361         if (argc >= 3) {
362                 size = simple_strtoul(argv[2], NULL, 16);
363                 *(char *)(addr + size) = 0;
364         }
365
366         printf("## Executing plain script at %08lx, size %ld\n", addr, size);
367         return parse_string_outer((char *)addr, FLAG_PARSE_SEMICOLON);
368 }
369
370 U_BOOT_CMD(
371         ssource, 3, 0, do_ssource,
372         "run script from memory (no header)",
373         "<addr> [size_hex]"     /* note: without size may parse trash after the script */
374 );
375
376 static int do_usbinit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
377 {
378         extern int drv_usbtty_init(void);
379         static int usbinit_done;
380         if (!usbinit_done) {
381                 usbinit_done = 1;
382                 return !drv_usbtty_init();
383         }
384         return 0;
385 }
386
387 U_BOOT_CMD(
388         usbinit, 1, 0, do_usbinit,
389         "initialize USB",
390         ""
391 );
392
393 static int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
394 {
395         menu_do_poweroff(NULL);
396         return 0;
397 }
398
399 U_BOOT_CMD(
400         poweroff, 1, 0, do_poweroff,
401         "power off this device",
402         ""
403 );